def __init__(self, trades: List[Trade], fx: FXConnector, trade_updated_handler=None): super().__init__() self.fx = fx self.balances = AccountBalances() self.order_updated_handler = trade_updated_handler # ExchangeInfo().update(fx.get_exchange_info()) self.strategies: List[TradingStrategy] = [] # self.logInfo('Creating {} with {} trades.'.format(self.__class__.__name__, len(trades))) # # for t in trades: # try: # if t.symbol not in ExchangeInfo().symbols: # self.logError('Exchange doesn\'t have market "{}"'.format(t.symbol)) # continue # # self.strategies.append( # TargetsAndStopLossStrategy(t, fx, trade_updated_handler, self.balances.get_balance(t.asset))) # except Exception: # self.logError(traceback.format_exc()) self.strategies_dict = {} self.tradeid_strategy_dict = {} self.trade_info_ticker_buf = {} self.process_delay = 500 self.last_ts = dt.now() self.first_processing = True self.socket_message_rcvd = False self.paused = False self.lock = RLock()
def add_trades(self, trades: [Trade], start_listening=True): self.logInfo('Adding {} trades to the TradeHandler.'.format( len(trades))) # try: self.stop_listening() # if not ExchangeInfo().has_all_symbol(self.strategies_dict.keys()): ExchangeInfo().update(self.fx.get_exchange_info()) AccountBalances().update_balances(self.fx.get_all_balances_dict()) for trade in trades: new_strategy = TargetsAndStopLossStrategy( trade, self.fx, self.order_updated_handler, self.balances.get_balance(trade.asset)) if self.handle_completed_strategy(new_strategy): self.logInfo('Strategy is completed [{}]'.format( new_strategy.symbol())) continue self.add_new_strategy(new_strategy, listen_symbols=False) self.fx.listen_symbols([s.symbol() for s in self.strategies], self.listen_handler, self.user_data_handler) if start_listening: self.start_listening()
def emergent_close_position(self): try: self.cancel_all_open_orders() AccountBalances().update_balances(self.fx.get_all_balances_dict()) # price = self.exchange_info.adjust_price(self.get_sl_limit_price()) bal = self.trade.get_cap(self.get_balance_for_side().avail) volume = round(bal / self.get_single_price(self.last_price), 8) if self.trade_side().is_buy() else bal if volume < self.exchange_info.minQty: adjusted_vol = 0 else: adjusted_vol = self.exchange_info.adjust_quanity(volume) self.logInfo('Closing positions ({}): {}, v: {:.08f}'.format( self.symbol(), self.trade_side(), adjusted_vol)) if adjusted_vol > 0: order = self.fx.create_makret_order(self.symbol(), self.trade_side().name, adjusted_vol) self.logInfo('Positions [{}] Closed'.format(self.symbol())) self.trade.set_completed() self.trigger_target_updated() except BinanceAPIException as bae: self.logError(str(bae))
def on_order_status_changed(self, t: Target, data): complete_trade = False if t.is_completed(): if t.is_entry_target(): # validate balance and activate trade only if there are trading targets if self.strategy_exit: AccountBalances().update_balances( self.fx.get_all_balances_dict()) self.trade.cap = self.get_balance_for_side().avail self.trade.set_active() self.trigger_target_updated() else: complete_trade = True elif t.is_exit_target(): if self.trade.exit and self.trade.exit.is_completed(): # if all targets are completed, set trade as completed complete_trade = True elif t.is_stoploss_target(): complete_trade = True if complete_trade: self.set_trade_completed() [s.on_order_status_changed(t, data) for s in self.all_strategies()]
def cancel_current_limit_order(self): if self.current_target.is_active(): try: status = self.fx.get_order_status(self.symbol(), self.current_target.id) if self.fx.cancel_order(self.symbol(), self.current_target.id): canceled_time = datetime.now() self.current_target.set_canceled() self.trigger_target_updated() # if balance hasn't been updated since order was cancelled if AccountBalances().update_required(canceled_time): if self.trade_side().is_sell(): self.balance.avail += ( float(status["origQty"]) - float(status["executedQty"])) else: bal = self.get_balance_for_side() price = float(status["price"]) bal.avail += round( (float(status["origQty"]) * price - float(status["executedQty"]) * price), 8) except BinanceAPIException: self.logError(traceback.format_exc())
def set_stoploss_order(self): if self.trade.sl_settings.initial_target.is_active(): return # self.logInfo('validating stoploss order') # if True: # validate it # return # else: # pass try: if self.simulate: order = self.fx.create_test_stop_order(self.symbol(), self.trade_side().name, self.current_stop_loss, 50) order['orderId'] = 2333123 else: self.cancel_all_orders() AccountBalances().update_balances( self.fx.get_all_balances_dict()) price = self.exchange_info.adjust_price( self.get_sl_limit_price()) bal = self.trade.get_cap(self.get_balance_for_side().avail) bal = round(bal / price, 8) if self.trade_side().is_buy() else bal volume = self.initial_sl().vol.get_val(bal) # stop_trigger try: order = self.fx.create_stop_order( sym=self.symbol(), side=self.trade_side().name, stop_price=self.exchange_info.adjust_price( self.current_stop_loss), price=price, volume=self.exchange_info.adjust_quanity(volume)) except BinanceAPIException as sl_exception: if sl_exception.message.lower().find( 'order would trigger immediately') > -1: order = self.fx.create_makret_order( self.symbol(), self.trade_side().name, self.exchange_info.adjust_quanity(volume)) else: raise self.trade.sl_settings.initial_target.set_active(order['orderId']) self.trigger_target_updated() self.logInfo('Setting stop loss order: {:.08f}:{:.08f}'.format( self.exchange_info.adjust_price(self.current_stop_loss), self.exchange_info.adjust_price(self.get_sl_limit_price()))) except BinanceAPIException as bae: self.logError(str(bae))
def adjust_stoploss_order(self, current_price): threshold = self.trade.sl_settings.zone_entry.get_val(self.current_stop_loss) if (self.trade.is_sell() and (self.current_stop_loss + threshold + self.exit_threshold) >= current_price) or \ (not self.trade.is_sell() and (self.current_stop_loss - threshold - self.exit_threshold) <= current_price): self.set_stoploss_order() # if price bounce between placing and canceling order - additing small exit threshold if self.exit_threshold == 0: self.exit_threshold = threshold / 2 else: self.exit_threshold = 0 if self.cancel_stoploss_orders(): AccountBalances().update_balances(self.fx.get_all_balances_dict())
def extract_info(self, strategy): trade = strategy.trade self.symbol = trade.symbol self.status = trade self.balance = AccountBalances().get_balance(trade.asset) if isinstance(strategy, TargetsAndStopLossStrategy): if strategy.strategy_sl: sl_name = self.class_name(strategy.strategy_sl) if strategy.strategy_sl.is_stoploss_order_active(): self.active_strategy = sl_name self.possible_strategies.append(sl_name) if strategy.strategy_entry and not strategy.strategy_entry.is_completed( ) and trade.is_new(): enty_name = self.class_name(strategy.strategy_exit) self.active_strategy = enty_name self.possible_strategies.append(enty_name)
class TradeHandler(Logger): def __init__(self, trades: List[Trade], fx: FXConnector, trade_updated_handler=None): super().__init__() self.fx = fx self.balances = AccountBalances() self.order_updated_handler = trade_updated_handler # ExchangeInfo().update(fx.get_exchange_info()) self.strategies: List[TradingStrategy] = [] # self.logInfo('Creating {} with {} trades.'.format(self.__class__.__name__, len(trades))) # # for t in trades: # try: # if t.symbol not in ExchangeInfo().symbols: # self.logError('Exchange doesn\'t have market "{}"'.format(t.symbol)) # continue # # self.strategies.append( # TargetsAndStopLossStrategy(t, fx, trade_updated_handler, self.balances.get_balance(t.asset))) # except Exception: # self.logError(traceback.format_exc()) self.strategies_dict = {} self.tradeid_strategy_dict = {} self.trade_info_ticker_buf = {} self.process_delay = 500 self.last_ts = dt.now() self.first_processing = True self.socket_message_rcvd = False self.paused = False self.lock = RLock() def pause(self): self.logInfo('Pausing trade handler') self.paused = True def resume(self): self.logInfo('Resuming trade handler') self.paused = False def process_initial_prices(self): try: if not self.first_processing: return self.first_processing = False tickers = self.fx.get_orderbook_tickers() # tickers = self.fx.get_all_tickers() # prices = {t['symbol']: float(t['price']) for t in tickers} prices = { t['symbol']: { 'b': float(t['bidPrice']), 'a': float(t['askPrice']) } for t in tickers } self.execute_strategies(prices) except Exception: self.logError(traceback.format_exc()) def start_listening(self): # self.init_trades() self.fx.start_listening() self.last_ts = dt.now() def remove_strategy(self, strategy: TradingStrategy, api_call=False): if strategy in self.strategies: self.strategies.remove(strategy) sym = strategy.symbol() if sym in self.strategies_dict: if len(self.strategies_dict[sym]) > 1: self.strategies_dict[sym].remove(strategy) else: self.strategies_dict.pop(sym, None) if strategy.trade.id in self.tradeid_strategy_dict: self.tradeid_strategy_dict.pop(strategy.trade.id, None) if api_call: strategy.set_trade_removed() self.fx.listen_symbols([s.symbol() for s in self.strategies], self.listen_handler, self.user_data_handler) self.socket_message_rcvd = False def add_new_strategy(self, strategy: TradingStrategy, listen_symbols=True): self.strategies.append(strategy) sym = strategy.symbol() if sym in self.strategies_dict: self.strategies_dict[sym].append(strategy) else: self.strategies_dict[sym] = [strategy] self.tradeid_strategy_dict[strategy.trade.id] = strategy # self.balances.update_balances(self.fx.get_all_balances_dict()) if not ExchangeInfo().has_all_symbol(self.strategies_dict.keys()): ExchangeInfo().update(self.fx.get_exchange_info()) if listen_symbols: self.fx.listen_symbols([s.symbol() for s in self.strategies], self.listen_handler, self.user_data_handler) self.socket_message_rcvd = False # def init_strategies(self): # # self.strategies_dict = {s.symbol(): s for s in self.strategies} # self.strategies_dict = {} # for strategy in self.strategies: # sym = strategy.symbol() # # if sym in self.strategies_dict: # self.strategies_dict[sym].append(strategy) # else: # self.strategies_dict[sym] = [strategy] # # self.tradeid_strategy_dict = {s.trade.id: s for s in self.strategies} # # self.balances.update_balances(self.fx.get_all_balances_dict()) # # if not ExchangeInfo().has_all_symbol(self.strategies_dict.keys()): # ExchangeInfo().update(self.fx.get_exchange_info()) # # self.process_initial_prices() # self.fx.listen_symbols([s.symbol() for s in self.strategies], self.listen_handler, self.user_data_handler) # self.socket_message_rcvd = False def stop_listening(self): self.fx.stop_listening() def user_data_handler(self, msg): try: if msg['e'] == 'outboundAccountInfo': self.balances.update_balances({ bal['a']: { 'f': float(bal['f']), 'l': float(bal['l']) } for bal in msg['B'] }) elif msg['e'] == 'executionReport': sym = msg['s'] if sym in self.strategies_dict: for s in self.strategies_dict[sym]: s.on_execution_rpt({ 'orderId': msg['i'], 'status': msg['X'], 'symbol': sym, 'side': msg['S'], 'vol': msg['q'], 'price': msg['p'], 'stop_price': msg['P'] }) # self.strategies_dict[sym].on_execution_rpt( # {'orderId': msg['i'], # 'status': msg['X'], # 'symbol': sym, # 'side': msg['S'], # 'vol': msg['q'], # 'price': msg['p'], # 'stop_price': msg['P']}) except Exception as e: self.logError(traceback.format_exc()) # self.logger.error(str(e)) def listen_handler(self, msg): try: if self.paused: return if not self.socket_message_rcvd: self.confirm_socket_msg_rcvd() delta = dt.now() - self.last_ts if isinstance(msg, list): for ticker in msg: if ticker['s'] in self.strategies_dict and ticker[ 'e'] == '24hrTicker': self.trade_info_ticker_buf[ticker['s']] = { 'b': float(ticker['b']), 'a': float(ticker['a']) } else: d = msg['data'] if 'error' in (msg.get('e', None), d.get('e', None)): self.logError(msg) return elif d['e'] == '24hrTicker': self.trade_info_ticker_buf[d['s']] = { 'b': float(d['b']), 'a': float(d['a']) } # delta = dt.now() - self.last_ts if (delta.seconds * 1000 + (delta).microseconds / 1000) > self.process_delay: self.last_ts = dt.now() self.check_strategies_status() prices = dict(self.trade_info_ticker_buf) self.trade_info_ticker_buf = {} self.execute_strategies(prices) except Exception as e: self.logError(traceback.print_exc()) def execute_strategies(self, prices): with self.lock: for s in self.strategies: if s.symbol() in prices and not s.paused: s.execute(prices[s.symbol()]) def check_strategies_status(self): for s in self.strategies[:]: self.handle_completed_strategy(s) def handle_completed_strategy(self, s): if s.is_completed(): self.remove_trade_by_strategy(s) return s.is_completed() def remove_trade_by_strategy(self, strategy, api_call=False): if not strategy: return with self.lock: self.logInfo('Removing trade [{}]'.format(strategy.symbol())) try: self.stop_listening() self.remove_strategy(strategy, api_call) finally: self.start_listening() def force_reconnect_sockets(self): with self.lock: self.stop_listening() self.fx.listen_symbols([s.symbol() for s in self.strategies], self.listen_handler, self.user_data_handler) self.start_listening() def get_strategy_by_id(self, id) -> TradingStrategy: return self.tradeid_strategy_dict.get(id, None) def emergency_close_by_id(self, id): strategy: TradingStrategy = self.tradeid_strategy_dict.get(id, None) strategy.emergent_close_position() def remove_trade_by_id(self, id, api_call=False, close_trade=False): with self.lock: self.remove_trade_by_strategy( self.tradeid_strategy_dict.get(id, None), api_call) def add_trades(self, trades: [Trade], start_listening=True): self.logInfo('Adding {} trades to the TradeHandler.'.format( len(trades))) # try: self.stop_listening() # if not ExchangeInfo().has_all_symbol(self.strategies_dict.keys()): ExchangeInfo().update(self.fx.get_exchange_info()) AccountBalances().update_balances(self.fx.get_all_balances_dict()) for trade in trades: new_strategy = TargetsAndStopLossStrategy( trade, self.fx, self.order_updated_handler, self.balances.get_balance(trade.asset)) if self.handle_completed_strategy(new_strategy): self.logInfo('Strategy is completed [{}]'.format( new_strategy.symbol())) continue self.add_new_strategy(new_strategy, listen_symbols=False) self.fx.listen_symbols([s.symbol() for s in self.strategies], self.listen_handler, self.user_data_handler) if start_listening: self.start_listening() # except Exception: # self.logError(traceback.format_exc()) # finally: def updated_trade(self, trade: Trade): with self.lock: if trade.id in self.tradeid_strategy_dict: # find by ID # self.strategies_dict[trade.symbol].update_trade(trade) self.tradeid_strategy_dict[trade.id].update_trade(trade) if self.handle_completed_strategy( self.tradeid_strategy_dict[trade.id]): self.logInfo('Strategy is completed [{}]'.format( trade.symbol)) return self.balances.update_balances(self.fx.get_all_balances_dict()) self.logInfo('Updating trade [{}]'.format(trade.symbol)) else: self.logInfo('Adding trade [{}]'.format(trade.symbol)) new_strategy = TargetsAndStopLossStrategy( trade, self.fx, self.order_updated_handler, self.balances.get_balance(trade.asset)) if self.handle_completed_strategy(new_strategy): self.logInfo('Strategy is completed [{}]'.format( new_strategy.symbol())) return try: self.stop_listening() self.add_new_strategy(new_strategy) finally: self.start_listening() def confirm_socket_msg_rcvd(self): self.socket_message_rcvd = True self.logInfo('WebSocket message received') def fire_trade_updated(self, trade, need_cloud_sync): if self.order_updated_handler: self.order_updated_handler(trade, need_cloud_sync)
def secondary_asset_balance(self): return AccountBalances().get_balance(self.secondary_asset())
def self_update_balances(self): AccountBalances().update_balances(self.fx.get_all_balances_dict())