Compare commits

..

No commits in common. "097a4bff517bbc722dec25bb708a3e47bf2dc5e2" and "e888ee0c78e66cf92b7a3ea690de7b906cdf8ac3" have entirely different histories.

4 changed files with 64 additions and 68 deletions

View File

@ -6,7 +6,6 @@
. Decorator for api endpoints error handling . Decorator for api endpoints error handling
. Input validation in api parameters . Input validation in api parameters
. credentials.py import fallback . credentials.py import fallback
. open_orders prefiltered before hitting the traders
2026.06.03: 2026.06.03:
. Fixed tp mode 2 non-functional . Fixed tp mode 2 non-functional

View File

@ -336,12 +336,9 @@ def main_routine():
pairs_to_fetch.append(instance.status.get_pair()) pairs_to_fetch.append(instance.status.get_pair())
open_orders = broker.fetch_open_orders(pairs_to_fetch) open_orders = broker.fetch_open_orders(pairs_to_fetch)
open_orders_by_pair = {}
for order in open_orders:
open_orders_by_pair.setdefault(order["symbol"], []).append(order)
with traders_lock: with traders_lock:
for instance in running_traders: for instance in running_traders:
future = executor.submit(instance.check_status, open_orders_by_pair) future = executor.submit(instance.check_status, open_orders)
futures.append(future) futures.append(future)
online_pairs.append(f"{instance.base}{instance.quote}") online_pairs.append(f"{instance.base}{instance.quote}")

View File

@ -36,11 +36,10 @@ class trader:
self.base,self.quote = base_quote.split("/") self.base,self.quote = base_quote.split("/")
self.status = StatusHandler(broker, self.base, self.quote) self.status = StatusHandler(broker, self.base, self.quote)
self.market = self.broker.fetch_market(base_quote) self.market = self.broker.fetch_market(base_quote)
now = time.time() self.market_load_time = int(time.time())
self.market_load_time = int(now)
self.market_reload_period = 86400 #Market reload period in seconds self.market_reload_period = 86400 #Market reload period in seconds
self.status.set_start_time(int(now)) self.status.set_start_time(int(time.time()))
self.last_time_seen = now self.last_time_seen = time.time()
self.base_diff_check = False self.base_diff_check = False
self.min_base_difference = .1 #Percentage difference between base in the tp order and its adjusted amount that triggers a recheck. self.min_base_difference = .1 #Percentage difference between base in the tp order and its adjusted amount that triggers a recheck.
@ -317,9 +316,8 @@ class trader:
self.status.set_is_paused(self.pause) self.status.set_is_paused(self.pause)
self.status.set_is_short(self.config.get_is_short()) self.status.set_is_short(self.config.get_is_short())
#self.status.set_no_of_safety_orders(self.config.get_no_of_safety_orders()) #self.status.set_no_of_safety_orders(self.config.get_no_of_safety_orders())
now = int(time.time()) self.status.set_deal_uptime(int(time.time()) - self.status.get_deal_start_time())
self.status.set_deal_uptime(now - self.status.get_deal_start_time()) self.status.set_total_uptime(int(time.time()) - self.status.get_start_time())
self.status.set_total_uptime(now - self.status.get_start_time())
self.status.set_tp_mode(self.config.get_tp_mode()) self.status.set_tp_mode(self.config.get_tp_mode())
self.status.set_profit_table(self.config.get_tp_table()) self.status.set_profit_table(self.config.get_tp_table())
self.status.set_autoswitch(self.config.get_autoswitch()) self.status.set_autoswitch(self.config.get_autoswitch())
@ -1101,7 +1099,7 @@ class trader:
return (base_left*price)+self.status.get_quote_spent()>=old_target return (base_left*price)+self.status.get_quote_spent()>=old_target
def check_status(self,open_orders: dict) -> int: #Should I change the order? Check the SO first? def check_status(self,open_orders: list) -> int: #Should I change the order? Check the SO first?
''' '''
Main routine. It checks for closed orders and proceeds accordingly. Main routine. It checks for closed orders and proceeds accordingly.
''' '''
@ -1130,8 +1128,7 @@ class trader:
#Extract ids from order list #Extract ids from order list
self.status.set_pause_reason("filtering open orders") self.status.set_pause_reason("filtering open orders")
#open_orders_list = [order for order in open_orders if order["symbol"]==self.status.get_pair()] open_orders_list = [order for order in open_orders if order["symbol"]==self.status.get_pair()]
open_orders_list = open_orders.get(self.status.get_pair(),[])
open_orders_ids = [order["id"] for order in open_orders_list] open_orders_ids = [order["id"] for order in open_orders_list]
self.status.set_pause_reason("check if tp_order is valid") self.status.set_pause_reason("check if tp_order is valid")
@ -1249,11 +1246,10 @@ class trader:
self.status.set_status_string(self.generate_status_strings()) self.status.set_status_string(self.generate_status_strings())
#Wrap up #Wrap up
now = int(time.time()) self.status.set_deal_uptime(int(time.time()) - self.status.get_deal_start_time())
self.status.set_deal_uptime(now - self.status.get_deal_start_time()) self.status.set_total_uptime(int(time.time()) - self.status.get_start_time())
self.status.set_total_uptime(now - self.status.get_start_time())
self.update_status(False) self.update_status(False)
self.last_time_seen = now self.last_time_seen = int(time.time())
return 0 return 0

View File

@ -16,7 +16,12 @@ _local_storage = threading.local()
def get_db_connection(): def get_db_connection():
current_time = time.time() current_time = time.time()
if not hasattr(_local_storage, 'connection'): if not hasattr(_local_storage, 'connection') or not hasattr(_local_storage, 'created_at') or (current_time - _local_storage.created_at) > 3600: # Reconnect every hour
if hasattr(_local_storage, 'connection'):
try:
_local_storage.connection.close()
except:
pass
_local_storage.connection = sqlite3.connect(profits_database, check_same_thread=False) _local_storage.connection = sqlite3.connect(profits_database, check_same_thread=False)
_local_storage.connection.row_factory = sqlite3.Row _local_storage.connection.row_factory = sqlite3.Row
_local_storage.created_at = current_time _local_storage.created_at = current_time
@ -116,11 +121,9 @@ def profit_report():
#Projection calculation #Projection calculation
days_in_month = calendar.monthrange(datetime.date.today().year, datetime.date.today().month)[1] days_in_month = calendar.monthrange(datetime.date.today().year, datetime.date.today().month)[1]
daily_30_avg = last_30_days[0][1]/30 if last_30_days else 0 daily_combined_media = (last_30_days[0][1]/30+last_7_days[0][1]/7)/2
daily_7_avg = last_7_days[0][1]/7 if last_7_days else 0 current_amount = last_n_months_rows[-1][1]
daily_combined_media = (daily_30_avg + daily_7_avg)/2 if (daily_30_avg or daily_7_avg) else 0 days_past_this_month = int(last_60_days_rows[-1][0][8:10])
current_amount = last_n_months_rows[-1][1] if last_n_months_rows else 0
days_past_this_month = int(last_60_days_rows[-1][0][8:10]) if last_60_days_rows else 0
#Per exchange #Per exchange
binance_amount = 0 binance_amount = 0
@ -147,16 +150,18 @@ def profit_report():
last_60_days_result = {row[0]: round(row[1],2) for row in last_60_days_rows} last_60_days_result = {row[0]: round(row[1],2) for row in last_60_days_rows}
last_18_months_result = {row[0]: round(row[1],2) for row in last_n_months_rows} last_18_months_result = {row[0]: round(row[1],2) for row in last_n_months_rows}
last_30_days_average = last_30_days[0][1]/30
last_7_days_average = last_7_days[0][1]/7
this_month_projection = current_amount + daily_combined_media*(days_in_month-days_past_this_month) this_month_projection = current_amount + daily_combined_media*(days_in_month-days_past_this_month)
binance_percentage = binance_amount/total_amount*100 if total_amount else 0 binance_percentage = binance_amount/total_amount*100
gateio_percentage = gateio_amount/total_amount*100 if total_amount else 0 gateio_percentage = gateio_amount/total_amount*100
kucoin_percentage = kucoin_amount/total_amount*100 if total_amount else 0 kucoin_percentage = kucoin_amount/total_amount*100
okex_percentage = okex_amount/total_amount*100 if total_amount else 0 okex_percentage = okex_amount/total_amount*100
return {"Last 60 days": last_60_days_result, return {"Last 60 days": last_60_days_result,
"Last 18 months": last_18_months_result, "Last 18 months": last_18_months_result,
"Last 30 days average": daily_30_avg, "Last 30 days average": last_30_days_average,
"Last 7 days average": daily_7_avg, "Last 7 days average": last_7_days_average,
"This month projection": this_month_projection, "This month projection": this_month_projection,
"Binance": binance_amount, "Binance": binance_amount,
"Binance percentage": binance_percentage, "Binance percentage": binance_percentage,
@ -177,15 +182,22 @@ def query_total_profit(pair=None, start_date=0):
if pair is None: if pair is None:
query = "SELECT SUM(amount) AS total_profit FROM profits_table WHERE timestamp >= ?" query = "SELECT SUM(amount) AS total_profit FROM profits_table WHERE timestamp >= ?"
params = (start_date,) with db_cursor() as cursor:
cursor.execute(query, (start_date,))
query_result = cursor.fetchall()
return query_result[0][0]
else: else:
query = """SELECT pair, SUM(amount) AS total_profit FROM profits_table WHERE timestamp >= ? AND pair = ?""" query = """SELECT pair, SUM(amount) AS total_profit
params = (start_date, pair) FROM profits_table
WHERE timestamp >= ?
with db_cursor() as cursor: GROUP BY pair;"""
cursor.execute(query, params) with db_cursor() as cursor:
result = cursor.fetchone() cursor.execute(query, (start_date,))
return result[0] if result[0] else 0 query_result = cursor.fetchall()
for item in query_result:
if item[0].replace("/","")==pair:
return item[1]
return 0
def daily_and_monthly_totals() -> tuple[float, float]: def daily_and_monthly_totals() -> tuple[float, float]:
@ -305,20 +317,20 @@ def last_n_lines(file_name,width,amount=4,full_log=False):
file_contents = [] file_contents = []
result = [] result = []
with open(file_name) as f:
file_contents = f.readlines()
if full_log: if full_log:
with open(file_name) as f:
file_contents = f.readlines()
for line in file_contents: for line in file_contents:
result.append(line.strip()) result.append(line.strip())
return result,len(file_contents) return result,len(file_contents)
contents,amount_of_lines = tail_log(file_name,amount) for line in file_contents[::-1][:amount]:
for line in contents:
trimmed = line.strip() trimmed = line.strip()
result.append(trimmed[:width]) result.append(trimmed[:width])
if len(trimmed)>width: if len(trimmed)>width:
result.append(trimmed[width:width*2]) result.append(trimmed[width:width*2])
return result,amount_of_lines return result[:amount],len(file_contents)
def tail_log(filename, lines=200): def tail_log(filename, lines=200):
@ -414,11 +426,12 @@ def fetch_full_log():
return jsonify({'Error': 'API key invalid'}), 401 return jsonify({'Error': 'API key invalid'}), 401
try: try:
exchange_name = request.args.get("exchange_name") exchange_name = request.args.get("exchange_name")
width = 0
last_lines, amount_of_lines = tail_log(f"../logs/{exchange_name}.log", 200) last_lines, amount_of_lines = tail_log(f"../logs/{exchange_name}.log", 200)
return jsonify({"line": last_lines[-200:], "amount_of_lines": amount_of_lines}) return jsonify({"line": last_lines[-200:], "amount_of_lines": amount_of_lines})
except Exception as e: except Exception as e:
print(e) print(e)
return {"line": [""]*10,"amount_of_lines": 0} return {"line": [""]*width,"amount_of_lines": 0}
@stats_api.route("/fetch_log") @stats_api.route("/fetch_log")
@ -509,28 +522,19 @@ def get_averages():
if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in get_valid_keys(): if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in get_valid_keys():
return jsonify({'Error': 'API key invalid'}), 401 return jsonify({'Error': 'API key invalid'}), 401
try: try:
with db_cursor() as cursor: daily_totals = query_daily_totals()
cursor.execute("""SELECT val_30 = 0
COALESCE(SUM(CASE WHEN timestamp >= ? THEN amount END), 0) AS sum_30d, val_7 = 0
COALESCE(SUM(CASE WHEN timestamp >= ? THEN amount END), 0) AS sum_7d, recent_days = sorted(daily_totals.keys(), reverse=True)[:30]
FROM profits_table""", acc_30 = [daily_totals[date] for date in recent_days[:30]]
(time.time()-30*86400, time.time()-7*86400)) acc_7 = [daily_totals[date] for date in recent_days[:7]]
row = cursor.fetchone() length_30 = min(30,len(acc_30)) #Last 30 days
sum_30d, sum_7d = float(row["sum_30d"]), float(row["sum_7d"]) length_7 = min(7,len(acc_7)) #Last 7 days
return jsonify({"30_day": sum_30d/30, "7_day": sum_7d/7}) for _ in range(length_30):
# daily_totals = query_daily_totals() val_30 += acc_30.pop()
# val_30 = 0 for _ in range(length_7):
# val_7 = 0 val_7 += acc_7.pop()
# recent_days = sorted(daily_totals.keys(), reverse=True)[:30] return jsonify({"30_day": val_30/length_30, "7_day": val_7/length_7})
# acc_30 = [daily_totals[date] for date in recent_days[:30]]
# acc_7 = [daily_totals[date] for date in recent_days[:7]]
# length_30 = min(30,len(acc_30)) #Last 30 days
# length_7 = min(7,len(acc_7)) #Last 7 days
# for _ in range(length_30):
# val_30 += acc_30.pop()
# for _ in range(length_7):
# val_7 += acc_7.pop()
# return jsonify({"30_day": val_30/length_30, "7_day": val_7/length_7})
except Exception as e: except Exception as e:
print(e) print(e)
return jsonify({'Error': 'Halp'}) return jsonify({'Error': 'Halp'})