fix: historical pnl_r correction script
This commit is contained in:
parent
45bad25156
commit
d351949a7c
98
backend/fix_historical_pnl.py
Normal file
98
backend/fix_historical_pnl.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#!/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 exit_p is None or 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
|
||||||
|
|
||||||
|
# 重算pnl_r
|
||||||
|
if status == "tp":
|
||||||
|
# 半仓TP1 + 半仓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":
|
||||||
|
# TP1半仓锁定 + SL_BE半仓归零
|
||||||
|
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":
|
||||||
|
# 全仓止损
|
||||||
|
if direction == "LONG":
|
||||||
|
new_pnl = (exit_p - entry) / risk_distance
|
||||||
|
else:
|
||||||
|
new_pnl = (entry - exit_p) / risk_distance
|
||||||
|
elif status == "timeout":
|
||||||
|
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":
|
||||||
|
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)
|
||||||
|
|
||||||
|
if abs(new_pnl - old_pnl) > 0.001:
|
||||||
|
print(f" #{pid} {status:10s} {direction:5s}: {old_pnl:+.4f}R → {new_pnl:+.4f}R (Δ{new_pnl-old_pnl:+.4f})")
|
||||||
|
cur.execute("UPDATE paper_trades SET pnl_r = %s WHERE id = %s", (new_pnl, 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()
|
||||||
Loading…
Reference in New Issue
Block a user