"""Basic review write/read helpers separated from strategy-iteration logic.""" import json from datetime import datetime from app.db.schema import get_conn def record_review(rec_id, symbol, outcome, pnl_48h, max_pnl_48h, triggered_signals, hit_signals, miss_signals, lesson): """Insert one recommendation review row.""" conn = get_conn() conn.execute(""" INSERT INTO review_log (rec_id, symbol, review_time, outcome, pnl_48h, max_pnl_48h, triggered_signals, hit_signals, miss_signals, lesson) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) """, ( rec_id, symbol, datetime.now().isoformat(), outcome, pnl_48h, max_pnl_48h, json.dumps(triggered_signals, ensure_ascii=False) if isinstance(triggered_signals, list) else triggered_signals, json.dumps(hit_signals, ensure_ascii=False) if isinstance(hit_signals, list) else hit_signals, json.dumps(miss_signals, ensure_ascii=False) if isinstance(miss_signals, list) else miss_signals, lesson, )) conn.commit() conn.close() def update_signal_performance(signal_type, category, is_hit, pnl): """Update rolling signal performance stats after review.""" conn = get_conn() row = conn.execute("SELECT * FROM signal_performance WHERE signal_type=%s", (signal_type,)).fetchone() if row: total = row["total_count"] + 1 hits = row["hit_count"] + (1 if is_hit else 0) misses = row["miss_count"] + (0 if is_hit else 1) old_avg_pnl = row["avg_pnl"] new_avg_pnl = round((old_avg_pnl * (total - 1) + pnl) / total, 2) hit_rate = round(hits / total * 100, 1) if total > 0 else 0 conn.execute(""" UPDATE signal_performance SET total_count=%s, hit_count=%s, miss_count=%s, hit_rate=%s, avg_pnl=%s, weight=%s, last_updated=%s WHERE signal_type=%s """, (total, hits, misses, hit_rate, new_avg_pnl, hit_rate / 50, datetime.now().isoformat(), signal_type)) else: conn.execute(""" INSERT INTO signal_performance (signal_type, category, total_count, hit_count, miss_count, hit_rate, avg_pnl, weight, last_updated) VALUES (%s, %s, 1, %s, %s, %s, %s, %s, %s) """, ( signal_type, category, 1 if is_hit else 0, 0 if is_hit else 1, 100 if is_hit else 0, pnl, 2.0 if is_hit else 0, datetime.now().isoformat(), )) conn.commit() conn.close() def get_signal_weights(): """Read dynamic signal weights.""" conn = get_conn() rows = conn.execute("SELECT signal_type, category, weight, hit_rate, avg_pnl, total_count FROM signal_performance").fetchall() conn.close() return {row["signal_type"]: dict(row) for row in rows} def record_missed_explosion(symbol, price_at_detect, price_before, gain_pct, reason_missed, features_detected, lesson): """Insert one missed-explosion review row.""" conn = get_conn() conn.execute(""" INSERT INTO missed_explosions (symbol, detect_time, price_at_detect, price_before, gain_pct, reason_missed, features_detected, lesson) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) """, ( symbol, datetime.now().isoformat(), price_at_detect, price_before, gain_pct, json.dumps(reason_missed, ensure_ascii=False) if isinstance(reason_missed, list) else reason_missed, json.dumps(features_detected, ensure_ascii=False) if isinstance(features_detected, list) else features_detected, lesson, )) conn.commit() conn.close()