def main(): print("Merkato Alpha v0.1.1\n") if no_merkatos_table_exists(): create_merkatos_table() if no_exchanges_table_exists(): create_exchanges_table() merkatos = get_all_merkatos() for merkato in merkatos: exchange_name = merkato['exchange'] exchange_class = get_relevant_exchange(exchange_name) round_trip_fee = round_trip_exchange_fees[exchange_name] config = load_config(exchange_name) exchange = exchange_class(config, merkato['alt'], merkato['base']) last_trade_price = exchange.get_last_trade_price() spread = merkato['spread'] initial_base = float(merkato['bid_reserved_balance']) * 4 + float(merkato['base_profit']) initial_quote = float(merkato['ask_reserved_balance']) * 4 + float(merkato['quote_profit']) quote_volume = merkato['quote_volume'] base_volume = merkato['base_volume'] UUID = merkato['exchange_pair'] base_profit = (base_volume) * (spread - round_trip_fee) quote_profit = (quote_volume) * (spread - round_trip_fee) print('STATS FOR {}'.format(UUID)) print('Quote Volume: {} Base Volume: {}'.format(quote_volume, base_volume)) print('Quote Profit: {} Base Profit: {}'.format(quote_profit, base_profit)) relative_base_prof = str((base_profit/initial_base) * 100) + '%' relative_quote_prof = str((quote_profit/initial_quote ) * 100) + '%' print('Relative Quote Profit: {} Relative Base Profit: {}'.format(relative_quote_prof, relative_base_prof)) print('*WARNING THIS RESET SHOULD ONLY BE DONE ON AT THE END OF EVERY MONTH, AND WILL REMOVE ALL CURRENT VOLUME*') print('*ANY FEE SHOULD BE REMOVED FROM THE ACCOUNT IMMIEDIATELY AFTER*') choice_to_continue = input('Would you like to continue? (y/n)') should_continue = choice_to_continue == 'y' or choice_to_continue == 'Y' if should_continue: print('The base profit that will be added to the account is {}'.format(base_profit * .7)) print('The quote profit that will be added to the account is {}'.format(quote_profit * .7)) print('The starting_price that will be updated for the merkato is {}'.format(last_trade_price)) finalize_choice = input('Do these numbers look correct?(y/n)') should_execute = finalize_choice == 'y' or choice_to_continue == 'Y' if should_execute: new_base_profit = merkato['base_profit'] + (base_profit * .7) new_quote_profit = merkato['quote_profit'] + (quote_profit * .7) update_merkato(UUID, 'base_profit', new_base_profit) update_merkato(UUID, 'quote_profit', new_quote_profit) update_merkato(UUID, 'base_volume', 0) update_merkato(UUID, 'quote_volume', 0) update_merkato(UUID, 'starting_price', last_trade_price) print('Profits updated new base:{} new quote: {} new starting_price: {}'.format(new_base_profit, new_quote_profit, last_trade_price))
def remove_reserve(self, amount, type_of_reserve): current_reserve_amount = self.ask_reserved_balance if type_of_reserve == ASK_RESERVE else self.bid_reserved_balance invalid_reserve_reduction = amount > current_reserve_amount if invalid_reserve_reduction: return False if type_of_reserve == ASK_RESERVE: new_amount = self.ask_reserved_balance - amount self.ask_reserved_balance = new_amount else: new_amount = self.bid_reserved_balance - amount self.bid_reserved_balance = new_amount update_merkato(self.mutex_UUID, type_of_reserve, new_amount) return True
def distribute_initial_orders(self, total_base, total_alt): ''' TODO: Function comment ''' current_price = (Decimal(self.exchange.get_highest_bid()) + Decimal(self.exchange.get_lowest_ask())) / 2 if self.user_interface: current_price = Decimal( self.user_interface.confirm_price(current_price)) update_merkato(self.mutex_UUID, STARTING_PRICE, current_price) ask_start = current_price + current_price * self.spread / 2 bid_start = current_price - current_price * self.spread / 2 self.distribute_bids(bid_start, total_base) self.distribute_asks(ask_start, total_alt)
def remove_reserve(self, amount, type_of_reserve): ''' TODO: Function comment ''' current_reserve_amount = self.ask_reserved_balance if type_of_reserve == ASK_RESERVE else self.bid_reserved_balance invalid_reserve_reduction = amount > current_reserve_amount if invalid_reserve_reduction: return False if type_of_reserve == ASK_RESERVE: new_amount = self.ask_reserved_balance - amount self.ask_reserved_balance = new_amount elif type_of_reserve == BID_RESERVE: new_amount = self.bid_reserved_balance - amount self.bid_reserved_balance = new_amount update_merkato(self.mutex_UUID, type_of_reserve, float(new_amount)) return True
def __init__(self, configuration, coin, base, spread, bid_reserved_balance, ask_reserved_balance, user_interface=None, profit_margin=0, first_order=''): validate_merkato_initialization(configuration, coin, base, spread) self.initialized = False UUID = configuration[EXCHANGE] + "coin={}_base={}".format(coin,base) self.mutex_UUID = UUID self.distribution_strategy = 1 self.spread = float(spread) self.profit_margin = profit_margin # Create ladders from the bid and ask bidget here self.bid_reserved_balance = bid_reserved_balance self.ask_reserved_balance = ask_reserved_balance self.user_interface = user_interface exchange_class = get_relevant_exchange(configuration[EXCHANGE]) self.exchange = exchange_class(configuration, coin=coin, base=base) merkato_does_exist = merkato_exists(UUID) if not merkato_does_exist: log.info("Creating New Merkato") self.cancelrange(ONE_SATOSHI, ONE_BITCOIN) total_pair_balances = self.exchange.get_balances() log.info("total pair balances", total_pair_balances) allocated_pair_balances = get_allocated_pair_balances(configuration['exchange'], base, coin) check_reserve_balances(total_pair_balances, allocated_pair_balances, coin_reserve=ask_reserved_balance, base_reserve=bid_reserved_balance) insert_merkato(configuration[EXCHANGE], UUID, base, coin, spread, bid_reserved_balance, ask_reserved_balance, first_order) history = self.exchange.get_my_trade_history() print('initial history', history) if len(history) > 0: print('updating history', history[0]['orderId']) new_last_order = history[0]['orderId'] update_merkato(self.mutex_UUID, LAST_ORDER, new_last_order) self.distribute_initial_orders(total_base=bid_reserved_balance, total_alt=ask_reserved_balance) else: #self.history = get_old_history(self.exchange.get_my_trade_history(), self.mutex_UUID) first_order = get_first_order(self.mutex_UUID) current_history = self.exchange.get_my_trade_history(first_order) last_order = get_last_order(self.mutex_UUID) new_history = get_new_history(current_history, last_order) self.rebalance_orders(new_history) self.initialized = True # to avoid being updated before orders placed
def update_orders(self, coin, amount_to_add): print('amount_to_add', amount_to_add, 'coin', coin) amount_to_add = Decimal(float(amount_to_add)) self.check_balances_available(coin, amount_to_add) add_percentage = self.calculate_add_percentage(coin, amount_to_add) print('add percentage', add_percentage) if coin == self.exchange.coin: old_reserves = self.ask_reserved_balance + self.quote_partials_balance else: old_reserves = self.bid_reserved_balance + self.base_partials_balance current_orders = self.exchange.get_my_open_orders() for order_id, order in current_orders.items(): current_amount = order['amount'] order_type = order['type'] order_price = Decimal(float(order['price'])) amount_to_add = Decimal( float(current_amount * (1 + add_percentage))) print('cancel order') print('coin', coin, 'self.exchange.coin', self.exchange.coin, 'order_type', order_type) if coin == self.exchange.coin and order_type == SELL: self.exchange.cancel_order(order['id']) self.exchange.sell(amount_to_add, order_price) print('replace sell') if coin == self.exchange.base and order_type == BUY: self.exchange.cancel_order(order['id']) self.exchange.buy(amount_to_add, order_price) print('replace buy') if coin == self.exchange.coin: print('old reserve balance', self.ask_reserved_balance) update_merkato(self.mutex_UUID, 'ask_reserved_balance', float(old_reserves * (1 + add_percentage))) self.ask_reserved_balance = Decimal( float(old_reserves * (1 + add_percentage))) print('new reserve balances', self.ask_reserved_balance) elif coin == self.exchange.base: print('old reserve balance', self.bid_reserved_balance) update_merkato(self.mutex_UUID, 'bid_reserved_balance', float(old_reserves * (1 + add_percentage))) self.bid_reserved_balance = Decimal( float(old_reserves * (1 + add_percentage))) print('new reserve balances', self.bid_reserved_balance)
def apply_filled_difference(self, tx, total_amount): filled_difference = total_amount - Decimal(tx['amount']) log.info('apply_filled_difference tx: {} total_amount: {}'.format( tx, total_amount)) tx_type = tx['type'] if filled_difference > 0: if tx_type == SELL: self.base_partials_balance -= filled_difference * Decimal( tx[PRICE]) update_merkato(self.mutex_UUID, 'base_partials_balance', float(self.base_partials_balance)) log.info( 'apply_filled_difference base_partials_balance: {}'.format( self.base_partials_balance)) if tx_type == BUY: self.quote_partials_balance -= filled_difference update_merkato(self.mutex_UUID, 'quote_partials_balance', float(self.quote_partials_balance)) log.info('apply_filled_difference quote_partials_balance: {}'. format(self.quote_partials_balance))
def handle_market_order(self, amount, price, type_to_place, tx_id): log.info( 'handle market order price: {}, amount: {}, type_to_place: {}'. format(price, amount, type_to_place)) last_id_before_market = get_last_order(self.mutex_UUID) if type_to_place == BUY: self.exchange.market_buy(amount, price) elif type_to_place == SELL: self.exchange.market_sell(amount, price) current_history = self.exchange.get_my_trade_history() if self.exchange.name != 'tux': self.exchange.process_new_transactions(current_history) market_history = get_new_history(current_history, last_id_before_market) market_data = get_market_results(market_history) # The sell gave us some BTC. The buy is executed with that BTC. # The market buy will get us X xmr in return. All of that xmr # should be placed at the original order's matching price. # # We need to do something here about the partials if it doesnt fully fill amount_executed = Decimal(market_data['amount_executed']) price_numerator = Decimal(market_data['price_numerator']) last_txid = market_data['last_txid'] log.info('market data: {}'.format(market_data)) update_merkato(self.mutex_UUID, LAST_ORDER, last_txid) market_order_filled = amount <= amount_executed if market_order_filled: if type_to_place == BUY: price = price * Decimal(1 + self.spread) self.exchange.sell(amount_executed, price) # Should never market order elif type_to_place == SELL: price = price * Decimal(1 - self.spread) self.exchange.buy(amount_executed, price) else: log.info( 'handle_market_order: partials affected, amount: {} amount_executed: {}' .format(amount, amount_executed)) if type_to_place == BUY: self.quote_partials_balance += amount_executed update_merkato(self.mutex_UUID, 'quote_partials_balance', float(self.quote_partials_balance)) log.info('market buy partials after: {}'.format( self.quote_partials_balance)) else: self.base_partials_balance += amount_executed * price_numerator update_merkato(self.mutex_UUID, 'base_partials_balance', float(self.base_partials_balance)) log.info('market sell partials after {}'.format( self.base_partials_balance))
def handle_is_in_filled_orders(self, tx): tx_type = tx[TYPE] filled_amount = Decimal(tx['amount']) price = Decimal(tx[PRICE]) tx_id = tx[ID] if tx_type == BUY: self.quote_partials_balance += filled_amount update_merkato(self.mutex_UUID, 'quote_partials_balance', float(self.quote_partials_balance)) if tx_type == SELL: self.base_partials_balance += filled_amount * price update_merkato(self.mutex_UUID, 'base_partials_balance', float(self.base_partials_balance)) log.info( '{}, orderid in filled_orders filled_amount: {} tx_id: {} '.format( tx_type, filled_amount, tx_id)) update_merkato(self.mutex_UUID, LAST_ORDER, tx_id)
def handle_partial_fill(self, type, filled_qty, tx_id): # This was a buy, so we gained more of the quote asset. # This was a partial fill, so the user's balance is increased by that amount. # However, that amount is 'reserved' (will be placed on the books once the # rest of the order is filled), and therefore is unavailable when creating new # Merkatos. Add this amount to a field 'quote_partials_balance'. log.info('handle_partial_fill type {} filledqty {} tx_id {}'.format( type, filled_qty, tx_id)) update_merkato(self.mutex_UUID, LAST_ORDER, tx_id) if type == BUY: self.quote_partials_balance += filled_qty # may need a multiply by price update_merkato(self.mutex_UUID, 'quote_partials_balance', float(self.quote_partials_balance)) elif type == SELL: self.base_partials_balance += filled_qty update_merkato(self.mutex_UUID, 'base_partials_balance', float(self.base_partials_balance))
def __init__(self, configuration, coin, base, spread, bid_reserved_balance, ask_reserved_balance, user_interface=None, profit_margin=0, first_order='', starting_price=.018, increased_orders=0, step=1.0033, distribution_strategy=1, init_base_balance=0, init_quote_balance=0, base_profit=0, quote_profit=0, buy_volume=0, sell_volume=0): validate_merkato_initialization(configuration, coin, base, spread) self.initialized = False UUID = configuration[EXCHANGE] + "coin={}_base={}".format(coin, base) self.mutex_UUID = UUID self.distribution_strategy = distribution_strategy self.spread = Decimal(spread) self.profit_margin = Decimal(profit_margin) self.starting_price = starting_price self.step = step self.increased_orders = increased_orders self.quote_profit = Decimal(quote_profit) self.base_profit = Decimal(base_profit) self.bid_reserved_balance = Decimal(float(bid_reserved_balance)) self.ask_reserved_balance = Decimal(float(ask_reserved_balance)) self.init_base_balance = init_base_balance self.init_quote_balance = init_quote_balance # The current sum of all partially filled orders self.base_partials_balance = 0 self.quote_partials_balance = 0 self.buy_volume = buy_volume self.sell_volume = sell_volume self.last_placed_UUID = '' #this assures that no faulty doubled up orders will be placed sequentially self.user_interface = user_interface exchange_class = get_relevant_exchange(configuration[EXCHANGE]) self.exchange = exchange_class(configuration, coin=coin, base=base) merkato_does_exist = merkato_exists(self.mutex_UUID) if not merkato_does_exist: log.info("Creating New Merkato") self.cancelrange(ONE_SATOSHI, ONE_BITCOIN) total_pair_balances = self.exchange.get_balances() log.info("total pair balances: {}".format(total_pair_balances)) allocated_pair_balances = get_allocated_pair_balances( configuration['exchange'], base, coin) check_reserve_balances(total_pair_balances, allocated_pair_balances, coin_reserve=ask_reserved_balance, base_reserve=bid_reserved_balance) insert_merkato(configuration[EXCHANGE], self.mutex_UUID, base, coin, spread, bid_reserved_balance, ask_reserved_balance, first_order, starting_price, init_base_balance=bid_reserved_balance, init_quote_balance=ask_reserved_balance, step=step) history = self.exchange.get_my_trade_history() log.debug('initial history: {}'.format(history)) if len(history) > 0: log.debug('updating history first ID: {}'.format( history[0][ID])) new_last_order = history[0][ID] update_merkato(self.mutex_UUID, LAST_ORDER, new_last_order) self.distribute_initial_orders(total_base=bid_reserved_balance, total_alt=ask_reserved_balance) else: first_order = get_first_order(self.mutex_UUID) current_history = self.exchange.get_my_trade_history() last_order = get_last_order(self.mutex_UUID) new_history = get_new_history(current_history, last_order) self.rebalance_orders(new_history) self.initialized = True # to avoid being updated before orders placed
def update_sell_volume(self, new_volume): self.sell_volume += float(new_volume) update_merkato(self.mutex_UUID, SELL_VOLUME, self.sell_volume)
def update_buy_volume(self, raw_volume, price): finalized_volume = float(raw_volume) * float(price) self.buy_volume += finalized_volume update_merkato(self.mutex_UUID, BUY_VOLUME, self.buy_volume)
def reset_merkato_metrics(merkato, base_balance, quote_balance): UUID = merkato['exchange_pair'] update_merkato(UUID, 'buy_volume', 0) update_merkato(UUID, 'sell_volume', 0) update_merkato(UUID, 'init_base_balance', base_balance) update_merkato(UUID, 'init_quote_balance', quote_balance) update_merkato(UUID, 'quote_profit', 0) update_merkato(UUID, 'base_profit', 0)
def merge_orders(self): # Takes all bids/asks that are at the same price, and combines them. # # Consider changing semantics from existing_order and order to order and new_order. # That is, existing_order currently becomes order, and order becomes new_order. # Coin is a string # TODO: Make orders/orderbook variables less semantically similar orders = self.exchange.get_my_open_orders() # Create a dictionary to store our desired orderbook orderbook = dict() for order in orders: log.info('order {}'.format(order)) price = orders[order][PRICE] coin = orders[order]["coin"] amount = float(orders[order]["amount"]) # Amount in asset total = float(orders[order]["total"]) # Total in BTC order_id = orders[order]['id'] log.debug(orders[order]) if coin != self.exchange.ticker: continue if price not in orderbook: price_data = create_price_data(orders, order) orderbook[price] = price_data log.debug("Found new bid at {}".format(price)) else: log.info("Collision at {}".format(price)) existing_order = orderbook[price] existing_order_id = existing_order['id'] existing_order_type = existing_order['type'] existing_order_total = float(existing_order['total']) existing_order_amount = float(existing_order['amount']) # Cancel the colliding orders self.exchange.cancel_order(order_id) self.exchange.cancel_order(existing_order_id) # Update the totals to represent the new totals existing_order['total'] = str(existing_order_total + total) existing_order['amount'] = str(existing_order_amount + amount) # Place a new order on the books with the sum if existing_order_type == "buy": log.info("Placing buy for {} - {} of {} at a price of {}".format( existing_order['total'], self.exchange.base, self.exchange.ticker, price )) new_id = self.exchange.buy(float(existing_order['total'])/float(price), float(price), self.exchange.ticker) else: # existing_order_type is sell log.info("Placing sell for {} - {} of {} at a price of {}".format( existing_order['total'], self.exchange.base, self.exchange.ticker, price )) new_id = self.exchange.sell(float(existing_order['amount']), float(price), self.exchange.ticker) if new_id == 0: log.warning("Something went wrong.") return 1 else: update_merkato(self.mutex_UUID, LAST_ORDER, new_id) log.debug("consolidation successful") existing_order['id'] = new_id log.debug(existing_order) log.info("Consolidation Successful") return 0
def rebalance_orders(self, new_txes): # This function places a matching order for every new transaction since last run # # profit_margin is a number from 0 to 1 representing the percent of the spread to return # to the user's balance before placing the matching order. # # TODO: Modify so that the parent function only passes in the new transactions, don't # do the index check internally. # new_history is an array of transactions # new_txes is the number of new transactions contained in new_history factor = self.spread*self.profit_margin/2 ordered_transactions = new_txes log.info('ordered transactions rebalanced: {}'.format(ordered_transactions)) for tx in ordered_transactions: log.info('length of ordered_transactions length length: {}'.format(len(ordered_transactions))) if tx['type'] == SELL: log.info("Found sell {} corresponding buy {}".format(tx, sell_price)) amount = float(tx['amount']) * float(tx[PRICE])*(1-factor) price = float(tx[PRICE]) buy_price = price * ( 1 - self.spread) log.info("found sell {}; corresponding buy {}".format(tx, buy_price)) market = self.exchange.buy(amount, buy_price) if market == MARKET: log.info('market sell', market) last_order_time = str(int(time.time())) self.exchange.market_buy(amount, buy_price) market_history = self.exchange.get_my_trade_history(start=last_order_time) market_data = get_market_results(market_history) # We have a sell executed. We want to place a matching buy order. # If the whole order is executed, no edge case. # If the order has a remainder, the remainder will be on the books at # the appropriate price. So no problem. # If the remainder is too small to have a matching order, it could # disappear, but this is such a minor edge case we can ignore it. # # The sell gave us some BTC. The buy is executed with that BTC. # The market buy will get us X xmr in return. All of that xmr # should be placed at the original order's matching price. amount_executed = float(market_data['amount_executed']) last_orderid = market_data['last_orderid'] log.info('market data: {}'.format(market_data)) self.exchange.sell(amount_executed, price) # Should never market order # A market buy occurred, so we need to update the db with the latest tx update_merkato(self.mutex_UUID, LAST_ORDER, last_orderid) if tx['type'] == BUY: amount = float(tx['amount'])*float((1-factor)) price = tx[PRICE] sell_price = float(price) * ( 1 + self.spread) log.info("Found buy {} corresponding sell {}".format(tx, sell_price)) market = self.exchange.sell(amount, sell_price) if market == MARKET: log.info('market buy {}'.format(market)) last_order_time = str(int(time.time())) self.exchange.market_sell(amount, sell_price) market_history = self.exchange.get_my_trade_history(start=last_order_time) market_data = get_market_results(market_history) # We have a buy executed. We want to place a matching sell order. # If the whole order is executed, no edge case. # If the order has a remainder, the remainder will be on the books at # the appropriate price. So no problem. # If the remainder is too small to have a matching order, it could # disappear, but this is such a minor edge case we can ignore it. # # The buy gave us some alt. The sell is executed with that alt. # The market sell will get us X btc in return. All of that btc # should be placed at the original order's matching price. amount_executed = float(market_data['total_gotten']) last_orderid = market_data['last_orderid'] log.info('market data {}'.format(market_data)) self.exchange.buy(amount_executed, float(price)) # Should never market order # A market buy occurred, so we need to update the db with the latest tx update_merkato(self.mutex_UUID, LAST_ORDER, last_orderid) if market != MARKET: log.info('market != MARKET market != MARKET') update_merkato(self.mutex_UUID, LAST_ORDER, tx['orderId']) first_order = get_first_order(self.mutex_UUID) no_first_order = first_order == '' if no_first_order: update_merkato(self.mutex_UUID, FIRST_ORDER, tx['orderId']) self.log_new_transactions(ordered_transactions) return ordered_transactions
def rebalance_orders(self, new_txes): # This function places matching orders for all orders that filled fully since last factor = self.spread * self.profit_margin / 2 ordered_transactions = new_txes log.info( 'ordered transactions rebalanced: {}'.format(ordered_transactions)) filled_orders = [] market_orders = [] if self.exchange.name != 'tux': self.exchange.process_new_transactions(ordered_transactions) for tx in ordered_transactions: log.info('Checking Transaction: {}\n'.format(tx)) orderid = tx['orderId'] tx_id = tx[ID] price = tx[PRICE] filled_amount = Decimal(tx['amount']) init_amount = Decimal(tx['initamount']) if self.exchange.name == 'tux': partial_fill_info = self.exchange.get_my_order_info(orderid) init_amount = partial_fill_info['initamount'] partial_fill = (partial_fill_info['state'] == 'closed') else: partial_fill = self.exchange.is_partial_fill( orderid) # todo implement for tux (binance done) total_amount = self.get_total_amount(init_amount, orderid) amount = Decimal(total_amount) * Decimal((1 - factor)) if partial_fill: self.handle_partial_fill(tx[TYPE], filled_amount, tx_id) continue if orderid in filled_orders: self.handle_is_in_filled_orders(tx) continue if tx[TYPE] == SELL: buy_price = Decimal(price) * (1 - self.spread) log.info( "Found sell {} corresponding buy price: {} amount: {}". format(tx, buy_price, amount)) market = self.exchange.buy(amount, buy_price) # A lock is probably needed somewhere near here in case of unexpected shutdowns if market == MARKET: log.info('MARKET ORDER buy {}'.format(market)) market_orders.append(( amount, buy_price, BUY, tx_id, )) self.apply_filled_difference(tx, total_amount) is_round_trip = float(price) <= (float(self.starting_price) * float(1 + (self.spread / 2))) if is_round_trip: log.info('Is round trip sell price: {}'.format(price)) self.base_volume += total_amount * Decimal(float(price)) update_merkato(self.mutex_UUID, BASE_VOLUME, float(self.base_volume)) if tx[TYPE] == BUY: sell_price = Decimal(price) * (1 + self.spread) log.info( "Found buy {} corresponding sell price: {} amount: {}". format(tx, sell_price, amount)) market = self.exchange.sell(amount, sell_price) if market == MARKET: log.info('MARKET ORDER sell {}'.format(market)) market_orders.append((amount, sell_price, SELL, tx_id)) self.apply_filled_difference(tx, total_amount) is_round_trip = float(price) >= (float(self.starting_price) * float( (1 - (self.spread / 2)))) if is_round_trip: log.info('Is round trip buy price: {}'.format(price)) self.quote_volume += total_amount update_merkato(self.mutex_UUID, QUOTE_VOLUME, float(self.quote_volume)) insert_transaction(self.mutex_UUID, self.exchange.base, self.exchange.coin, float(self.spread), tx_id, orderid, float(price), float(filled_amount), tx['time']) if market != MARKET: log.info('NOT MARKET ORDER') update_merkato(self.mutex_UUID, LAST_ORDER, tx[ID]) filled_orders.append(orderid) first_order = get_first_order(self.mutex_UUID) no_first_order = first_order == '' if no_first_order: update_merkato(self.mutex_UUID, FIRST_ORDER, tx_id) for order in market_orders: self.handle_market_order(*order) self.log_new_cointrackr_transactions(ordered_transactions) log.info('ending partials base: {} quote: {}'.format( self.base_partials_balance, self.quote_partials_balance)) return ordered_transactions
def rebalance_orders(self, new_txes): # This function places matching orders for all orders that filled fully since last factor = self.spread * self.profit_margin / 2 ordered_transactions = new_txes log.info( 'ordered transactions rebalanced: {}'.format(ordered_transactions)) filled_orders = [] market_orders = [] if self.exchange.name != 'tux': self.exchange.process_new_transactions(ordered_transactions) for tx in ordered_transactions: log.info('Checking Transaction: {}'.format(tx)) orderid = tx['orderId'] tx_id = tx[ID] price = tx[PRICE] filled_amount = Decimal(tx['amount']) init_amount = Decimal(tx['initamount']) if self.exchange.name == 'tux': partial_fill_info = self.exchange.get_my_order_info(orderid) init_amount = partial_fill_info['initamount'] partial_fill = (partial_fill_info['state'] == 'closed') else: partial_fill = self.exchange.is_partial_fill( orderid) # todo implement for tux (binance done) total_amount = self.get_total_amount(init_amount, orderid) amount = Decimal(total_amount) * Decimal((1 - factor)) if partial_fill: self.handle_partial_fill(tx[TYPE], filled_amount, tx_id) continue if orderid in filled_orders: self.handle_is_in_filled_orders(tx) continue if tx[TYPE] == SELL: buy_price = Decimal(price) * (1 - self.spread) # Convert from the coin amount into base at the executed price base_amt = Decimal(price) * amount # Convert the base amount into coin at the final price coin_amt = base_amt / buy_price # This is the actual number we want to apply, not the original executed amount. amount = coin_amt if self.last_placed_UUID != buy_price + amount: log.info( "Found sell {} corresponding buy price: {} amount: {}". format(tx, buy_price, amount)) order_response = self.exchange.buy(amount, buy_price) else: order_response = None self.update_sell_volume(filled_amount) if order_response == MARKET: log.info('MARKET ORDER buy {}'.format(order_response)) market_orders.append(( amount, buy_price, BUY, tx_id, )) self.apply_filled_difference(tx, total_amount) is_round_trip = float(price) <= (float(self.starting_price) * float(1 + (self.spread / 2))) if is_round_trip: log.info('Is round trip sell price: {}'.format(price)) self.base_profit += total_amount * Decimal( float(price)) * (self.spread - Decimal(self.exchange.fee * 2)) update_merkato(self.mutex_UUID, BASE_PROFIT, float(self.base_profit)) order_price = buy_price if tx[TYPE] == BUY: sell_price = Decimal(price) * (1 + self.spread) if self.last_placed_UUID != sell_price + amount: log.info( "Found buy {} corresponding sell price: {} amount: {}". format(tx, sell_price, amount)) order_response = self.exchange.sell(amount, sell_price) else: order_response = None self.update_buy_volume(filled_amount, price) if order_response == MARKET: log.info('MARKET ORDER sell {}'.format(order_response)) market_orders.append((amount, sell_price, SELL, tx_id)) self.apply_filled_difference(tx, total_amount) is_round_trip = float(price) >= (float(self.starting_price) * float(1 - (self.spread / 2))) if is_round_trip: log.info('Is round trip buy price: {}'.format(price)) self.quote_profit += total_amount * Decimal( self.spread - Decimal(self.exchange.fee * 2)) update_merkato(self.mutex_UUID, QUOTE_PROFIT, float(self.quote_profit)) order_price = sell_price if order_response != MARKET: log.info('NOT MARKET ORDER') update_merkato(self.mutex_UUID, LAST_ORDER, tx[ID]) filled_orders.append(orderid) first_order = get_first_order(self.mutex_UUID) no_first_order = first_order == '' if no_first_order: update_merkato(self.mutex_UUID, FIRST_ORDER, tx_id) self.last_placed_UUID = order_price + amount for order in market_orders: self.handle_market_order(*order) log.info('ending partials base: {} quote: {}'.format( self.base_partials_balance, self.quote_partials_balance)) return ordered_transactions
def calculate_add_percentage(ticker_pair, coin, amount_to_add): orderbook_sum = sum_orderbook(ticker_pair, coin) reserve_sum = get_reserves(ticker_pair, coin) total_amount = orderbook_sum + reserve_sum return amount_to_add/total_amount def update_orders(ticker_pair, coin, amount_to_add) add_percentage = calculate_add_percentage(ticker_pair, coin, amount_to_add) old_reserves = get_reserves(ticker_pair, coin) for order in current_orders: current_amount = order['amount'] order_type = order['type'] order_price = order['price'] amount_to_add = current_amount * (1 + add_percentage) cancel_order(order['id']) place_order(amount_to_add, order_price) if coin != 'BTC': update_merkato(UUID, 'ask_reserved_balance', old_reserves * (1 + add_percentage)) #update coin reserves with old_reserves * (1 + add_percentage) else: update_merkato(UUID, 'bid_reserved_balance', old_reserves * (1 + add_percentage)) #update base reserves def cancel_order(order_id): pass def place_order(amount, price) pass
def __init__(self, configuration, coin, base, spread, bid_reserved_balance, ask_reserved_balance, user_interface=None, profit_margin=0, first_order='', starting_price=.018, quote_volume=0, base_volume=0, step=1.0033): validate_merkato_initialization(configuration, coin, base, spread) self.initialized = False UUID = configuration[EXCHANGE] + "coin={}_base={}".format(coin, base) self.mutex_UUID = UUID self.distribution_strategy = 1 self.spread = Decimal(spread) self.profit_margin = Decimal(profit_margin) self.starting_price = starting_price self.quote_volume = Decimal(quote_volume) self.base_volume = Decimal(base_volume) self.step = step # Exchanges have a maximum number of orders every user can place. Due # to this, every Merkato has a reserve of coins that are not currently # allocated. As the price approaches unallocated regions, the reserves # are deployed. self.bid_reserved_balance = Decimal(float(bid_reserved_balance)) self.ask_reserved_balance = Decimal(float(ask_reserved_balance)) # The current sum of all partially filled orders self.base_partials_balance = 0 self.quote_partials_balance = 0 self.user_interface = user_interface exchange_class = get_relevant_exchange(configuration[EXCHANGE]) self.exchange = exchange_class(configuration, coin=coin, base=base) merkato_does_exist = merkato_exists(self.mutex_UUID) log.info("Creating New Merkato") self.cancelrange(ONE_SATOSHI, ONE_BITCOIN) total_pair_balances = self.exchange.get_balances() log.info("total pair balances: {}".format(total_pair_balances)) allocated_pair_balances = get_allocated_pair_balances( configuration['exchange'], base, coin) check_reserve_balances(total_pair_balances, allocated_pair_balances, coin_reserve=ask_reserved_balance, base_reserve=bid_reserved_balance) insert_merkato(configuration[EXCHANGE], self.mutex_UUID, base, coin, spread, bid_reserved_balance, ask_reserved_balance, first_order, starting_price) history = self.exchange.get_my_trade_history() log.debug('initial history: {}'.format(history)) if len(history) > 0: log.debug('updating history first ID: {}'.format(history[0][ID])) new_last_order = history[0][ID] update_merkato(self.mutex_UUID, LAST_ORDER, new_last_order) self.distribute_initial_orders(total_base=bid_reserved_balance, total_alt=ask_reserved_balance) self.initialized = True # to avoid being updated before orders placed