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