def update(self): # Get current state of trade history before placing orders log.info("Update entered") now = str(datetime.datetime.now().isoformat()[:-7].replace("T", " ")) last_trade_price = self.exchange.get_last_trade_price() 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) print('first_order', first_order) print('last_order', last_order) print('new_history', new_history) new_transactions = [] if len(new_history) > 0: # We have new transactions log.info('we have new history') log.debug("New transactions: {}".format(new_history)) new_transactions = self.rebalance_orders(new_history) #self.merge_orders() # context to be used for GUI plotting context = {"price": (now, last_trade_price), "filled_orders": new_transactions, "open_orders": self.exchange.get_my_open_orders(context_formatted=True), "balances": self.exchange.get_balances(), "orderbook": self.exchange.get_all_orders() } return context
def update(self): ''' TODO: Function comment ''' log.info("Update entered\n") now = str(datetime.datetime.now().isoformat()[:-7].replace("T", " ")) last_trade_price = self.exchange.get_last_trade_price() if last_trade_price == "EOF": # Test merkato datastream ended print("test datastream ended") return "stuffs" first_order = get_first_order(self.mutex_UUID) last_order = get_last_order(self.mutex_UUID) current_history = self.exchange.get_my_trade_history() new_history = get_new_history(current_history, last_order) log.info( 'update new_history: {} first_order: {} last_order: {} \n'.format( new_history, first_order, last_order)) new_transactions = [] if len(new_history) > 0: log.info('we have new history') log.debug("New transactions: {} \n".format(new_history)) new_transactions = self.rebalance_orders(new_history) #self.merge_orders() # todo: Talk about whether merging 'close enough' orders is reasonable. # context to be used for GUI plotting context = { "price": (now, last_trade_price), "filled_orders": new_transactions, "open_orders": self.exchange.get_my_open_orders(context_formatted=True), "balances": self.exchange.get_balances(), "orderbook": self.exchange.get_all_orders(), "starting_price": self.starting_price, "starting_base": self.bid_reserved_balance * 4, "starting_quote": self.ask_reserved_balance * 4, "spread": self.spread, "step": self.step } return context
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 get_context_history(self): now = str(datetime.datetime.now().isoformat()[:-7].replace("T", " ")) last_trade_price = self.exchange.get_last_trade_price() current_history = self.exchange.get_my_trade_history() first_order = get_first_order(self.mutex_UUID) new_history = get_new_history(current_history, first_order) self.exchange.process_new_transactions(new_history, context_only=True) context = { "price": (now, last_trade_price), "filled_orders": new_history, "open_orders": self.exchange.get_my_open_orders(context_formatted=True), "balances": self.exchange.get_balances(), "orderbook": self.exchange.get_all_orders(), "starting_price": self.starting_price, "starting_base": self.base_quote_balance, "starting_quote": self.init_quote_balance, "spread": self.spread, "step": self.step } return context
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 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 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 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 __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): 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 = 1.0033 # 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) 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) 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(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