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 repartition_pertenthousand = { "BTC": 2857, "ZEC": 3701, "DOGE": 1805, "DGB": 1015, "SC": 623, } 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: 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: 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: 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, 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["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"): sum_pertenthousand = sum([v for k, v in repartition_pertenthousand.items()]) repartition_in_base_currency = {} for currency, ptt in repartition_pertenthousand.items(): repartition_in_base_currency[currency] = int(ptt * base_currency_value / sum_pertenthousand) return repartition_in_base_currency 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[0] for k, v in value_in_base.items()]) new_repartition = dispatch_assets(total_base_value, repartition_pertenthousand, market, base_currency=base_currency) mouvements = {} 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] 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: rate = asset_ticker["askA"] return ["buy", rate, True] else: 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, 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)) current_assets = fetch_balances(market) print_orders(current_assets, repartition_pertenthousand, market)