aboutsummaryrefslogtreecommitdiff
path: root/store.py
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2018-02-24 19:53:58 +0100
committerIsmaël Bouya <ismael.bouya@normalesup.org>2018-02-24 19:53:58 +0100
commit3d0247f944d7510943dfaa64eeb0e15a43b6c989 (patch)
tree2695181c77e444d52bca5de47b6c99b9b6c1ed32 /store.py
parentc31df868c655612b8387a25111e69882f0fe6344 (diff)
downloadTrader-3d0247f944d7510943dfaa64eeb0e15a43b6c989.tar.gz
Trader-3d0247f944d7510943dfaa64eeb0e15a43b6c989.tar.zst
Trader-3d0247f944d7510943dfaa64eeb0e15a43b6c989.zip
Add report store to store messages and logs
Diffstat (limited to 'store.py')
-rw-r--r--store.py224
1 files changed, 208 insertions, 16 deletions
diff --git a/store.py b/store.py
index abbe5ee..dfadef2 100644
--- a/store.py
+++ b/store.py
@@ -1,6 +1,181 @@
1import portfolio 1import portfolio
2import simplejson as json
3from decimal import Decimal as D, ROUND_DOWN
4from datetime import date, datetime
2 5
3__all__ = ["BalanceStore", "TradeStore"] 6__all__ = ["BalanceStore", "ReportStore", "TradeStore"]
7
8class ReportStore:
9 logs = []
10 verbose_print = True
11
12 @classmethod
13 def print_log(cls, message):
14 message = str(message)
15 if cls.verbose_print:
16 print(message)
17
18 @classmethod
19 def add_log(cls, hash_):
20 hash_["date"] = datetime.now()
21 cls.logs.append(hash_)
22
23 @classmethod
24 def to_json(cls):
25 def default_json_serial(obj):
26 if isinstance(obj, (datetime, date)):
27 return obj.isoformat()
28 raise TypeError ("Type %s not serializable" % type(obj))
29 return json.dumps(cls.logs, default=default_json_serial)
30
31 @classmethod
32 def set_verbose(cls, verbose_print):
33 cls.verbose_print = verbose_print
34
35 @classmethod
36 def log_stage(cls, stage):
37 cls.print_log("-" * (len(stage) + 8))
38 cls.print_log("[Stage] {}".format(stage))
39
40 cls.add_log({
41 "type": "stage",
42 "stage": stage,
43 })
44
45 @classmethod
46 def log_balances(cls, market):
47 cls.print_log("[Balance]")
48 for currency, balance in BalanceStore.all.items():
49 cls.print_log("\t{}".format(balance))
50
51 cls.add_log({
52 "type": "balance",
53 "balances": BalanceStore.as_json()
54 })
55
56 @classmethod
57 def log_tickers(cls, market, amounts, other_currency,
58 compute_value, type):
59 values = {}
60 rates = {}
61 for currency, amount in amounts.items():
62 values[currency] = amount.as_json()["value"]
63 rates[currency] = amount.rate
64 cls.add_log({
65 "type": "tickers",
66 "compute_value": compute_value,
67 "balance_type": type,
68 "currency": other_currency,
69 "balances": values,
70 "rates": rates,
71 "total": sum(amounts.values()).as_json()["value"]
72 })
73
74 @classmethod
75 def log_dispatch(cls, amount, amounts, liquidity, repartition):
76 cls.add_log({
77 "type": "dispatch",
78 "liquidity": liquidity,
79 "repartition_ratio": repartition,
80 "total_amount": amount.as_json(),
81 "repartition": { k: v.as_json()["value"] for k, v in amounts.items() }
82 })
83
84 @classmethod
85 def log_trades(cls, matching_and_trades, only, debug):
86 trades = []
87 for matching, trade in matching_and_trades:
88 trade_json = trade.as_json()
89 trade_json["skipped"] = not matching
90 trades.append(trade_json)
91
92 cls.add_log({
93 "type": "trades",
94 "only": only,
95 "debug": debug,
96 "trades": trades
97 })
98
99 @classmethod
100 def log_orders(cls, orders, tick=None, only=None, compute_value=None):
101 cls.print_log("[Orders]")
102 TradeStore.print_all_with_order(ind="\t")
103 cls.add_log({
104 "type": "orders",
105 "only": only,
106 "compute_value": compute_value,
107 "tick": tick,
108 "orders": [order.as_json() for order in orders if order is not None]
109 })
110
111 @classmethod
112 def log_order(cls, order, tick, finished=False, update=None,
113 new_order=None, compute_value=None):
114 if finished:
115 cls.print_log("[Order] Finished {}".format(order))
116 elif update == "waiting":
117 cls.print_log("[Order] {}, tick {}, waiting".format(order, tick))
118 elif update == "adjusting":
119 cls.print_log("[Order] {}, tick {}, cancelling and adjusting to {}".format(order, tick, new_order))
120 elif update == "market_fallback":
121 cls.print_log("[Order] {}, tick {}, fallbacking to market value".format(order, tick))
122 elif update == "market_adjust":
123 cls.print_log("[Order] {}, tick {}, market value, cancelling and adjusting to {}".format(order, tick, new_order))
124
125 cls.add_log({
126 "type": "order",
127 "tick": tick,
128 "update": update,
129 "order": order.as_json(),
130 "compute_value": compute_value,
131 "new_order": new_order.as_json() if new_order is not None else None
132 })
133
134 @classmethod
135 def log_move_balances(cls, needed, moving, debug):
136 cls.add_log({
137 "type": "move_balances",
138 "debug": debug,
139 "needed": { k: v.as_json()["value"] if isinstance(v, portfolio.Amount) else v for k, v in needed.items() },
140 "moving": { k: v.as_json()["value"] if isinstance(v, portfolio.Amount) else v for k, v in moving.items() },
141 })
142
143 @classmethod
144 def log_http_request(cls, method, url, body, headers, response):
145 cls.add_log({
146 "type": "http_request",
147 "method": method,
148 "url": url,
149 "body": body,
150 "headers": headers,
151 "status": response.status_code,
152 "response": response.text
153 })
154
155 @classmethod
156 def log_error(cls, action, message=None, exception=None):
157 cls.print_log("[Error] {}".format(action))
158 if exception is not None:
159 cls.print_log(str("\t{}: {}".format(exception.__class__.__name__, exception)))
160 if message is not None:
161 cls.print_log("\t{}".format(message))
162
163 cls.add_log({
164 "type": "error",
165 "action": action,
166 "exception_class": exception.__class__.__name__ if exception is not None else None,
167 "exception_message": str(exception) if exception is not None else None,
168 "message": message,
169 })
170
171 @classmethod
172 def log_debug_action(cls, action):
173 cls.print_log("[Debug] {}".format(action))
174
175 cls.add_log({
176 "type": "debug_action",
177 "action": action,
178 })
4 179
5class BalanceStore: 180class BalanceStore:
6 all = {} 181 all = {}
@@ -16,6 +191,8 @@ class BalanceStore:
16 other_currency_amount = getattr(balance, type)\ 191 other_currency_amount = getattr(balance, type)\
17 .in_currency(other_currency, market, compute_value=compute_value) 192 .in_currency(other_currency, market, compute_value=compute_value)
18 amounts[currency] = other_currency_amount 193 amounts[currency] = other_currency_amount
194 ReportStore.log_tickers(market, amounts, other_currency,
195 compute_value, type)
19 return amounts 196 return amounts
20 197
21 @classmethod 198 @classmethod
@@ -25,6 +202,7 @@ class BalanceStore:
25 if balance["exchange_total"] != 0 or balance["margin_total"] != 0 or \ 202 if balance["exchange_total"] != 0 or balance["margin_total"] != 0 or \
26 currency in cls.all: 203 currency in cls.all:
27 cls.all[currency] = portfolio.Balance(currency, balance) 204 cls.all[currency] = portfolio.Balance(currency, balance)
205 ReportStore.log_balances(market)
28 206
29 @classmethod 207 @classmethod
30 def dispatch_assets(cls, amount, liquidity="medium", repartition=None): 208 def dispatch_assets(cls, amount, liquidity="medium", repartition=None):
@@ -38,14 +216,20 @@ class BalanceStore:
38 amounts[currency] = - amounts[currency] 216 amounts[currency] = - amounts[currency]
39 if currency not in BalanceStore.all: 217 if currency not in BalanceStore.all:
40 cls.all[currency] = portfolio.Balance(currency, {}) 218 cls.all[currency] = portfolio.Balance(currency, {})
219 ReportStore.log_dispatch(amount, amounts, liquidity, repartition)
41 return amounts 220 return amounts
42 221
222 @classmethod
223 def as_json(cls):
224 return { k: v.as_json() for k, v in cls.all.items() }
225
43class TradeStore: 226class TradeStore:
44 all = [] 227 all = []
45 debug = False 228 debug = False
46 229
47 @classmethod 230 @classmethod
48 def compute_trades(cls, values_in_base, new_repartition, only=None, market=None, debug=False): 231 def compute_trades(cls, values_in_base, new_repartition, only=None, market=None, debug=False):
232 computed_trades = []
49 cls.debug = cls.debug or debug 233 cls.debug = cls.debug or debug
50 base_currency = sum(values_in_base.values()).currency 234 base_currency = sum(values_in_base.values()).currency
51 for currency in BalanceStore.currencies(): 235 for currency in BalanceStore.currencies():
@@ -55,41 +239,49 @@ class TradeStore:
55 value_to = new_repartition.get(currency, portfolio.Amount(base_currency, 0)) 239 value_to = new_repartition.get(currency, portfolio.Amount(base_currency, 0))
56 240
57 if value_from.value * value_to.value < 0: 241 if value_from.value * value_to.value < 0:
58 cls.add_trade_if_matching( 242 computed_trades.append(cls.trade_if_matching(
59 value_from, portfolio.Amount(base_currency, 0), 243 value_from, portfolio.Amount(base_currency, 0),
60 currency, only=only, market=market) 244 currency, only=only, market=market))
61 cls.add_trade_if_matching( 245 computed_trades.append(cls.trade_if_matching(
62 portfolio.Amount(base_currency, 0), value_to, 246 portfolio.Amount(base_currency, 0), value_to,
63 currency, only=only, market=market) 247 currency, only=only, market=market))
64 else: 248 else:
65 cls.add_trade_if_matching(value_from, value_to, 249 computed_trades.append(cls.trade_if_matching(
66 currency, only=only, market=market) 250 value_from, value_to,
251 currency, only=only, market=market))
252 for matching, trade in computed_trades:
253 if matching:
254 cls.all.append(trade)
255 ReportStore.log_trades(computed_trades, only, cls.debug)
67 256
68 @classmethod 257 @classmethod
69 def add_trade_if_matching(cls, value_from, value_to, currency, 258 def trade_if_matching(cls, value_from, value_to, currency,
70 only=None, market=None): 259 only=None, market=None):
71 trade = portfolio.Trade(value_from, value_to, currency, 260 trade = portfolio.Trade(value_from, value_to, currency,
72 market=market) 261 market=market)
73 if only is None or trade.action == only: 262 matching = only is None or trade.action == only
74 cls.all.append(trade) 263 return [matching, trade]
75 return True
76 return False
77 264
78 @classmethod 265 @classmethod
79 def prepare_orders(cls, only=None, compute_value="default"): 266 def prepare_orders(cls, only=None, compute_value="default"):
267 orders = []
80 for trade in cls.all: 268 for trade in cls.all:
81 if only is None or trade.action == only: 269 if only is None or trade.action == only:
82 trade.prepare_order(compute_value=compute_value) 270 orders.append(trade.prepare_order(compute_value=compute_value))
271 ReportStore.log_orders(orders, only, compute_value)
83 272
84 @classmethod 273 @classmethod
85 def print_all_with_order(cls): 274 def print_all_with_order(cls, ind=""):
86 for trade in cls.all: 275 for trade in cls.all:
87 trade.print_with_order() 276 trade.print_with_order(ind=ind)
88 277
89 @classmethod 278 @classmethod
90 def run_orders(cls): 279 def run_orders(cls):
91 for order in cls.all_orders(state="pending"): 280 orders = cls.all_orders(state="pending")
281 for order in orders:
92 order.run() 282 order.run()
283 ReportStore.log_stage("run_orders")
284 ReportStore.log_orders(orders)
93 285
94 @classmethod 286 @classmethod
95 def all_orders(cls, state=None): 287 def all_orders(cls, state=None):