From ddd1df7957f62cc07e3cd955a050b7fe64ead759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20S=C3=A1nchez?= Date: Fri, 5 Sep 2025 10:52:14 -0300 Subject: [PATCH] 2025.09.05 --- changelog.txt | 5 +++- exchange_wrapper.py | 64 ++++++++++++++++++++++----------------------- main.py | 11 +++++--- trader.py | 43 ++++++++++++++++-------------- 4 files changed, 67 insertions(+), 56 deletions(-) diff --git a/changelog.txt b/changelog.txt index 96fe8d5..2c7b780 100755 --- a/changelog.txt +++ b/changelog.txt @@ -1,8 +1,11 @@ -next: +2025.09.05: . Now the trader supports multiple safety orders at the same time. . Removed forcing orders when importing a trader. Maybe it will be reinstated at a later date. . Removed endpoint /reload_safety_orders. . New endpoints: /mod_concurrent_safety orders, /mod_boosted_concurrent_safety_orders and /force_trader_close. +. Modified cleanup routine. +. Default wait_time back to 0.5 seconds. +. General optimizations. 2025.08.19: . Improved log trimming. diff --git a/exchange_wrapper.py b/exchange_wrapper.py index 15453e6..f37a178 100755 --- a/exchange_wrapper.py +++ b/exchange_wrapper.py @@ -14,7 +14,7 @@ class Broker: self.broker_config = broker_config self.exchange = exchange self.last_price = 0 - self.wait_time = 1 #Default wait time for API breathing room + self.wait_time = .5 #Default wait time for API breathing room self.cooldown_multiplier = 2 #Default cooldown multiplier value if "cooldown_multiplier" in self.broker_config: self.cooldown_multiplier = self.broker_config["cooldown_multiplier"] @@ -869,38 +869,38 @@ class Broker: return "the lowest price limit for sell orders is" in str(error_object).lower() - def new_limit_orders(self, orders: list) -> list: - sent_orders = [] - #Send the orders - tries = self.retries - while tries>=0: - try: - sent_orders = self.exchange.create_orders(orders) - except Exception as e: - self.logger.log_this(f"Exception while sending safety orders: {e}",1) - tries-=1 - time.sleep(self.wait_time) - if tries==0: - return [] + # def new_limit_orders(self, orders: list) -> list: + # sent_orders = [] + # #Send the orders + # tries = self.retries + # while tries>=0: + # try: + # sent_orders = self.exchange.create_orders(orders) + # except Exception as e: + # self.logger.log_this(f"Exception while sending safety orders: {e}",1) + # tries-=1 + # time.sleep(self.wait_time) + # if tries==0: + # return [] - #Retrieve the orders from the exchange by id to confirm that they were sent - #Specially for OKX, since the orders that create_orders return are empty (only id is present) - returned_orders = [] - for order in sent_orders: - tries = self.retries - while tries>=0: - try: - returned_orders.append(self.get_order(order["id"],order["symbol"])) - time.sleep(self.wait_time) - except Exception as e: - self.logger.log_this(f"Exception while retrieving safety orders: {e}",1) - tries-=1 - if tries==0: - if self.get_exchange_name()=="okex": - return returned_orders - returned_orders.append(order) #In the case of the other exchanges, we just assume that the order was sent and append it. - time.sleep(self.wait_time) - return returned_orders + # #Retrieve the orders from the exchange by id to confirm that they were sent + # #Specially for OKX, since the orders that create_orders return are empty (only id is present) + # returned_orders = [] + # for order in sent_orders: + # tries = self.retries + # while tries>=0: + # try: + # returned_orders.append(self.get_order(order["id"],order["symbol"])) + # time.sleep(self.wait_time) + # except Exception as e: + # self.logger.log_this(f"Exception while retrieving safety orders: {e}",1) + # tries-=1 + # if tries==0: + # if self.get_exchange_name()=="okex": + # return returned_orders + # returned_orders.append(order) #In the case of the other exchanges, we just assume that the order was sent and append it. + # time.sleep(self.wait_time) + # return returned_orders def new_limit_order(self,symbol,size,side,price,no_retries=False): diff --git a/main.py b/main.py index 7f8065b..6b4ef8e 100644 --- a/main.py +++ b/main.py @@ -18,7 +18,7 @@ import exchange_wrapper import trader -version = "2025.09.04" +version = "2025.09.05" ''' Color definitions. If you want to change them, check the reference at https://en.wikipedia.org/wiki/ANSI_escape_code#Colors @@ -311,14 +311,19 @@ def main_routine(): futures = [] pairs_to_fetch = [] online_pairs = [] + open_orders_dict = {} for instance in running_traders: pairs_to_fetch.append(instance.status.get_pair()) - open_orders = broker.fetch_open_orders(pairs_to_fetch) + for item in broker.fetch_open_orders(pairs_to_fetch): + if item["symbol"] not in open_orders_dict: + open_orders_dict[item["symbol"]] = [item] + else: + open_orders_dict[item["symbol"]].append(item) for instance in running_traders: - future = executor.submit(instance.check_status, open_orders) + future = executor.submit(instance.check_status, open_orders_dict[instance.status.get_pair()]) futures.append(future) online_pairs.append(f"{instance.base}{instance.quote}") diff --git a/trader.py b/trader.py index 2e1c9b2..cada2e6 100755 --- a/trader.py +++ b/trader.py @@ -441,19 +441,23 @@ class trader: if balance_to_clean is None: self.broker.logger.log_this("Can't fetch free base",1,self.status.get_pair()) return 1 - #balance_to_clean /= 2 #Maybe it's a good idea, sort of DCAing the dust. + balance_to_clean /= 2 #Maybe it's a good idea, sort of DCAing the dust. min_base_size = self.broker.get_min_base_size(self.status.get_pair()) - minimum_cleanup_size = self.status.get_safety_orders()[0]["amount"]*2 + #minimum_cleanup_size = self.status.get_safety_orders()[0]["amount"]*2 - if balance_to_clean-minimum_cleanup_size >= min_base_size: - self.broker.logger.log_this(f"Balance to clean: {balance_to_clean-minimum_cleanup_size} {self.base}",2,self.status.get_pair()) + if balance_to_clean >= min_base_size: + self.broker.logger.log_this(f"Balance to clean: {balance_to_clean} {self.base}",2,self.status.get_pair()) self.broker.logger.log_this("Sending cleanup order...",2,self.status.get_pair()) - cleanup_order = self.broker.new_limit_order(self.status.get_pair(),balance_to_clean-minimum_cleanup_size,"sell",self.status.get_take_profit_price()) - if cleanup_order not in [None,self.broker.get_empty_order()]: + cleanup_order = self.broker.new_limit_order(self.status.get_pair(),balance_to_clean,"sell",self.status.get_take_profit_price()) + if cleanup_order is None: + self.broker.logger.log_this("Problems with the cleanup order, new_limit_order returned None",1,self.status.get_pair()) + return 1 + elif cleanup_order==self.broker.get_empty_order(): + self.broker.logger.log_this("Problems with the cleanup order, new_limit_order returned an empty order",1,self.status.get_pair()) + return 1 + else: self.broker.logger.log_this("Cleanup successful",2,self.status.get_pair()) return 0 - self.broker.logger.log_this("Problems with the cleanup order",1,self.status.get_pair()) - return 1 self.broker.logger.log_this("No cleanup needed",2,self.status.get_pair()) return 0 @@ -1134,8 +1138,7 @@ class trader: #Extract ids from order list self.status.set_pause_reason("filtering open orders") - open_orders_list = [order for order in open_orders if order["symbol"]==self.status.get_pair()] - open_orders_ids = [order["id"] for order in open_orders_list] + open_orders_ids = [order["id"] for order in open_orders] self.status.set_pause_reason("check for tp_order is valid") #Checks if the take profit order is valid @@ -1158,16 +1161,16 @@ class trader: self.status.set_pause_reason("check if tp_order is filled") #Checks if the take profit order is filled if self.status.get_take_profit_order()["id"] not in open_orders_ids: - # I hate Kucoin: - # Check if the order has a wrong id. If so, update the order. - for order in open_orders_list: - if order["amount"]==self.status.get_take_profit_order()["amount"] and order["price"]==self.status.get_take_profit_order()["price"] and order["side"]==self.status.get_take_profit_order()["side"]: - #Right order, wrong id. Update order - self.broker.logger.log_this(f"Updating take profit order for {self.status.get_pair()}") - self.status.set_take_profit_order(order) - self.update_status(True) - return 0 - + # # I hate Kucoin: + # # Check if the order has a wrong id. If so, update the order. + # for order in open_orders: + # if order["amount"]==self.status.get_take_profit_order()["amount"] and order["price"]==self.status.get_take_profit_order()["price"] and order["side"]==self.status.get_take_profit_order()["side"]: + # #Right order, wrong id. Update order + # self.broker.logger.log_this(f"Updating take profit order for {self.status.get_pair()}",1,self.status.get_pair()) + # self.status.set_take_profit_order(order) + # self.update_status(True) + # return 0 + tp_status = self.broker.get_order(self.status.get_take_profit_order()["id"],self.status.get_pair()) if tp_status["status"]=="closed": if tp_status["filled"]>0: