Compare commits

..

No commits in common. "05b46203ab573c3ece3967665841fd94a0580173" and "ab77840bea4c932fe460a773ff3583c180daf494" have entirely different histories.

4 changed files with 57 additions and 67 deletions

View File

@ -1,11 +1,9 @@
2025.09.05: 2025.09.04:
. Fixed bug in unwrapped_last_call().
. Now the trader supports multiple safety orders at the same time. . 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 forcing orders when importing a trader. Maybe it will be reinstated at a later date.
. Removed endpoint /reload_safety_orders. . Removed endpoint /reload_safety_orders.
. New endpoints: /mod_concurrent_safety orders, /mod_boosted_concurrent_safety_orders and /force_trader_close. . 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: 2025.08.19:
. Improved log trimming. . Improved log trimming.

View File

@ -14,7 +14,7 @@ class Broker:
self.broker_config = broker_config self.broker_config = broker_config
self.exchange = exchange self.exchange = exchange
self.last_price = 0 self.last_price = 0
self.wait_time = .5 #Default wait time for API breathing room self.wait_time = 1 #Default wait time for API breathing room
self.cooldown_multiplier = 2 #Default cooldown multiplier value self.cooldown_multiplier = 2 #Default cooldown multiplier value
if "cooldown_multiplier" in self.broker_config: if "cooldown_multiplier" in self.broker_config:
self.cooldown_multiplier = self.broker_config["cooldown_multiplier"] 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() return "the lowest price limit for sell orders is" in str(error_object).lower()
# def new_limit_orders(self, orders: list) -> list: def new_limit_orders(self, orders: list) -> list:
# sent_orders = [] sent_orders = []
# #Send the orders #Send the orders
# tries = self.retries tries = self.retries
# while tries>=0: while tries>=0:
# try: try:
# sent_orders = self.exchange.create_orders(orders) sent_orders = self.exchange.create_orders(orders)
# except Exception as e: except Exception as e:
# self.logger.log_this(f"Exception while sending safety orders: {e}",1) self.logger.log_this(f"Exception while sending safety orders: {e}",1)
# tries-=1 tries-=1
# time.sleep(self.wait_time) time.sleep(self.wait_time)
# if tries==0: if tries==0:
# return [] return []
# #Retrieve the orders from the exchange by id to confirm that they were sent #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) #Specially for OKX, since the orders that create_orders return are empty (only id is present)
# returned_orders = [] returned_orders = []
# for order in sent_orders: for order in sent_orders:
# tries = self.retries tries = self.retries
# while tries>=0: while tries>=0:
# try: try:
# returned_orders.append(self.get_order(order["id"],order["symbol"])) returned_orders.append(self.get_order(order["id"],order["symbol"]))
# time.sleep(self.wait_time) time.sleep(self.wait_time)
# except Exception as e: except Exception as e:
# self.logger.log_this(f"Exception while retrieving safety orders: {e}",1) self.logger.log_this(f"Exception while retrieving safety orders: {e}",1)
# tries-=1 tries-=1
# if tries==0: if tries==0:
# if self.get_exchange_name()=="okex": if self.get_exchange_name()=="okex":
# return returned_orders 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. 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) time.sleep(self.wait_time)
# return returned_orders return returned_orders
def new_limit_order(self,symbol,size,side,price,no_retries=False): def new_limit_order(self,symbol,size,side,price,no_retries=False):

11
main.py
View File

@ -18,7 +18,7 @@ import exchange_wrapper
import trader import trader
version = "2025.09.05" version = "2025.09.04"
''' '''
Color definitions. If you want to change them, check the reference at https://en.wikipedia.org/wiki/ANSI_escape_code#Colors Color definitions. If you want to change them, check the reference at https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
@ -311,19 +311,14 @@ def main_routine():
futures = [] futures = []
pairs_to_fetch = [] pairs_to_fetch = []
online_pairs = [] online_pairs = []
open_orders_dict = {}
for instance in running_traders: for instance in running_traders:
pairs_to_fetch.append(instance.status.get_pair()) pairs_to_fetch.append(instance.status.get_pair())
for item in broker.fetch_open_orders(pairs_to_fetch): open_orders = 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: for instance in running_traders:
future = executor.submit(instance.check_status, open_orders_dict[instance.status.get_pair()]) future = executor.submit(instance.check_status, open_orders)
futures.append(future) futures.append(future)
online_pairs.append(f"{instance.base}{instance.quote}") online_pairs.append(f"{instance.base}{instance.quote}")

View File

@ -441,23 +441,19 @@ class trader:
if balance_to_clean is None: if balance_to_clean is None:
self.broker.logger.log_this("Can't fetch free base",1,self.status.get_pair()) self.broker.logger.log_this("Can't fetch free base",1,self.status.get_pair())
return 1 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()) 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 >= min_base_size: if balance_to_clean-minimum_cleanup_size >= 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(f"Balance to clean: {balance_to_clean-minimum_cleanup_size} {self.base}",2,self.status.get_pair())
self.broker.logger.log_this("Sending cleanup order...",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,"sell",self.status.get_take_profit_price()) 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 is None: if cleanup_order not in [None,self.broker.get_empty_order()]:
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()) self.broker.logger.log_this("Cleanup successful",2,self.status.get_pair())
return 0 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()) self.broker.logger.log_this("No cleanup needed",2,self.status.get_pair())
return 0 return 0
@ -1138,7 +1134,8 @@ class trader:
#Extract ids from order list #Extract ids from order list
self.status.set_pause_reason("filtering open orders") self.status.set_pause_reason("filtering open orders")
open_orders_ids = [order["id"] for order in 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]
self.status.set_pause_reason("check for tp_order is valid") self.status.set_pause_reason("check for tp_order is valid")
#Checks if the take profit order is valid #Checks if the take profit order is valid
@ -1161,15 +1158,15 @@ class trader:
self.status.set_pause_reason("check if tp_order is filled") self.status.set_pause_reason("check if tp_order is filled")
#Checks if the take profit order is filled #Checks if the take profit order is filled
if self.status.get_take_profit_order()["id"] not in open_orders_ids: if self.status.get_take_profit_order()["id"] not in open_orders_ids:
# # I hate Kucoin: # I hate Kucoin:
# # Check if the order has a wrong id. If so, update the order. # Check if the order has a wrong id. If so, update the order.
# for order in open_orders: 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"]: 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 #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.broker.logger.log_this(f"Updating take profit order for {self.status.get_pair()}")
# self.status.set_take_profit_order(order) self.status.set_take_profit_order(order)
# self.update_status(True) self.update_status(True)
# return 0 return 0
tp_status = self.broker.get_order(self.status.get_take_profit_order()["id"],self.status.get_pair()) 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["status"]=="closed":