feat: B组挖掘加模拟验证门槛——好果率提升须通过模拟交易(胜率≥50%/收益>0)才算候选,防止扫描噪声当策略
This commit is contained in:
@@ -84,6 +84,45 @@ def scan3(market, regime, panel, min_n=500):
|
||||
return results[:5]
|
||||
|
||||
|
||||
def _simulate_verify(market, regime, panel, cond, tp=10, sl=5, maxh=20):
|
||||
"""模拟验证:候选条件在目标温区的模拟交易胜率/收益
|
||||
返回 (trades, win_rate, avg_pnl) 或 None
|
||||
"""
|
||||
rm = load_regime_map(market)
|
||||
sub = panel.copy()
|
||||
sub["_regime"] = sub["date"].map(rm)
|
||||
sub = sub[(sub["_regime"] == regime) & cond].copy()
|
||||
if len(sub) < 200:
|
||||
return None
|
||||
sub = sub.sort_values(["code", "date"])
|
||||
trades = []
|
||||
for code, g in sub.groupby("code"):
|
||||
g = g.sort_values("date")
|
||||
idxs = list(g.index)
|
||||
for k, i in enumerate(idxs):
|
||||
fut = g.iloc[k+1:k+maxh+1]
|
||||
if len(fut) < 2:
|
||||
continue
|
||||
ep = g.loc[i, "close"]
|
||||
if ep <= 0:
|
||||
continue
|
||||
res = None
|
||||
for _, fb in fut.iterrows():
|
||||
if fb["close"] <= ep * (1 - sl / 100):
|
||||
res = -sl
|
||||
break
|
||||
if fb["close"] >= ep * (1 + tp / 100):
|
||||
res = tp
|
||||
break
|
||||
if res is None:
|
||||
res = (fut.iloc[-1]["close"] / ep - 1) * 100
|
||||
trades.append(res)
|
||||
if not trades:
|
||||
return None
|
||||
wins = [x for x in trades if x > 0]
|
||||
return len(trades), len(wins) / len(trades) * 100, sum(trades) / len(trades)
|
||||
|
||||
|
||||
def to_entry(cond_dict):
|
||||
entry = {}
|
||||
for feat, (op, val) in cond_dict.items():
|
||||
@@ -99,14 +138,33 @@ def mine(market="a", regimes=None):
|
||||
for rg in regimes:
|
||||
combos = scan3(market, rg, panel)
|
||||
for cond, n, rate, avg, extra in combos[:3]:
|
||||
cand = {
|
||||
"regime": rg, "market": market, "group": "B", "status": "candidate",
|
||||
"entry": to_entry(cond), "trades_est": n, "good_rate": rate,
|
||||
"avg60": avg, "excess_pp": extra,
|
||||
"hypothesis": f"[{rg}] 由果及因三因子: {list(cond.keys())} → 60日前20%占比{rate}%(超额+{extra}pp)",
|
||||
}
|
||||
out["candidates"].append(cand)
|
||||
print(f" [{rg}] {list(cond.keys())} n={n} 好果率{rate}% 超额+{extra}pp")
|
||||
# ── 模拟验证门槛(2026-08-16 教训:好果率≠能赚钱,须模拟胜率≥50%且收益>0)──
|
||||
# 构造条件 Series
|
||||
c = pd.Series(True, index=panel.index)
|
||||
for feat, (op, val) in cond.items():
|
||||
if feat not in panel.columns:
|
||||
c = None
|
||||
break
|
||||
c &= (panel[feat] < val) if op == "<" else (panel[feat] > val)
|
||||
verified = None
|
||||
if c is not None:
|
||||
verified = _simulate_verify(market, rg, panel, c)
|
||||
if verified:
|
||||
tn, twr, tavg = verified
|
||||
if twr < 50 or tavg <= 0:
|
||||
print(f" [{rg}] {list(cond.keys())} 模拟未达标(胜率{twr:.0f}%/均{tavg:.2f}%) 剔除", flush=True)
|
||||
continue
|
||||
cand = {
|
||||
"regime": rg, "market": market, "group": "B", "status": "verified",
|
||||
"entry": to_entry(cond), "trades_est": n, "good_rate": rate,
|
||||
"avg60": avg, "excess_pp": extra,
|
||||
"sim_trades": tn, "sim_win_rate": round(twr, 1), "sim_avg_pnl": round(tavg, 2),
|
||||
"hypothesis": f"[{rg}] 由果及因三因子: {list(cond.keys())} → 好果率{rate}% 模拟胜率{twr:.0f}%/均{tavg:.2f}%",
|
||||
}
|
||||
out["candidates"].append(cand)
|
||||
print(f" [{rg}] {list(cond.keys())} ✅模拟达标 胜率{twr:.0f}% 均{tavg:.2f}%", flush=True)
|
||||
else:
|
||||
print(f" [{rg}] {list(cond.keys())} 样本不足或条件无效 剔除", flush=True)
|
||||
return out
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user