aboutsummaryrefslogblamecommitdiff
path: root/script.py
blob: 187ff73024de4ecf14cf96c0e1905b22bbb05e68 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
           

                                        
 





                                     
               








                              



                                           
                        
                               

                       
                                 

                                              

                                                

                                                
                 
                               


                                 


                                                         








                                                                     
              





                                                                   
        
                                                                                                  
                                                                    

                              
                                                                                                      
                                                                        
                                  

                                                               
 







                                                       





                                                                



                                                      
                                                                     



                                                                                   




                                                            








                                                                                                    
                                                                                                         
                                                                                     
                                                                     



                                                                                                                        






                                                                                             
 















                                                                  
                     

                                        
             


































                                                                                                
                                                              











                                                                                          
                          

                                        
                                                     














                                                  
                                                                                     
                       

                                             

                                             
 
                                       
                                                                
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)