#!/usr/bin/env python3 """ fix_historical_pnl.py — 修正历史paper_trades中虚高的pnl_r 问题:TP/SL_BE场景用了硬编码倍数(1.5R/2.25R),实际应为0.75R/1.125R 修复:用(exit_price - entry_price) / risk_distance 重算 """ import os, sys sys.path.insert(0, os.path.dirname(__file__)) from db import get_sync_conn FEE_RATE = 0.0005 # Taker 0.05% def fix(): with get_sync_conn() as conn: cur = conn.cursor() # 读取所有已平仓记录 cur.execute(""" SELECT id, direction, entry_price, exit_price, tp1_price, tp2_price, sl_price, tp1_hit, status, pnl_r, atr_at_entry FROM paper_trades WHERE status NOT IN ('active', 'tp1_hit') ORDER BY id """) rows = cur.fetchall() fixed = 0 for row in rows: pid, direction, entry, exit_p, tp1, tp2, sl, tp1_hit, status, old_pnl, atr_entry = row if entry is None or atr_entry is None or atr_entry <= 0: continue risk_distance = 2.0 * 0.7 * atr_entry if risk_distance <= 0: continue # 实盘模拟:TP/SL以限价单价格成交 new_exit = exit_p # 默认不变 if status == "tp": new_exit = tp2 # TP以TP2价成交 if direction == "LONG": tp1_r = (tp1 - entry) / risk_distance tp2_r = (tp2 - entry) / risk_distance else: tp1_r = (entry - tp1) / risk_distance tp2_r = (entry - tp2) / risk_distance new_pnl = 0.5 * tp1_r + 0.5 * tp2_r elif status == "sl_be": new_exit = sl # SL_BE以SL价成交(成本价附近) if direction == "LONG": tp1_r = (tp1 - entry) / risk_distance else: tp1_r = (entry - tp1) / risk_distance new_pnl = 0.5 * tp1_r elif status == "sl": new_exit = sl # SL以SL价成交 if direction == "LONG": new_pnl = (sl - entry) / risk_distance else: new_pnl = (entry - sl) / risk_distance elif status == "timeout": new_exit = exit_p # 超时市价平仓,保持原exit if direction == "LONG": move = exit_p - entry else: move = entry - exit_p new_pnl = move / risk_distance if tp1_hit: tp1_r = abs(tp1 - entry) / risk_distance new_pnl = max(new_pnl, 0.5 * tp1_r) elif status == "signal_flip": new_exit = exit_p # 信号翻转市价平仓 if direction == "LONG": new_pnl = (exit_p - entry) / risk_distance else: new_pnl = (entry - exit_p) / risk_distance else: continue # 扣手续费 fee_r = (2 * FEE_RATE * entry) / risk_distance if risk_distance > 0 else 0 new_pnl -= fee_r new_pnl = round(new_pnl, 4) need_update = abs(new_pnl - old_pnl) > 0.001 or (new_exit and exit_p and abs(new_exit - exit_p) > 0.0001) if need_update: print(f" #{pid} {status:10s} {direction:5s}: pnl {old_pnl:+.4f}R → {new_pnl:+.4f}R | exit {exit_p} → {new_exit}") cur.execute("UPDATE paper_trades SET pnl_r = %s, exit_price = %s WHERE id = %s", (new_pnl, new_exit, pid)) fixed += 1 conn.commit() # 汇总 cur.execute("SELECT COUNT(*), SUM(pnl_r) FROM paper_trades WHERE status NOT IN ('active','tp1_hit')") total, total_pnl = cur.fetchone() print(f"\n修正了 {fixed}/{len(rows)} 笔交易") print(f"修正后总计: {total}笔, 总pnl={total_pnl:+.2f}R") if __name__ == "__main__": fix()