Compare commits

..

No commits in common. "main" and "2025.10.01" have entirely different histories.

7 changed files with 70 additions and 218 deletions

View File

@ -1,45 +1,3 @@
2025.12.01:
. Modified log output of new_market_order.
. Modified Kucoin's case in min_amount_of_base.
2025.11.11:
. deals_cache and log_list cache are now 20 items long.
. Less log spam.
2025.11.08:
. broker.set_default_order_size() now saves the config file to disk after changing the value.
. Variable renaming and other small stuff.
2025.10.24:
. Toggling liquidate_after_switch now writes the config file to disk so the setting persists between trades.
. Manually switching to long now sets double_check_price to false.
. Added a few comments to switch_to_long.
2025.10.12:
. do_cleanup relocated after generating the safety orders' prices.
2025.10.11:
. Minor simplification in do_cleanup.
. Removed a couple of (no longer needed?) pauses.
2025.10.10:
. New endpoint: /refresh_log_cache.
. Fixed an error in /add_so endpoint that incremented the config setting but not the status setting.
2025.10.09:
. Cleanup is done as soon as the trader starts, rather than after sending the take profit and safety orders.
2025.10.07:
. In short traders, if there are too few safety orders (less than 67% of the max amount), safety_order_deviance is increased from 2% to 3%.
2025.10.04:
. Fixed error while logging orders in new_simulated_market_order.
. renew_tp_and_so_routine now send the take profit order first, and then the safety orders.
2025.10.03:
. New broker config option: log_orders. If set to True, the orders will be logged in orders.log under logs directory.
. New API endpoint: /toggle_log_orders.
2025.10.01:
. Fixed base fees not being taken into account.

View File

@ -252,7 +252,6 @@ class ConfigHandler:
# self.broker.logger.log_this(f"liquidate_after_switch must be a boolean",1,self.get_pair())
# return 1
self.config_dictionary["liquidate_after_switch"] = liquidate_after_switch
self.save_to_file()
return 0
def set_tp_mode(self, tp_mode: int):

View File

@ -19,14 +19,13 @@ class Broker:
#Default values
self.wait_time = self.broker_config.get("wait_time",.5)
self.cooldown_multiplier = self.broker_config.get("cooldown_multiplier",2)
self.wait_after_initial_market_order = self.broker_config.get("wait_after_initial_market_order",1)
self.wait_after_initial_market_order = self.broker_config.get("wait_after_initial_market_order",2)
self.wait_before_new_safety_order = self.broker_config.get("wait_before_new_safety_order",1)
self.retries = self.broker_config.get("retries",5)
self.slippage_default_threshold = self.broker_config.get("slippage_default_threshold",.03)
self.follow_order_history = self.broker_config.get("follow_order_history",False)
self.write_order_history = self.broker_config.get("write_order_history", False)
self.logger = Logger(self.broker_config)
self.log_orders = self.broker_config.get("log_orders",False)
#Initialize database
self.profits_database_filename = "profits/profits_database.db"
@ -51,7 +50,7 @@ class Broker:
self.markets = self.exchange.load_markets()
#Populates deals cache
self.deals_cache_length = 20
self.deals_cache_length = 10
self.deals_list = self.preload_deals(amount_to_preload=self.deals_cache_length)
@ -82,16 +81,6 @@ class Broker:
def get_deals_cache(self):
return self.deals_list
def get_log_orders(self):
return self.log_orders
def set_log_orders(self,log_orders:bool):
self.log_orders = log_orders
return 0
def get_symbol(self,pair):
if "/" in pair:
return pair
@ -224,7 +213,6 @@ class Broker:
def set_default_order_size(self,size):
try:
self.broker_config["default_order_size"] = float(size)
self.rewrite_config_file()
except Exception as e:
self.logger.log_this(f"Exception in set_default_order_size: {e}",1)
return 1
@ -664,7 +652,7 @@ class Broker:
return amount
def new_simulated_market_order(self,symbol,size,side,amount_in_base=False,no_retries=False,log=""):
def new_simulated_market_order(self,symbol,size,side,amount_in_base=False,no_retries=False):
'''
TODO: Emulating Market Orders With Limit Orders
@ -694,9 +682,7 @@ class Broker:
while retries>0:
try:
if self.get_exchange_name()=="gateio" and side=="buy" and not amount_in_base:
new_order = self.exchange.create_market_buy_order_with_cost(symbol, size)
if self.log_orders:
self.logger.log_order(f"New simulated market order: Symbol: {symbol} - Side: {side} - Size: {size} - ID: {new_order['id']} - Origin: {log}")
new_order = self.exchange.create_market_buy_order_with_cost(symbol, size)
else:
order_book = self.get_order_book(symbol)
if order_book=={}:
@ -710,8 +696,6 @@ class Broker:
price = self.find_minimum_viable_price(order_book,base_amount,side)
#Maybe check for slippage here instead of within the trader itself? idk
new_order = self.exchange.create_order(symbol,"limit",side,base_amount,price)
if self.log_orders:
self.logger.log_order(f"New simulated market order: Symbol: {symbol} - Side: {side} - Size: {size} - Price: {price} - ID: {new_order['id']} - Origin: {log}")
time.sleep(self.wait_time)
return self.get_order(new_order["id"],symbol)
except Exception as e:
@ -758,7 +742,7 @@ class Broker:
return None
def new_market_order(self,symbol,size,side,amount_in_base=False,no_retries=False, log=""): #It should send a new market order to the exchange
def new_market_order(self,symbol,size,side,amount_in_base=False,no_retries=False): #It should send a new market order to the exchange
'''
Sends a new market order to the exchange.
@ -783,13 +767,11 @@ class Broker:
amount = self.amount_to_precision(symbol,size) #Market sell orders are always nominated in base currency
order_to_send = self.exchange.create_order(symbol,"market",side,amount)
if self.log_orders:
self.logger.log_order(f"New market order: Symbol: {symbol} - Side: {side} - Size: {size} - ID: {order_to_send['id']} - Origin: {log}")
time.sleep(self.wait_time)
return self.get_order(order_to_send["id"],symbol)
except Exception as e:
self.logger.log_this(f"Exception in new_market_order: {e} - Side: {side} - Size: {size}",1,symbol)
self.logger.log_this(f"Exception in new_market_order: {e}",1,symbol)
if no_retries:
break
time.sleep(self.wait_time)
@ -871,7 +853,7 @@ class Broker:
# return returned_orders
def new_limit_order(self,symbol,size,side,price,no_retries=False,log=""):
def new_limit_order(self,symbol,size,side,price,no_retries=False):
'''
Sends a new limit order.
@ -887,8 +869,6 @@ class Broker:
try:
order_to_send = self.exchange.create_order(symbol,"limit",side,self.amount_to_precision(symbol,size),price)
time.sleep(self.wait_time)
if self.log_orders:
self.logger.log_order(f"New limit order: Symbol: {symbol} - Side: {side} - Size: {size} - Price: {price} - ID: {order_to_send['id']} - Notes: {log}")
return self.get_order(order_to_send["id"],symbol)
except Exception as e:
self.logger.log_this(f"Exception in new_limit_order - Side: {side} - Size: {size} - {self.amount_to_precision(symbol,size)} - Exception: {e}",1,symbol)
@ -988,7 +968,7 @@ class Broker:
if self.get_exchange_name() in ["okex","bybit"]:
return float(market["limits"]["amount"]["min"])
elif self.get_exchange_name() in ["kucoin"]:
return max(float(market["limits"]["amount"]["min"]),(float(market["limits"]["cost"]["min"])+.25)/self.get_ticker_price(pair))
return (float(market["limits"]["cost"]["min"])+.25)/self.get_ticker_price(pair)
elif self.get_exchange_name() in ["gateio"]:
return (float(market["limits"]["cost"]["min"])+.1)/self.get_ticker_price(pair)
elif self.get_exchange_name()=="binance":
@ -1052,7 +1032,7 @@ class Logger:
self.broker_config = broker_config
self.exchange_name = self.broker_config["exchange"]
self.tg_credentials = credentials.get_credentials("telegram")
self.log_list_max_length = 20 # log cache
self.log_list_max_length = 10
self.log_list = collections.deque(maxlen=self.log_list_max_length)
self.preload_logs()
@ -1068,16 +1048,6 @@ class Logger:
return 1
def refresh_logs(self):
try:
self.log_list.clear()
self.preload_logs()
return 0
except Exception as e:
print(e)
return 1
def set_log_list_max_length(self, amount):
self.log_list_max_length = amount
return self.log_list_max_length
@ -1114,9 +1084,6 @@ class Logger:
self.log_this(f"Error in send_tg_message: {e}",1)
return 1
def log_order(self,message):
with open(f"logs/orders.log","a") as log_file:
log_file.write(time.strftime(f"[%Y/%m/%d %H:%M:%S] | {message}\n"))
def log_this(self,message,level=2,pair=None):
'''
@ -1138,6 +1105,8 @@ class Logger:
#Write to log file
with open(f"logs/{self.exchange_name}.log","a") as log_file:
log_file.write(text+"\n")
log_file.close()
#Append to log list
self.log_list.append(text)
except Exception as e:

67
main.py
View File

@ -18,7 +18,7 @@ import exchange_wrapper
import trader
version = "2025.12.01"
version = "2025.10.01"
'''
Color definitions. If you want to change them, check the reference at https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
@ -1238,20 +1238,6 @@ def switch_quote_currency():
return jsonify({'Error': 'Halp'})
@base_api.route("/toggle_log_orders", methods=['POST'])
def toggle_log_orders():
'''
POST request
Parameters:
None
'''
if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys:
return jsonify({'Error': 'API key invalid'}), 401
return unwrapped_toggle_log_orders()
@base_api.route("/toggle_restart", methods=['POST'])
def toggle_restart():
'''
@ -1308,21 +1294,6 @@ def get_log_list():
return unwrapped_get_log_list()
@base_api.route("/refresh_log_cache", methods=['POST'])
def refresh_log_cache():
'''
POST request
'''
if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys:
return jsonify({'Error': 'API key invalid'}), 401
try:
return unwrapped_refresh_log_cache()
except Exception as e:
print(e)
return jsonify({'Error': 'Halp'})
@base_api.route("/get_balance", methods=['GET'])
def get_balance():
'''
@ -1642,7 +1613,7 @@ def unwrapped_switch_to_long(base,quote,calculate_profits):
for instance in running_traders:
if f"{base}/{quote}"==instance.status.get_pair():
instance.set_pause(True, "Switching to long mode")
if instance.switch_to_long(ignore_old_long=ignore_old_long,double_check_price=False)==1:
if instance.switch_to_long(ignore_old_long=ignore_old_long)==1:
return jsonify({"Error": "Error in switch_to_long()"})
if instance.start_trader()==1:
instance.quit = True
@ -1810,7 +1781,8 @@ def unwrapped_add_safety_orders(base,quote,amount):
for instance in running_traders:
if symbol==instance.status.get_pair():
instance.set_pause(True, "Adding safety orders")
instance.status.set_no_of_safety_orders(instance.status.get_no_of_safety_orders()+int(amount))
#x.no_of_safety_orders += int(amount)
instance.config.set_no_of_safety_orders(instance.config.get_no_of_safety_orders()+int(amount))
broker.logger.log_this("Recalculating safety price table...",1,symbol)
instance.status.set_safety_price_table(instance.calculate_safety_prices(instance.status.get_start_price(),instance.config.get_no_of_safety_orders(),instance.config.get_safety_order_deviance()))
broker.logger.log_this(f"Done. Added {amount} safety orders",1,symbol)
@ -2280,7 +2252,6 @@ def unwrapped_toggle_autoswitch(base,quote):
broker.logger.log_this(f"Exception while toggling autoswitch: {e}",1,symbol)
return jsonify({"Error": "Halp"})
def unwrapped_toggle_liquidate_after_switch(base,quote):
'''
Signals a trader to enable or disable quitting after switching from short to long.
@ -2389,20 +2360,6 @@ def unwrapped_toggle_restart():
return jsonify({"Success": "attempt_to_restart disabled"})
def unwrapped_toggle_log_orders():
'''
Toggles on or off the logging of orders.
Returns:
jsonify: A jsonified dictionary detailing the outcome of the operation.
'''
broker.set_log_orders(not broker.get_log_orders())
if broker.get_log_orders():
return jsonify({"Success": "log_orders enabled"})
return jsonify({"Success": "log_orders disabled"})
def unwrapped_toggle_telegram():
'''
Switches on or off the Telegram notifications
@ -2474,22 +2431,6 @@ def unwrapped_get_log_list():
return jsonify({"Logs": broker.logger.get_log_list()})
def unwrapped_refresh_log_cache():
'''
Reloads the log file cache.
Parameters:
None
Returns:
jsonify: A jsonified dictionary containing the last n entries from the log file.
'''
if broker.logger.refresh_logs()==0:
return jsonify({"Success": "Logs refreshed"})
else:
return jsonify({"Error": "Error while refreshing logs"})
def unwrapped_get_deals_cache():
'''
Retrieves the last n entries from the broker's logger.

105
trader.py
View File

@ -42,8 +42,8 @@ class trader:
if self.config.get_is_short():
#Check if there is an old_long file. If so, load it.
try:
with open(f"status/{self.base}{self.quote}.oldlong") as old_long_file_handler:
self.status.set_old_long(load(old_long_file_handler))
with open(f"status/{self.base}{self.quote}.oldlong") as ol:
self.status.set_old_long(load(ol))
except Exception as e:
self.broker.logger.log_this(f"Exception: No old_long file. {e}",1,base_quote)
@ -155,7 +155,7 @@ class trader:
if self.status.get_old_long()["tp_amount"]-free_base>min_base_size:
amount_to_buy = self.broker.amount_to_precision(self.status.get_pair(),self.status.get_old_long()["tp_amount"]-free_base)
self.broker.logger.log_this(f"Buying missing {amount_to_buy} {self.base}",1,self.status.get_pair())
self.broker.new_market_order(self.status.get_pair(),amount_to_buy,"buy",amount_in_base=True,log="start_trader-missing_base")
self.broker.new_market_order(self.status.get_pair(),amount_to_buy,"buy",amount_in_base=True)
time.sleep(self.broker.get_wait_time()*2)
#Re-querying for the amount of base currency on the exchange
free_base = self.fetch_free_base()
@ -171,8 +171,6 @@ class trader:
return 1
self.status.set_order_size(order_size)
self.status.set_no_of_safety_orders(no_of_safety_orders)
if no_of_safety_orders<self.config.get_max_short_safety_orders()*.67:
self.config.set_safety_order_deviance(3)
self.broker.logger.log_this(f"Order size: {self.broker.amount_to_precision(self.status.get_pair(),order_size)}. Amount of safety orders: {no_of_safety_orders}",2,self.status.get_pair())
#Write the changes to the config file
@ -204,6 +202,7 @@ class trader:
self.status.set_pause_reason("start_trader - after slippage")
#Sending initial order
#
# Here, if the amount of the initial order is already available in the account, don't send a market order; just pull the current price and simulate that the order was sent and filled.
@ -212,7 +211,7 @@ class trader:
self.status.set_pause_reason("start_trader - sending first order")
self.broker.logger.log_this("Sending first order...",2,self.status.get_pair())
action = "sell" if self.config.get_is_short() else "buy"
first_order = self.broker.new_market_order(self.status.get_pair(),self.status.get_order_size(),action,log="start_trader")
first_order = self.broker.new_market_order(self.status.get_pair(),self.status.get_order_size(),action)
#self.broker.logger.log_this(f"First order id: {first_order}",1,self.status.get_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.status.get_pair())
@ -281,11 +280,6 @@ class trader:
self.status.set_start_price(self.broker.price_to_precision(self.status.get_pair(),self.status.get_quote_spent()/self.status.get_base_bought()))
self.status.set_safety_price_table(self.calculate_safety_prices(self.status.get_start_price(),self.status.get_no_of_safety_orders(),self.config.get_safety_order_deviance()))
# Send cleanup order (if cleanup)
self.status.set_pause_reason("start_trader - doing cleanup (if needed)")
if self.config.get_cleanup() and not self.config.get_is_short(): #Short traders do not need cleanup.
self.do_cleanup()
# Send the initial batch of safety orders
self.status.set_pause_reason("start_trader - sending safety orders")
self.broker.logger.log_this("Sending safety orders...",2,self.status.get_pair())
@ -299,6 +293,11 @@ class trader:
self.broker.cancel_order(self.status.get_take_profit_order()["id"],self.status.get_pair())
return 1
# Send cleanup order (if cleanup)
self.status.set_pause_reason("start_trader - doing cleanup (if needed)")
if self.config.get_cleanup() and not self.config.get_is_short(): #Short traders do not need cleanup.
self.do_cleanup()
self.status.set_deal_start_time(int(time.time()))
self.update_status(True)
self.set_pause(False)
@ -450,10 +449,14 @@ class trader:
self.broker.logger.log_this("Can't fetch free base",1,self.status.get_pair())
return 1
if balance_in_account*self.status.get_start_price()>self.broker.get_min_quote_size(self.status.get_pair()):
self.broker.logger.log_this(f"Balance to clean: {balance_in_account} {self.base}",2,self.status.get_pair())
#If the balance is greater than the size of the first safety order, sell the difference.
# Sometimes when an order is filled the balance is not updated immediately, so having a bit of a buffer irons out a couple of issues.
min_size = self.status.get_safety_orders()[0]["amount"]
if (balance_in_account-min_size)*self.status.get_start_price()>self.broker.get_min_quote_size(self.status.get_pair()):
self.broker.logger.log_this(f"Balance to clean: {balance_in_account-min_size} {self.base}",2,self.status.get_pair())
self.broker.logger.log_this("Sending cleanup order...",2,self.status.get_pair())
cleanup_order = self.broker.new_limit_order(self.status.get_pair(),balance_in_account,"sell",self.status.get_take_profit_price(),no_retries=True,log="cleanup")
cleanup_order = self.broker.new_limit_order(self.status.get_pair(),balance_in_account-min_size,"sell",self.status.get_take_profit_price(),no_retries=True)
if cleanup_order is None:
self.broker.logger.log_this("Problems with the cleanup order, new_limit_order returned None",1,self.status.get_pair())
return 1
@ -572,8 +575,8 @@ class trader:
"datetime": time.strftime("[%Y/%m/%d %H:%M:%S]")
})
try:
with open(f"status/{self.base}{self.quote}.oldlong","w") as old_long_file_handler:
old_long_file_handler.write(dumps(self.status.get_old_long(),indent=4))
with open(f"status/{self.base}{self.quote}.oldlong","w") as s:
s.write(dumps(self.status.get_old_long(),indent=4))
except Exception as e:
self.broker.logger.log_this(f"Exception while saving old_long file: {e}",1,self.status.get_pair())
@ -606,19 +609,17 @@ class trader:
if double_check_price:
#Waits a moment to see if the price has moved too much
self.broker.logger.log_this("Confirming price...",2,self.status.get_pair())
time.sleep(self.broker.get_wait_time()*4)
if not self.check_old_long(True):
self.broker.logger.log_this("False positive. Nothing to do.",1,self.status.get_pair())
return 2
#Check old_long data
self.broker.logger.log_this("Checking if old long data is valid.",2,self.status.get_pair())
if not ignore_old_long and self.status.get_old_long()=={}:
self.broker.logger.log_this("Can't find old long info on status_dict, searching for oldlong file",1,self.status.get_pair())
try:
with open(f"status/{self.base}{self.quote}.oldlong") as old_long_file_handler:
self.status.set_old_long(load(old_long_file_handler))
with open(f"status/{self.base}{self.quote}.oldlong") as f:
self.status.set_old_long(load(f))
except Exception as e:
#self.write_to_log(time.strftime(f"[%Y/%m/%d %H:%M:%S] | {self.status.get_pair()} | Can't find old long file"))
self.broker.logger.log_this(f"Can't file oldlong file. Exception: {e}",1,self.status.get_pair())
@ -626,7 +627,6 @@ class trader:
return 1
#Cancel open orders
self.broker.logger.log_this("Cancelling open orders",2,self.status.get_pair())
for order in self.status.get_safety_orders():
self.broker.cancel_order(order["id"],self.status.get_pair())
if self.status.get_take_profit_order() is not None:
@ -635,21 +635,18 @@ class trader:
self.broker.logger.log_this("Safety order is None",1,self.status.get_pair())
#Sell all base currency
self.broker.logger.log_this(f"Selling {self.status.get_pair().split('/')[0]}",2,self.status.get_pair())
self.liquidate_base(ignore_profits=ignore_old_long, already_received_quote=already_received_quote)
if self.config.get_liquidate_after_switch():
self.broker.logger.log_this("Liquidate after switch active. Raising quit flag.",1,self.status.get_pair())
self.quit = True
return 0
return 1
#Rewrite config file (if it exists)
if path.isfile(f"configs/{self.base}{self.quote}.bak") and path.isfile(f"configs/{self.base}{self.quote}.json"):
self.broker.logger.log_this("Restoring config file from backup",2,self.status.get_pair())
with open(f"configs/{self.base}{self.quote}.bak") as backup_config_file_handler:
old_config = load(backup_config_file_handler)
with open(f"configs/{self.base}{self.quote}.json","w") as config_file_handler:
config_file_handler.write(dumps(old_config, indent=4))
with open(f"configs/{self.base}{self.quote}.bak") as c:
old_config = load(c)
with open(f"configs/{self.base}{self.quote}.json","w") as c:
c.write(dumps(old_config, indent=4))
if self.config.load_from_file()==1:
self.config.reset_to_default()
else:
@ -671,7 +668,6 @@ class trader:
self.status.set_so_amount(0)
#Done. Ready for start_trader
self.broker.logger.log_this("Finished setting up the switch to long.",2,self.status.get_pair())
return 0
@ -687,7 +683,7 @@ class trader:
self.broker.logger.log_this("Can't fetch free base",1,self.status.get_pair())
return 1
#send market order selling the total amount of base in the last take profit short order
order = self.broker.new_market_order(self.status.get_pair(),free_base,"sell",log="liquidate_base")
order = self.broker.new_market_order(self.status.get_pair(),free_base,"sell")
time.sleep(self.broker.get_wait_time()*2)
tries = self.broker.get_retries()
while True:
@ -727,7 +723,7 @@ class trader:
#Send the market order
amount = self.status.get_take_profit_order()["amount"]
market_order = self.broker.new_market_order(self.status.get_pair(),amount,"sell",amount_in_base=True,log="force_close")
market_order = self.broker.new_market_order(self.status.get_pair(),amount,"sell",amount_in_base=True)
time.sleep(self.broker.get_wait_time()*2)
#Wait for it to be filled
@ -798,7 +794,7 @@ class trader:
if partial_filled_amount!=0 and len(partial_filled_price)>0 and partial_filled_amount>self.broker.get_min_base_size(self.status.get_pair()):
#send a market order and sum the profits and wait for it to be filled
self.broker.logger.log_this("Sending partial fill sell order...",1,self.status.get_pair())
market_order = self.broker.new_market_order(self.status.get_pair(),partial_filled_amount,"sell",amount_in_base=True,log="take_profit_routine-partial_fill")
market_order = self.broker.new_market_order(self.status.get_pair(),partial_filled_amount,"sell",amount_in_base=True)
time.sleep(self.broker.get_wait_time()*2)
tries = self.broker.get_retries()
while True:
@ -808,11 +804,11 @@ class trader:
partial_profit = market_order["cost"]-(avg_buy_price*partial_filled_amount)-self.parse_fees(market_order)[1]
self.status.set_partial_profit(self.status.get_partial_profit()+partial_profit)
break
self.broker.logger.log_this("Waiting for partial fill sell order to fill.",2,self.status.get_pair())
self.broker.logger.log_this("Waiting for partial fill sell order to fill.",1,self.status.get_pair())
tries-=1
time.sleep(self.broker.get_wait_time())
if tries==0:
self.broker.logger.log_this("Partial fill sell order not filled.",1,self.status.get_pair())
self.broker.logger.log_this("Partial fill sell order not filling.",1,self.status.get_pair())
break
if not self.broker.check_for_duplicate_profit_in_db(filled_order):
@ -929,9 +925,9 @@ class trader:
self.broker.logger.log_this(f"Sending a new safety order ({i+1}/{orders_to_place})",2,self.status.get_pair())
so_size = self.gib_so_size(self.status.get_order_size(),self.status.get_so_amount()+1,self.config.get_safety_order_scale())
if self.config.get_is_short():
new_order = self.broker.new_limit_order(self.status.get_pair(),so_size,"sell",self.status.get_safety_price_table()[self.status.get_so_amount()+1], log="send_new_safety_order_batch")
new_order = self.broker.new_limit_order(self.status.get_pair(),so_size,"sell",self.status.get_safety_price_table()[self.status.get_so_amount()+1])
else:
new_order = self.broker.new_limit_order(self.status.get_pair(),so_size/self.status.get_safety_price_table()[self.status.get_so_amount()+1],"buy",self.status.get_safety_price_table()[self.status.get_so_amount()+1],log="send_new_safety_order_batch")
new_order = self.broker.new_limit_order(self.status.get_pair(),so_size/self.status.get_safety_price_table()[self.status.get_so_amount()+1],"buy",self.status.get_safety_price_table()[self.status.get_so_amount()+1])
if new_order==1:
self.broker.logger.log_this("Not enough balance to send a new safety order",1,self.status.get_pair())
@ -1021,23 +1017,24 @@ class trader:
self.status.set_fees_paid_in_base(self.status.get_fees_paid_in_base() + self.parse_fees(old_tp_order)[0])
#Cooldown
#time.sleep(self.broker.get_wait_before_new_safety_order())
time.sleep(self.broker.get_wait_before_new_safety_order())
#Send new SO(s)
#Do not send new orders if the max amount is reached or surpassed.
#It can happen if the max amount of concurrent orders is modified through an API call.
max_orders = self.config.get_concurrent_safety_orders() if not self.status.get_is_boosted() else self.config.get_boosted_concurrent_safety_orders()
if len(self.status.get_safety_orders())<max_orders:
self.send_new_safety_order_batch(max_orders-len(self.status.get_safety_orders()))
#Cooldown
time.sleep(self.broker.get_wait_before_new_safety_order())
#Send new TP order
if self.send_new_tp_order()==1:
error_string = "Problems sending the new take profit order"
self.broker.logger.log_this("Problems sending the new take profit order",1,self.status.get_pair())
self.status.set_pause_reason(error_string)
return 4
#Cooldown
#time.sleep(self.broker.get_wait_before_new_safety_order())
#Send new safety order(s)
#Do not send new orders if the max amount is reached or surpassed.
max_orders = self.config.get_concurrent_safety_orders() if not self.status.get_is_boosted() else self.config.get_boosted_concurrent_safety_orders()
if len(self.status.get_safety_orders())<max_orders:
self.send_new_safety_order_batch(max_orders-len(self.status.get_safety_orders()))
return 4
#Update status dict
self.update_status(True)
@ -1199,9 +1196,9 @@ class trader:
filled_orders = []
for id in filled_ids:
order = self.broker.get_order(id, self.status.get_pair())
time.sleep(self.broker.get_wait_time())
if order["status"]=="closed":
filled_orders.append(order)
time.sleep(self.broker.get_wait_time())
if len(filled_orders)>0: #To make sure that the safety orders are actually filled (Kucoin sometimes sends incomplete order lists)
self.status.set_safety_orders_filled(self.status.get_safety_orders_filled()+len(filled_orders))
renew_outcome = self.renew_tp_and_so_routine(filled_orders)
@ -1351,10 +1348,10 @@ class trader:
return 1
if self.config.get_is_short():
self.status.set_take_profit_price(self.status.get_quote_spent()/self.status.get_base_bought()*(1-(self.get_tp_level(self.status.get_so_amount())-1)))
self.status.set_take_profit_order(self.broker.new_limit_order(self.status.get_pair(),self.status.get_base_bought(),"buy",self.status.get_take_profit_price(),log="new_tp_order"))
self.status.set_take_profit_order(self.broker.new_limit_order(self.status.get_pair(),self.status.get_base_bought(),"buy",self.status.get_take_profit_price()))
else:
self.status.set_take_profit_price(self.status.get_quote_spent()/self.status.get_base_bought()*self.get_tp_level(self.status.get_so_amount()))
self.status.set_take_profit_order(self.broker.new_limit_order(self.status.get_pair(),self.status.get_base_bought(),"sell",self.status.get_take_profit_price(),log="new_tp_order"))
self.status.set_take_profit_order(self.broker.new_limit_order(self.status.get_pair(),self.status.get_base_bought(),"sell",self.status.get_take_profit_price()))
if self.status.get_take_profit_order()==1: #This means that there was a miscalculation of base currency amount
if self.config.get_is_short(): #If in short mode, we don't recalculate anything.
return 1
@ -1518,7 +1515,7 @@ class trader:
return self.broker.get_empty_order()
#Sends the new order
return self.broker.new_limit_order(f"{self.base}/{new_quote}",old_order["amount"],old_order["side"],old_order["price"],log="quote_currency_replace_order")
return self.broker.new_limit_order(f"{self.base}/{new_quote}",old_order["amount"],old_order["side"],old_order["price"])
def quote_currency_switch_configs(self, new_quote: str) -> int:
@ -1545,8 +1542,8 @@ class trader:
#If there is an old_long file, also copy it
if self.config.get_is_short() and self.status.get_old_long()!={}:
try:
with open(f"status/{self.base}{self.quote}.oldlong","w") as old_long_file_handler:
old_long_file_handler.write(dumps(self.status.get_old_long(), indent=4))
with open(f"status/{self.base}{self.quote}.oldlong","w") as c:
c.write(dumps(self.status.get_old_long(), indent=4))
except Exception as e:
self.broker.logger.log_this(f"Exception while writing new old_long file: {e}",1,self.status.get_pair())

View File

@ -43,8 +43,7 @@ INSTANCE
10) edit_call_wait_time 11) reload_markets 12) fetch_full_log
13) paused_traders 14) fetch_log 15) edit_cooldown_multiplier
16) get_balance 17) cancel_global_last_call
18) mod_default_order_size 19) toggle_log_orders
20) refresh_log_cache
18) mod_default_order_size
EARN
31) toggle_pause 32) get_step_size 33) set_step_size
@ -342,19 +341,6 @@ if __name__=="__main__":
print(json.loads(requests.post(url, headers=headers, json=parameters).content))
input("Press ENTER to continue ")
elif command==19:
print("toggle_log_orders turns on or off the logging of orders")
if input("Proceed? (Y/n) ") in ["Y","y",""]:
url = f"{base_url}{port}/toggle_log_orders"
print(json.loads(requests.post(url, headers=headers).content))
input("Press ENTER to continue ")
elif command==20:
print("refresh_log_cache refreshes the log cache")
if input("Proceed? (Y/n) ") in ["Y","y",""]:
url = f"{base_url}{port}/refresh_log_cache"
print(json.loads(requests.post(url, headers=headers).content))
input("Press ENTER to continue ")
######################
######## EARN ########

View File

@ -5,8 +5,10 @@ import calendar
import logging
import threading
import os
from collections import deque
from typing import Iterable, List, Tuple
from contextlib import contextmanager
from flask import Flask, jsonify, request
from flask import Flask, jsonify, request, Response
from waitress import serve