2 import simplejson
as json
3 from decimal
import Decimal
as D
, ROUND_DOWN
4 from datetime
import date
, datetime
6 __all__
= ["BalanceStore", "ReportStore", "TradeStore"]
13 def print_log(cls
, message
):
14 message
= str(message
)
19 def add_log(cls
, hash_
):
20 hash_
["date"] = datetime
.now()
21 cls
.logs
.append(hash_
)
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
)
32 def set_verbose(cls
, verbose_print
):
33 cls
.verbose_print
= verbose_print
36 def log_stage(cls
, stage
):
37 cls
.print_log("-" * (len(stage
) + 8))
38 cls
.print_log("[Stage] {}".format(stage
))
46 def log_balances(cls
, market
, tag
=None):
47 cls
.print_log("[Balance]")
48 for currency
, balance
in BalanceStore
.all
.items():
49 cls
.print_log("\t{}".format(balance
))
54 "balances": BalanceStore
.as_json()
58 def log_tickers(cls
, market
, amounts
, other_currency
,
62 for currency
, amount
in amounts
.items():
63 values
[currency
] = amount
.as_json()["value"]
64 rates
[currency
] = amount
.rate
67 "compute_value": compute_value
,
69 "currency": other_currency
,
72 "total": sum(amounts
.values()).as_json()["value"]
76 def log_dispatch(cls
, amount
, amounts
, liquidity
, repartition
):
79 "liquidity": liquidity
,
80 "repartition_ratio": repartition
,
81 "total_amount": amount
.as_json(),
82 "repartition": { k: v.as_json()["value"] for k, v in amounts.items() }
86 def log_trades(cls
, matching_and_trades
, only
, debug
):
88 for matching
, trade
in matching_and_trades
:
89 trade_json
= trade
.as_json()
90 trade_json
["skipped"] = not matching
91 trades
.append(trade_json
)
101 def log_orders(cls
, orders
, tick
=None, only
=None, compute_value
=None):
102 cls
.print_log("[Orders]")
103 TradeStore
.print_all_with_order(ind
="\t")
107 "compute_value": compute_value
,
109 "orders": [order
.as_json() for order
in orders
if order
is not None]
113 def log_order(cls
, order
, tick
, finished
=False, update
=None,
114 new_order
=None, compute_value
=None):
116 cls
.print_log("[Order] Finished {}".format(order
))
117 elif update
== "waiting":
118 cls
.print_log("[Order] {}, tick {}, waiting".format(order
, tick
))
119 elif update
== "adjusting":
120 cls
.print_log("[Order] {}, tick {}, cancelling and adjusting to {}".format(order
, tick
, new_order
))
121 elif update
== "market_fallback":
122 cls
.print_log("[Order] {}, tick {}, fallbacking to market value".format(order
, tick
))
123 elif update
== "market_adjust":
124 cls
.print_log("[Order] {}, tick {}, market value, cancelling and adjusting to {}".format(order
, tick
, new_order
))
130 "order": order
.as_json(),
131 "compute_value": compute_value
,
132 "new_order": new_order
.as_json() if new_order
is not None else None
136 def log_move_balances(cls
, needed
, moving
, debug
):
138 "type": "move_balances",
140 "needed": { k: v.as_json()["value"] if isinstance(v, portfolio.Amount) else v for k, v in needed.items() }
,
141 "moving": { k: v.as_json()["value"] if isinstance(v, portfolio.Amount) else v for k, v in moving.items() }
,
145 def log_http_request(cls
, method
, url
, body
, headers
, response
):
147 "type": "http_request",
152 "status": response
.status_code
,
153 "response": response
.text
157 def log_error(cls
, action
, message
=None, exception
=None):
158 cls
.print_log("[Error] {}".format(action
))
159 if exception
is not None:
160 cls
.print_log(str("\t{}: {}".format(exception
.__class
__.__name
__, exception
)))
161 if message
is not None:
162 cls
.print_log("\t{}".format(message
))
167 "exception_class": exception
.__class
__.__name
__ if exception
is not None else None,
168 "exception_message": str(exception
) if exception
is not None else None,
173 def log_debug_action(cls
, action
):
174 cls
.print_log("[Debug] {}".format(action
))
177 "type": "debug_action",
186 return cls
.all
.keys()
189 def in_currency(cls
, other_currency
, market
, compute_value
="average", type="total"):
191 for currency
, balance
in cls
.all
.items():
192 other_currency_amount
= getattr(balance
, type)\
193 .in_currency(other_currency
, market
, compute_value
=compute_value
)
194 amounts
[currency
] = other_currency_amount
195 ReportStore
.log_tickers(market
, amounts
, other_currency
,
200 def fetch_balances(cls
, market
, tag
=None):
201 all_balances
= market
.fetch_all_balances()
202 for currency
, balance
in all_balances
.items():
203 if balance
["exchange_total"] != 0 or balance
["margin_total"] != 0 or \
205 cls
.all
[currency
] = portfolio
.Balance(currency
, balance
)
206 ReportStore
.log_balances(market
, tag
=tag
)
209 def dispatch_assets(cls
, amount
, liquidity
="medium", repartition
=None):
210 if repartition
is None:
211 repartition
= portfolio
.Portfolio
.repartition(liquidity
=liquidity
)
212 sum_ratio
= sum([v
[0] for k
, v
in repartition
.items()])
214 for currency
, (ptt
, trade_type
) in repartition
.items():
215 amounts
[currency
] = ptt
* amount
/ sum_ratio
216 if trade_type
== "short":
217 amounts
[currency
] = - amounts
[currency
]
218 if currency
not in BalanceStore
.all
:
219 cls
.all
[currency
] = portfolio
.Balance(currency
, {})
220 ReportStore
.log_dispatch(amount
, amounts
, liquidity
, repartition
)
225 return { k: v.as_json() for k, v in cls.all.items() }
232 def compute_trades(cls
, values_in_base
, new_repartition
, only
=None, market
=None, debug
=False):
234 cls
.debug
= cls
.debug
or debug
235 base_currency
= sum(values_in_base
.values()).currency
236 for currency
in BalanceStore
.currencies():
237 if currency
== base_currency
:
239 value_from
= values_in_base
.get(currency
, portfolio
.Amount(base_currency
, 0))
240 value_to
= new_repartition
.get(currency
, portfolio
.Amount(base_currency
, 0))
242 if value_from
.value
* value_to
.value
< 0:
243 computed_trades
.append(cls
.trade_if_matching(
244 value_from
, portfolio
.Amount(base_currency
, 0),
245 currency
, only
=only
, market
=market
))
246 computed_trades
.append(cls
.trade_if_matching(
247 portfolio
.Amount(base_currency
, 0), value_to
,
248 currency
, only
=only
, market
=market
))
250 computed_trades
.append(cls
.trade_if_matching(
251 value_from
, value_to
,
252 currency
, only
=only
, market
=market
))
253 for matching
, trade
in computed_trades
:
255 cls
.all
.append(trade
)
256 ReportStore
.log_trades(computed_trades
, only
, cls
.debug
)
259 def trade_if_matching(cls
, value_from
, value_to
, currency
,
260 only
=None, market
=None):
261 trade
= portfolio
.Trade(value_from
, value_to
, currency
,
263 matching
= only
is None or trade
.action
== only
264 return [matching
, trade
]
267 def prepare_orders(cls
, only
=None, compute_value
="default"):
269 for trade
in cls
.all
:
270 if only
is None or trade
.action
== only
:
271 orders
.append(trade
.prepare_order(compute_value
=compute_value
))
272 ReportStore
.log_orders(orders
, only
, compute_value
)
275 def print_all_with_order(cls
, ind
=""):
276 for trade
in cls
.all
:
277 trade
.print_with_order(ind
=ind
)
281 orders
= cls
.all_orders(state
="pending")
284 ReportStore
.log_stage("run_orders")
285 ReportStore
.log_orders(orders
)
288 def all_orders(cls
, state
=None):
289 all_orders
= sum(map(lambda v
: v
.orders
, cls
.all
), [])
293 return list(filter(lambda o
: o
.status
== state
, all_orders
))
296 def update_all_orders_status(cls
):
297 for order
in cls
.all_orders(state
="open"):