profit modifier (boost)

This commit is contained in:
Nicolás Sánchez 2024-11-08 15:06:38 -03:00
parent e421e9bbde
commit 0d191edb9e
7 changed files with 79 additions and 17 deletions

View File

@ -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: 2024.11.06b:
. Added /switch_to_long_price endpoint: It displays the price at which the automatic switch_to_long routine is triggered. . Added /switch_to_long_price endpoint: It displays the price at which the automatic switch_to_long routine is triggered.

View File

@ -18,5 +18,8 @@
"check_old_long_price": true, #Compares the current price with the old_long price. (check strategy documentation for more details) "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) "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) "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.
} }

View File

@ -87,11 +87,35 @@ class broker:
return 1 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): def check_for_duplicate_profit_in_db(self,order,no_retries=False):
''' '''
SQLite implementation of check_for_duplicate_profit(): SQLite implementation of check_for_duplicate_profit():
Compares the id of the last profit order with the one in the database. Compares the id of the last profit order with the one in the database.
''' '''
retries = self.retries retries = self.retries
while retries>0: while retries>0:
try: try:

View File

@ -22,7 +22,7 @@ In case the permissions of the certificate changes, reset them this way:
# ll /etc/letsencrypt/ # 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 Color definitions. If you want to change them, check the reference at https://en.wikipedia.org/wiki/ANSI_escape_code#Colors

View File

@ -3,7 +3,7 @@ import time
import json import json
# Connect to the SQLite database # Connect to the SQLite database
conn = sqlite3.connect('profits/profits_database.db') conn = sqlite3.connect('profits_database.db')
cursor = conn.cursor() cursor = conn.cursor()
# Execute a SELECT query to retrieve data from the database # Execute a SELECT query to retrieve data from the database
@ -17,8 +17,8 @@ rows = cursor.fetchall()
#human_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(row[0]))) #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 #print(human_time,row[1],round(row[2],2),row[3]) # Or do whatever processing you need
data = json.loads(rows[-3][-1]) #data = json.loads(rows[-3][-1])
print(data[-1]) print([item[0] for item in rows[-4:]])
print(f"Number of entries: {len(rows)}") print(f"Number of entries: {len(rows)}")
# Close the connection # Close the connection

View File

@ -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) 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: 3. Maintain local orderbooks for each trading pair, which enables:
3a. Smart order pricing: Prioritization of fill speed over instant profit or vice versa 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 4. Consolidate vocabulary (trader, pair and bot; instance & trader)
from a pre-populated list (the trading pairs can be selected by using Yang-Zhang, Parkinson or another volatility indicator) 5. Base add for short traders.
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.
Would be nice to have: 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) 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) 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) 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?: Maybe it's a good idea?:

View File

@ -27,6 +27,7 @@ class trader:
self.check_slippage = True self.check_slippage = True
if "check_slippage" in self.config_dict: if "check_slippage" in self.config_dict:
self.check_slippage = self.config_dict["check_slippage"] self.check_slippage = self.config_dict["check_slippage"]
self.is_boosted = False
self.start_time = int(time.time()) self.start_time = int(time.time())
self.total_amount_of_quote=0 self.total_amount_of_quote=0
self.total_amount_of_base=1 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(): 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["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["is_short"]=self.is_short
self.status_dict["quote_spent"]=self.total_amount_of_quote self.status_dict["quote_spent"]=self.total_amount_of_quote
self.status_dict["base_bought"]=self.total_amount_of_base self.status_dict["base_bought"]=self.total_amount_of_base
@ -1276,10 +1278,35 @@ class trader:
2. Custom percentage table 2. Custom percentage table
3. Linear percentage table 3. Linear percentage table
''' '''
tp_level = 1 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"] 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 limit = self.config_dict["no_of_safety_orders"]/3
if order_index<=1: if order_index<=1:
tp_level = self.config_dict["tp_level"]+0.005 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"][order_index] #Custom percentage table
tp_level = self.config_dict["tp_table"][-1] tp_level = self.config_dict["tp_table"][-1]
tp_level = self.config_dict["tp_level"] 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"]) 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] 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.
return 1+((tp_level-1)*multiplier) return 1+((tp_level+boost-1)*multiplier)
def seconds_to_time(self, total_seconds: float) -> str: def seconds_to_time(self, total_seconds: float) -> 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}" 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'])}" 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"]: if self.config_dict["autoswitch"]:
line1 = f"{line1} | AUTO" line1 = f"{line1} | AUTO"
if self.is_short and "old_long" in self.status_dict: if self.is_short and "old_long" in self.status_dict:
try: 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"] 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)}%" #line1 = f"{line1} {round(percentage_to_switch,2)}%"
multiplier = int(percentage_to_switch/100)+1 multiplier = int(percentage_to_switch/100)+1
@ -1748,6 +1778,8 @@ class trader:
self.start_time = self.status_dict["start_time"] self.start_time = self.status_dict["start_time"]
self.deal_start_time = self.status_dict["deal_start_time"] self.deal_start_time = self.status_dict["deal_start_time"]
self.stop_when_profit = self.status_dict["stop_when_profit"] 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? if "deal_order_history" not in self.status_dict: #No longer needed?
self.status_dict["deal_order_history"] = [] self.status_dict["deal_order_history"] = []