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