]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git/blame - market.py
Add processors
[perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git] / market.py
CommitLineData
aca4d437 1from ccxt import ExchangeError, NotSupported
774c099c 2import ccxt_wrapper as ccxt
f86ee140
IB
3import time
4from store import *
aca4d437 5from cachetools.func import ttl_cache
4c51aa71 6
f86ee140
IB
7class Market:
8 debug = False
9 ccxt = None
10 report = None
11 trades = None
12 balances = None
ecba1113 13
f86ee140
IB
14 def __init__(self, ccxt_instance, debug=False):
15 self.debug = debug
16 self.ccxt = ccxt_instance
17 self.ccxt._market = self
18 self.report = ReportStore(self)
19 self.trades = TradeStore(self)
20 self.balances = BalanceStore(self)
21
22 @classmethod
23 def from_config(cls, config, debug=False):
d24bb10c
IB
24 config["apiKey"] = config.pop("key")
25
f86ee140
IB
26 ccxt_instance = ccxt.poloniexE(config)
27
28 # For requests logging
29 ccxt_instance.session.origin_request = ccxt_instance.session.request
30 ccxt_instance.session._parent = ccxt_instance
31
32 def request_wrap(self, *args, **kwargs):
33 r = self.origin_request(*args, **kwargs)
34 self._parent._market.report.log_http_request(args[0],
35 args[1], kwargs["data"], kwargs["headers"], r)
36 return r
37 ccxt_instance.session.request = request_wrap.__get__(ccxt_instance.session,
38 ccxt_instance.session.__class__)
39
40 return cls(ccxt_instance, debug=debug)
41
42 def move_balances(self):
43 needed_in_margin = {}
44 moving_to_margin = {}
45
aca4d437
IB
46 for currency, balance in self.balances.all.items():
47 needed_in_margin[currency] = balance.margin_in_position - balance.margin_pending_gain
48 for trade in self.trades.pending:
49 needed_in_margin.setdefault(trade.base_currency, 0)
f86ee140 50 if trade.trade_type == "short":
aca4d437 51 needed_in_margin[trade.base_currency] -= trade.delta
f86ee140 52 for currency, needed in needed_in_margin.items():
aca4d437 53 current_balance = self.balances.all[currency].margin_available
f86ee140
IB
54 moving_to_margin[currency] = (needed - current_balance)
55 delta = moving_to_margin[currency].value
aca4d437 56 if self.debug and delta != 0:
f86ee140
IB
57 self.report.log_debug_action("Moving {} from exchange to margin".format(moving_to_margin[currency]))
58 continue
59 if delta > 0:
60 self.ccxt.transfer_balance(currency, delta, "exchange", "margin")
61 elif delta < 0:
62 self.ccxt.transfer_balance(currency, -delta, "margin", "exchange")
63 self.report.log_move_balances(needed_in_margin, moving_to_margin)
64
65 self.balances.fetch_balances()
66
aca4d437 67 @ttl_cache(ttl=3600)
f86ee140 68 def fetch_fees(self):
aca4d437
IB
69 return self.ccxt.fetch_fees()
70
71 @ttl_cache(maxsize=20, ttl=5)
72 def get_tickers(self, refresh=False):
73 try:
74 return self.ccxt.fetch_tickers()
75 except NotSupported:
76 return None
f86ee140 77
aca4d437 78 @ttl_cache(maxsize=20, ttl=5)
f86ee140
IB
79 def get_ticker(self, c1, c2, refresh=False):
80 def invert(ticker):
81 return {
82 "inverted": True,
83 "average": (1/ticker["bid"] + 1/ticker["ask"]) / 2,
84 "original": ticker,
85 }
86 def augment_ticker(ticker):
87 ticker.update({
88 "inverted": False,
89 "average": (ticker["bid"] + ticker["ask"] ) / 2,
90 })
7192b2e1 91 return ticker
f86ee140 92
aca4d437
IB
93 tickers = self.get_tickers()
94 if tickers is None:
f86ee140 95 try:
7192b2e1 96 ticker = augment_ticker(self.ccxt.fetch_ticker("{}/{}".format(c1, c2)))
f86ee140 97 except ExchangeError:
aca4d437 98 try:
7192b2e1 99 ticker = invert(augment_ticker(self.ccxt.fetch_ticker("{}/{}".format(c2, c1))))
aca4d437
IB
100 except ExchangeError:
101 ticker = None
102 else:
103 if "{}/{}".format(c1, c2) in tickers:
7192b2e1 104 ticker = augment_ticker(tickers["{}/{}".format(c1, c2)])
aca4d437 105 elif "{}/{}".format(c2, c1) in tickers:
7192b2e1 106 ticker = invert(augment_ticker(tickers["{}/{}".format(c2, c1)]))
aca4d437
IB
107 else:
108 ticker = None
109 return ticker
f86ee140
IB
110
111 def follow_orders(self, sleep=None):
112 if sleep is None:
113 sleep = 7 if self.debug else 30
114 if self.debug:
115 self.report.log_debug_action("Set follow_orders tick to {}s".format(sleep))
116 tick = 0
117 self.report.log_stage("follow_orders_begin")
118 while len(self.trades.all_orders(state="open")) > 0:
119 time.sleep(sleep)
120 tick += 1
121 open_orders = self.trades.all_orders(state="open")
122 self.report.log_stage("follow_orders_tick_{}".format(tick))
123 self.report.log_orders(open_orders, tick=tick)
124 for order in open_orders:
125 if order.get_status() != "open":
126 self.report.log_order(order, tick, finished=True)
127 else:
128 order.trade.update_order(order, tick)
129 self.report.log_stage("follow_orders_end")
130
9db7d156
IB
131 def prepare_trades(self, base_currency="BTC", liquidity="medium",
132 compute_value="average", repartition=None, only=None):
133
7bd830a8
IB
134 self.report.log_stage("prepare_trades",
135 base_currency=base_currency, liquidity=liquidity,
9db7d156
IB
136 compute_value=compute_value, only=only,
137 repartition=repartition)
f86ee140 138
9db7d156
IB
139 values_in_base = self.balances.in_currency(base_currency,
140 compute_value=compute_value)
f86ee140 141 total_base_value = sum(values_in_base.values())
9db7d156
IB
142 new_repartition = self.balances.dispatch_assets(total_base_value,
143 liquidity=liquidity, repartition=repartition)
144 self.trades.compute_trades(values_in_base, new_repartition, only=only)
2308a1c4 145