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)