diff --git a/deploy/profile-scripts/advice_reconciliation.py b/archive/d-cleanup-20260820/advice_reconciliation.py similarity index 100% rename from deploy/profile-scripts/advice_reconciliation.py rename to archive/d-cleanup-20260820/advice_reconciliation.py diff --git a/deploy/profile-scripts/strategy_evaluator.py b/archive/d-cleanup-20260820/strategy_evaluator.py similarity index 100% rename from deploy/profile-scripts/strategy_evaluator.py rename to archive/d-cleanup-20260820/strategy_evaluator.py diff --git a/deploy/profile-scripts/mofin_db.py b/deploy/profile-scripts/mofin_db.py index b3a80af2..6815133d 100644 --- a/deploy/profile-scripts/mofin_db.py +++ b/deploy/profile-scripts/mofin_db.py @@ -2486,3 +2486,72 @@ def write_holding_strategy_versioned(conn, code, name, data, source_trigger="bat data['version'] = new_ver return write_holding_strategy(conn, code, name, data, source_trigger) + + +# ── recommendation_log 写入(重评时同步记录推荐)── +def log_recommendation(conn, strategy_id, code, data, source_trigger="batch_12d"): + """记录推荐历史:重评写入策略卡时同步调用""" + now = __import__('datetime').datetime.now().isoformat() + conn.execute(""" + INSERT INTO recommendation_log + (strategy_id, code, strategy_source, version, recommend_time, + action, entry_low, entry_high, stop_loss, take_profit, + position_advice, timing_signal, rr_ratio, reason) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?) + """, (strategy_id, code, data.get('strategy_source', ''), data.get('version', ''), + now, data.get('action', ''), data.get('entry_low', 0), data.get('entry_high', 0), + data.get('stop_loss', 0), data.get('take_profit', 0), + data.get('position_advice', ''), data.get('timing_signal', ''), + data.get('rr_ratio', 0), data.get('reason', ''))) + print(f" [RECOMMEND] {code} v{data.get('version','')} 记录推荐", flush=True) + + +# ── execution_log 写入(交易执行时记录)── +def log_execution(conn, code, action, shares, price, cost=None, source="trade_capture", recommendation_id=None): + """记录执行历史:trade_capture/import_holding_xls 等执行后调用""" + now = __import__('datetime').datetime.now().isoformat() + conn.execute(""" + INSERT INTO execution_log + (code, action, shares, price, cost, execute_time, source, match_recommendation_id) + VALUES (?,?,?,?,?,?,?,?) + """, (code, action, shares, price, cost, now, source, recommendation_id)) + print(f" [EXEC] {code} {action} {shares}股 @{price} source={source}", flush=True) + + +# ── 匹配推荐与执行(盘后评估用)── +def match_recommendations_executions(conn, code, strategy_id): + """找到策略推荐与实际执行的对应关系""" + recs = conn.execute( + "SELECT id, recommend_time, action, entry_low, entry_high FROM recommendation_log " + "WHERE code=? AND strategy_id=? ORDER BY recommend_time", (code, strategy_id)).fetchall() + execs = conn.execute( + "SELECT id, execute_time, action, shares, price FROM execution_log " + "WHERE code=? ORDER BY execute_time", (code,)).fetchall() + + matches = [] + for rec in recs: + rec_time = rec[1] + rec_action = rec[2] + # 找推荐之后最近的执行 + for exe in execs: + if exe[1] >= rec_time and exe[2] == rec_action: + matches.append({ + "recommendation_id": rec[0], + "recommend_time": rec[1], + "recommend_action": rec[2], + "execution_id": exe[0], + "execute_time": exe[1], + "execute_action": exe[2], + "shares": exe[3], + "price": exe[4], + "matched": True, + }) + break + else: + matches.append({ + "recommendation_id": rec[0], + "recommend_time": rec[1], + "recommend_action": rec[2], + "matched": False, + }) + return matches diff --git a/deploy/profile-scripts/strategy_effectiveness.py b/deploy/profile-scripts/strategy_effectiveness.py index 3c605b9b..45bb2be2 100644 --- a/deploy/profile-scripts/strategy_effectiveness.py +++ b/deploy/profile-scripts/strategy_effectiveness.py @@ -241,3 +241,43 @@ def main(): if __name__ == "__main__": main() + + +# ── 扩展评估:读取 recommendation_log + execution_log ── +def evaluate_with_logs(conn, strategy): + """用推荐记录+执行记录增强评估""" + sid = strategy[0] + code = strategy[1] + + # 读推荐记录 + recs = conn.execute( + "SELECT recommend_time, action, entry_low, entry_high, stop_loss, take_profit " + "FROM recommendation_log WHERE strategy_id=? ORDER BY recommend_time", (sid,)).fetchall() + + # 读执行记录 + execs = conn.execute( + "SELECT execute_time, action, shares, price FROM execution_log " + "WHERE code=? ORDER BY execute_time", (code,)).fetchall() + + # 匹配:推荐后是否有对应执行 + rec_followed = 0 + rec_not_followed = 0 + for rec in recs: + rec_time, rec_action = rec[0], rec[1] + found = False + for exe in execs: + if exe[0] >= rec_time and exe[1] == rec_action: + found = True + rec_followed += 1 + break + if not found: + rec_not_followed += 1 + + compliance_rate = rec_followed / (rec_followed + rec_not_followed) * 100 if (rec_followed + rec_not_followed) > 0 else 0 + + return { + "recommendations": len(recs), + "executions_matched": rec_followed, + "executions_missed": rec_not_followed, + "compliance_rate": round(compliance_rate, 1), + } diff --git a/deploy/profile-scripts/trade_capture.py b/deploy/profile-scripts/trade_capture.py index 41efa1b8..7515ccbb 100644 --- a/deploy/profile-scripts/trade_capture.py +++ b/deploy/profile-scripts/trade_capture.py @@ -150,6 +150,9 @@ def execute_stock(stock): pass if not result.get("action"): result["action"] = "无变更" + # 记录执行日志 + from mofin_db import log_execution + log_execution(conn, code, result.get("action",""), int(stock.get("shares",0) or 0), float(stock.get("price",0) or 0), source="trade_capture") conn.commit() conn.close()