X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=script.py;h=187ff73024de4ecf14cf96c0e1905b22bbb05e68;hb=b2dab3acfb8cb23bdb54298c88aecfb7042fb8d5;hp=473253309441b5da16c5983096f941a05ef20c03;hpb=269ec87c7f2a9b9b8fd6a707881c5477680abf31;p=perso%2FImmae%2FProjets%2FCryptomonnaies%2FCryptoportfolio%2FTrader.git diff --git a/script.py b/script.py index 4732533..187ff73 100644 --- a/script.py +++ b/script.py @@ -1,12 +1,14 @@ import ccxt +# Put your poloniex api key in market.py +from market import market + +def static_var(varname, value): + def decorate(func): + setattr(func, varname, value) + return func + return decorate max_digits = 18 -current_assets = { - "ETH": int(2.00000000 * 10**max_digits), - "BTC": int(1.23000000 * 10**max_digits), - "ZEC": int(2.00000000 * 10**max_digits), - "SC" : int(2.000000000 * 10**max_digits), - } repartition_pertenthousand = { "BTC": 2857, @@ -16,34 +18,84 @@ repartition_pertenthousand = { "SC": 623, } -market = ccxt.poloniex() def formatted_price(value): return round(value / 10**max_digits, 8) +@static_var("cache", {}) def get_ticker(c1, c2, market): + def invert(ticker): + return { + "inverted": True, + "bid": float(1/ticker["ask"]), + "ask": float(1/ticker["bid"]), + "bidA": float(1/ticker["askA"]), + "askA": float(1/ticker["bidA"]), + "bidE": float(1/ticker["askE"]), + "askE": float(1/ticker["bidE"]), + } + def augment_ticker(ticker): + bid_factor = 1.01 + ask_factor = 0.99 + fees = fetch_fees(market) + # FIXME: need to do better than just a multiplier + ticker.update({ + "inverted": False, + # Adjusted + "bidA": ticker["bid"] * bid_factor, + "askA": ticker["ask"] * ask_factor, + # Expected in the end + "bidE": ticker["bid"] * bid_factor * (1 - fees["maker"]), + "askE": ticker["ask"] * ask_factor * (1 - fees["maker"]), + # fees + "bidF": ticker["bid"] * bid_factor * fees["maker"], + "askF": ticker["ask"] * ask_factor * fees["maker"], + }) + + if (c1, c2, market.__class__) in get_ticker.cache: + return get_ticker.cache[(c1, c2, market.__class__)] + if (c2, c1, market.__class__) in get_ticker.cache: + return invert(get_ticker.cache[(c2, c1, market.__class__)]) + try: - return market.fetch_ticker("{}/{}".format(c1, c2)) + get_ticker.cache[(c1, c2, market.__class__)] = market.fetch_ticker("{}/{}".format(c1, c2)) + augment_ticker(get_ticker.cache[(c1, c2, market.__class__)]) except ccxt.ExchangeError: try: - ticker = market.fetch_ticker("{}/{}".format(c2, c1)) - return { - "bid": float(1/ticker["ask"]), - "ask": float(1/ticker["bid"]), - } + get_ticker.cache[(c2, c1, market.__class__)] = market.fetch_ticker("{}/{}".format(c2, c1)) + augment_ticker(get_ticker.cache[(c2, c1, market.__class__)]) except ccxt.ExchangeError: - return None + get_ticker.cache[(c1, c2, market.__class__)] = None + return get_ticker(c1, c2, market) + +def fetch_balances(market): + balances = {} + fetched_balance = market.fetch_balance() + for key, value in fetched_balance["total"].items(): + if value > 0: + balances[key] = int(value * 10**max_digits) + return balances + +@static_var("cache", {}) +def fetch_fees(market): + if market.__class__ not in fetch_fees.cache: + fetch_fees.cache[market.__class__] = market.fetch_fees() + return fetch_fees.cache[market.__class__] def assets_value(assets, market, base_currency="BTC"): repartition_in_base_currency = {} for currency, asset_value in assets.items(): if currency == base_currency: - repartition_in_base_currency[currency] = asset_value + repartition_in_base_currency[currency] = [asset_value, 0] else: asset_ticker = get_ticker(currency, base_currency, market) if asset_ticker is None: raise Exception("This asset is not available in the chosen market") - repartition_in_base_currency[currency] = int(asset_ticker["bid"] * asset_value) + repartition_in_base_currency[currency] = [ + int(asset_ticker["bidE"] * asset_value), + int(asset_ticker["bidF"] * asset_value) + ] + return repartition_in_base_currency def dispatch_assets(base_currency_value, repartition_pertenthousand, market, base_currency="BTC"): @@ -53,42 +105,114 @@ def dispatch_assets(base_currency_value, repartition_pertenthousand, market, bas repartition_in_base_currency[currency] = int(ptt * base_currency_value / sum_pertenthousand) return repartition_in_base_currency -def give_orders(current_assets, repartition_pertenthousand, market, base_currency="BTC"): +def compute_moves(current_assets, repartition_pertenthousand, market, no_fees=True, base_currency="BTC"): value_in_base = assets_value(current_assets, market, base_currency=base_currency) - total_base_value = sum([ v for k, v in value_in_base.items()]) + total_base_value = sum([ v[0] for k, v in value_in_base.items()]) new_repartition = dispatch_assets(total_base_value, repartition_pertenthousand, market, base_currency=base_currency) mouvements = {} - for key in set(value_in_base.keys()).union(set(new_repartition.keys())): - mouvements[key] = value_in_base.get(key, 0) - new_repartition.get(key, 0) + if no_fees: + for key in set(value_in_base.keys()).union(set(new_repartition.keys())): + mouvements[key] = value_in_base.get(key, [0, 0])[0] - new_repartition.get(key, 0) + else: + for key in set(value_in_base.keys()).union(set(new_repartition.keys())): + value, fee = value_in_base.get(key, [0, 0]) + mouvements[key] = [value - new_repartition.get(key, 0), fee] - print("assets before repartition:") - for currency, value in current_assets.items(): - print("holding {} {}".format( - formatted_price(value), - currency)) - print("------------") - for currency, value in mouvements.items(): - if currency == base_currency: - continue - asset_ticker = get_ticker(currency, base_currency, market) + return mouvements + +def compute_order(currency, value, market, base_currency="BTC"): + if currency == base_currency or value == 0: + return [None, 0, False] + + asset_ticker = get_ticker(currency, base_currency, market) + if asset_ticker["inverted"]: + asset_ticker = get_ticker(base_currency, currency, market) if value > 0: - action = "sell" - currency_price = int(value / asset_ticker["bid"]) + rate = asset_ticker["askA"] + return ["buy", rate, True] else: - action = "buy" - currency_price = int(value / asset_ticker["ask"]) - if value != 0: - print("need to {} {} {}'s worth of {}, i.e. {} {}".format( - action, - formatted_price(abs(value)), - base_currency, + rate = asset_ticker["bidA"] + return ["sell", rate, True] + else: + if value > 0: + rate = asset_ticker["bidA"] + return ["sell", rate, False] + else: + rate = asset_ticker["askA"] + return ["buy", rate, False] + +def make_order(currency, value, market, base_currency="BTC"): + action, rate, inverted = compute_order(currency, value, market, base_currency=base_currency) + amount = formatted_price(abs(value)) + if not inverted: + symbol = "{}/{}".format(currency, base_currency) + else: + symbol = "{}/{}".format(base_currency, currency) + return market.create_order(symbol, 'limit', action, amount, price=rate) + +def make_orders(current_assets, repartition_pertenthousand, market, base_currency="BTC"): + mouvements = compute_moves( + current_assets, + repartition_pertenthousand, + market, + base_currency=base_currency) + + results = [] + for currency, value in sorted(mouvements.items(), key=lambda x: x[1]): + # FIXME: wait for sales to finish + results.append(make_order(currency, value, market, base_currency=base_currency)) + return results + +def print_assets(assets, indent="", market=None, base_currency="BTC"): + if market is not None: + format_string = "{}{} {} ({} {})" + else: + format_string = "{}{} {}" + base_currency_price = 0 + + for currency, value in assets.items(): + if market is not None: + asset_ticker = get_ticker(currency, base_currency, market) + base_currency_price = asset_ticker["bidE"] * value + print(format_string.format( + indent, + formatted_price(value), + currency, + formatted_price(base_currency_price), + base_currency)) + +def print_orders(current_assets, repartition_pertenthousand, market, base_currency="BTC"): + mouvements = compute_moves( + current_assets, + repartition_pertenthousand, + market, + no_fees=False, + base_currency=base_currency) + + for currency, [value, fee] in mouvements.items(): + action, rate, inverted = compute_order( currency, - formatted_price(abs(currency_price)), - currency)) - print("------------\nassets after repartition:") - for currency, value in new_repartition.items(): - print("holding {} {}".format(formatted_price(value), currency)) + value, + market, + base_currency=base_currency) + if action is not None: + currency_price = int(value / rate) + + if not inverted: + c1, c2 = [base_currency, currency] + v1, v2 = [value, currency_price] + else: + c1, c2 = [currency, base_currency] + v1, v2 = [currency_price, value] + + print("need to {} {} {}'s worth of {}, i.e. {} {} ( + {} {} fee)".format( + action, + formatted_price(abs(v1)), c1, + c2, + formatted_price(abs(v2)), c2, + formatted_price(fee), c2)) -give_orders(current_assets, repartition_pertenthousand, market) +current_assets = fetch_balances(market) +print_orders(current_assets, repartition_pertenthousand, market)