diff --git a/changelog.txt b/changelog.txt index b34ac9c..14192e7 100755 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +2025.10.03: +. New broker config option: log_orders. If set to True, the orders will be logged in orders.log under logs directory. +. New API endpoint: /toggle_log_orders. + 2025.10.01: . Fixed base fees not being taken into account. diff --git a/exchange_wrapper.py b/exchange_wrapper.py index ccf911b..fce5497 100755 --- a/exchange_wrapper.py +++ b/exchange_wrapper.py @@ -26,6 +26,7 @@ class Broker: self.follow_order_history = self.broker_config.get("follow_order_history",False) self.write_order_history = self.broker_config.get("write_order_history", False) self.logger = Logger(self.broker_config) + self.log_orders = self.broker_config.get("log_orders",False) #Initialize database self.profits_database_filename = "profits/profits_database.db" @@ -81,6 +82,16 @@ class Broker: def get_deals_cache(self): return self.deals_list + + def get_log_orders(self): + return self.log_orders + + + def set_log_orders(self,log_orders:bool): + self.log_orders = log_orders + return 0 + + def get_symbol(self,pair): if "/" in pair: return pair @@ -652,7 +663,7 @@ class Broker: return amount - def new_simulated_market_order(self,symbol,size,side,amount_in_base=False,no_retries=False): + def new_simulated_market_order(self,symbol,size,side,amount_in_base=False,no_retries=False,log=""): ''' TODO: Emulating Market Orders With Limit Orders @@ -682,7 +693,7 @@ class Broker: while retries>0: try: if self.get_exchange_name()=="gateio" and side=="buy" and not amount_in_base: - new_order = self.exchange.create_market_buy_order_with_cost(symbol, size) + new_order = self.exchange.create_market_buy_order_with_cost(symbol, size) else: order_book = self.get_order_book(symbol) if order_book=={}: @@ -696,6 +707,8 @@ class Broker: price = self.find_minimum_viable_price(order_book,base_amount,side) #Maybe check for slippage here instead of within the trader itself? idk new_order = self.exchange.create_order(symbol,"limit",side,base_amount,price) + if self.log_orders: + self.logger.log_order(f"New simulated market order: Symbol: {symbol} - Side: {side} - Size: {size} - Price: {price} - ID: {new_order['id']} - Origin: {log}") time.sleep(self.wait_time) return self.get_order(new_order["id"],symbol) except Exception as e: @@ -742,7 +755,7 @@ class Broker: return None - def new_market_order(self,symbol,size,side,amount_in_base=False,no_retries=False): #It should send a new market order to the exchange + def new_market_order(self,symbol,size,side,amount_in_base=False,no_retries=False, log=""): #It should send a new market order to the exchange ''' Sends a new market order to the exchange. @@ -767,6 +780,8 @@ class Broker: amount = self.amount_to_precision(symbol,size) #Market sell orders are always nominated in base currency order_to_send = self.exchange.create_order(symbol,"market",side,amount) + if self.log_orders: + self.logger.log_order(f"New market order: Symbol: {symbol} - Side: {side} - Size: {size} - ID: {order_to_send['id']} - Origin: {log}") time.sleep(self.wait_time) return self.get_order(order_to_send["id"],symbol) @@ -853,7 +868,7 @@ class Broker: # return returned_orders - def new_limit_order(self,symbol,size,side,price,no_retries=False): + def new_limit_order(self,symbol,size,side,price,no_retries=False,log=""): ''' Sends a new limit order. @@ -869,6 +884,8 @@ class Broker: try: order_to_send = self.exchange.create_order(symbol,"limit",side,self.amount_to_precision(symbol,size),price) time.sleep(self.wait_time) + if self.log_orders: + self.logger.log_order(f"New limit order: Symbol: {symbol} - Side: {side} - Size: {size} - Price: {price} - ID: {order_to_send['id']} - Notes: {log}") return self.get_order(order_to_send["id"],symbol) except Exception as e: self.logger.log_this(f"Exception in new_limit_order - Side: {side} - Size: {size} - {self.amount_to_precision(symbol,size)} - Exception: {e}",1,symbol) @@ -1084,6 +1101,9 @@ class Logger: self.log_this(f"Error in send_tg_message: {e}",1) return 1 + def log_order(self,message): + with open(f"logs/orders.log","a") as log_file: + log_file.write(time.strftime(f"[%Y/%m/%d %H:%M:%S] | {message}\n")) def log_this(self,message,level=2,pair=None): ''' @@ -1105,8 +1125,6 @@ class Logger: #Write to log file with open(f"logs/{self.exchange_name}.log","a") as log_file: log_file.write(text+"\n") - log_file.close() - #Append to log list self.log_list.append(text) except Exception as e: diff --git a/main.py b/main.py index 9b44e7f..eab5cbf 100644 --- a/main.py +++ b/main.py @@ -18,7 +18,7 @@ import exchange_wrapper import trader -version = "2025.10.01" +version = "2025.10.03" ''' Color definitions. If you want to change them, check the reference at https://en.wikipedia.org/wiki/ANSI_escape_code#Colors @@ -1238,6 +1238,20 @@ def switch_quote_currency(): return jsonify({'Error': 'Halp'}) +@base_api.route("/toggle_log_orders", methods=['POST']) +def toggle_log_orders(): + ''' + 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 + return unwrapped_toggle_log_orders() + + @base_api.route("/toggle_restart", methods=['POST']) def toggle_restart(): ''' @@ -2360,6 +2374,20 @@ def unwrapped_toggle_restart(): return jsonify({"Success": "attempt_to_restart disabled"}) +def unwrapped_toggle_log_orders(): + ''' + Toggles on or off the logging of orders. + + Returns: + jsonify: A jsonified dictionary detailing the outcome of the operation. + ''' + + broker.set_log_orders(not broker.get_log_orders()) + if broker.get_log_orders(): + return jsonify({"Success": "log_orders enabled"}) + return jsonify({"Success": "log_orders disabled"}) + + def unwrapped_toggle_telegram(): ''' Switches on or off the Telegram notifications diff --git a/trader.py b/trader.py index f6b8399..09a181b 100755 --- a/trader.py +++ b/trader.py @@ -155,7 +155,7 @@ class trader: if self.status.get_old_long()["tp_amount"]-free_base>min_base_size: amount_to_buy = self.broker.amount_to_precision(self.status.get_pair(),self.status.get_old_long()["tp_amount"]-free_base) self.broker.logger.log_this(f"Buying missing {amount_to_buy} {self.base}",1,self.status.get_pair()) - self.broker.new_market_order(self.status.get_pair(),amount_to_buy,"buy",amount_in_base=True) + self.broker.new_market_order(self.status.get_pair(),amount_to_buy,"buy",amount_in_base=True,log="start_trader-missing_base") time.sleep(self.broker.get_wait_time()*2) #Re-querying for the amount of base currency on the exchange free_base = self.fetch_free_base() @@ -211,7 +211,7 @@ class trader: self.status.set_pause_reason("start_trader - sending first order") self.broker.logger.log_this("Sending first order...",2,self.status.get_pair()) action = "sell" if self.config.get_is_short() else "buy" - first_order = self.broker.new_market_order(self.status.get_pair(),self.status.get_order_size(),action) + first_order = self.broker.new_market_order(self.status.get_pair(),self.status.get_order_size(),action,log="start_trader") #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()]: self.broker.logger.log_this(f"Error sending the first order. Market order returned {first_order}",1,self.status.get_pair()) @@ -456,7 +456,7 @@ class trader: if (balance_in_account-min_size)*self.status.get_start_price()>self.broker.get_min_quote_size(self.status.get_pair()): self.broker.logger.log_this(f"Balance to clean: {balance_in_account-min_size} {self.base}",2,self.status.get_pair()) self.broker.logger.log_this("Sending cleanup order...",2,self.status.get_pair()) - cleanup_order = self.broker.new_limit_order(self.status.get_pair(),balance_in_account-min_size,"sell",self.status.get_take_profit_price(),no_retries=True) + cleanup_order = self.broker.new_limit_order(self.status.get_pair(),balance_in_account-min_size,"sell",self.status.get_take_profit_price(),no_retries=True,log="cleanup") if cleanup_order is None: self.broker.logger.log_this("Problems with the cleanup order, new_limit_order returned None",1,self.status.get_pair()) return 1 @@ -683,7 +683,7 @@ class trader: self.broker.logger.log_this("Can't fetch free base",1,self.status.get_pair()) return 1 #send market order selling the total amount of base in the last take profit short order - order = self.broker.new_market_order(self.status.get_pair(),free_base,"sell") + order = self.broker.new_market_order(self.status.get_pair(),free_base,"sell",log="liquidate_base") time.sleep(self.broker.get_wait_time()*2) tries = self.broker.get_retries() while True: @@ -723,7 +723,7 @@ class trader: #Send the market order amount = self.status.get_take_profit_order()["amount"] - market_order = self.broker.new_market_order(self.status.get_pair(),amount,"sell",amount_in_base=True) + market_order = self.broker.new_market_order(self.status.get_pair(),amount,"sell",amount_in_base=True,log="force_close") time.sleep(self.broker.get_wait_time()*2) #Wait for it to be filled @@ -794,7 +794,7 @@ class trader: if partial_filled_amount!=0 and len(partial_filled_price)>0 and partial_filled_amount>self.broker.get_min_base_size(self.status.get_pair()): #send a market order and sum the profits and wait for it to be filled self.broker.logger.log_this("Sending partial fill sell order...",1,self.status.get_pair()) - market_order = self.broker.new_market_order(self.status.get_pair(),partial_filled_amount,"sell",amount_in_base=True) + market_order = self.broker.new_market_order(self.status.get_pair(),partial_filled_amount,"sell",amount_in_base=True,log="take_profit_routine-partial_fill") time.sleep(self.broker.get_wait_time()*2) tries = self.broker.get_retries() while True: @@ -925,9 +925,9 @@ class trader: 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()) if self.config.get_is_short(): - 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]) + 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], log="send_new_safety_order_batch") else: - 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]) + 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],log="send_new_safety_order_batch") if new_order==1: self.broker.logger.log_this("Not enough balance to send a new safety order",1,self.status.get_pair()) @@ -1348,10 +1348,10 @@ class trader: return 1 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_order(self.broker.new_limit_order(self.status.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(),log="new_tp_order")) 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_order(self.broker.new_limit_order(self.status.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(),log="new_tp_order")) if self.status.get_take_profit_order()==1: #This means that there was a miscalculation of base currency amount if self.config.get_is_short(): #If in short mode, we don't recalculate anything. return 1 @@ -1515,7 +1515,7 @@ class trader: return self.broker.get_empty_order() #Sends the new order - return self.broker.new_limit_order(f"{self.base}/{new_quote}",old_order["amount"],old_order["side"],old_order["price"]) + return self.broker.new_limit_order(f"{self.base}/{new_quote}",old_order["amount"],old_order["side"],old_order["price"],log="quote_currency_replace_order") def quote_currency_switch_configs(self, new_quote: str) -> int: diff --git a/utils/commander.py b/utils/commander.py index c94e0ad..6b65b2c 100644 --- a/utils/commander.py +++ b/utils/commander.py @@ -43,7 +43,7 @@ INSTANCE 10) edit_call_wait_time 11) reload_markets 12) fetch_full_log 13) paused_traders 14) fetch_log 15) edit_cooldown_multiplier 16) get_balance 17) cancel_global_last_call -18) mod_default_order_size +18) mod_default_order_size 19) toggle_log_orders EARN 31) toggle_pause 32) get_step_size 33) set_step_size @@ -341,6 +341,13 @@ if __name__=="__main__": print(json.loads(requests.post(url, headers=headers, json=parameters).content)) input("Press ENTER to continue ") + elif command==19: + print("toggle_log_orders turns on or off the logging of orders") + if input("Proceed? (Y/n) ") in ["Y","y",""]: + url = f"{base_url}{port}/toggle_log_orders" + print(json.loads(requests.post(url, headers=headers).content)) + input("Press ENTER to continue ") + ###################### ######## EARN ########