]>
Commit | Line | Data |
---|---|---|
1 | import ccxt | |
2 | # Put your poloniex api key in market.py | |
3 | from market import market | |
4 | ||
5 | def static_var(varname, value): | |
6 | def decorate(func): | |
7 | setattr(func, varname, value) | |
8 | return func | |
9 | return decorate | |
10 | ||
11 | max_digits = 18 | |
12 | ||
13 | repartition_pertenthousand = { | |
14 | "BTC": 2857, | |
15 | "ZEC": 3701, | |
16 | "DOGE": 1805, | |
17 | "DGB": 1015, | |
18 | "SC": 623, | |
19 | } | |
20 | ||
21 | ||
22 | def formatted_price(value): | |
23 | return round(value / 10**max_digits, 8) | |
24 | ||
25 | @static_var("cache", {}) | |
26 | def get_ticker(c1, c2, market): | |
27 | def invert(ticker): | |
28 | return { | |
29 | "inverted": True, | |
30 | "bid": float(1/ticker["ask"]), | |
31 | "ask": float(1/ticker["bid"]), | |
32 | "bidA": float(1/ticker["askA"]), | |
33 | "askA": float(1/ticker["bidA"]), | |
34 | "bidE": float(1/ticker["askE"]), | |
35 | "askE": float(1/ticker["bidE"]), | |
36 | } | |
37 | def augment_ticker(ticker): | |
38 | bid_factor = 1.01 | |
39 | ask_factor = 0.99 | |
40 | fees = fetch_fees(market) | |
41 | # FIXME: need to do better than just a multiplier | |
42 | ticker.update({ | |
43 | "inverted": False, | |
44 | # Adjusted | |
45 | "bidA": ticker["bid"] * bid_factor, | |
46 | "askA": ticker["ask"] * ask_factor, | |
47 | # Expected in the end | |
48 | "bidE": ticker["bid"] * bid_factor * (1 - fees["maker"]), | |
49 | "askE": ticker["ask"] * ask_factor * (1 - fees["maker"]), | |
50 | # fees | |
51 | "bidF": ticker["bid"] * bid_factor * fees["maker"], | |
52 | "askF": ticker["ask"] * ask_factor * fees["maker"], | |
53 | }) | |
54 | ||
55 | if (c1, c2, market.__class__) in get_ticker.cache: | |
56 | return get_ticker.cache[(c1, c2, market.__class__)] | |
57 | if (c2, c1, market.__class__) in get_ticker.cache: | |
58 | return invert(get_ticker.cache[(c2, c1, market.__class__)]) | |
59 | ||
60 | try: | |
61 | get_ticker.cache[(c1, c2, market.__class__)] = market.fetch_ticker("{}/{}".format(c1, c2)) | |
62 | augment_ticker(get_ticker.cache[(c1, c2, market.__class__)]) | |
63 | except ccxt.ExchangeError: | |
64 | try: | |
65 | get_ticker.cache[(c2, c1, market.__class__)] = market.fetch_ticker("{}/{}".format(c2, c1)) | |
66 | augment_ticker(get_ticker.cache[(c2, c1, market.__class__)]) | |
67 | except ccxt.ExchangeError: | |
68 | get_ticker.cache[(c1, c2, market.__class__)] = None | |
69 | return get_ticker(c1, c2, market) | |
70 | ||
71 | def fetch_balances(market): | |
72 | balances = {} | |
73 | fetched_balance = market.fetch_balance() | |
74 | for key, value in fetched_balance["total"].items(): | |
75 | if value > 0: | |
76 | balances[key] = int(value * 10**max_digits) | |
77 | return balances | |
78 | ||
79 | @static_var("cache", {}) | |
80 | def fetch_fees(market): | |
81 | if market.__class__ not in fetch_fees.cache: | |
82 | fetch_fees.cache[market.__class__] = market.fetch_fees() | |
83 | return fetch_fees.cache[market.__class__] | |
84 | ||
85 | def assets_value(assets, market, base_currency="BTC"): | |
86 | repartition_in_base_currency = {} | |
87 | for currency, asset_value in assets.items(): | |
88 | if currency == base_currency: | |
89 | repartition_in_base_currency[currency] = [asset_value, 0] | |
90 | else: | |
91 | asset_ticker = get_ticker(currency, base_currency, market) | |
92 | if asset_ticker is None: | |
93 | raise Exception("This asset is not available in the chosen market") | |
94 | repartition_in_base_currency[currency] = [ | |
95 | int(asset_ticker["bidE"] * asset_value), | |
96 | int(asset_ticker["bidF"] * asset_value) | |
97 | ] | |
98 | ||
99 | return repartition_in_base_currency | |
100 | ||
101 | def dispatch_assets(base_currency_value, repartition_pertenthousand, market, base_currency="BTC"): | |
102 | sum_pertenthousand = sum([v for k, v in repartition_pertenthousand.items()]) | |
103 | repartition_in_base_currency = {} | |
104 | for currency, ptt in repartition_pertenthousand.items(): | |
105 | repartition_in_base_currency[currency] = int(ptt * base_currency_value / sum_pertenthousand) | |
106 | return repartition_in_base_currency | |
107 | ||
108 | def compute_moves(current_assets, repartition_pertenthousand, market, no_fees=True, base_currency="BTC"): | |
109 | value_in_base = assets_value(current_assets, market, base_currency=base_currency) | |
110 | total_base_value = sum([ v[0] for k, v in value_in_base.items()]) | |
111 | ||
112 | new_repartition = dispatch_assets(total_base_value, repartition_pertenthousand, market, base_currency=base_currency) | |
113 | mouvements = {} | |
114 | ||
115 | if no_fees: | |
116 | for key in set(value_in_base.keys()).union(set(new_repartition.keys())): | |
117 | mouvements[key] = value_in_base.get(key, [0, 0])[0] - new_repartition.get(key, 0) | |
118 | else: | |
119 | for key in set(value_in_base.keys()).union(set(new_repartition.keys())): | |
120 | value, fee = value_in_base.get(key, [0, 0]) | |
121 | mouvements[key] = [value - new_repartition.get(key, 0), fee] | |
122 | ||
123 | return mouvements | |
124 | ||
125 | def compute_order(currency, value, market, base_currency="BTC"): | |
126 | if currency == base_currency or value == 0: | |
127 | return [None, 0, False] | |
128 | ||
129 | asset_ticker = get_ticker(currency, base_currency, market) | |
130 | if asset_ticker["inverted"]: | |
131 | asset_ticker = get_ticker(base_currency, currency, market) | |
132 | if value > 0: | |
133 | rate = asset_ticker["askA"] | |
134 | return ["buy", rate, True] | |
135 | else: | |
136 | rate = asset_ticker["bidA"] | |
137 | return ["sell", rate, True] | |
138 | else: | |
139 | if value > 0: | |
140 | rate = asset_ticker["bidA"] | |
141 | return ["sell", rate, False] | |
142 | else: | |
143 | rate = asset_ticker["askA"] | |
144 | return ["buy", rate, False] | |
145 | ||
146 | def make_order(currency, value, market, base_currency="BTC"): | |
147 | action, rate, inverted = compute_order(currency, value, market, base_currency=base_currency) | |
148 | amount = formatted_price(abs(value)) | |
149 | if not inverted: | |
150 | symbol = "{}/{}".format(currency, base_currency) | |
151 | else: | |
152 | symbol = "{}/{}".format(base_currency, currency) | |
153 | return market.create_order(symbol, 'limit', action, amount, price=rate) | |
154 | ||
155 | def make_orders(current_assets, repartition_pertenthousand, market, base_currency="BTC"): | |
156 | mouvements = compute_moves( | |
157 | current_assets, | |
158 | repartition_pertenthousand, | |
159 | market, | |
160 | base_currency=base_currency) | |
161 | ||
162 | results = [] | |
163 | for currency, value in sorted(mouvements.items(), key=lambda x: x[1]): | |
164 | # FIXME: wait for sales to finish | |
165 | results.append(make_order(currency, value, market, base_currency=base_currency)) | |
166 | return results | |
167 | ||
168 | def print_assets(assets, indent="", market=None, base_currency="BTC"): | |
169 | if market is not None: | |
170 | format_string = "{}{} {} ({} {})" | |
171 | else: | |
172 | format_string = "{}{} {}" | |
173 | base_currency_price = 0 | |
174 | ||
175 | for currency, value in assets.items(): | |
176 | if market is not None: | |
177 | asset_ticker = get_ticker(currency, base_currency, market) | |
178 | base_currency_price = asset_ticker["bidE"] * value | |
179 | print(format_string.format( | |
180 | indent, | |
181 | formatted_price(value), | |
182 | currency, | |
183 | formatted_price(base_currency_price), | |
184 | base_currency)) | |
185 | ||
186 | def print_orders(current_assets, repartition_pertenthousand, market, base_currency="BTC"): | |
187 | mouvements = compute_moves( | |
188 | current_assets, | |
189 | repartition_pertenthousand, | |
190 | market, | |
191 | no_fees=False, | |
192 | base_currency=base_currency) | |
193 | ||
194 | for currency, [value, fee] in mouvements.items(): | |
195 | action, rate, inverted = compute_order( | |
196 | currency, | |
197 | value, | |
198 | market, | |
199 | base_currency=base_currency) | |
200 | if action is not None: | |
201 | currency_price = int(value / rate) | |
202 | ||
203 | if not inverted: | |
204 | c1, c2 = [base_currency, currency] | |
205 | v1, v2 = [value, currency_price] | |
206 | else: | |
207 | c1, c2 = [currency, base_currency] | |
208 | v1, v2 = [currency_price, value] | |
209 | ||
210 | print("need to {} {} {}'s worth of {}, i.e. {} {} ( + {} {} fee)".format( | |
211 | action, | |
212 | formatted_price(abs(v1)), c1, | |
213 | c2, | |
214 | formatted_price(abs(v2)), c2, | |
215 | formatted_price(fee), c2)) | |
216 | ||
217 | current_assets = fetch_balances(market) | |
218 | print_orders(current_assets, repartition_pertenthousand, market) |