first draft

This commit is contained in:
Nicolás Sánchez 2025-08-22 15:16:51 -03:00
parent f5e5f4eb77
commit ca85e454f9
7 changed files with 261 additions and 297 deletions

View File

@ -1,3 +1,9 @@
next:
. Default wait time now 0.5 seconds.
. Now the trader supports multiple safety orders at the same time.
. Removed endpoint /reload_safety_orders.
. Removed forcing orders when importing a trader. Maybe it will be reinstated at a later date.
2025.08.19: 2025.08.19:
. Improved log trimming. . Improved log trimming.

View File

@ -14,6 +14,7 @@ class ConfigHandler:
"order_size": self.broker.get_default_order_size(), "order_size": self.broker.get_default_order_size(),
"no_of_safety_orders": 30, "no_of_safety_orders": 30,
"max_short_safety_orders": 45, "max_short_safety_orders": 45,
"max_concurrent_safety_orders": 5,
"safety_order_deviance": 2, "safety_order_deviance": 2,
"safety_order_scale": 0.0105, "safety_order_scale": 0.0105,
"dynamic_so_deviance": True, "dynamic_so_deviance": True,
@ -68,6 +69,9 @@ class ConfigHandler:
def get_max_short_safety_orders(self): def get_max_short_safety_orders(self):
return self.config_dictionary["max_short_safety_orders"] return self.config_dictionary["max_short_safety_orders"]
def get_max_concurrent_safety_orders(self):
return self.config_dictionary["max_concurrent_safety_orders"]
def get_safety_order_deviance(self): def get_safety_order_deviance(self):
return self.config_dictionary["safety_order_deviance"] return self.config_dictionary["safety_order_deviance"]
@ -173,6 +177,13 @@ class ConfigHandler:
self.config_dictionary["max_short_safety_orders"] = max_short_safety_orders self.config_dictionary["max_short_safety_orders"] = max_short_safety_orders
return 0 return 0
def set_max_concurrent_safety_orders(self, max_concurrent_safety_orders: int):
# if not isinstance(max_concurrent_safety_orders, int):
# self.broker.logger.log_this(f"Max concurrent safety orders provided is not an integer",1,self.get_pair())
# return 1
self.config_dictionary["max_concurrent_safety_orders"] = max_concurrent_safety_orders
return 0
def set_safety_order_deviance(self, safety_order_deviance: int): def set_safety_order_deviance(self, safety_order_deviance: int):
# if not isinstance(safety_order_deviance, int): # if not isinstance(safety_order_deviance, int):
# self.broker.logger.log_this(f"Safety order deviance provided is not an integer",1,self.get_pair()) # self.broker.logger.log_this(f"Safety order deviance provided is not an integer",1,self.get_pair())

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 = 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 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"]
@ -581,7 +581,7 @@ class Broker:
return [] return []
def get_closed_orders(self,no_retries=False): #It should return a list of all opened orders def get_closed_orders(self,pair=None,no_retries=False): #It should return a list of all opened orders
''' '''
Returns a list of all the open orders on the exchange Returns a list of all the open orders on the exchange
@ -592,7 +592,7 @@ class Broker:
retries = self.retries retries = self.retries
while retries>0: while retries>0:
try: try:
return self.exchange.fetch_closed_orders() return self.exchange.fetch_closed_orders(pair)
except Exception as e: except Exception as e:
self.logger.log_this(f"Exception in get_closed_orders: {e}",1) self.logger.log_this(f"Exception in get_closed_orders: {e}",1)
if no_retries: if no_retries:

74
main.py
View File

@ -39,6 +39,7 @@ worker_threads_overprovisioning = 3 #Number of worker threads to create over
#Only use 0 if you are sure that you won't be adding any. #Only use 0 if you are sure that you won't be adding any.
executor = None executor = None
#Shutdown handler
def shutdown_handler(signum, _): def shutdown_handler(signum, _):
broker.logger.log_this(f"Received signal {signum}, shutting down as gracefully as possible...", 2) broker.logger.log_this(f"Received signal {signum}, shutting down as gracefully as possible...", 2)
if executor: if executor:
@ -86,7 +87,7 @@ def time_to_unix(year: str, month: str, day: str) -> int:
return 0 return 0
def import_instance(base: str, quote: str, forced_tp_id = None, forced_so_id = None) -> int: def import_instance(base: str, quote: str) -> int:
''' '''
Imports an previously running trader instance from the status file. Imports an previously running trader instance from the status file.
@ -98,7 +99,7 @@ def import_instance(base: str, quote: str, forced_tp_id = None, forced_so_id = N
int: 0 if successful int: 0 if successful
''' '''
broker.logger.log_this(f"Importing {base}/{quote}") broker.logger.log_this(f"Importing {base}/{quote}")
instances_to_add.append(trader.trader(broker,f"{base}/{quote}",is_import=True,forced_tp_id=forced_tp_id,forced_so_id=forced_so_id)) instances_to_add.append(trader.trader(broker,f"{base}/{quote}",is_import=True))
if f"{base}{quote}" not in tickers: if f"{base}{quote}" not in tickers:
tickers.append(f"{base}{quote}") tickers.append(f"{base}{quote}")
return 0 return 0
@ -690,9 +691,7 @@ def import_pair():
data = request.json data = request.json
base = data["base"] base = data["base"]
quote = data["quote"] quote = data["quote"]
forced_tp_id = data["forced_tp_id"] if "forced_tp_id" in data else None return unwrapped_import_pair(base,quote)
forced_so_id = data["forced_so_id"] if "forced_so_id" in data else None
return unwrapped_import_pair(base,quote,forced_tp_id,forced_so_id)
except Exception as e: except Exception as e:
print(e) print(e)
return jsonify({'Error': 'Halp'}) return jsonify({'Error': 'Halp'})
@ -1060,7 +1059,7 @@ def toggle_cleanup():
return jsonify({'Error': 'Halp'}) return jsonify({'Error': 'Halp'})
@base_api.route("/toggle_autoswitch", methods=['POST']) #type:ignore @base_api.route("/toggle_autoswitch", methods=['POST'])
def toggle_autoswitch(): def toggle_autoswitch():
''' '''
POST request POST request
@ -1084,8 +1083,8 @@ def toggle_autoswitch():
return jsonify({'Error': 'Halp'}) return jsonify({'Error': 'Halp'})
@base_api.route("/toggle_liquidate_after_switch", methods=['POST']) #type:ignore @base_api.route("/toggle_liquidate_after_switch", methods=['POST'])
def toggle_liquidate_after_switch(): #type:ignore def toggle_liquidate_after_switch():
''' '''
POST request POST request
@ -1108,7 +1107,7 @@ def toggle_liquidate_after_switch(): #type:ignore
return jsonify({'Error': 'Halp'}) return jsonify({'Error': 'Halp'})
@base_api.route("/toggle_check_old_long_price", methods=['POST'])#type:ignore @base_api.route("/toggle_check_old_long_price", methods=['POST'])
def toggle_check_old_long_price(): def toggle_check_old_long_price():
''' '''
POST request POST request
@ -1338,30 +1337,7 @@ def reload_markets():
return unwrapped_reload_markets() return unwrapped_reload_markets()
@base_api.route("/reload_safety_order", methods=['POST']) @base_api.route("/reload_trader_config", methods=['POST'])
def reload_safety_order():
'''
POST request
Parameters:
None
'''
if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys:
return jsonify({'Error': 'API key invalid'}), 401
try:
if request.json is None:
return jsonify({'Error': 'request.json is None'})
data = request.json
base = data["base"]
quote = data["quote"]
return unwrapped_reload_safety_order(base,quote)
except Exception as e:
print(e)
return jsonify({'Error': 'Halp'})
@base_api.route("/reload_trader_config", methods=['POST'])#type:ignore
def reload_trader_config(): def reload_trader_config():
''' '''
POST request POST request
@ -1512,15 +1488,13 @@ def unwrapped_restart_pair(base,quote):
return jsonify({"Error": "Halp"}) return jsonify({"Error": "Halp"})
def unwrapped_import_pair(base,quote,forced_tp_id = None, forced_so_id = None): def unwrapped_import_pair(base,quote):
''' '''
Imports a previously running pair Imports a previously running pair
Parameters: Parameters:
base (str): The base currency of the pair base (str): The base currency of the pair
quote (str): The quote currency of the pair quote (str): The quote currency of the pair
forced_tp_id (str): The ID of the take profit order to use
forced_so_id (str): The ID of the stop order to use
Returns: Returns:
jsonified dictionary detailing the outcome of the operation. jsonified dictionary detailing the outcome of the operation.
@ -1528,7 +1502,7 @@ def unwrapped_import_pair(base,quote,forced_tp_id = None, forced_so_id = None):
try: try:
symbol = f"{base}/{quote}" symbol = f"{base}/{quote}"
import_instance(base,quote,forced_tp_id,forced_so_id) import_instance(base,quote)
broker.add_pair_to_config(f"{base}{quote}") broker.add_pair_to_config(f"{base}{quote}")
broker.rewrite_config_file() broker.rewrite_config_file()
broker.logger.log_this(f"Done",2,symbol) broker.logger.log_this(f"Done",2,symbol)
@ -2120,6 +2094,7 @@ def unwrapped_toggle_autoswitch(base,quote):
broker.logger.log_this("Autoswitch turned ON",1,symbol) broker.logger.log_this("Autoswitch turned ON",1,symbol)
instance.config.set_autoswitch(True) instance.config.set_autoswitch(True)
return jsonify({"Success": "Autoswitch is now ON"}) return jsonify({"Success": "Autoswitch is now ON"})
return jsonify({"Error": "Trader not running"})
except Exception as e: except Exception as e:
broker.logger.log_this(f"Exception while toggling autoswitch: {e}",1,symbol) broker.logger.log_this(f"Exception while toggling autoswitch: {e}",1,symbol)
return jsonify({"Error": "Halp"}) return jsonify({"Error": "Halp"})
@ -2148,6 +2123,7 @@ def unwrapped_toggle_liquidate_after_switch(base,quote):
broker.logger.log_this("Liquidate after switch turned ON",1,symbol) broker.logger.log_this("Liquidate after switch turned ON",1,symbol)
instance.config.set_liquidate_after_switch(True) instance.config.set_liquidate_after_switch(True)
return jsonify({"Success": "Liquidate after switch is now ON"}) return jsonify({"Success": "Liquidate after switch is now ON"})
return jsonify({"Error": "Trader not running"})
except Exception as e: except Exception as e:
broker.logger.log_this(f"Exception while toggling liquidate after switch: {e}",1,symbol) broker.logger.log_this(f"Exception while toggling liquidate after switch: {e}",1,symbol)
return jsonify({"Error": "Halp"}) return jsonify({"Error": "Halp"})
@ -2176,6 +2152,7 @@ def unwrapped_toggle_check_old_long_price(base,quote):
broker.logger.log_this("Check ON",1,symbol) broker.logger.log_this("Check ON",1,symbol)
instance.config.set_check_old_long_price(True) instance.config.set_check_old_long_price(True)
return jsonify({"Success": "Old long price check turned ON"}) return jsonify({"Success": "Old long price check turned ON"})
return jsonify({"Error": "Trader not running"})
except Exception as e: except Exception as e:
broker.logger.log_this(f"Exception while toggling check_old_long_price: {e}",1,symbol) broker.logger.log_this(f"Exception while toggling check_old_long_price: {e}",1,symbol)
return jsonify({"Error": "Halp"}) return jsonify({"Error": "Halp"})
@ -2363,29 +2340,6 @@ def unwrapped_reload_markets():
return jsonify({"Error": "Markets couldn't be reloaded"}) return jsonify({"Error": "Markets couldn't be reloaded"})
def unwrapped_reload_safety_order(base,quote):
'''
Reloads the safety order of a trader.
Parameters:
base (str): The base currency of the trader.
quote (str): The quote currency of the trader.
Returns:
jsonify: A jsonified dictionary detailing the outcome of the operation.
'''
try:
symbol = f"{base}/{quote}"
for trader in running_traders:
if trader.config.get_pair()==symbol:
trader.config.load_from_file()
return jsonify({"Success": "Safety order reloaded successfully"})
return jsonify({"Error": "Trader not found"})
except Exception as e:
broker.logger.log_this(f"Exception while reloading safety order: {e}",1,symbol)
return jsonify({"Error": "Safety order couldn't be reloaded"})
def unwrapped_get_balance(coin): def unwrapped_get_balance(coin):
''' '''
Returns the balance of a given coin. Returns the balance of a given coin.

View File

@ -12,7 +12,7 @@ class StatusHandler:
"pair": f"{base}/{quote}", "pair": f"{base}/{quote}",
"take_profit_order": broker.get_empty_order(), "take_profit_order": broker.get_empty_order(),
"take_profit_price": 0.0, "take_profit_price": 0.0,
"safety_order": broker.get_empty_order(), "safety_orders": [],
"next_so_price": 0.0, "next_so_price": 0.0,
"order_size": 0.0, "order_size": 0.0,
"partial_profit": 0.0, "partial_profit": 0.0,
@ -58,8 +58,11 @@ class StatusHandler:
def get_take_profit_price(self): def get_take_profit_price(self):
return self.status_dictionary["take_profit_price"] return self.status_dictionary["take_profit_price"]
def get_safety_order(self): def get_safety_orders(self):
return self.status_dictionary["safety_order"] """
Returns the list of open safety orders
"""
return self.status_dictionary["safety_orders"]
def get_next_so_price(self): def get_next_so_price(self):
return self.status_dictionary["next_so_price"] return self.status_dictionary["next_so_price"]
@ -181,8 +184,11 @@ class StatusHandler:
self.status_dictionary["so_order_id"] = order_id self.status_dictionary["so_order_id"] = order_id
return 0 return 0
def set_safety_order(self, order): def set_safety_orders(self, orders: list):
self.status_dictionary["safety_order"] = order """
Replaces the whole safety orders list
"""
self.status_dictionary["safety_orders"] = orders
return 0 return 0
def set_next_so_price(self, price: float): def set_next_so_price(self, price: float):
@ -381,6 +387,21 @@ class StatusHandler:
self.status_dictionary["deal_order_history"] = deal_history self.status_dictionary["deal_order_history"] = deal_history
return 0 return 0
def add_safety_order(self, order):
"""
Appends a newly-created safety order to the internal list
"""
self.status_dictionary["safety_orders"].append(order)
return 0
def remove_safety_order_by_id(self, order_id: str):
"""
Removes an order from the list (mostly used when that order is filled or canceled)
"""
orders = self.get_safety_orders()
self.status_dictionary["safety_orders"] = [order for order in orders if order["id"] != order_id]
return 0
def clear_deal_order_history(self): def clear_deal_order_history(self):
self.status_dictionary["deal_order_history"] = [] self.status_dictionary["deal_order_history"] = []
return 0 return 0

323
trader.py
View File

@ -50,7 +50,7 @@ class trader:
self.status.set_pause_reason("Initialization") self.status.set_pause_reason("Initialization")
if is_import: if is_import:
self.load_imported_trader(forced_tp_order_id=forced_tp_id, forced_safety_order_id=forced_so_id) self.load_imported_trader()
return None return None
# An alternative would be to set up a variable like self.is_initalized to false and finish the initialization here. # An alternative would be to set up a variable like self.is_initalized to false and finish the initialization here.
@ -105,15 +105,6 @@ class trader:
return self.market_reload_period return self.market_reload_period
def reload_safety_order(self) -> int:
'''
Reloads the safety order.
'''
self.status.set_safety_order(self.broker.get_order(self.status.get_safety_order()["id"],self.config.get_pair()))
return 0
def start_trader(self) -> int: def start_trader(self) -> int:
''' '''
Initializes the trader. Initializes the trader.
@ -124,7 +115,7 @@ class trader:
self.status.set_so_amount(0) self.status.set_so_amount(0)
self.status.clear_deal_order_history() self.status.clear_deal_order_history()
self.status.set_take_profit_order(self.broker.get_empty_order()) self.status.set_take_profit_order(self.broker.get_empty_order())
self.status.set_safety_order(self.broker.get_empty_order()) self.status.set_safety_orders([])
#Reloads the market #Reloads the market
new_market_data = self.broker.fetch_market(self.config.get_pair()) new_market_data = self.broker.fetch_market(self.config.get_pair())
@ -217,11 +208,9 @@ class trader:
#Wait until the first order gets filled #Wait until the first order gets filled
self.status.set_pause_reason("start_trader - waiting for the first order to get filled") self.status.set_pause_reason("start_trader - waiting for the first order to get filled")
while True: while True:
#Wait a bit longer, to catch a bug: #Wait a bit longer, sometimes a recently filled market order is not updated quickly enough.
#Sometimes the amount of base taken into account by the trader is lower than the amount bought, # When that happens, the amount of base taken into account by the trader is lower than the amount bought,
# which ends up misrepresenting the trade cost per unit of base, which causes the take profit price to skyrocket. # which ends up misrepresenting the trade cost per unit of base, which causes the take profit price to skyrocket.
# Maybe is the first market order getting "closed" before is fully filled?
# Or is there an error later in the trader?
time.sleep(self.broker.get_wait_time()) time.sleep(self.broker.get_wait_time())
returned_order = self.broker.get_order(first_order["id"],self.config.get_pair()) returned_order = self.broker.get_order(first_order["id"],self.config.get_pair())
if returned_order==self.broker.get_empty_order(): if returned_order==self.broker.get_empty_order():
@ -278,13 +267,15 @@ class trader:
self.status.set_start_price(self.broker.price_to_precision(self.config.get_pair(),self.status.get_quote_spent()/self.status.get_base_bought())) self.status.set_start_price(self.broker.price_to_precision(self.config.get_pair(),self.status.get_quote_spent()/self.status.get_base_bought()))
self.status.set_safety_price_table(self.calculate_safety_prices(self.status.get_start_price(),self.config.get_no_of_safety_orders(),self.config.get_safety_order_deviance())) self.status.set_safety_price_table(self.calculate_safety_prices(self.status.get_start_price(),self.config.get_no_of_safety_orders(),self.config.get_safety_order_deviance()))
# Send the first safety order # Send the initial batch of safety orders
self.status.set_pause_reason("start_trader - sending safety order") self.status.set_pause_reason("start_trader - sending safety orders")
self.broker.logger.log_this("Sending safety order...",2,self.config.get_pair()) self.broker.logger.log_this("Sending safety orders...",2,self.config.get_pair())
if self.send_new_safety_order(self.status.get_order_size())==0: max_initial_safety_orders = min(self.config.get_max_concurrent_safety_orders(),self.config.get_no_of_safety_orders()) #To never send more than the max amount of safety orders
self.broker.logger.log_this("Safety order sent",2,self.config.get_pair()) orders_placed = self.send_new_safety_order_batch(max_initial_safety_orders)
if orders_placed is not None:
self.broker.logger.log_this(f"{orders_placed}/{max_initial_safety_orders} safety orders placed",2,self.config.get_pair())
else: else:
self.broker.logger.log_this("Error sending safety order. Cancelling take profit order and aborting",1,self.config.get_pair()) self.broker.logger.log_this("Error sending safety orders. Cancelling take profit order and aborting",1,self.config.get_pair())
self.broker.cancel_order(self.status.get_take_profit_order()["id"],self.config.get_pair()) self.broker.cancel_order(self.status.get_take_profit_order()["id"],self.config.get_pair())
return 1 return 1
@ -312,9 +303,8 @@ class trader:
self.status.set_next_so_price(self.status.get_safety_price_table()[self.status.get_so_amount()]) self.status.set_next_so_price(self.status.get_safety_price_table()[self.status.get_so_amount()])
except Exception as e: except Exception as e:
self.broker.logger.log_this(f"Is safety_price_table populated? Exception: {e} | Safety price table: {self.status.get_safety_price_table()} | Safety order index: {self.status.get_so_amount()}",1,self.config.get_pair()) self.broker.logger.log_this(f"Is safety_price_table populated? Exception: {e} | Safety price table: {self.status.get_safety_price_table()} | Safety order index: {self.status.get_so_amount()}",1,self.config.get_pair())
if self.status.get_safety_order() is not None and self.status.get_safety_order()["price"] is not None and self.status.get_safety_order()!=self.broker.get_empty_order(): if self.status.get_safety_orders()!=[]:
self.status.set_next_so_price(self.status.get_safety_order()["price"]) self.status.set_next_so_price(self.status.get_safety_orders()[0]["price"])
self.status.set_is_paused(self.pause) self.status.set_is_paused(self.pause)
self.status.set_is_short(self.config.get_is_short()) self.status.set_is_short(self.config.get_is_short())
self.status.set_no_of_safety_orders(self.config.get_no_of_safety_orders()) self.status.set_no_of_safety_orders(self.config.get_no_of_safety_orders())
@ -519,9 +509,6 @@ class trader:
if self.status.get_take_profit_order() is None: if self.status.get_take_profit_order() is None:
self.broker.logger.log_this("Take profit order is None, can't switch to short",1,self.config.get_pair()) self.broker.logger.log_this("Take profit order is None, can't switch to short",1,self.config.get_pair())
return 1 return 1
if self.status.get_safety_order() is None:
self.broker.logger.log_this("Safety order is None, can't switch to short",1,self.config.get_pair())
return 1
#Pauses trader #Pauses trader
self.pause = True self.pause = True
@ -623,10 +610,8 @@ class trader:
#Cancel open orders #Cancel open orders
try: try:
if self.status.get_safety_order() is not None: for order in self.status.get_safety_orders():
self.broker.cancel_order(self.status.get_safety_order()["id"],self.config.get_pair()) self.broker.cancel_order(order["id"],self.config.get_pair())
else:
self.broker.logger.log_this("Take profit order is None",1,self.config.get_pair())
except Exception as e: except Exception as e:
self.broker.logger.log_this(f"Error in cancel_order while cancelling safety order. Exception: {e}",1,self.config.get_pair()) self.broker.logger.log_this(f"Error in cancel_order while cancelling safety order. Exception: {e}",1,self.config.get_pair())
try: try:
@ -666,7 +651,7 @@ class trader:
self.status.set_fees_paid_in_quote(0) self.status.set_fees_paid_in_quote(0)
self.status.set_fees_paid_in_base(0) self.status.set_fees_paid_in_base(0)
self.status.set_take_profit_order(self.broker.get_empty_order()) self.status.set_take_profit_order(self.broker.get_empty_order())
self.status.set_safety_order(self.broker.get_empty_order()) self.status.set_safety_orders([])
self.status.set_safety_price_table([]) self.status.set_safety_price_table([])
self.status.set_so_amount(0) self.status.set_so_amount(0)
@ -732,39 +717,33 @@ class trader:
self.status.save_to_file(is_backup=True) self.status.save_to_file(is_backup=True)
self.restart = True self.restart = True
return 1 return 1
if self.status.get_safety_order() is None:
self.status.set_pause_reason(time.strftime(f"[%Y/%m/%d %H:%M:%S] | {self.config.get_pair()} | Safety order is None"))
self.broker.logger.log_this("Error. Safety order is None",1,self.config.get_pair())
self.status.set_safety_order(self.broker.get_empty_order())
#Save the order in history. #Save the order in history.
if self.broker.get_follow_order_history(): if self.broker.get_follow_order_history():
self.status.update_deal_order_history(filled_order) self.status.update_deal_order_history(filled_order)
# Cancel the current safety order (first check if there is something to cancel) #Cancel all the safety orders ASAP
already_counted = False for order in self.status.get_safety_orders():
if self.status.get_safety_order()["id"]=="": self.broker.cancel_order(order["id"])
self.broker.logger.log_this("There is no safety order to cancel",2,self.config.get_pair()) #Check if some safety orders were filled
elif self.broker.cancel_order(self.status.get_safety_order()["id"],self.config.get_pair())==1: for order in self.status.get_safety_orders():
self.broker.logger.log_this("Old safety order probably filled. Can't cancel.",1,self.config.get_pair()) closed_order = self.broker.get_order(order["id"],self.config.get_pair())
closed_order = self.broker.get_order(self.status.get_safety_order()["id"],self.config.get_pair()) if closed_order["filled"]==0:
if closed_order!=self.broker.get_empty_order() and closed_order["status"]=="closed": #If this order wasn't filled, it is safe to assume that no order coming after this one was.
self.status.set_base_bought(self.status.get_base_bought() + closed_order["filled"]) break
#Sum the filled amounts
self.broker.logger.log_this(f"Old safety order is partially filled, ID: {closed_order['id']}",1,self.config.get_pair())
self.status.set_base_bought(self.status.get_base_bought() + closed_order["filled"] - self.parse_fees(closed_order)[0])
self.status.set_quote_spent(self.status.get_quote_spent() + closed_order["cost"]) self.status.set_quote_spent(self.status.get_quote_spent() + closed_order["cost"])
#Save the order #Save the order
if self.broker.get_follow_order_history(): if self.broker.get_follow_order_history():
self.status.update_deal_order_history(closed_order) self.status.update_deal_order_history(closed_order)
already_counted = True if closed_order["remaining"]!=0:
#If this order is not completely filled, it is safe to assume that no order coming after this one was partially filled.
break
#Now we can clear the safety order list
self.status.set_safety_orders([])
#IF NOT SHORT - Check if the SO was partially filled. If so, add the amounts to total_amount_of_base and total_amount_of_quote
if not self.config.get_is_short() and self.status.get_safety_order()["id"]!="" and not already_counted:
old_so_order = self.broker.get_order(self.status.get_safety_order()["id"],self.config.get_pair())
if old_so_order["filled"]>0:
self.broker.logger.log_this(f"Old safety order is partially filled, ID: {old_so_order['id']}",1,self.config.get_pair())
if self.broker.get_follow_order_history():
self.status.update_deal_order_history(old_so_order)
self.status.set_base_bought(self.status.get_base_bought() + old_so_order["filled"] - self.parse_fees(old_so_order)[0])
self.status.set_quote_spent(self.status.get_quote_spent() + old_so_order["cost"])
if not self.broker.check_for_duplicate_profit_in_db(filled_order): if not self.broker.check_for_duplicate_profit_in_db(filled_order):
self.status.set_pause_reason("calculating profit") self.status.set_pause_reason("calculating profit")
@ -837,58 +816,91 @@ class trader:
return restart_trader return restart_trader
def new_so_routine(self, filled_order: dict, send_new_so: bool) -> int: def send_new_safety_order_batch(self, amount: int):
''' """
Handles all the bureaucracy prior and after sending a new safety order Sends a new safety order batch to the broker
:param filled_order: dict :param amount: int - The amount of safety orders to send.
:param send_new_so: bool :return: The amount of orders succesfully sent. None if an error occurs.
:return: 0 OK, 1 not enough funds, if can't cancel old TP, can't send new TP
'''
#Let's do some type checking first If the amount of orders returned is less than the amount expected, we should not try to send more safety orders.
"""
if amount<1:
return 0
orders_to_place = min(self.config.get_no_of_safety_orders()-self.status.get_so_amount(),amount)
if orders_to_place<1:
return 0
orders_placed = 0
for _ in range(orders_to_place):
so_size = self.gib_so_size(self.status.get_order_size(),self.status.get_so_amount()+1,self.config.get_safety_order_scale())
if self.config.get_is_short():
new_order = self.broker.new_limit_order(self.config.get_pair(),so_size,"sell",self.status.get_safety_price_table()[self.status.get_so_amount()+1])
else:
new_order = self.broker.new_limit_order(self.config.get_pair(),so_size/self.status.get_safety_price_table()[self.status.get_so_amount()+1],"buy",self.status.get_safety_price_table()[self.status.get_so_amount()+1])
if new_order==1:
self.broker.logger.log_this("Not enough balance to send a new safety order",1,self.config.get_pair())
return orders_placed
elif new_order is None:
self.broker.logger.log_this("new_limit_order returned None",1,self.config.get_pair())
return orders_placed
orders_placed+=1
self.status.add_safety_order(new_order)
self.status.set_so_amount(self.status.get_so_amount()+1)
return orders_placed
def renew_tp_and_so_routine(self, filled_safety_orders: list):
'''
Modifies the current take profit order and sends a new safety order
:return: 0 OK, 1 take profit order is None, 2 not enough funds, 3 can't cancel TP (filled?), 4 can't send new TP
'''
safety_orders_to_remove_by_id = []
#Check if current TP order is valid
if self.status.get_take_profit_order() is None: if self.status.get_take_profit_order() is None:
self.broker.logger.log_this("Take profit order is None, can't send a new safety order",1,self.config.get_pair()) self.broker.logger.log_this("Take profit order is None, can't send a new safety order",1,self.config.get_pair())
return 1 return 1
if self.status.get_safety_order() is None: #I don't think this is necessary
self.broker.logger.log_this("Safety order is None, can't send a new safety order",1,self.config.get_pair())
return 1
#Pause the trader
self.pause = True self.pause = True
self.status.set_pause_reason("new_so_routine") self.status.set_pause_reason("renew_tp_and_so_routine")
#Save the order #Save the order
if self.broker.get_follow_order_history(): if self.broker.get_follow_order_history():
self.status.update_deal_order_history(filled_order) for item in filled_safety_orders:
self.status.update_deal_order_history(item)
# Add the amount filled in the last safety order to the totals #Add the amount filled in the last safety orders to the totals
new_fees_base,new_fees_quote = self.parse_fees(filled_order) for order in filled_safety_orders:
safety_orders_to_remove_by_id.append(order["id"])
new_fees_base,new_fees_quote = self.parse_fees(order)
self.status.set_fees_paid_in_quote(self.status.get_fees_paid_in_quote() + new_fees_quote) self.status.set_fees_paid_in_quote(self.status.get_fees_paid_in_quote() + new_fees_quote)
self.status.set_base_bought(self.status.get_base_bought() + filled_order["filled"] - new_fees_base) self.status.set_base_bought(self.status.get_base_bought() + order["filled"] - new_fees_base)
self.status.set_quote_spent(self.status.get_quote_spent() + filled_order["cost"]) self.status.set_quote_spent(self.status.get_quote_spent() + order["cost"])
#Remove the filled safety orders from the list
if safety_orders_to_remove_by_id!=[]:
new_order_list = []
#Remove filled orders from the list
for order in self.status.get_safety_orders():
if order["id"] not in safety_orders_to_remove_by_id:
new_order_list.append(order)
self.status.set_safety_orders(new_order_list)
#Cooldown #Cooldown
time.sleep(self.broker.get_wait_before_new_safety_order()) time.sleep(self.broker.get_wait_before_new_safety_order())
# Send the new safety order. If all expected safety orders are filled, it assigns an empty order to self.status.get_safety_order() #Send new SO(s)
if send_new_so: orders_sent = self.send_new_safety_order_batch(len(filled_safety_orders))
self.broker.logger.log_this("Sending a new safety order",2,self.config.get_pair())
if self.send_new_safety_order(self.status.get_order_size())==1:
error_string = "Problems sending the new safety order. Maybe not enough funds?"
self.broker.logger.log_this(error_string,1,self.config.get_pair())
self.status.set_pause_reason(error_string)
return 1
else:
self.status.set_safety_order(self.broker.get_empty_order())
self.status.set_so_amount(self.status.get_so_amount()+1)
# Cancel the old tp order #Cancel old TP order
if self.broker.cancel_order(self.status.get_take_profit_order()["id"],self.config.get_pair())==1: if self.broker.cancel_order(self.status.get_take_profit_order()["id"],self.config.get_pair())==1:
error_string = f"{self.config.get_pair()} | {self.status.get_take_profit_order()['id']} | Old TP order probably filled. Can't cancel. This trader should be restarted" error_string = f"{self.config.get_pair()} | {self.status.get_take_profit_order()['id']} | Old TP order probably filled. Can't cancel. This trader should be restarted"
self.broker.logger.log_this(f"Old take profit order is probably filled, can't cancel. This trader should be restarted. Order ID: {self.status.get_take_profit_order()['id']}",1,self.config.get_pair()) self.broker.logger.log_this(f"Old take profit order is probably filled, can't cancel. This trader should be restarted. Order ID: {self.status.get_take_profit_order()['id']}",1,self.config.get_pair())
self.status.set_pause_reason(error_string) self.status.set_pause_reason(error_string)
return 2 return 2
# Check if the old tp order was partially filled. If so, update the previous two variables accordingly #Check if old TP order was partially filled
# TODO: This should also be taken into account for the profit calculation # TODO: This should also be taken into account for the profit calculation
# Do the partial profit calculation and save it for later # Do the partial profit calculation and save it for later
old_tp_order = self.broker.get_order(self.status.get_take_profit_order()["id"],self.config.get_pair()) old_tp_order = self.broker.get_order(self.status.get_take_profit_order()["id"],self.config.get_pair())
@ -903,8 +915,6 @@ class trader:
# self.status.set_partial_profit(self.status.get_partial_profit()+old_tp_order["cost"]-(old_tp_order["filled"]*current_deal_price)-self.parse.fees(old_tp_order)[1]) # self.status.set_partial_profit(self.status.get_partial_profit()+old_tp_order["cost"]-(old_tp_order["filled"]*current_deal_price)-self.parse.fees(old_tp_order)[1])
# self.update_status(True) # self.update_status(True)
# #
# Maybe here we shouldn't substract fees yet, but add them up to the check.
#
self.status.set_base_bought(self.status.get_base_bought() - old_tp_order["filled"] - self.parse_fees(old_tp_order)[0]) self.status.set_base_bought(self.status.get_base_bought() - old_tp_order["filled"] - self.parse_fees(old_tp_order)[0])
self.status.set_quote_spent(self.status.get_quote_spent() - old_tp_order["cost"]) self.status.set_quote_spent(self.status.get_quote_spent() - old_tp_order["cost"])
self.status.set_fees_paid_in_quote(self.status.get_fees_paid_in_quote() + self.parse_fees(old_tp_order)[1]) self.status.set_fees_paid_in_quote(self.status.get_fees_paid_in_quote() + self.parse_fees(old_tp_order)[1])
@ -913,17 +923,21 @@ class trader:
#Cooldown #Cooldown
time.sleep(self.broker.get_wait_time()) time.sleep(self.broker.get_wait_time())
# Send the new take profit order #Send new TP order
if self.send_new_tp_order()==1: if self.send_new_tp_order()==1:
error_string = "Problems sending the new take profit order" error_string = "Problems sending the new take profit order"
self.broker.logger.log_this("Problems sending the new take profit order",1,self.config.get_pair()) self.broker.logger.log_this("Problems sending the new take profit order",1,self.config.get_pair())
self.status.set_pause_reason(error_string) self.status.set_pause_reason(error_string)
return 3 return 4
# Update the status_dict and that's it #Update status dict
self.update_status(True) self.update_status(True)
#Toggle the pause flag
self.pause = False self.pause = False
self.status.set_pause_reason("") self.status.set_pause_reason("")
#Done
return 0 return 0
@ -1024,18 +1038,15 @@ class trader:
#Extract ids from order list #Extract ids from order list
open_orders_ids = [order["id"] for order in open_orders if order["symbol"]==self.config.get_pair()] open_orders_ids = [order["id"] for order in open_orders if order["symbol"]==self.config.get_pair()]
#Checks if the orders are valid #Checks if the take profit order is valid
if self.status.get_take_profit_order() is None: if self.status.get_take_profit_order() is None:
self.broker.logger.log_this("Take profit order is None",1,self.config.get_pair()) self.broker.logger.log_this("Take profit order is None",1,self.config.get_pair())
return 1 return 1
if self.status.get_safety_order() is None:
#Here, would it be wise to attempt to reload the safety order from the status dict?
self.broker.logger.log_this("Safety order is None",1,self.config.get_pair())
self.status.set_safety_order(self.broker.get_empty_order())
#return 1
if self.status.get_take_profit_order()["id"]=="": if self.status.get_take_profit_order()["id"]=="":
self.broker.logger.log_this(f"Take profit order missing. Stopping trader. No order ID was provided.",1,self.config.get_pair()) self.broker.logger.log_this(f"Take profit order missing. Stopping trader. No order ID was provided.",1,self.config.get_pair())
self.broker.cancel_order(self.status.get_safety_order()["id"],self.config.get_pair()) #Cancelling safety orders
for item in self.status.get_safety_orders():
self.broker.cancel_order(item["id"],self.config.get_pair())
if self.config.get_attempt_restart(): if self.config.get_attempt_restart():
self.status.save_to_file(is_backup=True) self.status.save_to_file(is_backup=True)
self.restart = True self.restart = True
@ -1051,8 +1062,9 @@ class trader:
if tp_status["filled"]>0: if tp_status["filled"]>0:
return self.take_profit_routine(tp_status) return self.take_profit_routine(tp_status)
self.broker.logger.log_this(f"Take profit order closed but not filled, 0 filled. Stopping trader. Order ID: {self.status.get_take_profit_order()['id']}",1,self.config.get_pair()) self.broker.logger.log_this(f"Take profit order closed but not filled, 0 filled. Stopping trader. Order ID: {self.status.get_take_profit_order()['id']}",1,self.config.get_pair())
#Cancelling safety order and stopping trader #Cancelling safety orders
self.broker.cancel_order(self.status.get_safety_order()["id"],self.config.get_pair()) for item in self.status.get_safety_orders():
self.broker.cancel_order(item["id"],self.config.get_pair())
if self.config.get_attempt_restart(): if self.config.get_attempt_restart():
self.status.save_to_file(is_backup=True) self.status.save_to_file(is_backup=True)
self.restart = True self.restart = True
@ -1073,46 +1085,52 @@ class trader:
self.broker.logger.log_this(f"Take profit order search returned empty order. Order ID: {tp_status['id']}",1,self.config.get_pair()) self.broker.logger.log_this(f"Take profit order search returned empty order. Order ID: {tp_status['id']}",1,self.config.get_pair())
return 1 return 1
# Check if safety order is filled # Check if any safety order is filled
if self.status.get_safety_order()["id"] not in open_orders_ids and self.status.get_so_amount()<=self.config.get_no_of_safety_orders(): filled_ids = []
so_status = self.broker.get_order(self.status.get_safety_order()["id"],self.config.get_pair()) for order in self.status.get_safety_orders():
tp_order_status = self.broker.get_order(self.status.get_take_profit_order()["id"],self.config.get_pair()) if order["id"] not in open_orders_ids:
filled_ids.append(order["id"])
#Now we check 2 things: if filled_ids!=[]:
#1. That the prior safety order status is indeed closed (or canceled) closed_orders = self.broker.get_closed_orders(self.config.get_pair())
#2. That the take profit order is still opened (if not, the deal must have closed, both orders closing is quite common in high variance scenarios) filled_orders = [item for item in closed_orders if item["id"] in filled_ids and item["status"]=="closed"] #maybe item["status"] in ["closed", "canceled", ""]?
if so_status["status"] in ["closed", "canceled", ""] and tp_order_status["status"]=="open": renew_outcome = self.renew_tp_and_so_routine(filled_orders)
#Switch to short if all safety orders are sent and autoswitch is enabled. #0 OK, 1 take profit order is None, 2 not enough funds, 3 can't cancel TP (filled?), 4 can't send new TP
#May get into trouble if the trader is short of funds if renew_outcome==1:
if not self.config.get_is_short() and self.status.get_so_amount()==self.config.get_no_of_safety_orders() and self.config.get_autoswitch(): self.broker.logger.log_this(f"Error in trader: TP order is None. Restart will be attempted. renew_tp_and_so_routine returned 1",0,self.config.get_pair())
self.switch_to_short() if self.config.get_attempt_restart():
self.status.save_to_file(is_backup=True) self.status.save_to_file(is_backup=True)
self.restart = True self.restart = True
return 0 return 1
a = self.new_so_routine(so_status,self.status.get_so_amount()<self.config.get_no_of_safety_orders()) elif renew_outcome==2:
#0 OK, 1 not enough funds, 2 can't cancel old TP, 3 can't send new TP #Not enough funds?
if a==1: self.broker.logger.log_this(f"Can't send new safety order. Not enough funds? renew_tp_and_so_routine returned 2",1,self.config.get_pair())
self.broker.logger.log_this(f"Can't send new safety order. Not enough funds? new_so_routine returned {a}",1,self.config.get_pair()) #Set no_of_safety_orders to the same amount of orders open so the script does not try to send new safety orders
#If there are not enough funds do not even try to send more safety orders #This can be improved
#This way of doing it seems more practical than setting up yet another flag
self.config.set_no_of_safety_orders(self.status.get_so_amount()) self.config.set_no_of_safety_orders(self.status.get_so_amount())
return 1 return 1
elif a==2: elif renew_outcome==3:
self.broker.logger.log_this(f"Can't cancel old take profit order. new_so_routine returned {a}",1,self.config.get_pair()) self.broker.logger.log_this(f"Can't cancel old take profit order. renew_tp_and_so_routine returned 3",1,self.config.get_pair())
self.pause = False self.pause = False
self.status.set_pause_reason("") self.status.set_pause_reason("")
if self.config.get_attempt_restart(): if self.config.get_attempt_restart():
self.status.save_to_file(is_backup=True) self.status.save_to_file(is_backup=True)
self.restart = True self.restart = True
return 1 return 1
elif a==3: elif renew_outcome==4:
#self.pause = False self.broker.logger.log_this(f"Error in trader: Can't send new take profit order. Restart will be attempted. renew_tp_and_so_routine returned 4",0,self.config.get_pair())
self.broker.logger.log_this(f"Error in trader: Can't send new take profit order. Restart will be attempted. new_so_routine returned {a}",0,self.config.get_pair())
if self.config.get_attempt_restart(): if self.config.get_attempt_restart():
self.status.save_to_file(is_backup=True) self.status.save_to_file(is_backup=True)
self.restart = True self.restart = True
return 1 return 1
#else: check if the order is partially filled. If so, add the amounts to amount_of_quote and amount_of_base and update the take profit order.
#Check for autoswitch (long->short)
#Commented out because i'm not sure where this should go
#if not self.config.get_is_short() and self.status.get_so_amount()==self.config.get_no_of_safety_orders() and self.config.get_autoswitch():
# self.switch_to_short()
# self.status.save_to_file(is_backup=True)
# self.restart = True
# return 0
#Render status line(s) #Render status line(s)
self.status.set_status_string(self.generate_status_strings()) self.status.set_status_string(self.generate_status_strings())
@ -1245,28 +1263,6 @@ class trader:
return 1 return 1
def send_new_safety_order(self, size: float) -> int:
'''
Sends a new safety order to the exchange
'''
so_size = self.gib_so_size(size,self.status.get_so_amount()+1,self.config.get_safety_order_scale()) #safety_order_scale: safety order growth factor
if self.config.get_is_short():
new_order = self.broker.new_limit_order(self.config.get_pair(),so_size,"sell",self.status.get_safety_price_table()[self.status.get_so_amount()+1])
else:
new_order = self.broker.new_limit_order(self.config.get_pair(),so_size/self.status.get_safety_price_table()[self.status.get_so_amount()+1],"buy",self.status.get_safety_price_table()[self.status.get_so_amount()+1])
if new_order==1:
self.status.set_safety_order(self.broker.get_empty_order())
self.broker.logger.log_this("Not enough balance to send a new safety order",1,self.config.get_pair())
#elif new_order in [None,self.broker.get_empty_order()] #MAYUBE THIS CONDITIONAL IS BETTER
elif new_order is None:
self.status.set_safety_order(None)
return 1
else:
self.status.set_safety_order(new_order)
self.status.set_so_amount(self.status.get_so_amount()+1)
return 0
def telegram_bot_sendprofit(self,profit,order,base_profit=0) -> int: def telegram_bot_sendprofit(self,profit,order,base_profit=0) -> int:
''' '''
Sends the Telegram notification when profit is met Sends the Telegram notification when profit is met
@ -1351,9 +1347,6 @@ class trader:
if self.status.get_take_profit_order() is None: if self.status.get_take_profit_order() is None:
self.broker.logger.log_this("Take profit order is None",1,self.config.get_pair()) self.broker.logger.log_this("Take profit order is None",1,self.config.get_pair())
return 1 return 1
if self.status.get_safety_order() is None:
self.broker.logger.log_this("Safety order is None",1,self.config.get_pair())
return 1
#Replace the current take profit order with a new one with new quote currency #Replace the current take profit order with a new one with new quote currency
self.broker.logger.log_this("Replacing take profit order",2,self.config.get_pair()) self.broker.logger.log_this("Replacing take profit order",2,self.config.get_pair())
@ -1361,12 +1354,13 @@ class trader:
if self.status.get_take_profit_order()==self.broker.get_empty_order(): if self.status.get_take_profit_order()==self.broker.get_empty_order():
return 1 return 1
#Replace the current safety order (if any) with a new one with the new quote currency #Replace the current safety orders (if any) with new ones with the new quote currency
if self.status.get_safety_order()!=self.broker.get_empty_order(): #This is WRONG: We need to build a list of the newly sent orders and assign them with self.status.set_safety_orders()
new_order_list = []
for order in self.status.get_safety_orders():
self.broker.logger.log_this("Replacing safety order",2,self.config.get_pair()) self.broker.logger.log_this("Replacing safety order",2,self.config.get_pair())
self.status.set_safety_order(self.quote_currency_replace_order(self.status.get_safety_order(),new_quote)) new_order_list.append(self.quote_currency_replace_order(order,new_quote))
if self.status.get_safety_order()==self.broker.get_empty_order(): self.status.set_safety_orders(new_order_list)
return 1
#Calls switch_quote_currency_config #Calls switch_quote_currency_config
self.broker.logger.log_this("Modifying config file",2,self.config.get_pair()) self.broker.logger.log_this("Modifying config file",2,self.config.get_pair())
@ -1582,7 +1576,7 @@ class trader:
return status_string return status_string
def load_imported_trader(self, forced_tp_order_id = None, forced_safety_order_id = None) -> int: def load_imported_trader(self) -> int:
''' '''
Loads status dictionary, orders and sets up variables Loads status dictionary, orders and sets up variables
''' '''
@ -1596,22 +1590,21 @@ class trader:
self.config.set_no_of_safety_orders(self.status.get_no_of_safety_orders()) #If this is not loaded from status_dict, it will ignore if safety orders were added at runtime self.config.set_no_of_safety_orders(self.status.get_no_of_safety_orders()) #If this is not loaded from status_dict, it will ignore if safety orders were added at runtime
#Refresh take profit order #Refresh take profit order
order_id = self.status.get_take_profit_order()["id"] if forced_tp_order_id is None else forced_tp_order_id order_id = self.status.get_take_profit_order()["id"]
self.status.set_take_profit_order(self.broker.get_order(order_id,self.config.get_pair())) self.status.set_take_profit_order(self.broker.get_order(order_id,self.config.get_pair()))
if self.status.get_take_profit_order()==self.broker.get_empty_order(): if self.status.get_take_profit_order()==self.broker.get_empty_order():
self.broker.logger.log_this("Couldn't load take profit order (broker returned empty order). Aborting.",1,self.config.get_pair()) self.broker.logger.log_this("Couldn't load take profit order (broker returned empty order). Aborting.",1,self.config.get_pair())
self.quit = True self.quit = True
return 1 return 1
#Refresh safety order #Safety order list does not need refreshing
order_id = self.status.get_safety_order()["id"] if forced_safety_order_id is None else forced_safety_order_id #Probably validation while migrating to the new version.
self.status.set_safety_order(self.broker.get_order(order_id,self.config.get_pair())) #Refresh safety orders
if self.status.get_safety_order()==self.broker.get_empty_order() and self.status.get_so_amount()<self.config.get_no_of_safety_orders(): #new_order_list = []
#The second condition is important: it signals that the empty order returned was because of an error, not because the trader ran out of funds in the past. #for order in self.status.get_safety_orders():
#When the trader runs out of funds, safety_order_index=config.get_no_of_safety_orders() # new_order_list.append(self.broker.get_order(order["id"],self.config.get_pair()))
self.broker.logger.log_this("Couldn't load safety order. Aborting.",2,self.config.get_pair()) # time.sleep(self.broker.get_wait_time())
self.quit = True #self.status.set_safety_orders(new_order_list)
return 1
#Done #Done
self.pause = False self.pause = False

View File

@ -56,9 +56,8 @@ TRADERS
62) mod_tp_level 63) last_call 64) deferred_last_call 62) mod_tp_level 63) last_call 64) deferred_last_call
65) toggle_pause 66) toggle_cleanup 67) toggle_autoswitch 65) toggle_pause 66) toggle_cleanup 67) toggle_autoswitch
68) toggle_check_old_long_price 69) switch_quote_currency 68) toggle_check_old_long_price 69) switch_quote_currency
70) reload_safety_order 71) view_old_long 72) switch_price 70) view_old_long 71) switch_price 72) reload_trader_config
73) reload_trader_config 74) toggle_liquidate_after_switch 73) toggle_liquidate_after_switch 74) base_add_calculation
75) base_add_calculation
98) Change broker 99) Exit 98) Change broker 99) Exit
''' '''
@ -561,10 +560,6 @@ if __name__=="__main__":
print("In order for the importing to be successful, a status file must exist in the status directory ") print("In order for the importing to be successful, a status file must exist in the status directory ")
print("and the take profit order must be open.") print("and the take profit order must be open.")
trading_pair = input("Input trader in the format BASE/QUOTE: ").upper() trading_pair = input("Input trader in the format BASE/QUOTE: ").upper()
tp_id_input = input("Input take profit order id to use (if any)")
forced_tp_id = None if tp_id_input=="" else tp_id_input
so_id_input = input("Input safety order id to use (if any)")
forced_so_id = None if so_id_input=="" else so_id_input
if not validate_pair(trading_pair): if not validate_pair(trading_pair):
print("The input is invalid") print("The input is invalid")
@ -573,9 +568,7 @@ if __name__=="__main__":
url = f"{base_url}{port}/import_pair" url = f"{base_url}{port}/import_pair"
base,quote = trading_pair.split("/") base,quote = trading_pair.split("/")
parameters = {"base": base, parameters = {"base": base,
"quote": quote, "quote": quote}
"forced_tp_id": forced_tp_id,
"forced_so_id": forced_so_id}
print(json.loads(requests.post(url, headers=headers, json=parameters).content)) print(json.loads(requests.post(url, headers=headers, json=parameters).content))
input("Press ENTER to continue ") input("Press ENTER to continue ")
@ -798,20 +791,6 @@ if __name__=="__main__":
input("Press ENTER to continue ") input("Press ENTER to continue ")
elif command==70: elif command==70:
print("reload_safety_order reloads the safety order to the reader using the order id present in the status dictionary")
trading_pair = input("Input trader in the format BASE/QUOTE: ").upper()
if not validate_pair(trading_pair):
print("The input is invalid")
break
if input("Proceed? (Y/n) ") in ["Y","y",""]:
url = f"{base_url}{port}/reload_safety_order"
base,quote = trading_pair.split("/")
parameters = {"base": base,
"quote": quote}
print(json.loads(requests.post(url, headers=headers, json=parameters).content))
input("Press ENTER to continue ")
elif command==71:
print("Views the old_long information") print("Views the old_long information")
trading_pair = input("Input trader in the format BASE/QUOTE: ").upper() trading_pair = input("Input trader in the format BASE/QUOTE: ").upper()
if not validate_pair(trading_pair): if not validate_pair(trading_pair):
@ -824,7 +803,7 @@ if __name__=="__main__":
print(json.loads(requests.get(url,headers=headers).content)) print(json.loads(requests.get(url,headers=headers).content))
input("Press ENTER to continue ") input("Press ENTER to continue ")
elif command==72: elif command==71:
print("Returns the price target to reach to switch to long mode") print("Returns the price target to reach to switch to long mode")
trading_pair = input("Input trader in the format BASE/QUOTE: ").upper() trading_pair = input("Input trader in the format BASE/QUOTE: ").upper()
if not validate_pair(trading_pair): if not validate_pair(trading_pair):
@ -836,7 +815,7 @@ if __name__=="__main__":
print(json.loads(requests.get(url,headers=headers).content)) print(json.loads(requests.get(url,headers=headers).content))
input("Press ENTER to continue ") input("Press ENTER to continue ")
elif command==73: elif command==72:
print("Reloads from disk the configuration file of a trader") print("Reloads from disk the configuration file of a trader")
trading_pair = input("Input trader in the format BASE/QUOTE: ").upper() trading_pair = input("Input trader in the format BASE/QUOTE: ").upper()
if not validate_pair(trading_pair): if not validate_pair(trading_pair):
@ -850,7 +829,7 @@ if __name__=="__main__":
print(json.loads(requests.post(url, headers=headers, json=parameters).content)) print(json.loads(requests.post(url, headers=headers, json=parameters).content))
input("Press ENTER to continue ") input("Press ENTER to continue ")
elif command==74: elif command==73:
print("toggle_liquidate_after_switch enables or disables the liquidation after an automatic switch to long of a short trader") print("toggle_liquidate_after_switch enables or disables the liquidation after an automatic switch to long of a short trader")
print("This is only valid in a short trader, of course.") print("This is only valid in a short trader, of course.")
trading_pair = input("Input trader in the format BASE/QUOTE: ").upper() trading_pair = input("Input trader in the format BASE/QUOTE: ").upper()
@ -865,7 +844,7 @@ if __name__=="__main__":
print(json.loads(requests.post(url, headers=headers, json=parameters).content)) print(json.loads(requests.post(url, headers=headers, json=parameters).content))
input("Press ENTER to continue ") input("Press ENTER to continue ")
elif command==75: elif command==74:
print("Returns the amount of safety orders that can be added to a short trader with the available funds") print("Returns the amount of safety orders that can be added to a short trader with the available funds")
trading_pair = input("Input trader in the format BASE/QUOTE: ").upper() trading_pair = input("Input trader in the format BASE/QUOTE: ").upper()
if not validate_pair(trading_pair): if not validate_pair(trading_pair):