def __on_update_position(self, action, position): """ 포지션 갱신 """ # 포지션 사이즈 변경이 있었는지 체크 is_update_pos_size = self.get_position( )['currentQty'] != position['currentQty'] # 포지션 사이즈가 변경된 경우, Trail 개시가격을 현재의 가격에 리셋한다. if is_update_pos_size and position['currentQty'] != 0: self.set_trail_price(self.market_price) if is_update_pos_size: logger.info( f"Updated Position\n" f"Price: {self.get_position()['avgEntryPrice']} => {position['avgEntryPrice']}\n" f"Qty: {self.get_position()['currentQty']} => {position['currentQty']}\n" f"Balance: {self.get_balance()/100000000} XBT") notify( f"Updated Position\n" f"Price: {self.get_position()['avgEntryPrice']} => {position['avgEntryPrice']}\n" f"Qty: {self.get_position()['currentQty']} => {position['currentQty']}\n" f"Balance: {self.get_balance()/100000000} XBT") self.position = { **self.position, **position } if self.position is not None else self.position # 익손절의 평가 self.eval_exit()
def __on_update_position(self, action, position): """ ポジションを更新する """ # ポジションサイズの変更がされたか is_update_pos_size = self.get_position( )['currentQty'] != position['currentQty'] # ポジションサイズが変更された場合、トレイル開始価格を現在の価格にリセットする if is_update_pos_size and position['currentQty'] != 0: self.set_trail_price(self.market_price) if is_update_pos_size: logger.info( f"Updated Position\n" f"Price: {self.get_position()['avgEntryPrice']} => {position['avgEntryPrice']}\n" f"Qty: {self.get_position()['currentQty']} => {position['currentQty']}\n" f"Balance: {self.get_balance()/100000000} XBT") notify( f"Updated Position\n" f"Price: {self.get_position()['avgEntryPrice']} => {position['avgEntryPrice']}\n" f"Qty: {self.get_position()['currentQty']} => {position['currentQty']}\n" f"Balance: {self.get_balance()/100000000} XBT") self.position = { **self.position, **position } if self.position is not None else self.position # 利確損切の評価 self.eval_exit()
def run(self): if self.hyperopt: raise Exception( "Trading View Strategy dose not support hyperopt Mode.") elif self.back_test: raise Exception( "Trading View Strategy dose not support backtest Mode.") elif self.stub_test: self.exchange = BitMexStub() logger.info(f"Bot Mode : Stub") else: self.exchange = BitMex(demo=self.test_net) logger.info(f"Bot Mode : Trade") logger.info(f"Starting Bot") logger.info(f"Strategy : {type(self).__name__}") logger.info(f"Resolution : {self.resolution()}") logger.info(f"Balance : {self.exchange.get_balance()}") notify(f"Starting Bot\n" f"Strategy : {type(self).__name__}\n" f"Resolution : {self.resolution()}\n" f"Balance : {self.exchange.get_balance()/100000000} XBT") self.subscriber.on_message(self.__on_message)
def __amend_order(self, ord_id, side, ord_qty, limit=0, stop=0): """ 注文を更新する """ if limit > 0 and stop > 0: ord_type = "StopLimit" retry(lambda: self.private_client.Order.Order_amend( origClOrdID=ord_id, orderQty=ord_qty, price=limit, stopPx=stop) .result()) elif limit > 0: ord_type = "Limit" retry(lambda: self.private_client.Order.Order_amend( origClOrdID=ord_id, orderQty=ord_qty, price=limit).result()) elif stop > 0: ord_type = "Stop" retry(lambda: self.private_client.Order.Order_amend( origClOrdID=ord_id, orderQty=ord_qty, stopPx=stop).result()) else: ord_type = "Market" retry(lambda: self.private_client.Order.Order_amend( origClOrdID=ord_id, orderQty=ord_qty).result()) if self.enable_trade_log: logger.info(f"========= Amend Order ==============") logger.info(f"ID : {ord_id}") logger.info(f"Type : {ord_type}") logger.info(f"Side : {side}") logger.info(f"Qty : {ord_qty}") logger.info(f"Limit : {limit}") logger.info(f"Stop : {stop}") logger.info(f"======================================") notify( f"Amend Order\nType: {ord_type}\nSide: {side}\nQty: {ord_qty}\nLimit: {limit}\nStop: {stop}" )
def loop_function(): while self.is_running: try: # retries 10 times over 486secs # before raising error/exception # check binance_futures_api.py line 113 # for implementation details #client.stream_keepalive() listenKey = client.stream_get_listen_key() if self.listenKey != listenKey: logger.info("listenKey Changed!") notify("listenKey Changed!") self.listenKey = listenKey self.ws.close() # Send a heartbeat to Healthchecks.io if self.use_healthcecks: try: requests.get(conf['healthchecks.io'][self.account] ['listenkey_heartbeat']) #logger.info("Listen Key Heart Beat sent!") except Exception as e: pass time.sleep(600) except Exception as e: logger.error(f"Keep Alive Error - {str(e)}") #logger.error(traceback.format_exc()) notify(f"Keep Alive Error - {str(e)}")
def __on_update_position(self, action, position): """ Update position """ # Was the position size changed? is_update_pos_size = self.get_position( )['currentQty'] != position['currentQty'] # Reset trail to current price if position size changes if is_update_pos_size and position['currentQty'] != 0: self.set_trail_price(self.market_price) if is_update_pos_size: if 'avgEntryPrice' not in position: position.update( {'avgEntryPrice': self.get_position()['avgEntryPrice']}) logger.info( f"Updated Position\n" f"Price: {self.get_position()['avgEntryPrice']} => {position['avgEntryPrice']}\n" f"Qty: {self.get_position()['currentQty']} => {position['currentQty']}\n" f"Balance: {self.get_balance()/100000000} XBT") notify( f"Updated Position\n" f"Price: {self.get_position()['avgEntryPrice']} => {position['avgEntryPrice']}\n" f"Qty: {self.get_position()['currentQty']} => {position['currentQty']}\n" f"Balance: {self.get_balance()/100000000} XBT") self.position = { **self.position, **position } if self.position is not None else self.position # Evaluation of profit and loss self.eval_exit() self.eval_sltp()
def run(self): """ ˜ Bot 기동 """ if self.hyperopt: logger.info(f"Bot Mode : Hyperopt") self.params_search() return elif self.stub_test: logger.info(f"Bot Mode : Stub") self.exchange = BitMexStub() elif self.back_test: logger.info(f"Bot Mode : Back test") self.exchange = BitMexBackTest() else: logger.info(f"Bot Mode : Trade") self.exchange = BitMex(demo=self.test_net) self.exchange.ohlcv_len = self.ohlcv_len() self.exchange.on_update(self.bin_size, self.strategy) logger.info(f"Starting Bot") logger.info(f"Strategy : {type(self).__name__}") logger.info(f"Resolution : {self.resolution()}") logger.info(f"Balance : {self.exchange.get_balance()}") notify(f"Starting Bot\n" f"Strategy : {type(self).__name__}\n" f"Resolution : {self.resolution()}\n" f"Balance : {self.exchange.get_balance()/100000000} XBT") self.exchange.show_result()
def run(self): if self.hyperopt: raise Exception( "Trading View Strategy dose not support hyperopt Mode.") elif self.back_test: raise Exception( "Trading View Strategy dose not support backtest Mode.") elif self.stub_test: # if you want to use binance futures # self.exchange = BinanceFuturesStub(account=self.account, pair=self.pair) self.exchange = BitMexStub(account=self.account, pair=self.pair) logger.info(f"Bot Mode : Stub") else: # if you want to use binance #self.exchange = BinanceFutures(account=self.account, pair=self.pair, demo=self.test_net) self.exchange = BitMex(account=self.account, pair=self.pair, demo=self.test_net) logger.info(f"Bot Mode : Trade") logger.info(f"Starting Bot") logger.info(f"Strategy : {type(self).__name__}") logger.info(f"Balance : {self.exchange.get_balance()}") notify(f"Starting Bot\n" f"Strategy : {type(self).__name__}\n" f"Balance : {self.exchange.get_balance()/100000000} XBT") self.subscriber.on_message(self.__on_message)
def __new_order(self, ord_id, side, ord_qty, limit=0, stop=0, post_only=False): """ 注文を作成する """ if limit > 0 and post_only: ord_type = "Limit" retry(lambda: self.private_client.Order.Order_new(symbol="XBTUSD", ordType=ord_type, clOrdID=ord_id, side=side, orderQty=ord_qty, price=limit, execInst='ParticipateDoNotInitiate').result()) elif limit > 0 and stop > 0: ord_type = "StopLimit" retry(lambda: self.private_client.Order.Order_new(symbol="XBTUSD", ordType=ord_type, clOrdID=ord_id, side=side, orderQty=ord_qty, price=limit, stopPx=stop).result()) elif limit > 0: ord_type = "Limit" retry(lambda: self.private_client.Order.Order_new(symbol="XBTUSD", ordType=ord_type, clOrdID=ord_id, side=side, orderQty=ord_qty, price=limit).result()) elif stop > 0: ord_type = "Stop" retry(lambda: self.private_client.Order.Order_new(symbol="XBTUSD", ordType=ord_type, clOrdID=ord_id, side=side, orderQty=ord_qty, stopPx=stop).result()) elif post_only: # market order with post only ord_type = "Limit" i = 0 while True: prices = self.ob.get_prices() limit = prices[1] if side == "Buy" else prices[0] retry(lambda: self.private_client.Order.Order_new(symbol="XBTUSD", ordType=ord_type, clOrdID=ord_id, side=side, orderQty=ord_qty, price=limit, execInst='ParticipateDoNotInitiate').result()) time.sleep(1) if not self.cancel(ord_id): break time.sleep(2) i += 1 if i > 10: notify(f"Order retry count exceed") break self.cancel_all() else: ord_type = "Market" retry(lambda: self.private_client.Order.Order_new(symbol="XBTUSD", ordType=ord_type, clOrdID=ord_id, side=side, orderQty=ord_qty).result()) if self.enable_trade_log: logger.info(f"========= New Order ==============") logger.info(f"ID : {ord_id}") logger.info(f"Type : {ord_type}") logger.info(f"Side : {side}") logger.info(f"Qty : {ord_qty}") logger.info(f"Limit : {limit}") logger.info(f"Stop : {stop}") logger.info(f"======================================") notify(f"New Order\nType: {ord_type}\nSide: {side}\nQty: {ord_qty}\nLimit: {limit}\nStop: {stop}")
def run(self): """ ˜ Function to run the bot """ if self.hyperopt: logger.info(f"Bot Mode : Hyperopt") self.params_search() return elif self.stub_test: logger.info(f"Bot Mode : Stub") if self.exchange_arg == "binance": self.exchange = BinanceFuturesStub(account=self.account, pair=self.pair) elif self.exchange_arg == "bitmex": self.exchange = BitMexStub(account=self.account, pair=self.pair) else: logger.info(f"--exchange argument missing or invalid") return elif self.back_test: logger.info(f"Bot Mode : Back test") if self.exchange_arg == "binance": self.exchange = BinanceFuturesBackTest(account=self.account, pair=self.pair) elif self.exchange_arg == "bitmex": self.exchange = BitMexBackTest(account=self.account, pair=self.pair) else: logger.info(f"--exchange argument missing or invalid") return else: logger.info(f"Bot Mode : Trade") if self.exchange_arg == "binance": self.exchange = BinanceFutures(account=self.account, pair=self.pair, demo=self.test_net) elif self.exchange_arg == "bitmex": self.exchange = BitMex(account=self.account, pair=self.pair, demo=self.test_net) else: logger.info(f"--exchange argument missing or invalid") return self.exchange.ohlcv_len = self.ohlcv_len() self.exchange.on_update(self.bin_size, self.strategy) logger.info(f"Starting Bot") logger.info(f"Strategy : {type(self).__name__}") logger.info(f"Balance : {self.exchange.get_balance()}") notify(f"Starting Bot\n" f"Strategy : {type(self).__name__}\n" f"Balance : {self.exchange.get_balance()}") self.exchange.show_result()
def __amend_order(self, ord_id, side, ord_qty, limit=0, stop=0, post_only=False): """ 주문 갱신 """ try: if limit > 0 and stop > 0: ord_type = "StopLimit" retry(lambda: self.private_client.Order.Order_amend( origClOrdID=ord_id, orderQty=ord_qty, price=limit, stopPx=stop).result()) elif limit > 0: ord_type = "Limit" retry(lambda: self.private_client.Order.Order_amend( origClOrdID=ord_id, orderQty=ord_qty, price=limit).result( )) elif stop > 0: ord_type = "Stop" retry(lambda: self.private_client.Order.Order_amend( origClOrdID=ord_id, orderQty=ord_qty, stopPx=stop).result( )) elif post_only: # market order with post only ord_type = "Limit" prices = self.ob.get_prices() limit = prices[1] if side == "Buy" else prices[0] retry(lambda: self.private_client.Order.Order_amend( origClOrdID=ord_id, orderQty=ord_qty, price=limit).result( )) else: ord_type = "Market" retry(lambda: self.private_client.Order.Order_amend( origClOrdID=ord_id, orderQty=ord_qty).result()) except Exception as e: logger.error('Exception: __amend_order : %s' % e) if self.enable_trade_log: logger.info(f"========= Amend Order ==============") logger.info(f"ID : {ord_id}") logger.info(f"Type : {ord_type}") logger.info(f"Side : {side}") logger.info(f"Qty : {ord_qty}") logger.info(f"Limit : {limit}") logger.info(f"Stop : {stop}") logger.info(f"======================================") notify( f"Amend Order\nType: {ord_type}\nSide: {side}\nQty: {ord_qty}\nLimit: {limit}\nStop: {stop}" )
def __on_error(self, ws, message): """ WebSokcetでエラーが発生した場合 :param ws: :param message: """ logger.error(message) logger.error(traceback.format_exc()) notify(f"Error occurred. {message}") notify(traceback.format_exc())
def __new_order(self, ord_id, side, ord_qty, limit=0, stop=0): """ 注文を作成する """ if limit > 0 and stop > 0: ord_type = "StopLimit" retry(lambda: self.private_client.Order.Order_new(symbol="XBTUSD", ordType=ord_type, clOrdID=ord_id, side=side, orderQty=ord_qty, price=limit, stopPx=stop). result()) elif limit > 0: ord_type = "Limit" retry(lambda: self.private_client.Order.Order_new(symbol="XBTUSD", ordType=ord_type, clOrdID=ord_id, side=side, orderQty=ord_qty, price=limit). result()) elif stop > 0: ord_type = "Stop" retry(lambda: self.private_client.Order.Order_new(symbol="XBTUSD", ordType=ord_type, clOrdID=ord_id, side=side, orderQty=ord_qty, stopPx=stop). result()) else: ord_type = "Market" retry(lambda: self.private_client.Order.Order_new(symbol="XBTUSD", ordType=ord_type, clOrdID=ord_id, side=side, orderQty=ord_qty) .result()) if self.enable_trade_log: logger.info(f"========= New Order ==============") logger.info(f"ID : {ord_id}") logger.info(f"Type : {ord_type}") logger.info(f"Side : {side}") logger.info(f"Qty : {ord_qty}") logger.info(f"Limit : {limit}") logger.info(f"Stop : {stop}") logger.info(f"======================================") notify( f"New Order\nType: {ord_type}\nSide: {side}\nQty: {ord_qty}\nLimit: {limit}\nStop: {stop}" )
def __on_error(self, ws, message): """ On Error listener :param ws: :param message: """ logger.error(message) logger.error(traceback.format_exc()) notify(f"Error occurred. {message}") notify(traceback.format_exc())
def __on_update_margin(self, action, margin): """ Update margin """ if self.margin is not None: self.margin[0] = { "asset": self.quote_asset, "balance": float(margin['wb']), "crossWalletBalance": float(margin['cw']) } else: self.get_margin() notify(f"Balance: {self.margin[0]['balance']}") logger.info(f"Balance: {self.margin[0]['balance']} Cross Balance: {self.margin[0]['crossWalletBalance']}")
def __update_ohlcv(self, action, new_data): """ 데이터를 취득한 후, 전략을 실행 데이터가 없으면 서버와 접속해서 다운로드를 처음 함, 그 후는 어떻게 할까 """ if self.data is None: end_time = datetime.now(timezone.utc) start_time = end_time - self.ohlcv_len * delta(self.bin_size) d1 = self.fetch_ohlcv(self.bin_size, start_time, end_time) if len(d1) > 0: d2 = self.fetch_ohlcv( allowed_range[self.bin_size][0], d1.iloc[-1].name + delta(allowed_range[self.bin_size][0]), end_time) self.data = pd.concat([d1, d2], sort=True) else: self.data = d1 else: self.data = pd.concat([self.data, new_data], sort=True) # 마지막행은 불학정정보이기에 베제한다. (original) # re_sample_data = resample(self.data, self.bin_size)[:-1] # 마지막행도 반영한다. (by neo) re_sample_data = resample(self.data, self.bin_size)[:] if self.data.iloc[-1].name == re_sample_data.iloc[-1].name: self.data = re_sample_data.iloc[-1 * self.ohlcv_len:, :] if self.last_action_time is not None and \ self.last_action_time == re_sample_data.iloc[-1].name: return open = re_sample_data['open'].values close = re_sample_data['close'].values high = re_sample_data['high'].values low = re_sample_data['low'].values volume = re_sample_data['volume'].values try: if self.strategy is not None: self.strategy(open, close, high, low, volume) self.last_action_time = re_sample_data.iloc[-1].name except FatalError as e: # 致命的エラー logger.error(f"Fatal error. {e}") logger.error(traceback.format_exc()) notify(f"Fatal error occurred. Stopping Bot. {e}") notify(traceback.format_exc()) self.stop() except Exception as e: logger.error(f"An error occurred. {e}") logger.error(traceback.format_exc()) notify(f"An error occurred. {e}") notify(traceback.format_exc())
def __update_ohlcv(self, action, new_data): new_data = new_data.rename(index={new_data.iloc[0].name: new_data.iloc[0].name.ceil(freq="1T")}) """ get OHLCV data and execute the strategy """ if self.data is None: end_time = datetime.now(timezone.utc) start_time = end_time - self.ohlcv_len * delta(self.bin_size) # logger.info(f"start time fetch ohlcv: {start_time}") # logger.info(f"end time fetch ohlcv: {end_time}") self.data = self.fetch_ohlcv(self.bin_size, start_time, end_time) if self.data.iloc[-1].name > end_time: last_candle = self.data.iloc[-1].values self.data = self.data[:-1] self.data.loc[end_time.replace(microsecond=0)] = last_candle logger.info(f"Initial Buffer Fill - Last Candle: {self.data.iloc[-1].name}") else: if self.data.iloc[-1].name == new_data.iloc[0].name: self.data = pd.concat([self.data[:-1], new_data]) else: self.data = pd.concat([self.data, new_data]) # exclude current candle data re_sample_data = resample(self.data, self.bin_size)[:-1] if self.last_action_time is not None and self.last_action_time == re_sample_data.iloc[-1].name: return self.data = pd.concat([re_sample_data.iloc[-1 * self.ohlcv_len :, :], self.data.iloc[[-1]]]) open = re_sample_data["open"].values close = re_sample_data["close"].values high = re_sample_data["high"].values low = re_sample_data["low"].values volume = re_sample_data["volume"].values try: if self.strategy is not None: self.timestamp = re_sample_data.iloc[-1].name.isoformat() self.strategy(open, close, high, low, volume) self.eval_exit() self.last_action_time = re_sample_data.iloc[-1].name except FatalError as e: # Fatal error logger.error(f"Fatal error. {e}") logger.error(traceback.format_exc()) notify(f"Fatal error occurred. Stopping Bot. {e}") notify(traceback.format_exc()) self.stop() except Exception as e: logger.error(f"An error occurred. {e}") logger.error(traceback.format_exc()) notify(f"An error occurred. {e}") notify(traceback.format_exc())
def __update_ohlcv(self, action, new_data): """ get OHLCV data and execute the strategy """ if self.data is None: end_time = datetime.now(timezone.utc) start_time = end_time - self.ohlcv_len * delta(self.bin_size) #logger.info(f"start time fetch ohlcv: {start_time}") #logger.info(f"end time fetch ohlcv: {end_time}") d1 = self.fetch_ohlcv(self.bin_size, start_time, end_time) if len(d1) > 0: d2 = self.fetch_ohlcv( allowed_range[self.bin_size][0], d1.iloc[-1].name + delta(allowed_range[self.bin_size][0]), end_time) self.data = pd.concat([d1, d2]) else: self.data = d1 else: self.data = pd.concat([self.data, new_data]) # exclude current candle data re_sample_data = resample(self.data, self.bin_size)[:-1] if self.data.iloc[-1].name == re_sample_data.iloc[-1].name: self.data = re_sample_data.iloc[-1 * self.ohlcv_len:, :] if self.last_action_time is not None and \ self.last_action_time == re_sample_data.iloc[-1].name: return open = re_sample_data['open'].values close = re_sample_data['close'].values high = re_sample_data['high'].values low = re_sample_data['low'].values volume = re_sample_data['volume'].values try: if self.strategy is not None: self.strategy(open, close, high, low, volume) self.last_action_time = re_sample_data.iloc[-1].name except FatalError as e: # Fatal error logger.error(f"Fatal error. {e}") logger.error(traceback.format_exc()) notify(f"Fatal error occurred. Stopping Bot. {e}") notify(traceback.format_exc()) self.stop() except Exception as e: logger.error(f"An error occurred. {e}") logger.error(traceback.format_exc()) notify(f"An error occurred. {e}") notify(traceback.format_exc())
def __on_update_position(self, action, position): """ Update position """ if len(position) > 0: position = [p for p in position if p["s"].startswith(self.pair)] if len(position) == 0: # logger.info(f"Some other pair was traded!") return else: return # Was the position size changed? is_update_pos_size = self.get_position_size() != float(position[0]['pa']) # Reset trail to current price if position size changes if is_update_pos_size and float(position[0]['pa']) != 0: self.set_trail_price(self.market_price) if is_update_pos_size: logger.info(f"Updated Position\n" f"Price: {self.position[0]['entryPrice']} => {position[0]['ep']}\n" f"Qty: {self.position[0]['positionAmt']} => {position[0]['pa']}\n" f"Balance: {self.get_balance()} {self.quote_asset}") notify(f"Updated Position\n" f"Price: {self.position[0]['entryPrice']} => {position[0]['ep']}\n" f"Qty: {self.position[0]['positionAmt']} => {position[0]['pa']}\n" f"Balance: {self.get_balance()} {self.quote_asset}") self.position[0] = { "entryPrice": position[0]['ep'], "marginType": position[0]['mt'], "positionAmt": position[0]['pa'], "symbol": position[0]['s'], "unRealizedProfit": position[0]['up'], "positionSide": position[0]['ps'], } if self.position is not None else self.position[0] self.position_size = float(self.position[0]['positionAmt']) self.entry_price = float(self.position[0]['entryPrice']) # Evaluation of profit and loss self.eval_exit() self.eval_sltp()
def __update_ohlcv(self, action, new_data): """ データを取得して、戦略を実行する。 """ if self.data is None: end_time = datetime.now(timezone.utc) start_time = end_time - self.ohlcv_len * delta(self.bin_size) d1 = self.fetch_ohlcv(self.bin_size, start_time, end_time) if len(d1) > 0: d2 = self.fetch_ohlcv(allowed_range[self.bin_size][0], d1.iloc[-1].name + delta(allowed_range[self.bin_size][0]), end_time) self.data = pd.concat([d1, d2], sort=True) else: self.data = d1 else: self.data = pd.concat([self.data, new_data], sort=True) # 最後の行は不確定情報のため、排除する re_sample_data = resample(self.data, self.bin_size)[:-1] if self.data.iloc[-1].name == re_sample_data.iloc[-1].name: self.data = re_sample_data.iloc[-1 * self.ohlcv_len:, :] if self.last_action_time is not None and \ self.last_action_time == re_sample_data.iloc[-1].name: return open = re_sample_data['open'].values close = re_sample_data['close'].values high = re_sample_data['high'].values low = re_sample_data['low'].values volume = re_sample_data['volume'].values try: if self.strategy is not None: self.strategy(open, close, high, low, volume) self.last_action_time = re_sample_data.iloc[-1].name except FatalError as e: # 致命的エラー logger.error(f"Fatal error. {e}") logger.error(traceback.format_exc()) notify(f"Fatal error occurred. Stopping Bot. {e}") notify(traceback.format_exc()) self.stop() except Exception as e: logger.error(f"An error occurred. {e}") logger.error(traceback.format_exc()) notify(f"An error occurred. {e}") notify(traceback.format_exc())
def __on_update_position(self, action, position): """ Update position """ if len(position) > 0: position = [p for p in position if p["s"] == self.pair] # logger.info(f'position: {position}') # Was the position size changed? if len(position) == 1: is_update_pos_size = self.get_position_size != float(position[0]["pa"]) # Reset trail to current price if position size changes if is_update_pos_size and float(position[0]["pa"]) != 0: self.set_trail_price(self.market_price) if is_update_pos_size: logger.info(f"Updated Position") logger.info(f"Price: {self.position[0]['entryPrice']} => {position[0]['ep']}") logger.info(f"Qty: {self.position[0]['positionAmt']} => {position[0]['pa']}") logger.info(f"Balance: {self.get_balance()} USDT") notify(f"Updated Position\n" f"Price: {self.position[0]['entryPrice']} => {position[0]['ep']}\n" f"Qty: {self.position[0]['positionAmt']} => {position[0]['pa']}\n" f"Balance: {self.get_balance()} USDT") self.position[0] = ( { "entryPrice": position[0]["ep"], "marginType": position[0]["mt"], "positionAmt": position[0]["pa"], "symbol": position[0]["s"], "unRealizedProfit": position[0]["up"], "positionSide": position[0]["ps"], } if self.position is not None else self.position[0] ) self.position_size = float(self.position[0]["positionAmt"]) self.entry_price = float(self.position[0]["entryPrice"]) # Evaluation of profit and loss self.eval_exit() self.eval_sltp()
def __on_close(self, ws): """ On Close Listener :param ws: """ if 'close' in self.handlers: self.handlers['close']() if self.is_running: logger.info("Websocket restart") notify(f"Websocket restart") self.ws = websocket.WebSocketApp(self.endpoint, on_message=self.__on_message, on_error=self.__on_error, on_close=self.__on_close, header=self.__get_auth()) self.wst = threading.Thread(target=self.__start) self.wst.daemon = True self.wst.start()
def __on_close(self, ws): """ On Close Listener :param ws: """ if 'close' in self.handlers: self.handlers['close']() if self.is_running: logger.info(f"Websocket On Close: Restart") notify(f"Websocket On Close: Restart") time.sleep(60) # Listen Key can change after disconnects, so the url can change too self.ws = websocket.WebSocketApp(self.__get_wss_endpoint(), on_message=self.__on_message, on_error=self.__on_error, on_close=self.__on_close) self.wst = threading.Thread(target=self.__start) self.wst.daemon = True self.wst.start()
""" if key in self.handlers: self.handlers[key](action, value) def __on_close(self, ws): """ クローズした場合 :param ws: """ if 'close' in self.handlers: self.handlers['close']() <<<<<<< HEAD if self.is_running: logger.info("Websocket restart") notify(f"Websocket restart") self.ws = websocket.WebSocketApp(self.endpoint, on_message=self.__on_message, on_error=self.__on_error, on_close=self.__on_close, header=self.__get_auth()) self.wst = threading.Thread(target=self.__start) self.wst.daemon = True self.wst.start() def on_close(self, func): """ クローズの通知先を登録する。 :param func: """
def __update_ohlcv(self, action, new_data): # Binance can output wierd timestamps - Eg. 2021-05-25 16:04:59.999000+00:00 # We need to round up to the nearest second for further processing new_data = new_data.rename(index={new_data.iloc[0].name: new_data.iloc[0].name.ceil(freq='1T')}) """ get OHLCV data and execute the strategy """ if self.data is None: end_time = datetime.now(timezone.utc) start_time = end_time - self.ohlcv_len * delta(self.bin_size) #logger.info(f"start time fetch ohlcv: {start_time}") #logger.info(f"end time fetch ohlcv: {end_time}") self.data = self.fetch_ohlcv(self.bin_size, start_time, end_time) # The last candle is an incomplete candle with timestamp # in future if(self.data.iloc[-1].name > end_time): last_candle = self.data.iloc[-1].values # Store last candle self.data = self.data[:-1] # exclude last candle self.data.loc[end_time.replace(microsecond=0)] = last_candle #set last candle to end_time logger.info(f"Initial Buffer Fill - Last Candle: {self.data.iloc[-1].name}") else: #replace latest candle if timestamp is same or append if(self.data.iloc[-1].name == new_data.iloc[0].name): self.data = pd.concat([self.data[:-1], new_data]) else: self.data = pd.concat([self.data, new_data]) # exclude current candle data re_sample_data = resample(self.data, self.bin_size)[:-1] # logger.info(f"{self.last_action_time} : {self.data.iloc[-1].name} : {re_sample_data.iloc[-1].name}") if self.last_action_time is not None and \ self.last_action_time == re_sample_data.iloc[-1].name: return # The last candle in the buffer needs to be preserved # while resetting the buffer as it may be incomlete # or contains latest data from WS self.data = pd.concat([re_sample_data.iloc[-1 * self.ohlcv_len:, :], self.data.iloc[[-1]]]) #logger.info(f"Buffer Right Edge: {self.data.iloc[-1]}") open = re_sample_data['open'].values close = re_sample_data['close'].values high = re_sample_data['high'].values low = re_sample_data['low'].values volume = re_sample_data['volume'].values try: if self.strategy is not None: self.timestamp = re_sample_data.iloc[-1].name.isoformat() self.strategy(open, close, high, low, volume) self.last_action_time = re_sample_data.iloc[-1].name except FatalError as e: # Fatal error logger.error(f"Fatal error. {e}") logger.error(traceback.format_exc()) notify(f"Fatal error occurred. Stopping Bot. {e}") notify(traceback.format_exc()) self.stop() except Exception as e: logger.error(f"An error occurred. {e}") logger.error(traceback.format_exc()) notify(f"An error occurred. {e}") notify(traceback.format_exc())
def __new_order(self, ord_id, side, ord_qty, limit=0, stop=0, post_only=False, reduce_only=False, trailing_stop=0, activationPrice=0): """ create an order """ #removes "+" from order suffix, because of the new regular expression rule for newClientOrderId updated as ^[\.A-Z\:/a-z0-9_-]{1,36}$ (2021-01-26) ord_id = ord_id.replace("+", "k") if trailing_stop > 0 and activationPrice > 0: ord_type = "TRAILING_STOP_MARKET" retry(lambda: self.client.futures_create_order( symbol=self.pair, type=ord_type, newClientOrderId=ord_id, side=side, quantity=ord_qty, activationPrice=activationPrice, callbackRate=trailing_stop)) elif trailing_stop > 0: ord_type = "TRAILING_STOP_MARKET" retry(lambda: self.client.futures_create_order( symbol=self.pair, type=ord_type, newClientOrderId=ord_id, side=side, quantity=ord_qty, callbackRate=trailing_stop)) elif limit > 0 and post_only: ord_type = "LIMIT" retry(lambda: self.client.futures_create_order(symbol=self.pair, type=ord_type, newClientOrderId= ord_id, side=side, quantity=ord_qty, price=limit, timeInForce="GTX")) elif limit > 0 and stop > 0 and reduce_only: ord_type = "STOP" retry(lambda: self.client.futures_create_order(symbol=self.pair, type=ord_type, newClientOrderId= ord_id, side=side, quantity=ord_qty, price=limit, stopPrice=stop, reduceOnly="true")) elif limit > 0 and reduce_only: ord_type = "LIMIT" retry(lambda: self.client.futures_create_order(symbol=self.pair, type=ord_type, newClientOrderId= ord_id, side=side, quantity=ord_qty, price=limit, reduceOnly="true", timeInForce="GTC")) elif limit > 0 and stop > 0: ord_type = "STOP" retry(lambda: self.client.futures_create_order(symbol=self.pair, type=ord_type, newClientOrderId= ord_id, side=side, quantity=ord_qty, price=limit, stopPrice=stop)) elif limit > 0: ord_type = "LIMIT" retry(lambda: self.client.futures_create_order(symbol=self.pair, type=ord_type, newClientOrderId= ord_id, side=side, quantity=ord_qty, price=limit, timeInForce="GTC")) elif stop > 0 and reduce_only: ord_type = "STOP_MARKET" retry(lambda: self.client.futures_create_order(symbol=self.pair, type=ord_type, newClientOrderId= ord_id, side=side, quantity=ord_qty, stopPrice=stop, reduceOnly="true")) elif stop > 0: ord_type = "STOP" retry(lambda: self.client.futures_create_order(symbol=self.pair, type=ord_type, newClientOrderId= ord_id, side=side, quantity=ord_qty, stopPrice=stop)) elif post_only: # limit order with post only ord_type = "LIMIT" i = 0 while True: prices = self.get_orderbook_ticker() limit = float(prices['bidPrice']) if side == "Buy" else float( prices['askPrice']) retry(lambda: self.client.futures_create_order( symbol=self.pair, type=ord_type, newClientOrderId=ord_id, side=side, quantity=ord_qty, price=limit, timeInForce="GTX")) time.sleep(4) self.cancel(ord_id) if float(self.get_position()['positionAmt']) > 0: break i += 1 if i > 10: notify(f"Order retry count exceed") break self.cancel_all() else: ord_type = "MARKET" retry(lambda: self.client.futures_create_order(symbol=self.pair, type=ord_type, newClientOrderId= ord_id, side=side, quantity=ord_qty)) if self.enable_trade_log: logger.info(f"========= New Order ==============") logger.info(f"ID : {ord_id}") logger.info(f"Type : {ord_type}") logger.info(f"Side : {side}") logger.info(f"Qty : {ord_qty}") logger.info(f"Limit : {limit}") logger.info(f"Stop : {stop}") logger.info(f"======================================") notify( f"New Order\nType: {ord_type}\nSide: {side}\nQty: {ord_qty}\nLimit: {limit}\nStop: {stop}" )
def __update_ohlcv(self, action, new_data): """ get and update OHLCV data and execute the strategy """ # Binance can output wierd timestamps - Eg. 2021-05-25 16:04:59.999000+00:00 # We need to round up to the nearest second for further processing new_data = new_data.rename(index={new_data.iloc[0].name: new_data.iloc[0].name.ceil(freq='1T')}) if self.timeframe_data is None: self.timeframe_data = {} for t in self.bin_size: bin_size = t end_time = datetime.now(timezone.utc) start_time = end_time - self.ohlcv_len * delta(bin_size) self.timeframe_data[bin_size] = self.fetch_ohlcv(bin_size, start_time, end_time) self.timeframe_info[bin_size] = { "allowed_range": allowed_range_minute_granularity[t][0] if self.minute_granularity else allowed_range[t][0], "ohlcv": self.timeframe_data[t][:-1], # Dataframe with closed candles "last_action_time": None,#self.timeframe_data[bin_size].iloc[-1].name, # Last strategy execution time "last_candle": self.timeframe_data[bin_size].iloc[-2].values, # Store last complete candle "partial_candle": self.timeframe_data[bin_size].iloc[-1].values # Store incomplete candle } # The last candle is an incomplete candle with timestamp in future if self.timeframe_data[bin_size].iloc[-1].name > end_time: last_candle = self.timeframe_data[t].iloc[-1].values # Store last candle self.timeframe_data[bin_size] = self.timeframe_data[t][:-1] # Exclude last candle self.timeframe_data[bin_size].loc[end_time.replace(microsecond=0)] = last_candle #set last candle to end_time logger.info(f"Initial Buffer Fill - Last Candle: {self.timeframe_data[bin_size].iloc[-1].name}") #logger.info(f"{self.timeframe_data}") timeframes_to_update = [] for t in self.timeframe_info: if self.timeframe_info[t]["allowed_range"] == action: # append minute count of a timeframe when sorting when sorting is need otherwise just add a string timeframe timeframes_to_update.append(allowed_range_minute_granularity[t][3]) if self.timeframes_sorted != None else timeframes_to_update.append(t) # Sorting timeframes that will be updated if self.timeframes_sorted == True: timeframes_to_update.sort(reverse=True) if self.timeframes_sorted == False: timeframes_to_update.sort(reverse=False) #logger.info(f"timefeames to update: {timeframes_to_update}") for t in timeframes_to_update: # Find timeframe string based on its minute count value if self.timeframes_sorted != None: t = find_timeframe_string(t) # replace latest candle if timestamp is same or append if self.timeframe_data[t].iloc[-1].name == new_data.iloc[0].name: self.timeframe_data[t] = pd.concat([self.timeframe_data[t][:-1], new_data]) else: self.timeframe_data[t] = pd.concat([self.timeframe_data[t], new_data]) # exclude current candle data and store partial candle data re_sample_data = resample(self.timeframe_data[t], t, minute_granularity=True if self.minute_granularity else False) self.timeframe_info[t]['partial_candle'] = re_sample_data.iloc[-1].values # store partial candle data re_sample_data =re_sample_data[:-1] # exclude current candle data #logger.info(f"{self.timeframe_info[t]['last_action_time']} : {self.timeframe_data[t].iloc[-1].name} : {re_sample_data.iloc[-1].name}") if self.timeframe_info[t]["last_action_time"] is None: self.timeframe_info[t]["last_action_time"] = re_sample_data.iloc[-1].name if self.timeframe_info[t]["last_action_time"] == re_sample_data.iloc[-1].name: continue # The last candle in the buffer needs to be preserved # while resetting the buffer as it may be incomlete # or contains latest data from WS self.timeframe_data[t] = pd.concat([re_sample_data.iloc[-1 * self.ohlcv_len:, :], self.timeframe_data[t].iloc[[-1]]]) #store ohlcv dataframe to timeframe_info dictionary self.timeframe_info[t]["ohlcv"] = re_sample_data #logger.info(f"Buffer Right Edge: {self.data.iloc[-1]}") open = re_sample_data['open'].values close = re_sample_data['close'].values high = re_sample_data['high'].values low = re_sample_data['low'].values volume = re_sample_data['volume'].values try: if self.strategy is not None: self.timestamp = re_sample_data.iloc[-1].name.isoformat() self.strategy(t, open, close, high, low, volume) self.timeframe_info[t]['last_action_time'] = re_sample_data.iloc[-1].name except FatalError as e: # Fatal error logger.error(f"Fatal error. {e}") logger.error(traceback.format_exc()) notify(f"Fatal error occurred. Stopping Bot. {e}") notify(traceback.format_exc()) self.stop() except Exception as e: logger.error(f"An error occurred. {e}") logger.error(traceback.format_exc()) notify(f"An error occurred. {e}") notify(traceback.format_exc())