/mod_concurrent_safety_orders endpoint added

This commit is contained in:
Nicolás Sánchez 2025-08-25 10:39:03 -03:00
parent 069cff2402
commit 3daca5336e
5 changed files with 273 additions and 195 deletions

55
main.py
View File

@ -18,7 +18,7 @@ import exchange_wrapper
import trader import trader
version = "2025.08.24" version = "2025.08.25"
''' '''
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
@ -849,6 +849,32 @@ def mod_order_size():
return jsonify({'Error': 'Halp'}) return jsonify({'Error': 'Halp'})
@base_api.route("/mod_concurrent_safety_orders", methods=['POST'])
def mod_concurrent_safety_orders():
'''
POST request
Parameters:
base: str
quote: str
amount: int
'''
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"]
amount = data["amount"]
return unwrapped_mod_concurrent_safety_orders(base,quote,amount)
except Exception as e:
print(e)
return jsonify({'Error': 'Halp'})
@base_api.route("/mod_default_order_size", methods=['POST']) @base_api.route("/mod_default_order_size", methods=['POST'])
def mod_default_order_size(): def mod_default_order_size():
''' '''
@ -1784,6 +1810,7 @@ def unwrapped_mod_order_size(base,quote,amount):
for instance in running_traders: for instance in running_traders:
if symbol==instance.config.get_pair(): if symbol==instance.config.get_pair():
instance.config.set_order_size(float(amount)) instance.config.set_order_size(float(amount))
instance.config.save_to_file()
broker.logger.log_this("Done. The change will take effect when the next deal is started",2,symbol) broker.logger.log_this("Done. The change will take effect when the next deal is started",2,symbol)
return jsonify({"Success": "Success. The change will take effect when the next deal is started"}) return jsonify({"Success": "Success. The change will take effect when the next deal is started"})
except Exception: except Exception:
@ -1791,6 +1818,32 @@ def unwrapped_mod_order_size(base,quote,amount):
return jsonify({"Error": "Error changing order size"}) return jsonify({"Error": "Error changing order size"})
def unwrapped_mod_concurrent_safety_orders(base,quote,amount):
'''
Modifies the amount of safety orders that a trader keeps opened at the same time.
Parameters:
base (str): The base currency of the pair.
quote (str): The quote currency of the pair.
amount (str): The new amount.
Returns:
jsonify: A jsonified dictionary detailing the outcome of the operation
'''
try:
symbol = f"{base}/{quote}"
for instance in running_traders:
if symbol==instance.config.get_pair():
instance.config.set_concurrent_safety_orders(int(amount))
instance.config.save_to_file()
broker.logger.log_this("Done. The change will take effect as new safety orders are sent or filled",2,symbol)
return jsonify({"Success": "Success. The change will take effect as new safety orders are sent or filled"})
except Exception:
broker.logger.log_this("Error changing safety orders amount. Ignoring...",2,symbol)
return jsonify({"Error": "Error changing safety orders amount"})
def unwrapped_mod_default_order_size(amount): def unwrapped_mod_default_order_size(amount):
''' '''
Modifies the default order size of a broker. Modifies the default order size of a broker.

View File

@ -155,6 +155,10 @@ class StatusHandler:
def get_status_file_path(self): def get_status_file_path(self):
return self.status_file_path return self.status_file_path
def set_pair(self, trading_pair):
self.pair = trading_pair
return 0
def set_status_file_path(self, new_file_path: str): def set_status_file_path(self, new_file_path: str):
# if not isinstance(new_file_path, str): # if not isinstance(new_file_path, str):
# self.broker.logger.log_this(f"File path provided is not a string",1,self.get_pair()) # self.broker.logger.log_this(f"File path provided is not a string",1,self.get_pair())

View File

@ -9,8 +9,7 @@ Mandatory:
5. Dockerize. 5. Dockerize.
6. Inspect orderbook liquidity prior to changing mode from short to long (big sell market order needs to have liquidity). 6. Inspect orderbook liquidity prior to changing mode from short to long (big sell market order needs to have liquidity).
7. API endpoint to modify the amount of concurrent safety orders 7. API endpoint to modify the amount of concurrent safety orders
8. Maybe when boosted, also increment the amount of concurrent safety orders? 8. Use create_orders ccxt method to send the batch of safety orders (Binance does not support it in spot trading)
9. Use create_orders ccxt method to send the batch of safety orders (Binance does not support it in spot trading)
Would be nice to have: Would be nice to have:

382
trader.py
View File

@ -118,77 +118,77 @@ class trader:
self.status.set_safety_orders_filled(0) self.status.set_safety_orders_filled(0)
#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.status.get_pair())
if new_market_data is not None: if new_market_data is not None:
self.market = new_market_data self.market = new_market_data
self.pause = True self.pause = True
self.status.set_pause_reason("start_trader") self.status.set_pause_reason("start_trader")
if self.config.get_is_short(): if self.status.get_is_short():
self.broker.logger.log_this("Calculating optimal order size...",2,self.config.get_pair()) self.broker.logger.log_this("Calculating optimal order size...",2,self.status.get_pair())
#Get minimum order size from exchange #Get minimum order size from exchange
self.broker.logger.log_this("Fetching minimum order size...",2,self.config.get_pair()) self.broker.logger.log_this("Fetching minimum order size...",2,self.status.get_pair())
min_base_size = self.broker.get_min_base_size(self.config.get_pair()) min_base_size = self.broker.get_min_base_size(self.status.get_pair())
if min_base_size is None: if min_base_size is None:
self.broker.logger.log_this("Can't fetch the minimum order size",1,self.config.get_pair()) self.broker.logger.log_this("Can't fetch the minimum order size",1,self.status.get_pair())
return 1 return 1
#Fetch the amount of free base available on the exchange #Fetch the amount of free base available on the exchange
self.broker.logger.log_this("Fetching free base currency on the exchange...",2,self.config.get_pair()) self.broker.logger.log_this("Fetching free base currency on the exchange...",2,self.status.get_pair())
free_base = self.fetch_free_base() free_base = self.fetch_free_base()
if free_base is None: if free_base is None:
self.broker.logger.log_this("Can't fetch the amount of base at the exchange",1,self.config.get_pair()) self.broker.logger.log_this("Can't fetch the amount of base at the exchange",1,self.status.get_pair())
return 1 return 1
#Buy missing base sold because of rounding errors (rare) #Buy missing base sold because of rounding errors (rare)
if self.status.get_old_long()!={}: if self.status.get_old_long()!={}:
diff = self.broker.amount_to_precision(self.config.get_pair(), self.status.get_old_long()["tp_amount"] - free_base) diff = self.broker.amount_to_precision(self.status.get_pair(), self.status.get_old_long()["tp_amount"] - free_base)
if diff>min_base_size: if diff>min_base_size:
self.broker.logger.log_this(f"Buying missing {diff} {self.base}",1,self.config.get_pair()) self.broker.logger.log_this(f"Buying missing {diff} {self.base}",1,self.status.get_pair())
self.broker.new_market_order(self.config.get_pair(),diff,"buy",amount_in_base=True) self.broker.new_market_order(self.status.get_pair(),diff,"buy",amount_in_base=True)
time.sleep(self.broker.get_wait_time()*2) time.sleep(self.broker.get_wait_time()*2)
#Re-quering for the amount of base currency on the exchange #Re-quering for the amount of base currency on the exchange
free_base = self.fetch_free_base() free_base = self.fetch_free_base()
if free_base is None: if free_base is None:
self.broker.logger.log_this("Can't fetch the amount of base at the exchange",1,self.config.get_pair()) self.broker.logger.log_this("Can't fetch the amount of base at the exchange",1,self.status.get_pair())
return 1 return 1
#Calculate order size and amount of safety orders #Calculate order size and amount of safety orders
self.broker.logger.log_this("Calculating the order size...",2,self.config.get_pair()) self.broker.logger.log_this("Calculating the order size...",2,self.status.get_pair())
order_size,no_of_safety_orders = self.calculate_order_size(free_base,min_base_size,self.config.get_max_short_safety_orders()) order_size,no_of_safety_orders = self.calculate_order_size(free_base,min_base_size,self.config.get_max_short_safety_orders())
if order_size is None or no_of_safety_orders is None: if order_size is None or no_of_safety_orders is None:
self.broker.logger.log_this("Can't calculate optimal size",1,self.config.get_pair()) self.broker.logger.log_this("Can't calculate optimal size",1,self.status.get_pair())
return 1 return 1
self.config.set_order_size(order_size) self.status.set_order_size(order_size)
self.config.set_no_of_safety_orders(no_of_safety_orders) self.status.set_no_of_safety_orders(no_of_safety_orders)
self.broker.logger.log_this(f"Order size: {self.broker.amount_to_precision(self.config.get_pair(),order_size)}. Amount of safety orders: {no_of_safety_orders}",2,self.config.get_pair()) self.broker.logger.log_this(f"Order size: {self.broker.amount_to_precision(self.status.get_pair(),order_size)}. Amount of safety orders: {no_of_safety_orders}",2,self.status.get_pair())
#Write the changes to the config file #Write the changes to the config file
self.config.save_to_file() self.config.save_to_file()
else: else:
#Check order size #Check order size
self.status.set_pause_reason("start_trader - checking order size") self.status.set_pause_reason("start_trader - checking order size")
self.broker.logger.log_this("Checking for order size",2,self.config.get_pair()) self.broker.logger.log_this("Checking for order size",2,self.status.get_pair())
minimum_order_size_allowed = self.broker.get_min_quote_size(self.config.get_pair()) minimum_order_size_allowed = self.broker.get_min_quote_size(self.status.get_pair())
if minimum_order_size_allowed is not None and minimum_order_size_allowed>self.config.get_order_size(): if minimum_order_size_allowed is not None and minimum_order_size_allowed>self.config.get_order_size():
self.broker.logger.log_this(f"Order size too small. Minimum order size is {minimum_order_size_allowed} {self.quote}",1,self.config.get_pair()) self.broker.logger.log_this(f"Order size too small. Minimum order size is {minimum_order_size_allowed} {self.quote}",1,self.status.get_pair())
if minimum_order_size_allowed<self.config.get_order_size()*2: if minimum_order_size_allowed<self.config.get_order_size()*2:
#int(n)+1 is treated here as a simplified ceil function, since minimum_order_size_allowed will always be positive. #int(n)+1 is treated here as a simplified ceil function, since minimum_order_size_allowed will always be positive.
self.broker.logger.log_this(f"Due to exchange limits, trader initial order size will be {float(int(minimum_order_size_allowed)+1)} {self.quote}",1,self.config.get_pair()) self.broker.logger.log_this(f"Due to exchange limits, trader initial order size will be {float(int(minimum_order_size_allowed)+1)} {self.quote}",1,self.status.get_pair())
self.config.set_order_size(float(int(minimum_order_size_allowed)+1)) self.config.set_order_size(float(int(minimum_order_size_allowed)+1))
else: else:
self.broker.logger.log_this("Limit difference is more than 2x the configured order size. Please adjust the order size in the trader config file and restart the trader.",1,self.config.get_pair()) self.broker.logger.log_this("Limit difference is more than 2x the configured order size. Please adjust the order size in the trader config file and restart the trader.",1,self.status.get_pair())
return 1 return 1
#check slippage #check slippage
if self.config.get_check_slippage(): if self.config.get_check_slippage():
self.broker.logger.log_this("Checking slippage...",2,self.config.get_pair()) self.broker.logger.log_this("Checking slippage...",2,self.status.get_pair())
self.status.set_pause_reason("start_trader - checking slippage") self.status.set_pause_reason("start_trader - checking slippage")
if self.check_orderbook_depth(self.broker.get_slippage_default_threshold(),self.config.get_order_size()): if self.check_orderbook_depth(self.broker.get_slippage_default_threshold(),self.config.get_order_size()):
#Slippage threshold exceeded #Slippage threshold exceeded
self.broker.logger.log_this("Slippage threshold exceeded",1,self.config.get_pair()) self.broker.logger.log_this("Slippage threshold exceeded",1,self.status.get_pair())
return 3 return 3
self.status.set_pause_reason("start_trader - after slippage") self.status.set_pause_reason("start_trader - after slippage")
@ -196,12 +196,12 @@ class trader:
#Sending initial order #Sending initial order
self.status.set_pause_reason("start_trader - sending first order") self.status.set_pause_reason("start_trader - sending first order")
self.broker.logger.log_this("Sending first order...",2,self.config.get_pair()) self.broker.logger.log_this("Sending first order...",2,self.status.get_pair())
action = "sell" if self.config.get_is_short() else "buy" action = "sell" if self.config.get_is_short() else "buy"
first_order = self.broker.new_market_order(self.config.get_pair(),self.config.get_order_size(),action) first_order = self.broker.new_market_order(self.status.get_pair(),self.status.get_order_size(),action)
#self.broker.logger.log_this(f"First order id: {first_order}",1,self.config.get_pair()) #self.broker.logger.log_this(f"First order id: {first_order}",1,self.status.get_pair())
if first_order in [None,self.broker.get_empty_order()]: if first_order in [None,self.broker.get_empty_order()]:
self.broker.logger.log_this(f"Error sending the first order. Market order returned {first_order}",1,self.config.get_pair()) self.broker.logger.log_this(f"Error sending the first order. Market order returned {first_order}",1,self.status.get_pair())
return 1 return 1
tries = self.broker.get_retries()*2 #This is really necessary, don't change it. Don't. DON'T. tries = self.broker.get_retries()*2 #This is really necessary, don't change it. Don't. DON'T.
@ -212,22 +212,22 @@ class trader:
# When that happens, 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.
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.status.get_pair())
if returned_order==self.broker.get_empty_order(): if returned_order==self.broker.get_empty_order():
self.broker.logger.log_this("Problems with the initial order",1,self.config.get_pair()) self.broker.logger.log_this("Problems with the initial order",1,self.status.get_pair())
return 1 return 1
elif returned_order["status"]=="closed": elif returned_order["status"]=="closed":
break break
elif returned_order["status"]=="expired": elif returned_order["status"]=="expired":
self.broker.logger.log_this(f"First order expired. Id: {returned_order['id']}",1,self.config.get_pair()) self.broker.logger.log_this(f"First order expired. Id: {returned_order['id']}",1,self.status.get_pair())
return 1 return 1
else: else:
tries-=1 tries-=1
self.broker.logger.log_this("Waiting for initial order to get filled...",2,self.config.get_pair()) self.broker.logger.log_this("Waiting for initial order to get filled...",2,self.status.get_pair())
self.broker.logger.log_this(f"Order ID: {returned_order['id']}",2,self.config.get_pair()) self.broker.logger.log_this(f"Order ID: {returned_order['id']}",2,self.status.get_pair())
if tries==0: if tries==0:
self.broker.logger.log_this("Restart retries exhausted.",0,self.config.get_pair()) self.broker.logger.log_this("Restart retries exhausted.",0,self.status.get_pair())
self.broker.cancel_order(returned_order["id"],self.config.get_pair()) self.broker.cancel_order(returned_order["id"],self.status.get_pair())
#self.restart = True #This restart is tricky, it can end up in an endless loop of retries #self.restart = True #This restart is tricky, it can end up in an endless loop of retries
#At this point, both the take_profit_routine initialization AND the subsequent restart attempt failed. #At this point, both the take_profit_routine initialization AND the subsequent restart attempt failed.
#Since it only reaches this point very unfrequently, let we'll the trader get stuck in a pause state. #Since it only reaches this point very unfrequently, let we'll the trader get stuck in a pause state.
@ -241,8 +241,8 @@ class trader:
# Reset the fee count and sum fees from the first order # Reset the fee count and sum fees from the first order
self.status.set_fees_paid_in_base(self.parse_fees(returned_order)[0]) self.status.set_fees_paid_in_base(self.parse_fees(returned_order)[0])
self.status.set_fees_paid_in_quote(self.parse_fees(returned_order)[1]) self.status.set_fees_paid_in_quote(self.parse_fees(returned_order)[1])
self.broker.logger.log_this(f"Fees paid: {self.status.get_fees_paid_in_base()} {self.base}, {self.status.get_fees_paid_in_quote()} {self.quote}",2,self.config.get_pair()) self.broker.logger.log_this(f"Fees paid: {self.status.get_fees_paid_in_base()} {self.base}, {self.status.get_fees_paid_in_quote()} {self.quote}",2,self.status.get_pair())
self.broker.logger.log_this(f"Take profit order ID: {returned_order['id']}",2,self.config.get_pair()) self.broker.logger.log_this(f"Take profit order ID: {returned_order['id']}",2,self.status.get_pair())
# Sum total amount of quote and base # Sum total amount of quote and base
if returned_order["filled"]!=None: if returned_order["filled"]!=None:
@ -251,33 +251,33 @@ class trader:
self.status.set_base_bought(self.status.get_base_bought()-self.status.get_fees_paid_in_base()) self.status.set_base_bought(self.status.get_base_bought()-self.status.get_fees_paid_in_base())
self.status.set_quote_spent(returned_order["cost"]) self.status.set_quote_spent(returned_order["cost"])
else: else:
self.broker.logger.log_this("Error starting trader. Aborting.",1,self.config.get_pair()) self.broker.logger.log_this("Error starting trader. Aborting.",1,self.status.get_pair())
return 1 return 1
# Send the take profit order # Send the take profit order
self.status.set_pause_reason("start_trader - sending tp order") self.status.set_pause_reason("start_trader - sending tp order")
self.broker.logger.log_this("Sending take profit order...",2,self.config.get_pair()) self.broker.logger.log_this("Sending take profit order...",2,self.status.get_pair())
if self.send_new_tp_order()==0: if self.send_new_tp_order()==0:
self.broker.logger.log_this("Take profit order sent",2,self.config.get_pair()) self.broker.logger.log_this("Take profit order sent",2,self.status.get_pair())
else: else:
self.broker.logger.log_this("Error sending take profit order. Aborting.",1,self.config.get_pair()) self.broker.logger.log_this("Error sending take profit order. Aborting.",1,self.status.get_pair())
return 1 return 1
# Generate the safety prices table # Generate the safety prices table
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.status.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.status.get_no_of_safety_orders(),self.config.get_safety_order_deviance()))
# Send the initial batch of safety orders # Send the initial batch of safety orders
self.status.set_pause_reason("start_trader - sending safety orders") self.status.set_pause_reason("start_trader - sending safety orders")
self.broker.logger.log_this("Sending safety orders...",2,self.config.get_pair()) self.broker.logger.log_this("Sending safety orders...",2,self.status.get_pair())
amount_of_so = self.config.get_concurrent_safety_orders() if not self.status.get_is_boosted() else self.config.get_boosted_concurrent_safety_orders() amount_of_so = self.config.get_concurrent_safety_orders() if not self.status.get_is_boosted() else self.config.get_boosted_concurrent_safety_orders()
max_initial_safety_orders = min(amount_of_so,self.config.get_no_of_safety_orders()) #To never send more than the max amount of safety orders max_initial_safety_orders = min(amount_of_so,self.status.get_no_of_safety_orders()) #To never send more than the max amount of safety orders
orders_placed = self.send_new_safety_order_batch(max_initial_safety_orders) orders_placed = self.send_new_safety_order_batch(max_initial_safety_orders)
if orders_placed is not None: 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()) self.broker.logger.log_this(f"{orders_placed}/{max_initial_safety_orders} safety orders placed",2,self.status.get_pair())
else: else:
self.broker.logger.log_this("Error sending safety orders. 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.status.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.status.get_pair())
return 1 return 1
# Send cleanup order (if cleanup) # Send cleanup order (if cleanup)
@ -308,7 +308,7 @@ class trader:
self.status.set_next_so_price(0) self.status.set_next_so_price(0)
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())
self.status.set_deal_uptime(int(time.time()) - self.status.get_deal_start_time()) self.status.set_deal_uptime(int(time.time()) - self.status.get_deal_start_time())
self.status.set_total_uptime(int(time.time()) - self.status.get_start_time()) self.status.set_total_uptime(int(time.time()) - self.status.get_start_time())
self.status.set_tp_mode(self.config.get_tp_mode()) self.status.set_tp_mode(self.config.get_tp_mode())
@ -316,7 +316,7 @@ class trader:
self.status.set_autoswitch(self.config.get_autoswitch()) self.status.set_autoswitch(self.config.get_autoswitch())
self.status.set_liquidate_after_switch(self.config.get_liquidate_after_switch()) self.status.set_liquidate_after_switch(self.config.get_liquidate_after_switch())
except Exception as e: except Exception as e:
self.broker.logger.log_this(f"Can't update status dictionary. Exception: {e}",1,self.config.get_pair()) self.broker.logger.log_this(f"Can't update status dictionary. Exception: {e}",1,self.status.get_pair())
return 1 return 1
if write_to_disk: if write_to_disk:
@ -400,7 +400,7 @@ class trader:
# try: # try:
# fee_rate = self.market["maker"] if order["type"]=="limit" else self.market["taker"] # fee_rate = self.market["maker"] if order["type"]=="limit" else self.market["taker"]
# except Exception as e: # except Exception as e:
# self.broker.logger.log_this(f"Exception fetching market information: {e}. Using default fee rate of 0.1%",1,self.config.get_pair()) # self.broker.logger.log_this(f"Exception fetching market information: {e}. Using default fee rate of 0.1%",1,self.status.get_pair())
# fee_rate = 0.001 # fee_rate = 0.001
# #
# if order["side"]=="buy": # if order["side"]=="buy":
@ -434,22 +434,22 @@ class trader:
balance_to_clean = self.fetch_free_base() balance_to_clean = self.fetch_free_base()
if balance_to_clean is None: if balance_to_clean is None:
self.broker.logger.log_this("Can't fetch free base",1,self.config.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.config.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-minimum_cleanup_size >= 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-minimum_cleanup_size} {self.base}",2,self.config.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.config.get_pair()) self.broker.logger.log_this("Sending cleanup order...",2,self.status.get_pair())
cleanup_order = self.broker.new_limit_order(self.config.get_pair(),balance_to_clean-minimum_cleanup_size,"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 not in [None,self.broker.get_empty_order()]: if cleanup_order not in [None,self.broker.get_empty_order()]:
self.broker.logger.log_this("Cleanup successful",2,self.config.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.config.get_pair()) self.broker.logger.log_this("Problems with the cleanup order",1,self.status.get_pair())
return 1 return 1
self.broker.logger.log_this("No cleanup needed",2,self.config.get_pair()) self.broker.logger.log_this("No cleanup needed",2,self.status.get_pair())
return 0 return 0
@ -463,16 +463,16 @@ class trader:
while amount_of_so>minimum_amount_of_safety_orders: while amount_of_so>minimum_amount_of_safety_orders:
optimal_order_size = self.return_optimal_order_size(free_base,min_base_size,amount_of_so,self.config.get_safety_order_scale()) #safety_order_scale: safety order growth factor optimal_order_size = self.return_optimal_order_size(free_base,min_base_size,amount_of_so,self.config.get_safety_order_scale()) #safety_order_scale: safety order growth factor
if optimal_order_size!=0: if optimal_order_size!=0:
self.broker.logger.log_this(f"Optimal order size is {optimal_order_size}",2,self.config.get_pair()) self.broker.logger.log_this(f"Optimal order size is {optimal_order_size}",2,self.status.get_pair())
break break
amount_of_so-=1 amount_of_so-=1
if optimal_order_size==0: if optimal_order_size==0:
self.broker.logger.log_this("Not enough base to switch. Order size would be too small",1,self.config.get_pair()) self.broker.logger.log_this("Not enough base to switch. Order size would be too small",1,self.status.get_pair())
self.pause = False self.pause = False
self.status.set_pause_reason("") self.status.set_pause_reason("")
return None,None return None,None
if optimal_order_size<min_base_size: #Sometimes amount_to_precision rounds to a value less than the minimum if optimal_order_size<min_base_size: #Sometimes amount_to_precision rounds to a value less than the minimum
self.broker.logger.log_this("Optimal order size is smaller than the minimum order size",1,self.config.get_pair()) self.broker.logger.log_this("Optimal order size is smaller than the minimum order size",1,self.status.get_pair())
self.pause = False self.pause = False
self.status.set_pause_reason("") self.status.set_pause_reason("")
return None,None return None,None
@ -488,11 +488,11 @@ class trader:
currency = self.base currency = self.base
balance = self.broker.get_coins_balance() balance = self.broker.get_coins_balance()
if balance==[]: if balance==[]:
self.broker.logger.log_this("Can't fetch free base from the exchange",1,self.config.get_pair()) self.broker.logger.log_this("Can't fetch free base from the exchange",1,self.status.get_pair())
return None return None
if currency in balance["free"]: if currency in balance["free"]:
return float(balance["free"][currency]) return float(balance["free"][currency])
self.broker.logger.log_this("Currency not present in balance",1,self.config.get_pair()) self.broker.logger.log_this("Currency not present in balance",1,self.status.get_pair())
return 0 return 0
@ -508,7 +508,7 @@ class trader:
#Let's do some type checking first #Let's do some type checking first
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.status.get_pair())
return 1 return 1
#Pauses trader #Pauses trader
@ -516,47 +516,47 @@ class trader:
self.status.set_pause_reason("switch_to_short") self.status.set_pause_reason("switch_to_short")
#Fetch the real amount of available base #Fetch the real amount of available base
self.broker.logger.log_this(f"Fetching available {self.base}",2,self.config.get_pair()) self.broker.logger.log_this(f"Fetching available {self.base}",2,self.status.get_pair())
free_base = self.fetch_free_base() free_base = self.fetch_free_base()
if free_base is None: if free_base is None:
return 1 return 1
#Fetch the minimal order size #Fetch the minimal order size
min_base_size = self.broker.get_min_base_size(self.config.get_pair()) min_base_size = self.broker.get_min_base_size(self.status.get_pair())
if min_base_size is None: if min_base_size is None:
self.broker.logger.log_this("Error. Can't fetch market info from the exchange",1,self.config.get_pair()) self.broker.logger.log_this("Error. Can't fetch market info from the exchange",1,self.status.get_pair())
self.pause = False self.pause = False
self.status.set_pause_reason("") self.status.set_pause_reason("")
return 1 return 1
#Check if there is enough base #Check if there is enough base
if self.broker.amount_to_precision(self.config.get_pair(),free_base+self.status.get_take_profit_order()["amount"])<=min_base_size: if self.broker.amount_to_precision(self.status.get_pair(),free_base+self.status.get_take_profit_order()["amount"])<=min_base_size:
self.broker.logger.log_this("Error. Not enough base currency",1,self.config.get_pair()) self.broker.logger.log_this("Error. Not enough base currency",1,self.status.get_pair())
self.pause = False self.pause = False
self.status.set_pause_reason("") self.status.set_pause_reason("")
return 1 return 1
#Calculate order size #Calculate order size
self.broker.logger.log_this("Calculating optimal order size",2,self.config.get_pair()) self.broker.logger.log_this("Calculating optimal order size",2,self.status.get_pair())
optimal_order_size,amount_of_so = self.calculate_order_size(free_base+self.status.get_take_profit_order()["amount"],min_base_size,amount_of_so=self.config.get_max_short_safety_orders()) optimal_order_size,amount_of_so = self.calculate_order_size(free_base+self.status.get_take_profit_order()["amount"],min_base_size,amount_of_so=self.config.get_max_short_safety_orders())
if optimal_order_size is None or amount_of_so is None: if optimal_order_size is None or amount_of_so is None:
return 1 return 1
self.broker.logger.log_this(f"New order size: {optimal_order_size}",2,self.config.get_pair()) self.broker.logger.log_this(f"New order size: {optimal_order_size}",2,self.status.get_pair())
self.broker.logger.log_this(f"Amount of safety orders: {amount_of_so}",2,self.config.get_pair()) self.broker.logger.log_this(f"Amount of safety orders: {amount_of_so}",2,self.status.get_pair())
#Close old orders #Close old orders
self.broker.logger.log_this("Switching trader mode to short",2,self.config.get_pair()) self.broker.logger.log_this("Switching trader mode to short",2,self.status.get_pair())
self.broker.logger.log_this("Closing orders...",2,self.config.get_pair()) self.broker.logger.log_this("Closing orders...",2,self.status.get_pair())
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.status.get_pair())==1:
self.broker.logger.log_this("Can't cancel the take profit order. Can't switch mode",1,self.config.get_pair()) self.broker.logger.log_this("Can't cancel the take profit order. Can't switch mode",1,self.status.get_pair())
self.pause = False self.pause = False
self.status.set_pause_reason("") self.status.set_pause_reason("")
return 1 return 1
if self.status.get_take_profit_order()["id"]!="": if self.status.get_take_profit_order()["id"]!="":
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.status.get_pair())
#Save the old take profit order info for later use #Save the old take profit order info for later use
self.broker.logger.log_this("Saving state in status_dict",2,self.config.get_pair()) self.broker.logger.log_this("Saving state in status_dict",2,self.status.get_pair())
self.status.set_old_long({"tp_price": self.status.get_take_profit_order()["price"], self.status.set_old_long({"tp_price": self.status.get_take_profit_order()["price"],
"tp_amount": self.status.get_take_profit_order()["amount"], "tp_amount": self.status.get_take_profit_order()["amount"],
"quote_spent": self.status.get_quote_spent(), "quote_spent": self.status.get_quote_spent(),
@ -567,22 +567,22 @@ class trader:
with open(f"status/{self.base}{self.quote}.oldlong","w") as s: with open(f"status/{self.base}{self.quote}.oldlong","w") as s:
s.write(dumps(self.status.get_old_long(),indent=4)) s.write(dumps(self.status.get_old_long(),indent=4))
except Exception as e: except Exception as e:
self.broker.logger.log_this(f"Exception while saving old_long file: {e}",1,self.config.get_pair()) self.broker.logger.log_this(f"Exception while saving old_long file: {e}",1,self.status.get_pair())
#Modify config file accordingly #Modify config file accordingly
self.broker.logger.log_this("Modifying config file and saving a backup",2,self.config.get_pair()) self.broker.logger.log_this("Modifying config file and saving a backup",2,self.status.get_pair())
try: try:
self.config.save_to_file(f"configs/{self.base}{self.quote}.bak") self.config.save_to_file(f"configs/{self.base}{self.quote}.bak")
self.config.set_is_short(True) self.config.set_is_short(True)
self.config.save_to_file() self.config.save_to_file()
self.broker.logger.log_this("Config file updated",2,self.config.get_pair()) self.broker.logger.log_this("Config file updated",2,self.status.get_pair())
except Exception as e: except Exception as e:
self.broker.logger.log_this(f"Error. Can't write the config file. Exception: {e}",1,self.config.get_pair()) self.broker.logger.log_this(f"Error. Can't write the config file. Exception: {e}",1,self.status.get_pair())
#self.pause = False #self.pause = False
return 1 return 1
self.status.set_stop_when_profit(False) self.status.set_stop_when_profit(False)
#self.config.set_is_short(True) #self.config.set_is_short(True)
self.broker.logger.log_this("Done configuring. Starting trader...",2,self.config.get_pair()) self.broker.logger.log_this("Done configuring. Starting trader...",2,self.status.get_pair())
return 0 return 0
@ -593,35 +593,35 @@ class trader:
''' '''
if not self.config.get_is_short(): if not self.config.get_is_short():
self.broker.logger.log_this("Trader already in long mode, nothing to do",1,self.config.get_pair()) self.broker.logger.log_this("Trader already in long mode, nothing to do",1,self.status.get_pair())
return 1 return 1
self.broker.logger.log_this("Attempting to switch to long trader",0,self.config.get_pair()) self.broker.logger.log_this("Attempting to switch to long trader",0,self.status.get_pair())
#Check old_long data #Check old_long data
if not ignore_old_long and self.status.get_old_long()=={}: if not ignore_old_long and self.status.get_old_long()=={}:
self.broker.logger.log_this("Can't find old long info on status_dict, searching for oldlong file",1,self.config.get_pair()) self.broker.logger.log_this("Can't find old long info on status_dict, searching for oldlong file",1,self.status.get_pair())
try: try:
with open(f"status/{self.base}{self.quote}.oldlong") as f: with open(f"status/{self.base}{self.quote}.oldlong") as f:
self.status.set_old_long(load(f)) self.status.set_old_long(load(f))
except Exception as e: except Exception as e:
#self.write_to_log(time.strftime(f"[%Y/%m/%d %H:%M:%S] | {self.config.get_pair()} | Can't find old long file")) #self.write_to_log(time.strftime(f"[%Y/%m/%d %H:%M:%S] | {self.status.get_pair()} | Can't find old long file"))
self.broker.logger.log_this(f"Can't file oldlong file. Exception: {e}",1,self.config.get_pair()) self.broker.logger.log_this(f"Can't file oldlong file. Exception: {e}",1,self.status.get_pair())
self.quit = True self.quit = True
return 1 return 1
#Cancel open orders #Cancel open orders
try: try:
for order in self.status.get_safety_orders(): for order in self.status.get_safety_orders():
self.broker.cancel_order(order["id"],self.config.get_pair()) self.broker.cancel_order(order["id"],self.status.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.status.get_pair())
try: try:
if self.status.get_take_profit_order() is not None: if self.status.get_take_profit_order() is not None:
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.status.get_pair())
else: else:
self.broker.logger.log_this("Safety order is None",1,self.config.get_pair()) self.broker.logger.log_this("Safety order is None",1,self.status.get_pair())
except Exception as e: except Exception as e:
self.broker.logger.log_this(f"Error in cancel_order while cancelling take profit order. Exception: {e}",1,self.config.get_pair()) self.broker.logger.log_this(f"Error in cancel_order while cancelling take profit order. Exception: {e}",1,self.status.get_pair())
#Sell all base currency #Sell all base currency
self.liquidate_base(ignore_profits=ignore_old_long, already_received_quote=already_received_quote) self.liquidate_base(ignore_profits=ignore_old_long, already_received_quote=already_received_quote)
@ -639,13 +639,13 @@ class trader:
if self.config.load_from_file()==1: if self.config.load_from_file()==1:
self.config.reset_to_default() self.config.reset_to_default()
else: else:
self.broker.logger.log_this("Config/backup file does not exist",1,self.config.get_pair()) self.broker.logger.log_this("Config/backup file does not exist",1,self.status.get_pair())
self.config.reset_to_default() self.config.reset_to_default()
self.config.save_to_file() self.config.save_to_file()
#Remove old_long file (if it exists) #Remove old_long file (if it exists)
if path.isfile(f"status/{self.base}{self.quote}.oldlong"): if path.isfile(f"status/{self.base}{self.quote}.oldlong"):
self.broker.logger.log_this("Removing old_long file...",2,self.config.get_pair()) self.broker.logger.log_this("Removing old_long file...",2,self.status.get_pair())
remove(f"status/{self.base}{self.quote}.oldlong") remove(f"status/{self.base}{self.quote}.oldlong")
#Set up a few variables #Set up a few variables
@ -669,20 +669,20 @@ class trader:
#Find out the amount of free base #Find out the amount of free base
free_base = self.fetch_free_base() free_base = self.fetch_free_base()
if free_base is None: if free_base is None:
self.broker.logger.log_this("Can't fetch free base",1,self.config.get_pair()) self.broker.logger.log_this("Can't fetch free base",1,self.status.get_pair())
return 1 return 1
#send market order selling the total amount of base in the last take profit short order #send market order selling the total amount of base in the last take profit short order
order = self.broker.new_market_order(self.config.get_pair(),free_base,"sell") order = self.broker.new_market_order(self.status.get_pair(),free_base,"sell")
tries = self.broker.get_retries() tries = self.broker.get_retries()
while True: while True:
time.sleep(self.broker.get_wait_time()) time.sleep(self.broker.get_wait_time())
market_tp_order = self.broker.get_order(order["id"],self.config.get_pair()) market_tp_order = self.broker.get_order(order["id"],self.status.get_pair())
if market_tp_order["status"]=="closed": if market_tp_order["status"]=="closed":
_, fees_paid = self.parse_fees(market_tp_order) _, fees_paid = self.parse_fees(market_tp_order)
break break
tries-=1 tries-=1
if tries==0: if tries==0:
self.broker.logger.log_this("Liquidation order not filling. Skipping base liquidation",1,self.config.get_pair()) self.broker.logger.log_this("Liquidation order not filling. Skipping base liquidation",1,self.status.get_pair())
return 1 return 1
#calculate profits #calculate profits
@ -691,9 +691,9 @@ class trader:
#Add profits to file and send telegram notifying profits #Add profits to file and send telegram notifying profits
self.profit_to_db(profit,market_tp_order["id"],self.broker.get_write_order_history()) self.profit_to_db(profit,market_tp_order["id"],self.broker.get_write_order_history())
self.broker.logger.log_this(f"Switch successful. Profit: {round(profit,2)} {self.quote}",0,self.config.get_pair()) self.broker.logger.log_this(f"Switch successful. Profit: {round(profit,2)} {self.quote}",0,self.status.get_pair())
self.broker.logger.log_this(f"Sell price: {market_tp_order['price']} {self.quote}",0,self.config.get_pair()) self.broker.logger.log_this(f"Sell price: {market_tp_order['price']} {self.quote}",0,self.status.get_pair())
self.broker.logger.log_this(f"Order ID: {market_tp_order['id']}",0,self.config.get_pair()) self.broker.logger.log_this(f"Order ID: {market_tp_order['id']}",0,self.status.get_pair())
return 0 return 0
@ -713,8 +713,8 @@ class trader:
#Let's do some type checking first #Let's do some type checking first
if self.status.get_take_profit_order() is None: if self.status.get_take_profit_order() is None:
self.status.set_pause_reason(time.strftime(f"[%Y/%m/%d %H:%M:%S] | {self.config.get_pair()} | TP order is None")) self.status.set_pause_reason(time.strftime(f"[%Y/%m/%d %H:%M:%S] | {self.status.get_pair()} | TP order is None"))
self.broker.logger.log_this("Error. Take profit order is None, trader will be restarted",0,self.config.get_pair()) self.broker.logger.log_this("Error. Take profit order is None, trader will be restarted",0,self.status.get_pair())
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
@ -725,15 +725,15 @@ class trader:
#Cancel all the safety orders ASAP #Cancel all the safety orders ASAP
for order in self.status.get_safety_orders(): for order in self.status.get_safety_orders():
self.broker.cancel_order(order["id"],self.config.get_pair()) self.broker.cancel_order(order["id"],self.status.get_pair())
#Check if some safety orders were filled #Check if some safety orders were filled
for order in self.status.get_safety_orders(): for order in self.status.get_safety_orders():
closed_order = self.broker.get_order(order["id"],self.config.get_pair()) closed_order = self.broker.get_order(order["id"],self.status.get_pair())
if closed_order["filled"]==0: if closed_order["filled"]==0:
#If this order wasn't filled, it is safe to assume that no order coming after this one was. #If this order wasn't filled, it is safe to assume that no order coming after this one was.
break break
#Sum the filled amounts #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.broker.logger.log_this(f"Old safety order is partially filled, ID: {closed_order['id']}",1,self.status.get_pair())
self.status.set_base_bought(self.status.get_base_bought() + closed_order["filled"] - self.parse_fees(closed_order)[0]) 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
@ -762,14 +762,14 @@ class trader:
if profit>0: #Negative profits are not saved because the cleanup takes care of the unsold base currency (the notorious small change issue that plagues some exchanges) if profit>0: #Negative profits are not saved because the cleanup takes care of the unsold base currency (the notorious small change issue that plagues some exchanges)
self.profit_to_db(profit,filled_order["id"],self.broker.get_write_order_history()) self.profit_to_db(profit,filled_order["id"],self.broker.get_write_order_history())
else: #For logging purposes else: #For logging purposes
self.broker.logger.log_this(f"NEGATIVE PROFIT - Total amount of base: {self.status.get_base_bought()}, base in the order: {filled_order['amount']}, base filled: {filled_order['filled']}, base 'profit': {base_profit}",1,self.config.get_pair()) self.broker.logger.log_this(f"NEGATIVE PROFIT - Total amount of base: {self.status.get_base_bought()}, base in the order: {filled_order['amount']}, base filled: {filled_order['filled']}, base 'profit': {base_profit}",1,self.status.get_pair())
self.telegram_bot_sendprofit(profit,filled_order,base_profit=base_profit) self.telegram_bot_sendprofit(profit,filled_order,base_profit=base_profit)
# Print profit message on screen # Print profit message on screen
extra = ' and {:.4f}'.format(base_profit) + f" {self.base}" if base_profit>0 else "" extra = ' and {:.4f}'.format(base_profit) + f" {self.base}" if base_profit>0 else ""
self.broker.logger.log_this(f"Trader closed a deal. Profit: {'{:.4f}'.format(profit)} {self.quote}{extra}",2,self.config.get_pair()) self.broker.logger.log_this(f"Trader closed a deal. Profit: {'{:.4f}'.format(profit)} {self.quote}{extra}",2,self.status.get_pair())
self.broker.logger.log_this(f"Fill price: {filled_order['price']} {self.quote}",2,self.config.get_pair()) self.broker.logger.log_this(f"Fill price: {filled_order['price']} {self.quote}",2,self.status.get_pair())
self.broker.logger.log_this(f"Safety orders triggered: {self.status.get_safety_orders_filled()}",2,self.config.get_pair()) self.broker.logger.log_this(f"Safety orders triggered: {self.status.get_safety_orders_filled()}",2,self.status.get_pair())
self.status.set_pause_reason("take_profit_routine - check time limit") self.status.set_pause_reason("take_profit_routine - check time limit")
#Checks if there is a time limit for the trader #Checks if there is a time limit for the trader
@ -778,7 +778,7 @@ class trader:
self.status.set_pause_reason("take_profit_routine - if stop_when_profit") self.status.set_pause_reason("take_profit_routine - if stop_when_profit")
if self.status.get_stop_when_profit(): #Signal to stop when trade is closed if self.status.get_stop_when_profit(): #Signal to stop when trade is closed
self.broker.logger.log_this("Pair shutting down. So long and thanks for all the fish",0,self.config.get_pair()) self.broker.logger.log_this("Pair shutting down. So long and thanks for all the fish",0,self.status.get_pair())
self.quit = True self.quit = True
return 1 return 1
@ -787,18 +787,18 @@ class trader:
self.status.set_pause_reason("Checking slippage") self.status.set_pause_reason("Checking slippage")
if self.config.get_check_slippage(): if self.config.get_check_slippage():
self.broker.logger.log_this("Checking slippage...",2,self.config.get_pair()) self.broker.logger.log_this("Checking slippage...",2,self.status.get_pair())
price_to_compare = self.broker.get_top_bid_price(self.config.get_pair()) if self.config.get_is_short() else self.broker.get_top_ask_price(self.config.get_pair()) price_to_compare = self.broker.get_top_bid_price(self.status.get_pair()) if self.config.get_is_short() else self.broker.get_top_ask_price(self.status.get_pair())
if abs(filled_order["price"]-price_to_compare)/filled_order["price"]>self.broker.get_slippage_default_threshold(): if abs(filled_order["price"]-price_to_compare)/filled_order["price"]>self.broker.get_slippage_default_threshold():
self.broker.logger.log_this(f"Slippage threshold exceeded, waiting for cooldown and restarting trader",1,self.config.get_pair()) self.broker.logger.log_this(f"Slippage threshold exceeded, waiting for cooldown and restarting trader",1,self.status.get_pair())
time.sleep(self.broker.get_wait_time()*self.broker.get_cooldown_multiplier()) time.sleep(self.broker.get_wait_time()*self.broker.get_cooldown_multiplier())
#The trader is restarted by the instance instead of by itself to allow a couple of more seconds for the price to return to normal. #The trader is restarted by the instance instead of by itself to allow a couple of more seconds for the price to return to normal.
#This could also be the default behavior. #This could also be the default behavior.
self.pause = False self.pause = False
self.restart = True self.restart = True
return 1 return 1
elif self.check_orderbook_depth(self.broker.get_slippage_default_threshold(),self.config.get_order_size(),filled_order["price"]): elif self.check_orderbook_depth(self.broker.get_slippage_default_threshold(),self.status.get_order_size(),filled_order["price"]):
self.broker.logger.log_this(f"Orderbook depth not sufficient, waiting for cooldown and restarting trader",1,self.config.get_pair()) self.broker.logger.log_this(f"Orderbook depth not sufficient, waiting for cooldown and restarting trader",1,self.status.get_pair())
time.sleep(self.broker.get_wait_time()*self.broker.get_cooldown_multiplier()) time.sleep(self.broker.get_wait_time()*self.broker.get_cooldown_multiplier())
self.pause = False self.pause = False
self.restart = True self.restart = True
@ -813,7 +813,7 @@ class trader:
self.pause = False self.pause = False
self.restart = True self.restart = True
self.status.save_to_file(is_backup=True) self.status.save_to_file(is_backup=True)
self.broker.logger.log_this(self.trader_restart_errors[restart_trader],1,self.config.get_pair()) self.broker.logger.log_this(self.trader_restart_errors[restart_trader],1,self.status.get_pair())
return restart_trader return restart_trader
@ -827,22 +827,22 @@ class trader:
if amount<1: if amount<1:
return 0 return 0
orders_to_place = min(self.config.get_no_of_safety_orders()-self.status.get_so_amount(),amount) orders_to_place = min(self.status.get_no_of_safety_orders()-self.status.get_so_amount(),amount)
if orders_to_place<1: if orders_to_place<1:
return 0 return 0
orders_placed = 0 orders_placed = 0
for i in range(orders_to_place): for i in range(orders_to_place):
self.broker.logger.log_this(f"Sending a new safety order ({i+1}/{orders_to_place})",2,self.config.get_pair()) self.broker.logger.log_this(f"Sending a new safety order ({i+1}/{orders_to_place})",2,self.status.get_pair())
so_size = self.gib_so_size(self.status.get_order_size(),self.status.get_so_amount()+1,self.config.get_safety_order_scale()) 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(): 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]) new_order = self.broker.new_limit_order(self.status.get_pair(),so_size,"sell",self.status.get_safety_price_table()[self.status.get_so_amount()+1])
else: 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]) new_order = self.broker.new_limit_order(self.status.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: if new_order==1:
self.broker.logger.log_this("Not enough balance to send a new safety order",1,self.config.get_pair()) self.broker.logger.log_this("Not enough balance to send a new safety order",1,self.status.get_pair())
return orders_placed return orders_placed
elif new_order is None: elif new_order is None:
self.broker.logger.log_this("new_limit_order returned None",1,self.config.get_pair()) self.broker.logger.log_this("new_limit_order returned None",1,self.status.get_pair())
return orders_placed return orders_placed
orders_placed+=1 orders_placed+=1
self.status.add_safety_order(new_order) self.status.add_safety_order(new_order)
@ -859,7 +859,7 @@ class trader:
#Check if current TP order is valid #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.status.get_pair())
return 1 return 1
#Pause the trader #Pause the trader
@ -889,16 +889,16 @@ class trader:
self.status.set_safety_orders(new_order_list) self.status.set_safety_orders(new_order_list)
#Cancel 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.status.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.status.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.status.get_pair())
self.status.set_pause_reason(error_string) self.status.set_pause_reason(error_string)
return 2 return 2
#Check if old TP order was partially filled #Check if old TP order was partially filled
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.status.get_pair())
if old_tp_order["filled"]>0: if old_tp_order["filled"]>0:
self.broker.logger.log_this(f"Old take profit order is partially filled, id {old_tp_order['id']}",1,self.config.get_pair()) self.broker.logger.log_this(f"Old take profit order is partially filled, id {old_tp_order['id']}",1,self.status.get_pair())
if self.broker.get_follow_order_history(): if self.broker.get_follow_order_history():
self.status.update_deal_order_history(old_tp_order) self.status.update_deal_order_history(old_tp_order)
#self.status.set_base_bought(old_tp_order["remaining"]) #self.status.set_base_bought(old_tp_order["remaining"])
@ -922,12 +922,12 @@ class trader:
if len(self.status.get_safety_orders())<max_orders: if len(self.status.get_safety_orders())<max_orders:
self.send_new_safety_order_batch(len(filled_safety_orders)) self.send_new_safety_order_batch(len(filled_safety_orders))
#Cooldown #Cooldown
time.sleep(self.broker.get_wait_time()) time.sleep(self.broker.get_wait_time())
#Send new TP 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.status.get_pair())
self.status.set_pause_reason(error_string) self.status.set_pause_reason(error_string)
return 4 return 4
@ -959,7 +959,7 @@ class trader:
self.warnings["speol_notified"] = True self.warnings["speol_notified"] = True
if not self.config.get_autoswitch(): if not self.config.get_autoswitch():
message = f"{self.base}@{self.status.get_price()} ({str(self.broker.exchange)}), exceeds old long price of {self.status.get_old_long()['tp_price']}" message = f"{self.base}@{self.status.get_price()} ({str(self.broker.exchange)}), exceeds old long price of {self.status.get_old_long()['tp_price']}"
self.broker.logger.log_this(message,0,self.config.get_pair()) self.broker.logger.log_this(message,0,self.status.get_pair())
return 0 return 0
@ -980,9 +980,9 @@ class trader:
if self.config.get_is_short(): #Do not check for slippage in short traders (Pending to be implemented) if self.config.get_is_short(): #Do not check for slippage in short traders (Pending to be implemented)
return False return False
order_book = self.broker.get_order_book(self.config.get_pair(),no_retries=True) order_book = self.broker.get_order_book(self.status.get_pair(),no_retries=True)
if order_book=={}: if order_book=={}:
self.broker.logger.log_this("Can't fetch orderbook",1,self.config.get_pair()) self.broker.logger.log_this("Can't fetch orderbook",1,self.status.get_pair())
return False return False
suma = 0 suma = 0
try: try:
@ -1002,7 +1002,7 @@ class trader:
return True return True
return False return False
except Exception as e: except Exception as e:
self.broker.logger.log_this(f"Exception in check_orderbook_depth: {e}",1,self.config.get_pair()) self.broker.logger.log_this(f"Exception in check_orderbook_depth: {e}",1,self.status.get_pair())
return False return False
@ -1037,53 +1037,53 @@ class trader:
return 1 return 1
#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.status.get_pair()]
#Checks if the take profit order is 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.status.get_pair())
return 1 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.status.get_pair())
#Cancelling safety orders #Cancelling safety orders
for item in self.status.get_safety_orders(): for item in self.status.get_safety_orders():
self.broker.cancel_order(item["id"],self.config.get_pair()) self.broker.cancel_order(item["id"],self.status.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
self.broker.logger.log_this("Raising restart flag: take profit order missing, trader will be restarted",0,self.config.get_pair()) self.broker.logger.log_this("Raising restart flag: take profit order missing, trader will be restarted",0,self.status.get_pair())
else: else:
self.broker.logger.log_this("Take profit order missing. Trader restart disabled.",2,self.config.get_pair()) self.broker.logger.log_this("Take profit order missing. Trader restart disabled.",2,self.status.get_pair())
return 1 return 1
#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:
tp_status = self.broker.get_order(self.status.get_take_profit_order()["id"],self.config.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":
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.status.get_pair())
#Cancelling safety orders #Cancelling safety orders
for item in self.status.get_safety_orders(): for item in self.status.get_safety_orders():
self.broker.cancel_order(item["id"],self.config.get_pair()) self.broker.cancel_order(item["id"],self.status.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
self.broker.logger.log_this("Take profit order closed but not filled, trader will be restarted.",0,self.config.get_pair()) self.broker.logger.log_this("Take profit order closed but not filled, trader will be restarted.",0,self.status.get_pair())
else: else:
self.broker.logger.log_this("Take profit order closed but not filled, trader restart disabled.",1,self.config.get_pair()) self.broker.logger.log_this("Take profit order closed but not filled, trader restart disabled.",1,self.status.get_pair())
return 1 return 1
elif tp_status["status"]=="canceled": elif tp_status["status"]=="canceled":
#TODO: Here, if the safety order is still open, we could resend the tp order. #TODO: Here, if the safety order is still open, we could resend the tp order.
if self.config.get_attempt_restart(): if self.config.get_attempt_restart():
self.broker.logger.log_this("Take profit order canceled. Restarting the trader.",1,self.config.get_pair()) self.broker.logger.log_this("Take profit order canceled. Restarting the trader.",1,self.status.get_pair())
self.status.save_to_file(is_backup=True) self.status.save_to_file(is_backup=True)
self.restart = True self.restart = True
else: else:
self.broker.logger.log_this("Take profit order canceled. Trader restart disabled.",1,self.config.get_pair()) self.broker.logger.log_this("Take profit order canceled. Trader restart disabled.",1,self.status.get_pair())
return 1 return 1
elif tp_status["status"]=="": elif tp_status["status"]=="":
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.status.get_pair())
return 1 return 1
# Check if any safety order is filled # Check if any safety order is filled
@ -1093,26 +1093,26 @@ class trader:
filled_ids.append(order["id"]) filled_ids.append(order["id"])
if filled_ids!=[]: if filled_ids!=[]:
closed_orders = self.broker.get_closed_orders(self.config.get_pair()) closed_orders = self.broker.get_closed_orders(self.status.get_pair())
filled_orders = [item for item in closed_orders if item["id"] in filled_ids and item["status"]=="closed"] #maybe item["status"] in ["closed", "canceled", ""]? filled_orders = [item for item in closed_orders if item["id"] in filled_ids and item["status"]=="closed"] #maybe item["status"] in ["closed", "canceled", ""]?
self.status.set_safety_orders_filled(self.status.get_safety_orders_filled()+len(filled_orders)) self.status.set_safety_orders_filled(self.status.get_safety_orders_filled()+len(filled_orders))
renew_outcome = self.renew_tp_and_so_routine(filled_orders) renew_outcome = self.renew_tp_and_so_routine(filled_orders)
#0 OK, 1 take profit order is None, 2 not enough funds, 3 can't cancel TP (filled?), 4 can't send new TP #0 OK, 1 take profit order is None, 2 not enough funds, 3 can't cancel TP (filled?), 4 can't send new TP
if renew_outcome==1: if renew_outcome==1:
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.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.status.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
elif renew_outcome==2: elif renew_outcome==2:
#Not enough funds? #Not enough funds?
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? renew_tp_and_so_routine returned 2",1,self.status.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 #Set no_of_safety_orders to the same amount of orders open so the script does not try to send new safety orders
#This can be improved #This can be improved
self.config.set_no_of_safety_orders(self.status.get_so_amount()) self.status.set_no_of_safety_orders(self.status.get_so_amount())
return 1 return 1
elif renew_outcome==3: elif renew_outcome==3:
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.broker.logger.log_this(f"Can't cancel old take profit order. renew_tp_and_so_routine returned 3",1,self.status.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():
@ -1120,7 +1120,7 @@ class trader:
self.restart = True self.restart = True
return 1 return 1
elif renew_outcome==4: elif renew_outcome==4:
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. renew_tp_and_so_routine returned 4",0,self.status.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
@ -1128,7 +1128,7 @@ class trader:
#Check for autoswitch (long->short) #Check for autoswitch (long->short)
#Commented out because i'm not sure where this should go #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(): #if not self.config.get_is_short() and self.status.get_so_amount()==self.status.get_no_of_safety_orders() and self.config.get_autoswitch():
# self.switch_to_short() # self.switch_to_short()
# self.status.save_to_file(is_backup=True) # self.status.save_to_file(is_backup=True)
# self.restart = True # self.restart = True
@ -1169,7 +1169,7 @@ class trader:
if self.config.get_is_short() or self.config.get_tp_mode()==0: #Fixed take profit percentage if self.config.get_is_short() or self.config.get_tp_mode()==0: #Fixed take profit percentage
tp_level = self.config.get_tp_level() tp_level = self.config.get_tp_level()
elif self.config.get_tp_mode()==1: #Variable percentage elif self.config.get_tp_mode()==1: #Variable percentage
limit = self.config.get_no_of_safety_orders()/3 limit = self.status.get_no_of_safety_orders()/3
if order_index<=1: if order_index<=1:
tp_level = self.config.get_tp_level()+0.005 tp_level = self.config.get_tp_level()+0.005
elif order_index<=limit: elif order_index<=limit:
@ -1185,7 +1185,7 @@ class trader:
tp_level = self.config.get_tp_table()[-1] tp_level = self.config.get_tp_table()[-1]
tp_level = self.config.get_tp_level() tp_level = self.config.get_tp_level()
elif self.config.get_tp_mode()==3: #Linear percentage table elif self.config.get_tp_mode()==3: #Linear percentage table
profit_table = self.linear_space(self.config.get_tp_level()+0.005,self.config.get_tp_level()-0.005,self.config.get_no_of_safety_orders()) profit_table = self.linear_space(self.config.get_tp_level()+0.005,self.config.get_tp_level()-0.005,self.status.get_no_of_safety_orders())
tp_level = profit_table[-1] tp_level = profit_table[-1]
if order_index<len(profit_table): #If more safety orders were added, instead of recalculating the whole table if order_index<len(profit_table): #If more safety orders were added, instead of recalculating the whole table
tp_level = profit_table[order_index] #it just returns the last value. Otherwise, the percentage gets very small. tp_level = profit_table[order_index] #it just returns the last value. Otherwise, the percentage gets very small.
@ -1211,7 +1211,7 @@ class trader:
time.sleep(self.broker.get_wait_time()) time.sleep(self.broker.get_wait_time())
new_balance = self.broker.get_coins_balance() new_balance = self.broker.get_coins_balance()
if bool(new_balance): if bool(new_balance):
self.broker.logger.log_this(f"Adjusting base amount to {new_balance['free'][self.base]}, total balance: {new_balance['total'][self.base]}",1,self.config.get_pair()) self.broker.logger.log_this(f"Adjusting base amount to {new_balance['free'][self.base]}, total balance: {new_balance['total'][self.base]}",1,self.status.get_pair())
return new_balance["free"][self.base] return new_balance["free"][self.base]
return None return None
@ -1223,14 +1223,14 @@ class trader:
tries = self.broker.get_retries() tries = self.broker.get_retries()
while tries>0: while tries>0:
if self.status.get_base_bought()==0: if self.status.get_base_bought()==0:
self.broker.logger.log_this("Amount of base equals 0, can't send take profit order",1,self.config.get_pair()) self.broker.logger.log_this("Amount of base equals 0, can't send take profit order",1,self.status.get_pair())
return 1 return 1
if self.config.get_is_short(): if self.config.get_is_short():
self.status.set_take_profit_price(self.status.get_quote_spent()/self.status.get_base_bought()*(1-(self.get_tp_level(self.status.get_so_amount())-1))) self.status.set_take_profit_price(self.status.get_quote_spent()/self.status.get_base_bought()*(1-(self.get_tp_level(self.status.get_so_amount())-1)))
self.status.set_take_profit_order(self.broker.new_limit_order(self.config.get_pair(),self.status.get_base_bought(),"buy",self.status.get_take_profit_price())) self.status.set_take_profit_order(self.broker.new_limit_order(self.status.get_pair(),self.status.get_base_bought(),"buy",self.status.get_take_profit_price()))
else: else:
self.status.set_take_profit_price(self.status.get_quote_spent()/self.status.get_base_bought()*self.get_tp_level(self.status.get_so_amount())) self.status.set_take_profit_price(self.status.get_quote_spent()/self.status.get_base_bought()*self.get_tp_level(self.status.get_so_amount()))
self.status.set_take_profit_order(self.broker.new_limit_order(self.config.get_pair(),self.status.get_base_bought(),"sell",self.status.get_take_profit_price())) self.status.set_take_profit_order(self.broker.new_limit_order(self.status.get_pair(),self.status.get_base_bought(),"sell",self.status.get_take_profit_price()))
if self.status.get_take_profit_order()==1: #This means that there was a miscalculation of base currency amount, let's correct it. if self.status.get_take_profit_order()==1: #This means that there was a miscalculation of base currency amount, let's correct it.
if self.config.get_is_short(): #If in short mode, we don't recalculate anything. if self.config.get_is_short(): #If in short mode, we don't recalculate anything.
return 1 return 1
@ -1242,7 +1242,7 @@ class trader:
return 0 return 0
tries-=1 tries-=1
time.sleep(self.broker.get_wait_time()) time.sleep(self.broker.get_wait_time())
self.broker.logger.log_this("Problems sending take profit order",1,self.config.get_pair()) self.broker.logger.log_this("Problems sending take profit order",1,self.status.get_pair())
return 1 return 1
@ -1254,12 +1254,12 @@ class trader:
while retries>0: while retries>0:
try: try:
order_history = dumps(self.status.get_deal_order_history()) if write_deal_order_history else "" order_history = dumps(self.status.get_deal_order_history()) if write_deal_order_history else ""
dataset = (time.time(),self.config.get_pair(),amount,self.broker.get_exchange_name(),str(orderid),order_history) dataset = (time.time(),self.status.get_pair(),amount,self.broker.get_exchange_name(),str(orderid),order_history)
#Write profit to cache #Write profit to cache
self.broker.write_profit_to_cache(dataset) self.broker.write_profit_to_cache(dataset)
return self.broker.write_profit_to_db(dataset) return self.broker.write_profit_to_db(dataset)
except Exception as e: except Exception as e:
self.broker.logger.log_this(f"Exception while writing profit: {e}",1,self.config.get_pair()) self.broker.logger.log_this(f"Exception while writing profit: {e}",1,self.status.get_pair())
retries-=1 retries-=1
time.sleep(.1) #Shorter wait time since it's not an API call time.sleep(.1) #Shorter wait time since it's not an API call
return 1 return 1
@ -1271,11 +1271,11 @@ class trader:
''' '''
try: try:
extra = f" and {round(base_profit,6)} {self.base}" if base_profit>0 else "" extra = f" and {round(base_profit,6)} {self.base}" if base_profit>0 else ""
message = f"{self.config.get_pair()} closed a {'short' if self.config.get_is_short() else 'long'} trade.\nProfit: {round(profit,6)} {self.quote}{extra}\nSafety orders triggered: {self.status.get_safety_orders_filled()}\nTake profit price: {order['price']} {self.quote}\nTrade size: {round(order['cost'],2)} {self.quote}\nDeal uptime: {self.seconds_to_time(self.status.get_deal_uptime())}\nOrder ID: {order['id']}\nExchange: {self.broker.get_exchange_name().capitalize()}\n" message = f"{self.status.get_pair()} closed a {'short' if self.config.get_is_short() else 'long'} trade.\nProfit: {round(profit,6)} {self.quote}{extra}\nSafety orders triggered: {self.status.get_safety_orders_filled()}\nTake profit price: {order['price']} {self.quote}\nTrade size: {round(order['cost'],2)} {self.quote}\nDeal uptime: {self.seconds_to_time(self.status.get_deal_uptime())}\nOrder ID: {order['id']}\nExchange: {self.broker.get_exchange_name().capitalize()}\n"
self.broker.logger.send_tg_message(message) self.broker.logger.send_tg_message(message)
return 0 return 0
except Exception as e: except Exception as e:
self.broker.logger.log_this(f"Exception in telegram_bot_sendprofit: {e}",1,self.config.get_pair()) self.broker.logger.log_this(f"Exception in telegram_bot_sendprofit: {e}",1,self.status.get_pair())
return 1 return 1
@ -1341,17 +1341,17 @@ class trader:
#First let's check if the market exists #First let's check if the market exists
market = self.broker.fetch_market(f"{self.base}/{new_quote}") market = self.broker.fetch_market(f"{self.base}/{new_quote}")
if market is None: if market is None:
self.broker.logger.log_this("Market might not exist",1,self.config.get_pair()) self.broker.logger.log_this("Market might not exist",1,self.status.get_pair())
return 1 return 1
if "active" in market and not market["active"]: if "active" in market and not market["active"]:
self.broker.logger.log_this("Market is closed",1,self.config.get_pair()) self.broker.logger.log_this("Market is closed",1,self.status.get_pair())
return 1 return 1
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.status.get_pair())
return 1 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.status.get_pair())
self.status.set_take_profit_order(self.quote_currency_replace_order(self.status.get_take_profit_order(),new_quote)) self.status.set_take_profit_order(self.quote_currency_replace_order(self.status.get_take_profit_order(),new_quote))
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
@ -1360,21 +1360,21 @@ class trader:
#This is WRONG: We need to build a list of the newly sent orders and assign them with self.status.set_safety_orders() #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 = [] new_order_list = []
for order in self.status.get_safety_orders(): 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.status.get_pair())
new_order_list.append(self.quote_currency_replace_order(order,new_quote)) new_order_list.append(self.quote_currency_replace_order(order,new_quote))
self.status.set_safety_orders(new_order_list) self.status.set_safety_orders(new_order_list)
#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.status.get_pair())
self.quote_currency_switch_configs(new_quote) self.quote_currency_switch_configs(new_quote)
#Updates status_dict #Updates status_dict
self.broker.logger.log_this("Updating status file",2,self.config.get_pair()) self.broker.logger.log_this("Updating status file",2,self.status.get_pair())
self.status.set_status_file_path(f"status/{self.base}{self.quote}.status") self.status.set_status_file_path(f"status/{self.base}{self.quote}.status")
self.update_status(True) self.update_status(True)
#Done #Done
self.broker.logger.log_this("Quote swap successful",2,self.config.get_pair()) self.broker.logger.log_this("Quote swap successful",2,self.status.get_pair())
return 0 return 0
@ -1383,8 +1383,8 @@ class trader:
Cancels the order and returns the new updated order Cancels the order and returns the new updated order
''' '''
#Cancels the old order #Cancels the old order
if self.broker.cancel_order(old_order["id"],self.config.get_pair())==1: if self.broker.cancel_order(old_order["id"],self.status.get_pair())==1:
self.broker.logger.log_this(f"Can't cancel old order {old_order['id']}",1,self.config.get_pair()) self.broker.logger.log_this(f"Can't cancel old order {old_order['id']}",1,self.status.get_pair())
return self.broker.get_empty_order() return self.broker.get_empty_order()
#Sends the new order #Sends the new order
@ -1400,7 +1400,7 @@ class trader:
self.broker.add_pair_to_config(f"{self.base}{new_quote}") self.broker.add_pair_to_config(f"{self.base}{new_quote}")
if self.broker.rewrite_config_file()==1: if self.broker.rewrite_config_file()==1:
#Error writing broker config file, undoing changes #Error writing broker config file, undoing changes
self.broker.logger.log_this("Error writing new broker config file",1,self.config.get_pair()) self.broker.logger.log_this("Error writing new broker config file",1,self.status.get_pair())
self.quote_currency_undo_changes(new_quote,self.quote,False) self.quote_currency_undo_changes(new_quote,self.quote,False)
return 1 return 1
@ -1408,6 +1408,7 @@ class trader:
old_quote = self.quote old_quote = self.quote
self.quote = new_quote self.quote = new_quote
self.config.set_pair(f"{self.base}/{self.quote}") self.config.set_pair(f"{self.base}/{self.quote}")
self.status.set_pair(f"{self.base}/{self.quote}")
self.profit_filename = f"profits/{self.base}{self.quote}.profits" self.profit_filename = f"profits/{self.base}{self.quote}.profits"
self.log_filename = f"logs/{self.base}{self.quote}.log" self.log_filename = f"logs/{self.base}{self.quote}.log"
@ -1417,12 +1418,12 @@ class trader:
with open(f"status/{self.base}{self.quote}.oldlong","w") as c: with open(f"status/{self.base}{self.quote}.oldlong","w") as c:
c.write(dumps(self.status.get_old_long(), indent=4)) c.write(dumps(self.status.get_old_long(), indent=4))
except Exception as e: except Exception as e:
self.broker.logger.log_this(f"Exception while writing new old_long file: {e}",1,self.config.get_pair()) self.broker.logger.log_this(f"Exception while writing new old_long file: {e}",1,self.status.get_pair())
#Write the new config file #Write the new config file
self.config.set_config_file_path(f"configs/{self.base}{self.quote}.json") self.config.set_config_file_path(f"configs/{self.base}{self.quote}.json")
if self.config.save_to_file()==1: if self.config.save_to_file()==1:
self.broker.logger.log_this(f"Error while writing the new trader config file",1,self.config.get_pair()) self.broker.logger.log_this(f"Error while writing the new trader config file",1,self.status.get_pair())
#Undoing changes #Undoing changes
self.quote_currency_undo_changes(new_quote,old_quote,True) self.quote_currency_undo_changes(new_quote,old_quote,True)
return 1 return 1
@ -1441,12 +1442,13 @@ class trader:
self.broker.add_pair_to_config(f"{self.base}{self.quote}") self.broker.add_pair_to_config(f"{self.base}{self.quote}")
self.config.set_pair(f"{self.base}/{self.quote}") self.config.set_pair(f"{self.base}/{self.quote}")
self.status.set_pair(f"{self.base}/{self.quote}")
self.profit_filename = f"profits/{self.base}{self.quote}.profits" self.profit_filename = f"profits/{self.base}{self.quote}.profits"
self.log_filename = f"logs/{self.base}{self.quote}.log" self.log_filename = f"logs/{self.base}{self.quote}.log"
#Writing config file #Writing config file
if write_broker_file and self.broker.rewrite_config_file()==1: if write_broker_file and self.broker.rewrite_config_file()==1:
self.broker.logger.log_this("Error in quote_currency_undo_changed: error writing new broker config file",1,self.config.get_pair()) self.broker.logger.log_this("Error in quote_currency_undo_changed: error writing new broker config file",1,self.status.get_pair())
#Done #Done
return 0 return 0
@ -1555,9 +1557,9 @@ class trader:
except Exception as e: except Exception as e:
print(e) print(e)
safety_order_string = f"{self.status.get_safety_orders_filled()}/{self.get_color('cyan')}{len(self.status.get_safety_orders())}{self.get_color('white')}/{self.config.get_no_of_safety_orders()}".rjust(27) safety_order_string = f"{self.status.get_safety_orders_filled()}/{self.get_color('cyan')}{len(self.status.get_safety_orders())}{self.get_color('white')}/{self.status.get_no_of_safety_orders()}".rjust(27)
prices = f"{low_boundary_color}{low_boundary}{self.get_color('white')}|{price_color}{mid_boundary}{self.get_color('white')}|{target_price_color}{high_boundary}{self.get_color('white')}|{pct_color}{pct_to_profit_str}%{self.get_color('white')}" prices = f"{low_boundary_color}{low_boundary}{self.get_color('white')}|{price_color}{mid_boundary}{self.get_color('white')}|{target_price_color}{high_boundary}{self.get_color('white')}|{pct_color}{pct_to_profit_str}%{self.get_color('white')}"
line1 = f"{p}{pair_color}{self.config.get_pair().center(13)}{self.get_color('white')}| {safety_order_string} |{prices}| Uptime: {self.seconds_to_time(self.status.get_deal_uptime())}" line1 = f"{p}{pair_color}{self.status.get_pair().center(13)}{self.get_color('white')}| {safety_order_string} |{prices}| Uptime: {self.seconds_to_time(self.status.get_deal_uptime())}"
if self.status.get_is_boosted(): if self.status.get_is_boosted():
line1 = f"{line1} | BOOSTED" line1 = f"{line1} | BOOSTED"
if self.config.get_autoswitch(): if self.config.get_autoswitch():
@ -1584,18 +1586,18 @@ class trader:
''' '''
#Load status dict #Load status dict
if self.status.load_from_file()==1: if self.status.load_from_file()==1:
self.broker.logger.log_this(f"Error: Couldn't load status dict. Aborting",1,self.config.get_pair()) self.broker.logger.log_this(f"Error: Couldn't load status dict. Aborting",1,self.status.get_pair())
self.quit = True self.quit = True
return 1 return 1
self.status.set_pause_reason("Importing trader") self.status.set_pause_reason("Importing 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"] 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.status.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.status.get_pair())
self.quit = True self.quit = True
return 1 return 1
@ -1604,7 +1606,7 @@ class trader:
#Refresh safety orders #Refresh safety orders
#new_order_list = [] #new_order_list = []
#for order in self.status.get_safety_orders(): #for order in self.status.get_safety_orders():
# new_order_list.append(self.broker.get_order(order["id"],self.config.get_pair())) # new_order_list.append(self.broker.get_order(order["id"],self.status.get_pair()))
# time.sleep(self.broker.get_wait_time()) # time.sleep(self.broker.get_wait_time())
#self.status.set_safety_orders(new_order_list) #self.status.set_safety_orders(new_order_list)

View File

@ -11,7 +11,7 @@ try:
api_key = credentials.get_credentials("testnet_api_key")["key"] api_key = credentials.get_credentials("testnet_api_key")["key"]
base_url = credentials.get_url("testnet") #type: ignore base_url = credentials.get_url("testnet") #type: ignore
exchanges = {"Binance":"/binance"} exchanges = {"Binance":"/binance"}
if sys.argv[1]=="--local_testnet": elif sys.argv[1]=="--local_testnet":
is_testnet = True is_testnet = True
string_to_add = "LOCAL TESTNET " string_to_add = "LOCAL TESTNET "
api_key = credentials.get_credentials("local_testnet_api_key")["key"] api_key = credentials.get_credentials("local_testnet_api_key")["key"]
@ -64,6 +64,7 @@ TRADERS
68) toggle_check_old_long_price 69) switch_quote_currency 68) toggle_check_old_long_price 69) switch_quote_currency
70) view_old_long 71) switch_price 72) reload_trader_config 70) view_old_long 71) switch_price 72) reload_trader_config
73) toggle_liquidate_after_switch 74) base_add_calculation 73) toggle_liquidate_after_switch 74) base_add_calculation
75) mod_concurrent_safety_orders
98) Change broker 99) Exit 98) Change broker 99) Exit
''' '''
@ -860,4 +861,23 @@ if __name__=="__main__":
base,quote = trading_pair.split("/") base,quote = trading_pair.split("/")
url = f"{base_url}{port}/base_add_so_calculation?base={base}&quote={quote}" url = f"{base_url}{port}/base_add_so_calculation?base={base}&quote={quote}"
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==75:
print("mod_concurrent_safety_orders modifies the amount of safety orders opened at the same time")
trading_pair = input("Input trader in the format BASE/QUOTE: ").upper()
new_amount = input("Desired amount of orders: ")
if not validate_pair(trading_pair):
print("The input is invalid")
break
if not validate_int(new_amount):
print("The amount entered is invalid")
break
if input("Proceed? (Y/n) ") in ["Y","y",""]:
url = f"{base_url}{port}/mod_concurrent_safety_orders"
base,quote = trading_pair.split("/")
parameters = {"base": base,
"quote": quote,
"amount": new_amount}
print(json.loads(requests.post(url, headers=headers, json=parameters).content))
input("Press ENTER to continue ")