]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git/blame - script.py
Include fees in computation
[perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git] / script.py
CommitLineData
269ec87c 1import ccxt
4c51aa71
IB
2# Put your poloniex api key in market.py
3from market import market
269ec87c 4
987bd3d3
IB
5def static_var(varname, value):
6 def decorate(func):
7 setattr(func, varname, value)
8 return func
9 return decorate
10
269ec87c 11max_digits = 18
269ec87c
IB
12
13repartition_pertenthousand = {
14 "BTC": 2857,
15 "ZEC": 3701,
16 "DOGE": 1805,
17 "DGB": 1015,
18 "SC": 623,
19 }
20
269ec87c
IB
21
22def formatted_price(value):
23 return round(value / 10**max_digits, 8)
24
987bd3d3 25@static_var("cache", {})
269ec87c 26def get_ticker(c1, c2, market):
987bd3d3
IB
27 def invert(ticker):
28 return {
48bedc24 29 "inverted": True,
987bd3d3
IB
30 "bid": float(1/ticker["ask"]),
31 "ask": float(1/ticker["bid"]),
48bedc24
IB
32 "bidA": float(1/ticker["askA"]),
33 "askA": float(1/ticker["bidA"]),
b2dab3ac
IB
34 "bidE": float(1/ticker["askE"]),
35 "askE": float(1/ticker["bidE"]),
987bd3d3 36 }
48bedc24 37 def augment_ticker(ticker):
b2dab3ac
IB
38 bid_factor = 1.01
39 ask_factor = 0.99
40 fees = fetch_fees(market)
48bedc24
IB
41 # FIXME: need to do better than just a multiplier
42 ticker.update({
43 "inverted": False,
b2dab3ac
IB
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"],
48bedc24 53 })
987bd3d3
IB
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
269ec87c 60 try:
987bd3d3 61 get_ticker.cache[(c1, c2, market.__class__)] = market.fetch_ticker("{}/{}".format(c1, c2))
48bedc24 62 augment_ticker(get_ticker.cache[(c1, c2, market.__class__)])
269ec87c
IB
63 except ccxt.ExchangeError:
64 try:
987bd3d3 65 get_ticker.cache[(c2, c1, market.__class__)] = market.fetch_ticker("{}/{}".format(c2, c1))
48bedc24 66 augment_ticker(get_ticker.cache[(c2, c1, market.__class__)])
269ec87c 67 except ccxt.ExchangeError:
987bd3d3
IB
68 get_ticker.cache[(c1, c2, market.__class__)] = None
69 return get_ticker(c1, c2, market)
269ec87c 70
73e77742
IB
71def 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
b2dab3ac
IB
79@static_var("cache", {})
80def 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
269ec87c
IB
85def 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:
b2dab3ac 89 repartition_in_base_currency[currency] = [asset_value, 0]
269ec87c
IB
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")
b2dab3ac
IB
94 repartition_in_base_currency[currency] = [
95 int(asset_ticker["bidE"] * asset_value),
96 int(asset_ticker["bidF"] * asset_value)
97 ]
98
269ec87c
IB
99 return repartition_in_base_currency
100
101def 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
b2dab3ac 108def compute_moves(current_assets, repartition_pertenthousand, market, no_fees=True, base_currency="BTC"):
269ec87c 109 value_in_base = assets_value(current_assets, market, base_currency=base_currency)
b2dab3ac 110 total_base_value = sum([ v[0] for k, v in value_in_base.items()])
269ec87c
IB
111
112 new_repartition = dispatch_assets(total_base_value, repartition_pertenthousand, market, base_currency=base_currency)
113 mouvements = {}
114
b2dab3ac
IB
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]
269ec87c 122
89b80539
IB
123 return mouvements
124
125def 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:
269ec87c 139 if value > 0:
89b80539
IB
140 rate = asset_ticker["bidA"]
141 return ["sell", rate, False]
269ec87c 142 else:
89b80539
IB
143 rate = asset_ticker["askA"]
144 return ["buy", rate, False]
145
146def 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
155def 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
168def 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)
b2dab3ac 178 base_currency_price = asset_ticker["bidE"] * value
89b80539
IB
179 print(format_string.format(
180 indent,
181 formatted_price(value),
182 currency,
183 formatted_price(base_currency_price),
184 base_currency))
185
186def print_orders(current_assets, repartition_pertenthousand, market, base_currency="BTC"):
187 mouvements = compute_moves(
188 current_assets,
189 repartition_pertenthousand,
190 market,
b2dab3ac 191 no_fees=False,
89b80539
IB
192 base_currency=base_currency)
193
b2dab3ac 194 for currency, [value, fee] in mouvements.items():
89b80539
IB
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
b2dab3ac 210 print("need to {} {} {}'s worth of {}, i.e. {} {} ( + {} {} fee)".format(
269ec87c 211 action,
89b80539
IB
212 formatted_price(abs(v1)), c1,
213 c2,
b2dab3ac
IB
214 formatted_price(abs(v2)), c2,
215 formatted_price(fee), c2))
269ec87c 216
73e77742 217current_assets = fetch_balances(market)
89b80539 218print_orders(current_assets, repartition_pertenthousand, market)