-
Notifications
You must be signed in to change notification settings - Fork 853
/
Copy pathib_accounting_client.py
189 lines (150 loc) · 6.48 KB
/
ib_accounting_client.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
import datetime
from sysbrokers.IB.client.ib_client import (
ibClient,
STALE_SECONDS_ALLOWED_ACCOUNT_SUMMARY,
)
from syscore.constants import arg_not_supplied
from sysobjects.spot_fx_prices import currencyValue, listOfCurrencyValues
class ibAccountingClient(ibClient):
def broker_get_account_value_across_currency(
self, account_id: str = arg_not_supplied
) -> listOfCurrencyValues:
list_of_values_per_currency = self._get_named_value_across_currency(
named_value="NetLiquidation", account_id=account_id
)
return list_of_values_per_currency
def broker_get_excess_liquidity_value_across_currency(
self, account_id: str = arg_not_supplied
) -> listOfCurrencyValues:
list_of_values_per_currency = self._get_named_value_across_currency(
named_value="FullExcessLiquidity", account_id=account_id
)
return list_of_values_per_currency
def _get_named_value_across_currency(
self, named_value: str, account_id: str = arg_not_supplied
) -> listOfCurrencyValues:
list_of_currencies = self._get_list_of_currencies_for_named_values(named_value)
list_of_values_per_currency = list(
[
currencyValue(
currency,
self._get_named_value_for_currency_across_accounts(
currency, account_id=account_id, named_value=named_value
),
)
for currency in list_of_currencies
]
)
list_of_values_per_currency = listOfCurrencyValues(list_of_values_per_currency)
return list_of_values_per_currency
def _get_named_value_for_currency_across_accounts(
self,
currency: str,
named_value: str,
account_id: str = arg_not_supplied,
) -> float:
liquidiation_values_across_accounts_dict = (
self._get_named_value_across_accounts(named_value)
)
if account_id is arg_not_supplied:
list_of_account_ids = liquidiation_values_across_accounts_dict.keys()
else:
list_of_account_ids = [account_id]
values_for_currency = [
liquidiation_values_across_accounts_dict[account_id].get(currency, 0.0)
for account_id in list_of_account_ids
]
return sum(values_for_currency)
def _get_list_of_currencies_for_named_values(self, named_value: str) -> list:
liquidiation_values_across_accounts_dict = (
self._get_named_value_across_accounts(named_value)
)
currencies = [
list(account_dict.keys())
for account_dict in liquidiation_values_across_accounts_dict.values()
]
currencies = sum(currencies, []) # flatten
return list(set(currencies))
def _get_named_value_across_accounts(self, named_value: str) -> dict:
# returns a dict, accountid as keys, of dicts, currencies as keys
account_summary_dict = self._ib_get_account_summary()
accounts = account_summary_dict.keys()
liquidiation_values_across_accounts_dict = dict(
[
(
account_id,
self._get_named_account_value_for_single_account(
account_id=account_id, named_value=named_value
),
)
for account_id in accounts
]
)
return liquidiation_values_across_accounts_dict
def _get_named_account_value_for_single_account(
self, named_value: str, account_id: str
) -> dict:
# returns a dict, with currencies as keys
# eg FullExcessLiquidity, or NetLiquidation
account_summary_dict = self._ib_get_account_summary()
return account_summary_dict[account_id][named_value]
def _ib_get_account_summary(self) -> dict:
data_stale = self._is_ib_account_summary_cache_stale()
if data_stale:
self._ib_update_account_summary_cache()
account_summary_data = self.account_summary_data
return account_summary_data
@property
def account_summary_data(self) -> dict:
return self._account_summary_data
def _is_ib_account_summary_cache_stale(self) -> bool:
elapsed_seconds = self._get_elapsed_seconds_since_last_cache_update()
if elapsed_seconds > STALE_SECONDS_ALLOWED_ACCOUNT_SUMMARY:
return True
else:
return False
def _get_elapsed_seconds_since_last_cache_update(self) -> float:
account_summary_data_update = getattr(
self, "_account_summary_data_update", None
)
if account_summary_data_update is None:
# force an update
return STALE_SECONDS_ALLOWED_ACCOUNT_SUMMARY * 9999
elapsed_seconds = (
datetime.datetime.now() - account_summary_data_update
).total_seconds()
return elapsed_seconds
def _ib_update_account_summary_cache(self):
account_summary_dict = self._ib_get_account_summary_from_broker()
self._record_cache_update()
self._account_summary_data = account_summary_dict
def _record_cache_update(self):
self._account_summary_data_update = datetime.datetime.now()
def _ib_get_account_summary_from_broker(self) -> dict:
account_summary_rawdata = self.ib.accountSummary()
# Weird format let's clean it up
account_summary_dict = clean_up_account_summary(account_summary_rawdata)
return account_summary_dict
def clean_up_account_summary(account_summary_rawdata: list) -> dict:
list_of_accounts = _unique_list_from_total(account_summary_rawdata, "account")
list_of_tags = _unique_list_from_total(account_summary_rawdata, "tag")
account_summary_dict = {}
for account_id in list_of_accounts:
account_summary_dict[account_id] = {}
for tag in list_of_tags:
account_summary_dict[account_id][tag] = {}
for account_item in account_summary_rawdata:
try:
value = float(account_item.value)
except ValueError:
value = account_item.value
account_summary_dict[account_item.account][account_item.tag][
account_item.currency
] = value
return account_summary_dict
def _unique_list_from_total(account_summary_data: list, tag_name: str):
list_of_items = [
getattr(account_value, tag_name) for account_value in account_summary_data
]
list_of_items = list(set(list_of_items))
return list_of_items