ConfigHandler almost ready (without parameter validation)
This commit is contained in:
parent
487115c15e
commit
72c8773263
|
|
@ -1,9 +1,12 @@
|
|||
import json
|
||||
|
||||
class ConfigHandler:
|
||||
'''
|
||||
Handles the configuration of the trader and the validation of the parameters
|
||||
'''
|
||||
|
||||
def __init__(self, pair, config_dict = None):
|
||||
def __init__(self, pair, broker, config_dict = None):
|
||||
self.broker = broker
|
||||
self.default_config_dictionary = {
|
||||
"pair": pair,
|
||||
"is_short": False,
|
||||
|
|
@ -19,6 +22,7 @@ class ConfigHandler:
|
|||
"autoswitch": False,
|
||||
"attempt_restart": True,
|
||||
"tp_mode": 3,
|
||||
"tp_level": 1.02,
|
||||
"tp_table": [],
|
||||
"check_slippage": True,
|
||||
"programmed_stop": False,
|
||||
|
|
@ -38,9 +42,6 @@ class ConfigHandler:
|
|||
|
||||
|
||||
|
||||
def get_config(self):
|
||||
return self.config_dictionary
|
||||
|
||||
def get_pair(self):
|
||||
return self.config_dictionary["pair"]
|
||||
|
||||
|
|
@ -83,6 +84,9 @@ class ConfigHandler:
|
|||
def get_tp_mode(self):
|
||||
return self.config_dictionary["tp_mode"]
|
||||
|
||||
def get_tp_level(self):
|
||||
return self.config_dictionary["tp_level"]
|
||||
|
||||
def get_tp_table(self):
|
||||
return self.config_dictionary["tp_table"]
|
||||
|
||||
|
|
@ -166,6 +170,10 @@ class ConfigHandler:
|
|||
self.config_dictionary["tp_mode"] = tp_mode
|
||||
return 0
|
||||
|
||||
def set_tp_level(self, tp_level: float):
|
||||
self.config_dictionary["tp_level"] = tp_level
|
||||
return 0
|
||||
|
||||
def set_tp_table(self, tp_table: list):
|
||||
self.config_dictionary["tp_table"] = tp_table
|
||||
return 0
|
||||
|
|
@ -203,3 +211,32 @@ class ConfigHandler:
|
|||
def set_check_old_long_price(self, check_old_long_price: bool):
|
||||
self.config_dictionary["check_old_long_price"] = check_old_long_price
|
||||
return 0
|
||||
|
||||
|
||||
def save_to_file(self, file_path: str):
|
||||
try:
|
||||
with open(file_path, "w") as f:
|
||||
json.dump(self.config_dictionary, f)
|
||||
return 0
|
||||
except Exception as e:
|
||||
self.broker.logger.log_this(f"Error saving config to file: {file_path}: {e}",1,self.get_pair())
|
||||
return 1
|
||||
|
||||
def load_from_file(self, file_path: str):
|
||||
try:
|
||||
with open(file_path, "r") as f:
|
||||
self.set_config(json.load(f))
|
||||
return 0
|
||||
except Exception as e:
|
||||
self.broker.logger.log_this(f"Error loading config to file: {file_path}: {e}",1,self.get_pair())
|
||||
return 1
|
||||
|
||||
def get_config(self):
|
||||
return self.config_dictionary
|
||||
|
||||
def set_config(self, config_dictionary: dict):
|
||||
'''
|
||||
Validates every key in the config dictionary and sets the config dictionary
|
||||
'''
|
||||
self.config_dictionary = config_dictionary
|
||||
return 0
|
||||
|
|
@ -25,6 +25,7 @@ class Broker:
|
|||
self.logger = Logger(self.read_config)
|
||||
self.write_order_history = True #This should be a toggle in config_file
|
||||
|
||||
#Initialize database
|
||||
self.profits_database_filename = "profits/profits_database.db"
|
||||
self.database_connection = sqlite3.connect(self.profits_database_filename)
|
||||
self.database_cursor = self.database_connection.cursor()
|
||||
|
|
@ -41,6 +42,7 @@ class Broker:
|
|||
self.database_connection.commit()
|
||||
self.database_connection.close()
|
||||
|
||||
#Load markets
|
||||
self.exchange.load_markets()
|
||||
|
||||
#Populates deals cache
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import json
|
||||
|
||||
class StatusHandler:
|
||||
'''
|
||||
Handles the status of the trader and the validation of the parameters
|
||||
'''
|
||||
|
||||
def __init__(self, status_dict = None):
|
||||
def __init__(self, broker, status_dict = None):
|
||||
self.broker = broker
|
||||
self.default_status_dictionary = {
|
||||
"tp_order_id": "",
|
||||
"take_profit_order": {},
|
||||
|
|
@ -274,10 +277,38 @@ class StatusHandler:
|
|||
self.status_dictionary["deal_order_history"] = deal_history
|
||||
return 0
|
||||
|
||||
|
||||
def update_deal_order_history(self, new_deal: dict):
|
||||
self.status_dictionary["deal_order_history"].append(new_deal)
|
||||
return 0
|
||||
|
||||
|
||||
def save_to_file(self, file_path: str):
|
||||
try:
|
||||
with open(file_path, "w") as f:
|
||||
json.dump(self.status_dictionary, f)
|
||||
return 0
|
||||
except Exception as e:
|
||||
self.broker.logger.log_this(f"Error saving status to file: {file_path}: {e}",1)
|
||||
return 1
|
||||
|
||||
def load_from_file(self, file_path: str):
|
||||
try:
|
||||
with open(file_path, "r") as f:
|
||||
self.set_status(json.load(f))
|
||||
return 0
|
||||
except Exception as e:
|
||||
self.broker.logger.log_this(f"Error loading status from file: {file_path}: {e}",1)
|
||||
return 1
|
||||
|
||||
|
||||
def get_status(self):
|
||||
return self.status_dictionary
|
||||
|
||||
def set_status(self, dictionary: dict):
|
||||
'''
|
||||
Validates every key in the dictionary and then sets the status dictionary
|
||||
'''
|
||||
|
||||
self.status_dictionary = dictionary
|
||||
return 0
|
||||
164
trader.py
164
trader.py
|
|
@ -2,6 +2,8 @@ import csv
|
|||
import json
|
||||
import time
|
||||
import os
|
||||
from config_handler import ConfigHandler
|
||||
from status_handler import StatusHandler
|
||||
|
||||
class trader:
|
||||
def __init__(self, broker, config_dict: dict, is_import: bool = False):
|
||||
|
|
@ -12,20 +14,16 @@ class trader:
|
|||
self.broker = broker
|
||||
self.tp_order = self.broker.get_empty_order()
|
||||
self.so = self.broker.get_empty_order()
|
||||
self.config_dict = config_dict
|
||||
self.pair = self.config_dict["pair"]
|
||||
self.config = ConfigHandler(config_dict["pair"],broker,config_dict)
|
||||
self.pair = self.config.get_pair()
|
||||
self.market = self.broker.fetch_market(self.pair)
|
||||
self.market_load_time = int(time.time())
|
||||
self.market_reload_period = 86400 #Market reload period in seconds
|
||||
self.base,self.quote = self.pair.split("/")
|
||||
self.is_short = self.config_dict["is_short"]
|
||||
self.profit_table = self.config_dict["tp_table"]
|
||||
self.max_short_safety_orders = 45
|
||||
if "max_short_safety_orders" in config_dict:
|
||||
self.max_short_safety_orders = config_dict["max_short_safety_orders"]
|
||||
self.check_slippage = True
|
||||
if "check_slippage" in self.config_dict:
|
||||
self.check_slippage = self.config_dict["check_slippage"]
|
||||
self.is_short = self.config.get_is_short()
|
||||
self.profit_table = self.config.get_tp_table()
|
||||
self.max_short_safety_orders = self.config.get_max_short_safety_orders()
|
||||
self.check_slippage = self.config.get_check_slippage()
|
||||
self.is_boosted = False
|
||||
self.start_time = int(time.time())
|
||||
self.total_amount_of_quote=0
|
||||
|
|
@ -42,10 +40,8 @@ class trader:
|
|||
"quote_spent": 0,
|
||||
"base_bought": 0,
|
||||
"so_amount": 0,
|
||||
#"max_so_amount": config_dict["no_of_safety_orders"],
|
||||
"take_profit_price": 1,
|
||||
"next_so_price": 1,
|
||||
#"acc_profit": 0,
|
||||
"tp_order_id": "",
|
||||
"take_profit_order": {},
|
||||
"so_order_id": "",
|
||||
|
|
@ -61,10 +57,12 @@ class trader:
|
|||
"short_price_exceeds_old_long": False,
|
||||
"speol_notified": False
|
||||
}
|
||||
if "stop_time" in self.config_dict and int(self.config_dict["stop_time"])<int(time.time()):
|
||||
#What is this? Why?
|
||||
if self.config.get_programmed_stop() and int(self.config.get_programmed_stop_time())<int(time.time()):
|
||||
self.config_dict.pop("stop_time",None)
|
||||
#Write config file changes
|
||||
self.broker.rewrite_config_file()
|
||||
|
||||
if self.is_short:
|
||||
#Check if there is an old_long file. If so, load it.
|
||||
try:
|
||||
|
|
@ -75,15 +73,10 @@ class trader:
|
|||
self.profit_filename = f"profits/{self.base}{self.quote}.profits"
|
||||
self.log_filename = f"logs/{self.base}{self.quote}.log"
|
||||
|
||||
self.boosted_deals_range = 4
|
||||
self.boosted_time_range = 3600
|
||||
self.boosted_amount = .01
|
||||
if "boosted_deals_range" in self.config_dict:
|
||||
self.boosted_deals_range = self.config_dict["boosted_deals_range"]
|
||||
if "boosted_time_range" in self.config_dict:
|
||||
self.boosted_time_range = self.config_dict["boosted_time_range"]
|
||||
if "boosted_amount" in self.config_dict:
|
||||
self.boosted_amount = self.config_dict["boosted_amount"]
|
||||
self.boosted_deals_range = self.config.get_boosted_deals_range()
|
||||
self.boosted_time_range = self.config.get_boosted_time_range()
|
||||
self.boosted_amount = self.config.get_boosted_amount()
|
||||
|
||||
self.deals_timestamps = self.broker.get_trades_timestamps(self.pair,self.boosted_time_range)
|
||||
|
||||
self.stop_when_profit = False
|
||||
|
|
@ -101,7 +94,7 @@ class trader:
|
|||
elif start_result==1: #If initialization fails
|
||||
self.quit = True
|
||||
elif start_result==2: #Retries exceeded
|
||||
if "force_restart_if_retries_exhausted" in self.config_dict and self.config_dict["force_restart_if_retries_exhausted"]:
|
||||
if self.config.get_force_restart_if_retries_exhausted():
|
||||
self.pause = False
|
||||
self.restart = True
|
||||
else:
|
||||
|
|
@ -194,8 +187,8 @@ class trader:
|
|||
if order_size is None or no_of_safety_orders is None:
|
||||
self.broker.logger.log_this("Can't calculate optimal size",1,self.pair)
|
||||
return 1
|
||||
self.config_dict["order_size"] = order_size
|
||||
self.config_dict["no_of_safety_orders"] = no_of_safety_orders
|
||||
self.config.set_order_size(order_size)
|
||||
self.config.set_no_of_safety_orders(no_of_safety_orders)
|
||||
self.broker.logger.log_this(f"Order size: {self.broker.amount_to_precision(self.pair,order_size)}. Amount of safety orders: {no_of_safety_orders}",2,self.pair)
|
||||
|
||||
#Write the changes to the config file
|
||||
|
|
@ -207,12 +200,12 @@ class trader:
|
|||
self.status_dict["pause_reason"] = "start_trader - checking order size"
|
||||
self.broker.logger.log_this("Checking for order size",2,self.pair)
|
||||
minimum_order_size_allowed = self.broker.get_min_quote_size(self.pair)
|
||||
if minimum_order_size_allowed is not None and minimum_order_size_allowed>self.config_dict["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.pair)
|
||||
if minimum_order_size_allowed<self.config_dict["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.
|
||||
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.pair)
|
||||
self.config_dict["order_size"] = float(int(minimum_order_size_allowed)+1)
|
||||
self.config.set_order_size(float(int(minimum_order_size_allowed)+1))
|
||||
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.pair)
|
||||
return 1
|
||||
|
|
@ -221,19 +214,19 @@ class trader:
|
|||
if self.check_slippage:
|
||||
self.broker.logger.log_this("Checking slippage...",2,self.pair)
|
||||
self.status_dict["pause_reason"] = "start_trader - checking slippage"
|
||||
if self.check_orderbook_depth(self.broker.get_slippage_default_threshold(),self.config_dict["order_size"]):
|
||||
if self.check_orderbook_depth(self.broker.get_slippage_default_threshold(),self.config.get_order_size()):
|
||||
#Slippage threshold exceeded
|
||||
self.broker.logger.log_this("Slippage threshold exceeded",1,self.pair)
|
||||
return 3
|
||||
|
||||
self.status_dict["pause_reason"] = "start_trader - after slippage"
|
||||
self.status_dict["order_size"] = self.config_dict["order_size"]
|
||||
self.status_dict["order_size"] = self.config.get_order_size()
|
||||
|
||||
#Sending initial order
|
||||
self.status_dict["pause_reason"] = "start_trader - sending first order"
|
||||
self.broker.logger.log_this("Sending first order...",2,self.pair)
|
||||
action = "sell" if self.is_short else "buy"
|
||||
first_order = self.broker.new_market_order(self.pair,self.config_dict["order_size"],action)
|
||||
first_order = self.broker.new_market_order(self.pair,self.config.get_order_size(),action)
|
||||
#self.broker.logger.log_this(f"First order id: {first_order}",1,self.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.pair)
|
||||
|
|
@ -300,9 +293,8 @@ class trader:
|
|||
return 1
|
||||
|
||||
# Generate the safety prices table
|
||||
#self.start_price = returned_order["average"]
|
||||
self.start_price = self.broker.price_to_precision(self.pair,self.total_amount_of_quote/self.total_amount_of_base)
|
||||
self.safety_price_table = self.calculate_safety_prices(self.start_price,self.config_dict["no_of_safety_orders"],self.config_dict["safety_order_deviance"])
|
||||
self.safety_price_table = self.calculate_safety_prices(self.start_price,self.config.get_no_of_safety_orders(),self.config.get_safety_order_deviance())
|
||||
|
||||
# Send the first safety order
|
||||
self.status_dict["pause_reason"] = "start_trader - sending safety order"
|
||||
|
|
@ -316,7 +308,7 @@ class trader:
|
|||
|
||||
# Send cleanup order (if cleanup)
|
||||
self.status_dict["pause_reason"] = "start_trader - doing cleanup (if needed)"
|
||||
if self.config_dict["cleanup"] and not self.is_short: #Short traders do not need cleanup.
|
||||
if self.config.get_cleanup() and not self.is_short: #Short traders do not need cleanup.
|
||||
self.do_cleanup()
|
||||
|
||||
# Write variables to status_dict and reset deal_uptime
|
||||
|
|
@ -337,7 +329,7 @@ class trader:
|
|||
config_filename = f"configs/{self.base}{self.quote}.json"
|
||||
with open(config_filename,"r") as y:
|
||||
config_dict = json.load(y)
|
||||
if self.config_dict["autoswitch"]: # In what context was this useful?
|
||||
if self.config.get_autoswitch(): # In what context was this useful?
|
||||
config_dict["autoswitch"] = True #
|
||||
return config_dict
|
||||
|
||||
|
|
@ -373,7 +365,7 @@ class trader:
|
|||
self.status_dict["quote_spent"]=self.total_amount_of_quote
|
||||
self.status_dict["base_bought"]=self.total_amount_of_base
|
||||
self.status_dict["so_amount"]=self.safety_order_index
|
||||
self.status_dict["no_of_safety_orders"]=self.config_dict["no_of_safety_orders"]
|
||||
self.status_dict["no_of_safety_orders"]=self.config.get_no_of_safety_orders()
|
||||
self.status_dict["take_profit_price"]=self.take_profit_price
|
||||
self.status_dict["safety_price_table"]=self.safety_price_table
|
||||
self.status_dict["deal_uptime"]=int(time.time()) - self.deal_start_time
|
||||
|
|
@ -381,14 +373,14 @@ class trader:
|
|||
self.status_dict["fees_paid_in_base"]=self.fees_paid_in_base
|
||||
self.status_dict["fees_paid_in_quote"]=self.fees_paid_in_quote
|
||||
self.status_dict["start_price"]=self.start_price
|
||||
self.status_dict["tp_mode"]=self.config_dict["tp_mode"]
|
||||
self.status_dict["profit_table"]=self.config_dict["tp_table"]
|
||||
self.status_dict["tp_mode"]=self.config.get_tp_mode()
|
||||
self.status_dict["profit_table"]=self.config.get_tp_table()
|
||||
self.status_dict["start_time"]=self.start_time
|
||||
self.status_dict["deal_start_time"]=self.deal_start_time
|
||||
self.status_dict["stop_when_profit"]=self.stop_when_profit
|
||||
self.status_dict["autoswitch"]=False
|
||||
if "autoswitch" in self.config_dict:
|
||||
self.status_dict["autoswitch"]=self.config_dict["autoswitch"]
|
||||
self.status_dict["autoswitch"]=self.config.get_autoswitch()
|
||||
except Exception as e:
|
||||
self.broker.logger.log_this(f"Can't update status dictionary. Exception: {e}",1,self.pair)
|
||||
|
||||
|
|
@ -546,7 +538,7 @@ class trader:
|
|||
optimal_order_size = 0
|
||||
minimum_amount_of_safety_orders = 1 #This variable could be a config knob
|
||||
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_dict["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:
|
||||
self.broker.logger.log_this(f"Optimal order size is {optimal_order_size}",2,self.pair)
|
||||
break
|
||||
|
|
@ -770,7 +762,7 @@ class trader:
|
|||
self.safety_order_index = 0
|
||||
|
||||
#Disabling autoswitch
|
||||
#self.config_dict["autoswitch"] = False
|
||||
#self.config.set_autoswitch(False)
|
||||
|
||||
#Done. Ready for start_trader
|
||||
return 0
|
||||
|
|
@ -894,7 +886,7 @@ class trader:
|
|||
|
||||
self.status_dict["pause_reason"] = "take_profit_routine - check time limit"
|
||||
#Checks if there is a time limit for the trader
|
||||
if "stop_time" in self.config_dict and time.time()>int(self.config_dict["stop_time"]):
|
||||
if self.config.get_programmed_stop() and time.time()>int(self.config.get_programmed_stop_time()):
|
||||
self.stop_when_profit = True
|
||||
|
||||
self.status_dict["pause_reason"] = "take_profit_routine - if stop_when_profit"
|
||||
|
|
@ -918,7 +910,7 @@ class trader:
|
|||
self.pause = False
|
||||
self.restart = True
|
||||
return 1
|
||||
elif self.check_orderbook_depth(self.broker.get_slippage_default_threshold(),self.config_dict["order_size"],filled_order["price"]):
|
||||
elif self.check_orderbook_depth(self.broker.get_slippage_default_threshold(),self.config.get_order_size(),filled_order["price"]):
|
||||
self.broker.logger.log_this(f"Orderbook depth not sufficient, waiting for cooldown and restarting trader",1,self.pair)
|
||||
time.sleep(self.broker.get_wait_time()*self.broker.get_cooldown_multiplier())
|
||||
self.pause = False
|
||||
|
|
@ -1046,7 +1038,7 @@ class trader:
|
|||
if not self.warnings["speol_notified"] and price_exceeds:
|
||||
#Only notify one time AND if autoswitch is off
|
||||
self.warnings["speol_notified"] = True
|
||||
if not self.config_dict["autoswitch"]:
|
||||
if not self.config.get_autoswitch():
|
||||
message = f"{self.base}@{self.status_dict['price']} ({str(self.broker.exchange)}), exceeds old long price of {self.status_dict['old_long']['tp_price']}"
|
||||
self.broker.logger.log_this(message,0,self.pair)
|
||||
return 0
|
||||
|
|
@ -1120,7 +1112,7 @@ class trader:
|
|||
if self.tp_order["id"]=="":
|
||||
self.broker.logger.log_this(f"Take profit order missing. Stopping bot. Order ID: {self.tp_order['id']}",1,self.pair)
|
||||
self.broker.cancel_order(self.so["id"],self.pair)
|
||||
if self.config_dict["attempt_restart"]:
|
||||
if self.config.get_attempt_restart():
|
||||
self.write_status_file(is_backup=True)
|
||||
self.restart = True
|
||||
self.broker.logger.log_this("Raising restart flag: take profit order missing, trader will be restarted",0,self.pair)
|
||||
|
|
@ -1137,7 +1129,7 @@ class trader:
|
|||
self.broker.logger.log_this(f"Take profit order closed but not filled, 0 filled. Stopping bot. Order ID: {self.tp_order['id']}",1,self.pair)
|
||||
#Cancelling safety order and stopping bot
|
||||
self.broker.cancel_order(self.so["id"],self.pair)
|
||||
if self.config_dict["attempt_restart"]:
|
||||
if self.config.get_attempt_restart():
|
||||
self.write_status_file(is_backup=True)
|
||||
self.restart = True
|
||||
self.broker.logger.log_this("Take profit order closed but not filled, trader will be restarted.",0,self.pair)
|
||||
|
|
@ -1146,7 +1138,7 @@ class trader:
|
|||
return 1
|
||||
elif tp_status["status"]=="canceled":
|
||||
#TODO: Here, if the safety order is still open, we could resend the tp order.
|
||||
if self.config_dict["attempt_restart"]:
|
||||
if self.config.get_attempt_restart():
|
||||
self.broker.logger.log_this("Take profit order canceled. Restarting the bot.",1,self.pair)
|
||||
self.write_status_file(is_backup=True)
|
||||
self.restart = True
|
||||
|
|
@ -1158,7 +1150,7 @@ class trader:
|
|||
return 1
|
||||
|
||||
# Check if safety order is filled
|
||||
if self.so["id"] not in open_orders_ids and self.safety_order_index<=self.config_dict["no_of_safety_orders"]:
|
||||
if self.so["id"] not in open_orders_ids and self.safety_order_index<=self.config.get_no_of_safety_orders():
|
||||
#so_status = self.so
|
||||
#if self.so["id"]!="":
|
||||
so_status = self.broker.get_order(self.so["id"],self.pair)
|
||||
|
|
@ -1170,42 +1162,42 @@ class trader:
|
|||
if so_status["status"] in ["closed", "canceled", ""] and tp_order_status["status"]=="open":
|
||||
#Switch to short if all safety orders are sent and autoswitch is enabled.
|
||||
#May get into trouble if the trader is short of funds
|
||||
if not self.is_short and self.safety_order_index==self.config_dict["no_of_safety_orders"] and self.config_dict["autoswitch"]:
|
||||
if not self.is_short and self.safety_order_index==self.config.get_no_of_safety_orders() and self.config.get_autoswitch():
|
||||
self.switch_to_short()
|
||||
self.write_status_file(is_backup=True)
|
||||
self.restart = True
|
||||
return 0
|
||||
a = self.new_so_routine(so_status,self.safety_order_index<self.config_dict["no_of_safety_orders"])
|
||||
a = self.new_so_routine(so_status,self.safety_order_index<self.config.get_no_of_safety_orders())
|
||||
#0 OK, 1 not enough funds, 2 can't cancel old TP, 3 can't send new TP
|
||||
if a==1:
|
||||
self.broker.logger.log_this(f"Can't send new safety order. Not enough funds? new_so_routine returned {a}",1,self.pair)
|
||||
#If there are not enough funds do not even try to send more safety orders
|
||||
#This way of doing it seems more practical than setting up yet another flag
|
||||
self.config_dict["no_of_safety_orders"] = self.safety_order_index
|
||||
self.config.set_no_of_safety_orders(self.safety_order_index)
|
||||
return 1
|
||||
elif a==2:
|
||||
self.broker.logger.log_this(f"Can't cancel old take profit order. new_so_routine returned {a}",1,self.pair)
|
||||
self.pause = False
|
||||
self.status_dict["pause_reason"] = ""
|
||||
if self.config_dict["attempt_restart"]:
|
||||
if self.config.get_attempt_restart():
|
||||
self.write_status_file(is_backup=True)
|
||||
self.restart = True
|
||||
return 1
|
||||
elif a==3:
|
||||
#self.pause = False
|
||||
self.broker.logger.log_this(f"Error in trader: Can't send new take profit order. Restart will be attempted. new_so_routine returned {a}",0,self.pair)
|
||||
if self.config_dict["attempt_restart"]:
|
||||
if self.config.get_attempt_restart():
|
||||
self.write_status_file(is_backup=True)
|
||||
self.restart = True
|
||||
return 1
|
||||
|
||||
#Check if short price exceeds old long price. If so, send a Telegram message
|
||||
if self.is_short and "old_long" in self.status_dict and self.config_dict["check_old_long_price"]:
|
||||
if self.is_short and "old_long" in self.status_dict and self.config.get_check_old_long_price():
|
||||
self.check_old_long_price()
|
||||
|
||||
self.status_dict["pause_reason"] = "check for autoswitch"
|
||||
#If it's a short bot that used to be long AND autoswitch is enabled
|
||||
if self.is_short and "autoswitch" in self.config_dict and self.config_dict["autoswitch"] and "old_long" in self.status_dict:
|
||||
if self.is_short and "autoswitch" in self.config_dict and self.config.get_autoswitch() and "old_long" in self.status_dict:
|
||||
#If selling the base currency left at the current market price plus the quote already received turns out to be more than the old long deal target,
|
||||
# it means that we already are in profit territory, switch back to long.
|
||||
#A more conservative approach would be old_target = self.status_dict["old_long"]["quote_spent"], just breaking even.
|
||||
|
|
@ -1241,7 +1233,7 @@ class trader:
|
|||
|
||||
def get_tp_level(self, order_index: int = 0) -> float:
|
||||
'''
|
||||
Returns the correct take profit percentage, according to the strategy (config_dict["tp_mode"]):
|
||||
Returns the correct take profit percentage, according to the strategy (config.get_tp_mode()):
|
||||
0. Fixed percentage
|
||||
1. Variable percentage (+0.5% to -0.5% of the fixed percentage)
|
||||
2. Custom percentage table
|
||||
|
|
@ -1257,26 +1249,26 @@ class trader:
|
|||
self.is_boosted = True
|
||||
boost_percentage = self.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
|
||||
limit = self.config_dict["no_of_safety_orders"]/3
|
||||
if self.is_short or self.config.get_tp_mode()==0: #Fixed take profit percentage
|
||||
tp_level = self.config.get_tp_level()
|
||||
elif self.config.get_tp_mode()==1: #Variable percentage
|
||||
limit = self.config.get_no_of_safety_orders()/3
|
||||
if order_index<=1:
|
||||
tp_level = self.config_dict["tp_level"]+0.005
|
||||
tp_level = self.config.get_tp_level()+0.005
|
||||
elif order_index<=limit:
|
||||
tp_level = self.config_dict["tp_level"]
|
||||
tp_level = self.config.get_tp_level()
|
||||
elif limit<=order_index<=limit*2:
|
||||
tp_level = self.config_dict["tp_level"]-0.0025
|
||||
tp_level = self.config.get_tp_level()-0.0025
|
||||
else:
|
||||
tp_level = self.config_dict["tp_level"]-0.005
|
||||
elif self.config_dict["tp_mode"]==2:
|
||||
tp_level = self.config.get_tp_level()-0.005
|
||||
elif self.config.get_tp_mode()==2:
|
||||
if ["tp_table"] in self.config_dict:
|
||||
if len(self.config_dict["tp_table"])>=order_index:
|
||||
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
|
||||
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"])
|
||||
if len(self.config.get_tp_table())>=order_index:
|
||||
tp_level = self.config.get_tp_table()[order_index] #Custom percentage table
|
||||
tp_level = self.config.get_tp_table()[-1]
|
||||
tp_level = self.config.get_tp_level()
|
||||
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())
|
||||
tp_level = profit_table[-1]
|
||||
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.
|
||||
|
|
@ -1366,7 +1358,7 @@ class trader:
|
|||
'''
|
||||
Sends a new safety order to the exchange
|
||||
'''
|
||||
so_size = self.gib_so_size(size,self.safety_order_index+1,self.config_dict["safety_order_scale"]) #safety_order_scale: safety order growth factor
|
||||
so_size = self.gib_so_size(size,self.safety_order_index+1,self.config.get_safety_order_scale()) #safety_order_scale: safety order growth factor
|
||||
if self.is_short:
|
||||
new_order = self.broker.new_limit_order(self.pair,so_size,"sell",self.safety_price_table[self.safety_order_index+1])
|
||||
else:
|
||||
|
|
@ -1428,16 +1420,16 @@ class trader:
|
|||
Generates a table of safety order's prices
|
||||
'''
|
||||
safety_price_table = [start_price]
|
||||
if self.config_dict["dynamic_so_deviance"]:# and no_of_safety_orders>=30:
|
||||
#if self.config_dict["dynamic_so_deviance"] and not self.is_short:
|
||||
if self.config.get_dynamic_so_deviance():# and no_of_safety_orders>=30:
|
||||
#if self.config.get_dynamic_so_deviance() and not self.is_short:
|
||||
#bias should be a real number between -1 and 1 (1>n>-1, NOT 1=>n>=-1)
|
||||
#If bias -> 1, more space between the first orders, if -> -1, more space between the last orders, if 0, no change..
|
||||
if "bias" in self.config_dict:
|
||||
deviance_factor = safety_order_deviance*self.clip_value(self.config_dict["bias"],-.99,.99)
|
||||
deviance_factor = safety_order_deviance*self.clip_value(self.config.get_bias(),-.99,.99)
|
||||
so_deviance_table = self.linear_space(safety_order_deviance+deviance_factor,safety_order_deviance-deviance_factor,no_of_safety_orders)
|
||||
else:
|
||||
#Old way of calculating deviance
|
||||
so_deviance_table = self.linear_space(safety_order_deviance-self.config_dict["dsd_range"],safety_order_deviance+self.config_dict["dsd_range"],no_of_safety_orders)
|
||||
so_deviance_table = self.linear_space(safety_order_deviance-self.config.get_dsd_range(),safety_order_deviance+self.config.get_dsd_range(),no_of_safety_orders)
|
||||
so_deviance_table.extend([so_deviance_table[-1]]*2) #This extra entries are needed in the next for loop
|
||||
else:
|
||||
so_deviance_table = [safety_order_deviance]*(no_of_safety_orders+2)
|
||||
|
|
@ -1540,10 +1532,10 @@ class trader:
|
|||
#Change pair-related variables
|
||||
old_quote = self.quote
|
||||
self.quote = new_quote
|
||||
self.config_dict["pair"] = f"{self.base}/{self.quote}"
|
||||
self.config.set_pair(f"{self.base}/{self.quote}")
|
||||
self.profit_filename = f"profits/{self.base}{self.quote}.profits"
|
||||
self.log_filename = f"logs/{self.base}{self.quote}.log"
|
||||
self.pair = self.config_dict["pair"]
|
||||
self.pair = self.config.get_pair()
|
||||
|
||||
#If there is an old_long file, also copy it
|
||||
if self.is_short and "old_long" in self.status_dict:
|
||||
|
|
@ -1576,10 +1568,10 @@ class trader:
|
|||
self.broker.remove_pair_from_config(f"{self.base}{new_quote}")
|
||||
self.broker.add_pair_to_config(f"{self.base}{self.quote}")
|
||||
|
||||
self.config_dict["pair"] = f"{self.base}/{self.quote}"
|
||||
self.config.set_pair(f"{self.base}/{self.quote}")
|
||||
self.profit_filename = f"profits/{self.base}{self.quote}.profits"
|
||||
self.log_filename = f"logs/{self.base}{self.quote}.log"
|
||||
self.pair = self.config_dict["pair"]
|
||||
self.pair = self.config.get_pair()
|
||||
|
||||
#Writing config file
|
||||
if write_broker_file and self.broker.rewrite_config_file()==1:
|
||||
|
|
@ -1626,7 +1618,7 @@ class trader:
|
|||
mid_percentage = 10
|
||||
high_percentage = 20
|
||||
|
||||
safety_order_string = f"{self.status_dict['so_amount']-1}/{self.config_dict['no_of_safety_orders']}".rjust(5)
|
||||
safety_order_string = f"{self.status_dict['so_amount']-1}/{self.config.get_no_of_safety_orders()}".rjust(5)
|
||||
|
||||
#Check if necessary
|
||||
low_price = 0
|
||||
|
|
@ -1701,12 +1693,12 @@ class trader:
|
|||
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.get_autoswitch():
|
||||
line1 = f"{line1} | AUTO"
|
||||
if multiplier>1:
|
||||
#Only displays the multiplier if autoswitch is enabled.
|
||||
line1 = f"{line1}x{multiplier}"
|
||||
if "stop_time" in self.config_dict and time.time()<=int(self.config_dict["stop_time"]):
|
||||
if self.config.get_programmed_stop() and time.time()<=self.config.get_programmed_stop_time():
|
||||
line1 = f"{line1} | PROGRAMMED LAST DEAL"
|
||||
if self.stop_when_profit==True:
|
||||
line1 = f"{line1} | LAST DEAL"
|
||||
|
|
@ -1733,7 +1725,7 @@ class trader:
|
|||
self.total_amount_of_quote = self.status_dict["quote_spent"]
|
||||
self.total_amount_of_base = self.status_dict["base_bought"]
|
||||
self.safety_order_index = self.status_dict["so_amount"]
|
||||
self.config_dict["no_of_safety_orders"] = self.status_dict["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_dict["no_of_safety_orders"]) #If this is not loaded from status_dict, it will ignore if safety orders were added at runtime
|
||||
self.take_profit_price = self.status_dict["take_profit_price"]
|
||||
self.safety_price_table = self.status_dict["safety_price_table"]
|
||||
self.fees_paid_in_base = self.status_dict["fees_paid_in_base"]
|
||||
|
|
@ -1756,9 +1748,9 @@ class trader:
|
|||
|
||||
#Load safety order
|
||||
self.so = self.broker.get_order(self.status_dict["so_order_id"],self.pair)
|
||||
if self.so==self.broker.get_empty_order() and self.safety_order_index<self.config_dict["no_of_safety_orders"]:
|
||||
if self.so==self.broker.get_empty_order() and self.safety_order_index<self.config.get_no_of_safety_orders():
|
||||
#The second condition is important: it signals that the empty order returned was because of an error, not because the trader ran out of funds in the past.
|
||||
#When the trader runs out of funds, safety_order_index=config_dict["no_of_safety_orders"]
|
||||
#When the trader runs out of funds, safety_order_index=config.get_no_of_safety_orders()
|
||||
self.broker.logger.log_this("Couldn't load safety order. Aborting.",2,self.pair)
|
||||
self.quit = True
|
||||
return 1
|
||||
|
|
|
|||
Loading…
Reference in New Issue