StatusHandler initial implementation and variable cleanup
This commit is contained in:
parent
168c5c823f
commit
bb796f6933
|
|
@ -1,5 +1,9 @@
|
|||
2025.03.01:
|
||||
. StatusHandler initial implementation.
|
||||
. Variable cleanup
|
||||
|
||||
2025.02.27
|
||||
. config_handler: centralized configuration handling in an object, for easier future development (parameter validation, for example)
|
||||
. ConfigHandler: centralized configuration handling in an object, for easier future development (parameter validation, for example)
|
||||
. Bugfixes everywhere.
|
||||
. Exchange_wrapper now validates every symbol against the market information from the exchange, both when adding and importing a trader.
|
||||
|
||||
|
|
|
|||
45
main.py
45
main.py
|
|
@ -24,7 +24,7 @@ In case the permissions of the certificate changes, reset them this way:
|
|||
# ll /etc/letsencrypt/
|
||||
'''
|
||||
|
||||
version = "2025.02.27"
|
||||
version = "2025.03.01"
|
||||
|
||||
'''
|
||||
Color definitions. If you want to change them, check the reference at https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
||||
|
|
@ -246,10 +246,10 @@ def restart_pair_no_json(base: str, quote: str) -> int:
|
|||
x.write_status_file(True)
|
||||
#Here, we could open a duster (if needed)
|
||||
for order in order_list:
|
||||
if order["symbol"]==f"{base}/{quote}" and x.is_short and order["side"]=="sell":
|
||||
if order["symbol"]==f"{base}/{quote}" and x.config.get_is_short() and order["side"]=="sell":
|
||||
broker.logger.log_this(f"Cancelling old sell orders",2,f"{base}/{quote}")
|
||||
broker.cancel_order(order["id"],order["symbol"])
|
||||
elif order["symbol"]==f"{base}/{quote}" and not x.is_short and order["side"]=="buy":
|
||||
elif order["symbol"]==f"{base}/{quote}" and not x.config.get_is_short() and order["side"]=="buy":
|
||||
broker.logger.log_this(f"Cancelling old buy orders",2,f"{base}/{quote}")
|
||||
broker.cancel_order(order["id"],order["symbol"])
|
||||
running_instances.remove(x)
|
||||
|
|
@ -328,7 +328,7 @@ def main_loop():
|
|||
curr = 0
|
||||
top = 0
|
||||
for x in running_instances:
|
||||
if not x.is_short:
|
||||
if not x.config.get_is_short():
|
||||
curr += int(x.get_status_dict()["so_amount"]) # For the safety order occupancy percentage calculation
|
||||
top += int(x.config.get_no_of_safety_orders()) # It shows the percentage of safety orders not filled
|
||||
if not x.quit: #Why? Maybe to protect return_status() from weird errors if the trader errored out?
|
||||
|
|
@ -337,8 +337,6 @@ def main_loop():
|
|||
x.get_status_dict()["price"] = price_list[x.pair]
|
||||
except Exception as e:
|
||||
broker.logger.log_this(f"Exception while querying for pair price, key not present on price_list dictionary: {e}",1,x.pair)
|
||||
#if "status_string" in x.status_dict:
|
||||
# screen_buffer.append(x.status_dict["status_string"])
|
||||
worker_status[x.pair] = x.get_status_dict()
|
||||
|
||||
#Clear the screen buffer
|
||||
|
|
@ -346,12 +344,10 @@ def main_loop():
|
|||
|
||||
#Append worker data to screen buffer, shorts first.
|
||||
for x in running_instances:
|
||||
if x.is_short and "status_string" in x.get_status_dict():
|
||||
#screen_buffer.append(x.status_dict["status_string"])
|
||||
if x.config.get_is_short() and "status_string" in x.get_status_dict():
|
||||
screen_buffer.append(str(x))
|
||||
for x in running_instances:
|
||||
if not x.is_short and "status_string" in x.get_status_dict():
|
||||
#screen_buffer.append(x.status_dict["status_string"])
|
||||
if not x.config.get_is_short() and "status_string" in x.get_status_dict():
|
||||
screen_buffer.append(str(x))
|
||||
|
||||
#Updates some global status variables prior to deletion of those
|
||||
|
|
@ -369,8 +365,6 @@ def main_loop():
|
|||
|
||||
#Prints general info
|
||||
instance_uptime = int(time.time()) - instance_start_time
|
||||
#long_traders = len([None for x in running_instances if not x.is_short])
|
||||
#short_traders = len([None for x in running_instances if x.is_short])
|
||||
screen_buffer.append(time.strftime(f"[%Y/%m/%d %H:%M:%S] | {len(running_instances)} traders online | Instance uptime: {seconds_to_time(instance_uptime)}"))
|
||||
|
||||
if top==0: #To protect from division by zero when there are no traders active
|
||||
|
|
@ -1455,13 +1449,8 @@ def unwrapped_switch_to_short(base,quote):
|
|||
broker.logger.log_this(f"Reinitializing trader",2,f"{base}/{quote}")
|
||||
for x in running_instances:
|
||||
if f"{base}/{quote}"==x.pair:
|
||||
#Clearing some variables
|
||||
#x.fees_paid_in_quote = 0
|
||||
#x.fees_paid_in_base = 0
|
||||
x.tp_order = x.broker.empty_order
|
||||
x.status.set_take_profit_order(x.broker.empty_order)
|
||||
x.so = x.broker.empty_order
|
||||
#x.safety_price_table.clear() #This clearing (probably) is the origin of the update_status error after switching to short.
|
||||
#x.safety_order_index = 0
|
||||
|
||||
#Reloading config file
|
||||
x.config.load_from_file()
|
||||
|
|
@ -1593,7 +1582,7 @@ def unwrapped_add_safety_orders(base,quote,amount):
|
|||
#x.no_of_safety_orders += int(amount)
|
||||
x.config.set_no_of_safety_orders(x.config.get_no_of_safety_orders()+int(amount))
|
||||
broker.logger.log_this("Recalculating safety price table...",1,f"{base}/{quote}")
|
||||
x.safety_price_table = x.calculate_safety_prices(x.start_price,x.config.get_no_of_safety_orders(),x.config.get_safety_order_deviance())
|
||||
x.status.set_safety_price_table(x.calculate_safety_prices(x.status.get_start_price(),x.config.get_no_of_safety_orders(),x.config.get_safety_order_deviance()))
|
||||
broker.logger.log_this(f"Done. Added {amount} safety orders",1,f"{base}/{quote}")
|
||||
x.update_status(True)
|
||||
x.pause = False
|
||||
|
|
@ -1783,10 +1772,10 @@ def unwrapped_add_quote(base,quote,amount):
|
|||
|
||||
for x in running_instances:
|
||||
if f"{base}/{quote}"==x.pair:
|
||||
if x.is_short:
|
||||
if x.config.get_is_short():
|
||||
return jsonify({"Error": "Quote can't be added to short bots"})
|
||||
x.pause = True
|
||||
new_average_price = (x.total_amount_of_quote+float(amount))/(x.total_amount_of_base+(float(amount)/x.get_status_dict()["price"]))
|
||||
new_average_price = (x.status.get_quote_spent()+float(amount))/(x.status.get_base_bought()+(float(amount)/x.get_status_dict()["price"]))
|
||||
broker.logger.log_this(f"Your new average buy price will be {new_average_price} {x.quote}",2,f"{base}/{quote}")
|
||||
broker.logger.log_this(f"Your new take profit price price will be {new_average_price*x.get_tp_level()} {x.quote}",2,f"{base}/{quote}")
|
||||
new_order = broker.new_market_order(x.pair,float(amount),"buy")
|
||||
|
|
@ -1807,13 +1796,13 @@ def unwrapped_add_quote(base,quote,amount):
|
|||
elif returned_order["status"]=="closed":
|
||||
broker.logger.log_this("Order sent",2,f"{base}/{quote}")
|
||||
new_fees_in_base, new_fees_in_quote = x.parse_fees(returned_order)
|
||||
x.fees_paid_in_base += new_fees_in_base
|
||||
x.fees_paid_in_quote += new_fees_in_quote
|
||||
x.total_amount_of_base = x.total_amount_of_base + returned_order["filled"] - new_fees_in_base
|
||||
x.total_amount_of_quote += returned_order["cost"]
|
||||
x.status.set_fees_paid_in_base(x.status.get_fees_paid_in_base() + new_fees_in_base)
|
||||
x.status.set_fees_paid_in_quote(x.status.get_fees_paid_in_quote() + new_fees_in_quote)
|
||||
x.status.set_base_bought(x.status.get_base_bought() + returned_order["filled"] - new_fees_in_base)
|
||||
x.status.set_quote_spent(x.status.get_quote_spent()+returned_order["cost"])
|
||||
broker.logger.log_this("Cancelling old take profit order and sending a new one",2,f"{base}/{quote}")
|
||||
attempts = 5
|
||||
while broker.cancel_order(x.tp_order["id"],x.pair)==1:
|
||||
while broker.cancel_order(x.status.get_tp_order_id(),x.pair)==1:
|
||||
broker.logger.log_this("Can't cancel old take profit order, retrying...",2,f"{base}/{quote}")
|
||||
time.sleep(broker.get_wait_time())
|
||||
attempts-=1
|
||||
|
|
@ -1821,8 +1810,8 @@ def unwrapped_add_quote(base,quote,amount):
|
|||
broker.logger.log_this("Can't cancel old take profit order, cancelling...",2,f"{base}/{quote}")
|
||||
x.pause = False
|
||||
return jsonify({"Error": "Can't cancel old take profit order."})
|
||||
x.take_profit_price = x.total_amount_of_quote/x.total_amount_of_base*x.get_tp_level()
|
||||
x.tp_order = broker.new_limit_order(x.pair,x.total_amount_of_base,"sell",x.take_profit_price)
|
||||
x.status.set_take_profit_price(x.status.get_quote_spent()/x.status.get_base_bought()*x.get_tp_level())
|
||||
x.status.set_take_profit_order(broker.new_limit_order(x.pair,x.status.get_base_bought(),"sell",x.status.get_take_profit_price()))
|
||||
x.update_status(True)
|
||||
break
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,19 +1,18 @@
|
|||
import json
|
||||
import time
|
||||
|
||||
class StatusHandler:
|
||||
'''
|
||||
Handles the status of the trader and the validation of the parameters
|
||||
'''
|
||||
|
||||
def __init__(self, broker, pair, status_dict = None):
|
||||
def __init__(self, broker, base, quote, status_dict = None):
|
||||
self.broker = broker
|
||||
self.default_status_dictionary = {
|
||||
"pair": pair,
|
||||
"tp_order_id": "",
|
||||
"take_profit_order": {},
|
||||
"pair": f"{base}/{quote}",
|
||||
"take_profit_order": broker.get_empty_order(),
|
||||
"take_profit_price": 0.0,
|
||||
"so_order_id": "",
|
||||
"safety_order": {},
|
||||
"safety_order": broker.get_empty_order(),
|
||||
"next_so_price": 0.0,
|
||||
"order_size": 0.0,
|
||||
"partial_profit": 0.0,
|
||||
|
|
@ -43,33 +42,29 @@ class StatusHandler:
|
|||
"status_string": "",
|
||||
"deal_order_history": []
|
||||
}
|
||||
self.status_file_path = f"configs/{pair.split('/')[0]}{pair.split('/')[1]}.json"
|
||||
self.status_file_path = f"status/{base}{quote}.status"
|
||||
self.status_dictionary = self.default_status_dictionary.copy()
|
||||
|
||||
if status_dict is not None:
|
||||
self.status_dictionary = {**self.status_dictionary, **status_dict}
|
||||
self.save_to_file()
|
||||
self.save_to_file()
|
||||
|
||||
|
||||
|
||||
def get_tp_order_id(self):
|
||||
return self.status_dictionary["tp_order_id"]
|
||||
|
||||
def get_take_profit_order(self):
|
||||
return self.status_dictionary["take_profit_order"]
|
||||
|
||||
def get_take_profit_price(self):
|
||||
return self.status_dictionary["take_profit_price"]
|
||||
|
||||
def get_so_order_id(self):
|
||||
return self.status_dictionary["so_order_id"]
|
||||
|
||||
def get_safety_order(self):
|
||||
return self.status_dictionary["safety_order"]
|
||||
|
||||
def get_next_so_price(self):
|
||||
return self.status_dictionary["next_so_price"]
|
||||
|
||||
def get_order_size(self):
|
||||
return self.status_dictionary["order_size"]
|
||||
|
||||
def get_partial_profit(self):
|
||||
return self.status_dictionary["partial_profit"]
|
||||
|
||||
|
|
@ -149,7 +144,7 @@ class StatusHandler:
|
|||
self.status_dictionary["tp_order_id"] = order_id
|
||||
return 0
|
||||
|
||||
def set_take_profit_order(self, order: dict):
|
||||
def set_take_profit_order(self, order):
|
||||
self.status_dictionary["take_profit_order"] = order
|
||||
return 0
|
||||
|
||||
|
|
@ -161,7 +156,7 @@ class StatusHandler:
|
|||
self.status_dictionary["so_order_id"] = order_id
|
||||
return 0
|
||||
|
||||
def set_safety_order(self, order: dict):
|
||||
def set_safety_order(self, order):
|
||||
self.status_dictionary["safety_order"] = order
|
||||
return 0
|
||||
|
||||
|
|
@ -281,18 +276,27 @@ class StatusHandler:
|
|||
self.status_dictionary["deal_order_history"] = deal_history
|
||||
return 0
|
||||
|
||||
def clear_deal_order_history(self):
|
||||
self.status_dictionary["deal_order_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 = None):
|
||||
def save_to_file(self, file_path = None, is_backup = False):
|
||||
if file_path is None:
|
||||
file_path = self.status_file_path
|
||||
if is_backup:
|
||||
try:
|
||||
with open(time.strftime(f"{file_path}_%Y-%m-%d_%H:%M:%S.json"), "w") as f:
|
||||
f.write(json.dumps(self.status_dictionary, indent=4))
|
||||
except Exception as e:
|
||||
self.broker.logger.log_this(f"Error creating status backup file: {e}",1)
|
||||
try:
|
||||
with open(file_path, "w") as f:
|
||||
json.dump(self.status_dictionary, f)
|
||||
f.write(json.dumps(self.status_dictionary, indent=4))
|
||||
return 0
|
||||
except Exception as e:
|
||||
self.broker.logger.log_this(f"Error saving status to file: {file_path}: {e}",1)
|
||||
|
|
@ -303,13 +307,12 @@ class StatusHandler:
|
|||
file_path = self.status_file_path
|
||||
try:
|
||||
with open(file_path, "r") as f:
|
||||
self.set_status(json.load(f))
|
||||
self.status_dictionary = {**self.default_status_dictionary, **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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue