statistics_server:

. Added guards when calculating averages
. Fixed useless variable assignment in fetch_full_log
. Streamlined profit queries
. Optimized last_n_lines
. Cleaned up db connection code
This commit is contained in:
Nicolás Sánchez 2026-06-05 18:54:31 -03:00
parent ab7c3cd021
commit 097a4bff51
1 changed files with 49 additions and 53 deletions

View File

@ -16,12 +16,7 @@ _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') or not hasattr(_local_storage, 'created_at') or (current_time - _local_storage.created_at) > 3600: # Reconnect every hour if not hasattr(_local_storage, 'connection'):
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
@ -121,9 +116,11 @@ 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_combined_media = (last_30_days[0][1]/30+last_7_days[0][1]/7)/2 daily_30_avg = last_30_days[0][1]/30 if last_30_days else 0
current_amount = last_n_months_rows[-1][1] daily_7_avg = last_7_days[0][1]/7 if last_7_days else 0
days_past_this_month = int(last_60_days_rows[-1][0][8:10]) daily_combined_media = (daily_30_avg + daily_7_avg)/2 if (daily_30_avg or daily_7_avg) else 0
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
@ -150,18 +147,16 @@ 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 binance_percentage = binance_amount/total_amount*100 if total_amount else 0
gateio_percentage = gateio_amount/total_amount*100 gateio_percentage = gateio_amount/total_amount*100 if total_amount else 0
kucoin_percentage = kucoin_amount/total_amount*100 kucoin_percentage = kucoin_amount/total_amount*100 if total_amount else 0
okex_percentage = okex_amount/total_amount*100 okex_percentage = okex_amount/total_amount*100 if total_amount else 0
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": last_30_days_average, "Last 30 days average": daily_30_avg,
"Last 7 days average": last_7_days_average, "Last 7 days average": daily_7_avg,
"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,
@ -182,22 +177,15 @@ 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 >= ?"
with db_cursor() as cursor: params = (start_date,)
cursor.execute(query, (start_date,))
query_result = cursor.fetchall()
return query_result[0][0]
else: else:
query = """SELECT pair, SUM(amount) AS total_profit query = """SELECT pair, SUM(amount) AS total_profit FROM profits_table WHERE timestamp >= ? AND pair = ?"""
FROM profits_table params = (start_date, pair)
WHERE timestamp >= ?
GROUP BY pair;""" with db_cursor() as cursor:
with db_cursor() as cursor: cursor.execute(query, params)
cursor.execute(query, (start_date,)) result = cursor.fetchone()
query_result = cursor.fetchall() return result[0] if result[0] else 0
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]:
@ -317,20 +305,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)
for line in file_contents[::-1][:amount]: contents,amount_of_lines = tail_log(file_name,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],len(file_contents) return result,amount_of_lines
def tail_log(filename, lines=200): def tail_log(filename, lines=200):
@ -423,15 +411,14 @@ def fetch_full_log():
It trims the full log to 200 lines, to avoid sending too much data to the client. It trims the full log to 200 lines, to avoid sending too much data to the client.
''' '''
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:
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": [""]*width,"amount_of_lines": 0} return {"line": [""]*10,"amount_of_lines": 0}
@stats_api.route("/fetch_log") @stats_api.route("/fetch_log")
@ -522,19 +509,28 @@ 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:
daily_totals = query_daily_totals() with db_cursor() as cursor:
val_30 = 0 cursor.execute("""SELECT
val_7 = 0 COALESCE(SUM(CASE WHEN timestamp >= ? THEN amount END), 0) AS sum_30d,
recent_days = sorted(daily_totals.keys(), reverse=True)[:30] COALESCE(SUM(CASE WHEN timestamp >= ? THEN amount END), 0) AS sum_7d,
acc_30 = [daily_totals[date] for date in recent_days[:30]] FROM profits_table""",
acc_7 = [daily_totals[date] for date in recent_days[:7]] (time.time()-30*86400, time.time()-7*86400))
length_30 = min(30,len(acc_30)) #Last 30 days row = cursor.fetchone()
length_7 = min(7,len(acc_7)) #Last 7 days sum_30d, sum_7d = float(row["sum_30d"]), float(row["sum_7d"])
for _ in range(length_30): return jsonify({"30_day": sum_30d/30, "7_day": sum_7d/7})
val_30 += acc_30.pop() # daily_totals = query_daily_totals()
for _ in range(length_7): # val_30 = 0
val_7 += acc_7.pop() # val_7 = 0
return jsonify({"30_day": val_30/length_30, "7_day": val_7/length_7}) # recent_days = sorted(daily_totals.keys(), reverse=True)[:30]
# 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'})