This commit is contained in:
parent
b5783838ff
commit
cb5f6010b3
|
|
@ -1,3 +1,8 @@
|
|||
2025.02.27
|
||||
. config_handler: 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.
|
||||
|
||||
2025.02.02:
|
||||
. new_so_routine now cancels the old take profit order after the new safety order is sent.
|
||||
|
||||
|
|
|
|||
|
|
@ -34,10 +34,13 @@ class ConfigHandler:
|
|||
"check_old_long_price": False #switch_to_short should flip this to True unless stated
|
||||
}
|
||||
self.config_file_path = f"configs/{pair.split('/')[0]}{pair.split('/')[1]}.json"
|
||||
if config_dict is None:
|
||||
self.config_dictionary = self.default_config_dictionary.copy()
|
||||
else:
|
||||
self.config_dictionary = self.default_config_dictionary.copy()
|
||||
|
||||
#Loads from disk the config file (if it exists)
|
||||
self.load_from_file()
|
||||
if config_dict is not None:
|
||||
self.config_dictionary = {**self.default_config_dictionary, **config_dict}
|
||||
self.save_to_file()
|
||||
|
||||
|
||||
|
||||
|
|
@ -232,7 +235,7 @@ class ConfigHandler:
|
|||
self.set_config({**self.default_config_dictionary, **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())
|
||||
self.broker.logger.log_this(f"Config file does not exist or is not readable: {e}",1,self.get_pair())
|
||||
return 1
|
||||
|
||||
def get_config(self):
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class Broker:
|
|||
self.database_connection.close()
|
||||
|
||||
#Load markets
|
||||
self.exchange.load_markets()
|
||||
self.markets = self.exchange.load_markets()
|
||||
|
||||
#Populates deals cache
|
||||
self.deals_cache_length = 10
|
||||
|
|
@ -66,12 +66,20 @@ class Broker:
|
|||
def get_deals_cache(self):
|
||||
return self.deals_list
|
||||
|
||||
def get_symbol(self,pair):
|
||||
if "/" in pair:
|
||||
return pair
|
||||
for item in self.markets:
|
||||
if f"{self.markets[item]['base']}{self.markets[item]['quote']}"==pair:
|
||||
return self.markets[item]["symbol"]
|
||||
return "Error"
|
||||
|
||||
def all_markets(self,no_retries=False):
|
||||
retries = self.retries
|
||||
while retries>0:
|
||||
try:
|
||||
return self.exchange.load_markets()
|
||||
self.markets = self.exchange.load_markets()
|
||||
return self.markets
|
||||
except Exception as e:
|
||||
self.logger.log_this(f"Exception in reload_markets: {e}")
|
||||
if no_retries:
|
||||
|
|
@ -83,7 +91,7 @@ class Broker:
|
|||
|
||||
def reload_markets(self):
|
||||
try:
|
||||
self.exchange.load_markets(reload=True)
|
||||
self.markets = self.exchange.load_markets(reload=True)
|
||||
return 0
|
||||
except Exception as e:
|
||||
self.logger.log_this(f"Exception in reload_markets: {e}")
|
||||
|
|
|
|||
91
main.py
91
main.py
|
|
@ -80,24 +80,21 @@ def time_to_unix(year: str, month: str, day: str) -> int:
|
|||
return 0
|
||||
|
||||
|
||||
def import_instance(pair: str) -> int:
|
||||
def import_instance(base: str, quote: str) -> int:
|
||||
'''
|
||||
Imports an previously running trader instance from the status file.
|
||||
|
||||
Parameters:
|
||||
pair (str): The trading pair to import with the format BASEQUOTE (no slash).
|
||||
base (str): The base currency of the pair.
|
||||
quote (str): The quote currency of the pair.
|
||||
|
||||
Returns:
|
||||
int: 0 if successful
|
||||
'''
|
||||
broker.logger.log_this(f"Importing {pair}")
|
||||
#with open(f"status/{pair}.status", "r") as f:
|
||||
# status_file_contents = json.load(f)
|
||||
with open(f"configs/{pair}.json", "r") as g:
|
||||
config_file_contents = json.load(g)
|
||||
instances_to_add.append(trader.trader(broker,config_file_contents,is_import=True))
|
||||
if pair not in tickers:
|
||||
tickers.append(pair)
|
||||
broker.logger.log_this(f"Importing {base}/{quote}")
|
||||
instances_to_add.append(trader.trader(broker,f"{base}/{quote}",is_import=True))
|
||||
if f"{base}{quote}" not in tickers:
|
||||
tickers.append(f"{base}{quote}")
|
||||
return 0
|
||||
|
||||
|
||||
|
|
@ -120,73 +117,31 @@ def add_instance(base: str, quote: str) -> int:
|
|||
broker.logger.log_this(f"Pair already running, duplicate instances are not allowed",1,pair)
|
||||
return 1
|
||||
|
||||
#Check if config file already exists; if not, generate a new one
|
||||
if not os.path.isfile(f"configs/{pair}.json"):
|
||||
broker.logger.log_this(f"Config file does not exist. Generating...",1,pair)
|
||||
details = generate_config_file(base,quote)
|
||||
with open(f"configs/{pair}.json","w") as cf:
|
||||
cf.write(json.dumps(details, indent=4))
|
||||
else:
|
||||
with open(f"configs/{pair}.json", "r") as cf:
|
||||
details = json.load(cf)
|
||||
|
||||
#Initialize the trader object and add the pair to the tickers list
|
||||
instances_to_add.append(trader.trader(broker,details))
|
||||
instances_to_add.append(trader.trader(broker,f"{base}/{quote}"))
|
||||
if pair not in tickers:
|
||||
tickers.append(pair)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def initialize_instance(pair: str) -> int:
|
||||
def initialize_instance(base: str, quote: str) -> int:
|
||||
'''
|
||||
Loads the pair config file and initializes the trader object by adding it to the running instances list
|
||||
|
||||
Parameters:
|
||||
pair (str): The pair to initialize with the format BASEQUOTE (no slash)
|
||||
base (str): The base currency of the pair.
|
||||
quote (str): The quote currency of the pair.
|
||||
Returns:
|
||||
int: 0 if successful
|
||||
'''
|
||||
|
||||
with open(f"configs/{pair}.json", "r") as y:
|
||||
config_details = json.load(y)
|
||||
broker.logger.log_this(f"Initializing {pair}")
|
||||
running_instances.append(trader.trader(broker,config_details))
|
||||
if pair not in tickers:
|
||||
tickers.append(pair)
|
||||
broker.logger.log_this(f"Initializing {f'{base}/{quote}'}")
|
||||
running_instances.append(trader.trader(broker,f'{base}/{quote}'))
|
||||
if f'{base}{quote}' not in tickers:
|
||||
tickers.append(f'{base}{quote}')
|
||||
return 0
|
||||
|
||||
|
||||
def generate_config_file(base: str, quote: str) -> dict:
|
||||
'''
|
||||
Generates a config file with default values for a given pair and returns that content in dictionary form.
|
||||
TODO: Add a pair check against exchange's tickers data to properly support BASEQUOTE input format (without a slash)
|
||||
1. load tickers
|
||||
2. search for pair in dictionary
|
||||
3. assign proper base and quote values from the dictionary
|
||||
'''
|
||||
return {"pair": f"{base}/{quote}",
|
||||
"order_size": broker.get_default_order_size(),
|
||||
"tp_level": 1.02,
|
||||
"no_of_safety_orders": 30,
|
||||
"safety_order_deviance": 2,
|
||||
"safety_order_scale": 0.0105,
|
||||
"write_logs": True,
|
||||
"cleanup": True,
|
||||
"telegram": True,
|
||||
"tp_mode": 3,
|
||||
"tp_table": [],
|
||||
"is_short": False,
|
||||
"autoswitch": False,
|
||||
"check_old_long_price": True,
|
||||
"attempt_restart": True,
|
||||
"dynamic_so_deviance": True,
|
||||
"dsd_range": 1,
|
||||
"slippage_default_threshold": .02,
|
||||
"generated_at": int(time.time())
|
||||
}
|
||||
|
||||
|
||||
def set_exchange(config: dict):
|
||||
'''
|
||||
Takes the config dictionary as an input and returns the exchange object
|
||||
|
|
@ -1279,7 +1234,7 @@ def reload_safety_order():
|
|||
|
||||
|
||||
def run_API():
|
||||
serve(base_api, host="0.0.0.0", port=broker.get_config()["port"], threads=4)
|
||||
serve(base_api, host="0.0.0.0", port=broker.get_config()["port"], threads=16)
|
||||
#base_api.run(host="0.0.0.0", port=broker.get_config()["port"])
|
||||
|
||||
|
||||
|
|
@ -1438,7 +1393,7 @@ def unwrapped_import_pair(base,quote):
|
|||
'''
|
||||
|
||||
try:
|
||||
import_instance(base+quote)
|
||||
import_instance(base,quote)
|
||||
broker.add_pair_to_config(f"{base}{quote}")
|
||||
broker.rewrite_config_file()
|
||||
broker.logger.log_this(f"Done",2,f"{base}/{quote}")
|
||||
|
|
@ -2282,7 +2237,11 @@ if __name__=="__main__":
|
|||
os._exit(1)
|
||||
#broker.logger.log_this(f"Initializing {len(broker.get_pairs())} instances",2)
|
||||
for x in broker.get_pairs():
|
||||
initialize_instance(x)
|
||||
symbol = broker.get_symbol(x)
|
||||
if symbol!="Error":
|
||||
initialize_instance(symbol.split("/")[0],symbol.split("/")[1])
|
||||
else:
|
||||
broker.logger.log_this(f"Can't initialize {x}, symbol query returned Error",1)
|
||||
else:
|
||||
toggle = input(f"This will import {len(broker.get_pairs())} instances, proceed? (Y/n) ")
|
||||
if toggle not in ["Y","y",""]:
|
||||
|
|
@ -2290,7 +2249,11 @@ if __name__=="__main__":
|
|||
os._exit(1)
|
||||
#broker.logger.log_this(f"Importing {len(broker.get_pairs())} instances",2)
|
||||
for x in broker.get_pairs():
|
||||
import_instance(x)
|
||||
symbol = broker.get_symbol(x)
|
||||
if symbol!="Error":
|
||||
import_instance(symbol.split("/")[0],symbol.split("/")[1])
|
||||
else:
|
||||
broker.logger.log_this(f"Can't import {x}, symbol query returned Error",1)
|
||||
broker.logger.log_this(f"All instances imported!",2)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from config_handler import ConfigHandler
|
|||
from status_handler import StatusHandler
|
||||
|
||||
class trader:
|
||||
def __init__(self, broker, config_dict: dict, is_import: bool = False):
|
||||
def __init__(self, broker, pair: str, is_import: bool = False):
|
||||
self.pause = True #Signals the trader to not process order info when an API call manhandles the trader
|
||||
#True by default, once the trader is started the start_trader method toggles it
|
||||
self.quit = False
|
||||
|
|
@ -14,7 +14,7 @@ class trader:
|
|||
self.broker = broker
|
||||
self.tp_order = self.broker.get_empty_order()
|
||||
self.so = self.broker.get_empty_order()
|
||||
self.config = ConfigHandler(config_dict["pair"],broker,config_dict)
|
||||
self.config = ConfigHandler(pair,broker)
|
||||
self.pair = self.config.get_pair()
|
||||
self.market = self.broker.fetch_market(self.pair)
|
||||
self.market_load_time = int(time.time())
|
||||
|
|
|
|||
Loading…
Reference in New Issue