Implement fetching account balances
This commit is contained in:
@@ -87,3 +87,16 @@ class Sinks(util.Base):
|
||||
# {"EUR": {"IBAN": "xxx", "BIC": "xxx"},
|
||||
# "GBP": {"SORT": "04-04-04", "ACCOUNT": "1922-2993"}}
|
||||
# self.markets.distribute_account_details(currencies, account_infos)
|
||||
|
||||
def get_total_usd(self):
|
||||
"""
|
||||
Get the total balance of our accounts in USD.
|
||||
"""
|
||||
total_nordigen = self.nordigen.get_total_map()
|
||||
total_truelayer = self.truelayer.get_total_map()
|
||||
|
||||
# Yes, we can save an API call by merging but I think this is clearer
|
||||
total_nordigen_usd = self.money.multiple_to_usd(total_nordigen)
|
||||
total_truelayer_usd = self.money.multiple_to_usd(total_truelayer)
|
||||
|
||||
return total_truelayer_usd + total_nordigen_usd
|
||||
|
||||
@@ -5,7 +5,7 @@ from twisted.internet.task import LoopingCall
|
||||
import requests
|
||||
from simplejson.errors import JSONDecodeError
|
||||
from json import dumps, loads
|
||||
from lib.serde.nordigen import TXRoot, AccessToken, Institutions, Agreement, Requisitions, AccountDetails
|
||||
from lib.serde.nordigen import TXRoot, AccessToken, Institutions, Agreement, Requisitions, AccountDetails, AccountBalancesRoot
|
||||
from serde import ValidationError
|
||||
|
||||
# Project imports
|
||||
@@ -242,9 +242,6 @@ class Nordigen(util.Base):
|
||||
continue
|
||||
accounts = self.get_accounts(req["id"])
|
||||
for account_id in accounts:
|
||||
# if not account_id in self.banks:
|
||||
# print("account_id", account_id, "not in self.banks!")
|
||||
# continue
|
||||
account_info = self.get_account(account_id)
|
||||
if not account_info:
|
||||
continue
|
||||
@@ -281,7 +278,55 @@ class Nordigen(util.Base):
|
||||
headers = {"accept": "application/json", "Authorization": f"Bearer {self.token}"}
|
||||
path = f"{settings.Nordigen.Base}/accounts/{account_id}/transactions/"
|
||||
r = requests.get(path, headers=headers)
|
||||
obj = TXRoot.from_json(r.content)
|
||||
try:
|
||||
obj = TXRoot.from_json(r.content)
|
||||
except ValidationError as err:
|
||||
self.log.error(f"Validation error: {err}")
|
||||
return
|
||||
parsed = obj.to_dict()["transactions"]["booked"]
|
||||
self.normalise_transactions(parsed)
|
||||
return parsed
|
||||
|
||||
def get_balance(self, account_id):
|
||||
"""
|
||||
Get the balance and currency of an account.
|
||||
:param account_id: the account ID
|
||||
:return: tuple of (currency, amount)
|
||||
:rtype: tuple
|
||||
"""
|
||||
headers = {"accept": "application/json", "Authorization": f"Bearer {self.token}"}
|
||||
path = f"{settings.Nordigen.Base}/accounts/{account_id}/balances/"
|
||||
r = requests.get(path, headers=headers)
|
||||
try:
|
||||
obj = AccountBalancesRoot.from_json(r.content)
|
||||
except ValidationError as err:
|
||||
self.log.error(f"Validation error: {err}")
|
||||
return
|
||||
parsed = obj.to_dict()["balances"]
|
||||
total = 0
|
||||
currency = None
|
||||
for entry in parsed:
|
||||
if currency:
|
||||
if not currency == entry["balanceAmount"]["currency"]:
|
||||
self.log.error("Different currencies in balance query.")
|
||||
return
|
||||
total += float(entry["balanceAmount"]["amount"])
|
||||
currency = entry["balanceAmount"]["currency"]
|
||||
return (currency, total)
|
||||
|
||||
def get_total_map(self):
|
||||
"""
|
||||
Return a dictionary keyed by currencies with the amounts as values.
|
||||
:return: dict keyed by currency, values are amounts
|
||||
:rtype: dict
|
||||
"""
|
||||
totals = {}
|
||||
for account_id in self.banks:
|
||||
currency, amount = self.get_balance(account_id)
|
||||
if not amount:
|
||||
continue
|
||||
if currency in totals:
|
||||
totals[currency] += amount
|
||||
else:
|
||||
totals[currency] = amount
|
||||
return totals
|
||||
|
||||
@@ -6,7 +6,9 @@ import requests
|
||||
from simplejson.errors import JSONDecodeError
|
||||
from time import time
|
||||
from json import dumps, loads
|
||||
from lib.serde.truelayer import AccountBalancesRoot
|
||||
import urllib
|
||||
from serde import ValidationError
|
||||
|
||||
# Project imports
|
||||
from settings import settings
|
||||
@@ -274,8 +276,55 @@ class TrueLayer(util.Base):
|
||||
parsed = r.json()
|
||||
except JSONDecodeError:
|
||||
self.log.error(f"Error parsing transactions response: {r.content}")
|
||||
return False
|
||||
return (False, False)
|
||||
if "results" in parsed:
|
||||
return parsed["results"]
|
||||
else:
|
||||
return False
|
||||
return (False, False)
|
||||
|
||||
def get_balance(self, bank, account_id):
|
||||
"""
|
||||
Get the balance of an account.
|
||||
:param bank: the bank to check
|
||||
:param account_id: the account ID
|
||||
:return: tuple of (currency, amount)
|
||||
:rtype: tuple
|
||||
"""
|
||||
token = self.get_key(bank)
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
path = f"{settings.TrueLayer.DataBase}/accounts/{account_id}/balance"
|
||||
r = requests.get(path, headers=headers)
|
||||
try:
|
||||
obj = AccountBalancesRoot.from_json(r.content)
|
||||
except ValidationError as err:
|
||||
self.log.error(f"Validation error: {err}")
|
||||
return
|
||||
parsed = obj.to_dict()["results"]
|
||||
total = 0
|
||||
currency = None
|
||||
for entry in parsed:
|
||||
if currency:
|
||||
if not currency == entry["currency"]:
|
||||
self.log.error("Different currencies in balance query.")
|
||||
return
|
||||
total += entry["available"]
|
||||
currency = entry["currency"]
|
||||
return (currency, total)
|
||||
|
||||
def get_total_map(self):
|
||||
"""
|
||||
Return a dictionary keyed by currencies with the amounts as values.
|
||||
:return: dict keyed by currency, values are amounts
|
||||
:rtype: dict
|
||||
"""
|
||||
totals = {}
|
||||
for bank in self.banks:
|
||||
for account_id in self.banks[bank]:
|
||||
currency, amount = self.get_balance(bank, account_id)
|
||||
if not amount:
|
||||
continue
|
||||
if currency in totals:
|
||||
totals[currency] += amount
|
||||
else:
|
||||
totals[currency] = amount
|
||||
return totals
|
||||
|
||||
Reference in New Issue
Block a user