From 0d191edb9efdc2bcb4113442a010f34b7d6beeb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20S=C3=A1nchez?= Date: Fri, 8 Nov 2024 15:06:38 -0300 Subject: [PATCH] profit modifier (boost) --- changelog.txt | 4 +++ configs/trader_config.json.example | 5 +++- exchange_wrapper.py | 24 ++++++++++++++++++ main.py | 2 +- profits/db_read.py | 8 +++--- todo.txt | 13 +++++----- trader.py | 40 +++++++++++++++++++++++++++--- 7 files changed, 79 insertions(+), 17 deletions(-) diff --git a/changelog.txt b/changelog.txt index 37cdc4b..661756f 100755 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +2024.11.08: +. Added boosted take profit level: If the trader is actively closing deals, the expected profit level is raised by x%. + Defaults are: Four deals in one hour, 1% raise. + 2024.11.06b: . Added /switch_to_long_price endpoint: It displays the price at which the automatic switch_to_long routine is triggered. diff --git a/configs/trader_config.json.example b/configs/trader_config.json.example index 5680550..3cad8b6 100755 --- a/configs/trader_config.json.example +++ b/configs/trader_config.json.example @@ -18,5 +18,8 @@ "check_old_long_price": true, #Compares the current price with the old_long price. (check strategy documentation for more details) "dynamic_so_deviance": true, #Uses a non-linear safety order deviance algorithm. (check strategy documentation for more details) "dsd_range": 1, #Range of the dynamic deviance algorithm. (check strategy documentation for more details) - "bias": -0.5 #Bias of the dynamic deviance algorithm. (check strategy documentation for more details) + "bias": -0.5, #Bias of the dynamic deviance algorithm. (check strategy documentation for more details) + "boosted_deals_range": 4, #Amount of deals within a timespan to trigger the boost algorithm. + "boosted_time_range": 3600, #Timespan in seconds to count closed trades. + "boosted_amount": .1 #Amount of percentage to add to the profit target if boosted. } diff --git a/exchange_wrapper.py b/exchange_wrapper.py index 281e6e1..500274e 100755 --- a/exchange_wrapper.py +++ b/exchange_wrapper.py @@ -87,11 +87,35 @@ class broker: return 1 + def return_last_n_deals_timestamps(self, pair, amount, no_retries=False): + ''' + Returns a list containing the last n deals timestamps. + ''' + + retries = self.retries + while retries>0: + try: + database_connection = sqlite3.connect("profits/profits_database.db") + database_cursor = database_connection.cursor() + database_cursor.execute(f"SELECT * FROM profits_table WHERE pair = '{pair}' ORDER BY timestamp DESC LIMIT {amount};") + rows = database_cursor.fetchall() + database_connection.close() + return [item[0] for item in rows] + except Exception as e: + self.logger.log_this(f"Exception in return_last_n_deals_timestamps: {e}",1) + if no_retries: + break + retries-=1 + time.sleep(self.wait_time) + return [] + + def check_for_duplicate_profit_in_db(self,order,no_retries=False): ''' SQLite implementation of check_for_duplicate_profit(): Compares the id of the last profit order with the one in the database. ''' + retries = self.retries while retries>0: try: diff --git a/main.py b/main.py index 834e912..fdee2f0 100644 --- a/main.py +++ b/main.py @@ -22,7 +22,7 @@ In case the permissions of the certificate changes, reset them this way: # ll /etc/letsencrypt/ ''' -version = "2024.11.06b" +version = "2024.11.08" ''' Color definitions. If you want to change them, check the reference at https://en.wikipedia.org/wiki/ANSI_escape_code#Colors diff --git a/profits/db_read.py b/profits/db_read.py index fd863c1..dec96d9 100644 --- a/profits/db_read.py +++ b/profits/db_read.py @@ -3,7 +3,7 @@ import time import json # Connect to the SQLite database -conn = sqlite3.connect('profits/profits_database.db') +conn = sqlite3.connect('profits_database.db') cursor = conn.cursor() # Execute a SELECT query to retrieve data from the database @@ -17,9 +17,9 @@ rows = cursor.fetchall() #human_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(row[0]))) #print(human_time,row[1],round(row[2],2),row[3]) # Or do whatever processing you need -data = json.loads(rows[-3][-1]) -print(data[-1]) +#data = json.loads(rows[-3][-1]) +print([item[0] for item in rows[-4:]]) print(f"Number of entries: {len(rows)}") # Close the connection -conn.close() \ No newline at end of file +conn.close() diff --git a/todo.txt b/todo.txt index 0675aa3..e17cf9d 100755 --- a/todo.txt +++ b/todo.txt @@ -5,13 +5,8 @@ Mandatory: 2. Instead of giving a list of order_ids to each trader, give a list of the open orders and that's it (for easier future development, partial order fills for example) 3. Maintain local orderbooks for each trading pair, which enables: 3a. Smart order pricing: Prioritization of fill speed over instant profit or vice versa -4. Round-robin trading pairs: Instead of a fixed list of trading pairs, after n closed deals the trader is terminated and a new one spawns, picking the trading pair - from a pre-populated list (the trading pairs can be selected by using Yang-Zhang, Parkinson or another volatility indicator) - This could be very benefitial, since it limits the long time commitment to a small list of trading pairs, enabling the instance to react to market trends very - rapidly. -5. Consolidate vocabulary (trader, pair and bot; instance & trader) -6. Base add for short traders. -7. Dynamic take_profit level: If a trading pair is closing deals frequently, raise the take profit level to take advantage of the volatility. +4. Consolidate vocabulary (trader, pair and bot; instance & trader) +5. Base add for short traders. Would be nice to have: @@ -19,6 +14,10 @@ Would be nice to have: 0. Trader order: alphabetical; by uptime; by safety orders, by percentage_to_completion. (Although this may be more suitable for the web and mobile apps) 1. Local implementation of amount_to_precision, cost_to_precision and price_to_precision. (Unless the plan is to continue to use CCXT forever) 2. Instead of cancelling and resending the take profit order, you could just edit it (Kucoin only supports editing on high frequency orders) +3. Round-robin trading pairs: Instead of a fixed list of trading pairs, after n closed deals the trader is terminated and a new one spawns, picking the trading pair + from a pre-populated list (the trading pairs can be selected by using Yang-Zhang, Parkinson or another volatility indicator) + This could be very benefitial, since it limits the long time commitment to a small list of trading pairs, enabling the instance to react to market trends very + rapidly. Maybe it's a good idea?: diff --git a/trader.py b/trader.py index 13d3d4e..df32541 100755 --- a/trader.py +++ b/trader.py @@ -27,6 +27,7 @@ class trader: self.check_slippage = True if "check_slippage" in self.config_dict: self.check_slippage = self.config_dict["check_slippage"] + self.is_boosted = False self.start_time = int(time.time()) self.total_amount_of_quote=0 self.total_amount_of_base=1 @@ -344,6 +345,7 @@ class trader: if self.so is not None and self.so["price"] is not None and self.so!=self.broker.get_empty_order(): self.status_dict["next_so_price"]=self.so["price"] + self.status_dict["is_boosted"]=self.is_boosted self.status_dict["is_short"]=self.is_short self.status_dict["quote_spent"]=self.total_amount_of_quote self.status_dict["base_bought"]=self.total_amount_of_base @@ -1276,10 +1278,35 @@ class trader: 2. Custom percentage table 3. Linear percentage table ''' + tp_level = 1 - if self.is_short or self.config_dict["tp_mode"]==0: #Fixed take profit percentage + boost = 0 + + #BOOST ROUTINE: If the trader closed certain amount of deals within the last t timespan, raise the take profit level by x% + #Default values + boosted_deals_range = 4 + boosted_time_range = 3600 + boosted_amount = .01 + + #Load config values (if present) + if "boosted_deals_range" in self.config_dict: + boosted_deals_range = self.config_dict["boosted_deals_range"] + if "boosted_time_range" in self.config_dict: + boosted_time_range = self.config_dict["boosted_time_range"] + if "boosted_amount" in self.config_dict: + boosted_amount = self.config_dict["boosted_amount"] + + self.is_boosted = False + + if not self.is_short: + last_deals_timestamps = self.broker.return_last_n_deals_timestamps(self.pair,boosted_deals_range) + if len(last_deals_timestamps)==boosted_deals_range and all(item >= time.time()-boosted_time_range for item in last_deals_timestamps): + self.is_boosted = True + boost+=boosted_amount + + if self.is_short or self.config_dict["tp_mode"]==0: #Fixed take profit percentage tp_level = self.config_dict["tp_level"] - elif self.config_dict["tp_mode"]==1: #Variable percentage + elif self.config_dict["tp_mode"]==1: #Variable percentage limit = self.config_dict["no_of_safety_orders"]/3 if order_index<=1: tp_level = self.config_dict["tp_level"]+0.005 @@ -1295,12 +1322,12 @@ class trader: tp_level = self.config_dict["tp_table"][order_index] #Custom percentage table tp_level = self.config_dict["tp_table"][-1] tp_level = self.config_dict["tp_level"] - elif self.config_dict["tp_mode"]==3: #Linear percentage table + elif self.config_dict["tp_mode"]==3: #Linear percentage table profit_table = self.linear_space(self.config_dict["tp_level"]+0.005,self.config_dict["tp_level"]-0.005,self.config_dict["no_of_safety_orders"]) tp_level = profit_table[-1] if order_index str: @@ -1701,10 +1728,13 @@ class trader: prices = f"{red}{low_boundary}{white}|{price_color}{mid_boundary}{white}|{target_price_color}{high_boundary}{white}|{pct_color}{pct_to_profit_str}%{white}" line1 = f"{p}{pair_color}{self.pair.center(13)}{white}| {safety_order_string} |{prices}| Uptime: {self.seconds_to_time(self.status_dict['deal_uptime'])}" + if self.is_boosted: + line1 = f"{line1} | BOOSTED" if self.config_dict["autoswitch"]: line1 = f"{line1} | AUTO" if self.is_short and "old_long" in self.status_dict: try: + #When adding a trader, this line always throws an exception since status_dict["price"] is not yet populated percentage_to_switch = (self.status_dict["old_long"]["tp_price"]-self.status_dict["price"])*100/self.status_dict["price"] #line1 = f"{line1} {round(percentage_to_switch,2)}%" multiplier = int(percentage_to_switch/100)+1 @@ -1748,6 +1778,8 @@ class trader: self.start_time = self.status_dict["start_time"] self.deal_start_time = self.status_dict["deal_start_time"] self.stop_when_profit = self.status_dict["stop_when_profit"] + if "is_boosted" in self.status_dict: + self.is_boosted = self.status_dict["is_boosted"] if "deal_order_history" not in self.status_dict: #No longer needed? self.status_dict["deal_order_history"] = []