feat: 择优激活规则(strategy_activation_selector)——质量分(综合分×普适有效年占比)排序+家族去重+出手上限,输出各温区应激活策略

This commit is contained in:
xxm
2026-08-17 04:01:26 +08:00
parent da1c6299ed
commit 2998bfb06f
@@ -0,0 +1,117 @@
# -*- coding: utf-8 -*-
"""择优激活规则 v2:修正上限逻辑——按质量排序选最优N个,家族去重,软上限
输出:每个温区应激活的策略(写入 strategy_weights.json 的 active"""
import sys, json, sqlite3
sys.path.insert(0, "/home/hmo/MoFin")
sys.path.insert(0, "/home/hmo/MoFin/deploy/profile-scripts")
import strategy_qualify as sq
MIN_YEARLY = 15 # 年化成交下限(低于此=机会不足)
MAX_YEARLY = 300 # 年化成交上限(软上限,质量优先)
FAMILIES = {
"s2_panic": "s2", "s2_panic_v2": "s2", "s2_panic_v3": "s2",
"v_lurk_v1": "vlurk", "v_lurk_v2": "vlurk", "v_lurk_v3": "vlurk",
"v_mr": "vmr", "v_mr2": "vmr", "v_mr3": "vmr", "v_mr4": "vmr",
"v_oversold": "vover", "v_weak": "vweak",
"b_td1": "b_td", "b_td1_v2": "b_td", "b_td1_v3": "b_td",
"v1.0": "v1", "v2.0": "v2", "v3.0": "v3", "v_next": "vnext",
"hk_pe_mom": "hkpe", "hk_pe_oversold": "hkpe", "hk_mr1": "hkmr", "hk_mr2": "hkmr",
}
def efficiency_factor(sig, pos):
ratio = sig / pos if pos else 99
if ratio <= 2: return 1.0
if ratio <= 5: return 0.9
if ratio <= 10: return 0.75
return 0.5
def get_strategy_info(version, market, regime):
conn = sqlite3.connect("/home/hmo/MoFin/data/mofin.db", timeout=10)
r = conn.execute(
"SELECT trades, positions_taken, win_rate, sharpe_ratio, profit_factor, total_return_pct, "
"portfolio_max_dd_pct, universality_score, universality_years, universality_valid_years "
"FROM strategy_regime_perf_by_period WHERE strategy=? AND market=? AND regime=? AND period_tag='2y'",
(version, market, regime)).fetchone()
dep = conn.execute("SELECT MAX(deprecated) FROM strategy_research WHERE version=? AND deprecated IS NOT NULL AND deprecated!=''", (version,)).fetchone()[0]
conn.close()
if not r:
return None
sig, pos, wr, sh, pf, ret, dd, univ, uyears, uvalid = r
pos = pos or 0
ret_c = min(ret or 0, 100) / 100 * 30
wr_c = (wr or 0) / 100 * 20
sh_c = min(max(sh or 0, 0), 20) / 20 * 20
pf_c = min(pf or 0, 5) / 5 * 15
dd_c = (1 - min(dd or 0, 50) / 50) * 15
n = sig or 0
conf = min(1, n / 40)
eff = efficiency_factor(sig, pos)
comp = round((ret_c + wr_c + sh_c + pf_c + dd_c) * conf * eff)
return {"sig": sig, "pos": pos, "comp": comp, "univ": univ or 0,
"uyears": uyears or 0, "uvalid": uvalid or 0, "dep": dep}
def select_best(regime, market='a'):
conn = sqlite3.connect("/home/hmo/MoFin/data/mofin.db", timeout=10)
rows = conn.execute("SELECT DISTINCT strategy FROM strategy_regime_perf_by_period WHERE market=? AND regime=?", (market, regime)).fetchall()
conn.close()
avail = sq.load_availability()
candidates = []
for (version,) in rows:
info = get_strategy_info(version, market, regime)
if not info or info["dep"]:
continue
if not avail.get(version, {}).get("available", False):
continue
try:
q = sq.evaluate_all_regimes(version, market=market)
qr = q.get(regime, {})
if not (qr.get("long_ok") and qr.get("mid_ok") and qr.get("short_ok")):
continue
except Exception:
continue
# 质量分 = 综合分 × 普适(有效年占比越高越好)
univ_ratio = (info["uvalid"] / info["uyears"]) if info["uyears"] else 0
quality = info["comp"] * (0.5 + 0.5 * univ_ratio)
candidates.append({"version": version, "info": info, "quality": round(quality, 1),
"family": FAMILIES.get(version, version)})
candidates.sort(key=lambda x: -x["quality"])
# 择优:家族去重 + 软上限
selected = []
used_fam = set()
total_yearly = 0
for c in candidates:
if c["family"] in used_fam:
continue
# 出手次数太少的不选(年化 < MIN_YEARLY/2
yearly = c["info"]["pos"] / 2 # 2y→年化
if yearly < 5:
continue
if total_yearly + yearly > MAX_YEARLY and selected:
break # 超上限停止
used_fam.add(c["family"])
selected.append(c)
total_yearly += yearly
return selected, total_yearly
if __name__ == "__main__":
print("# 择优激活建议(质量分=综合分×普适有效年占比)")
all_sel = {}
for market in ["a", "hk"]:
for regime in ["trend_down", "choppy", "trend_up"]:
sel, total = select_best(regime, market)
if sel:
print(f"\n## {market} {regime}: 年化总出手≈{total:.0f}")
for c in sel:
i = c["info"]
print(f" {c['version']:18} 质量{c['quality']:5.1f} 综合{i['comp']:4} 信号{i['sig']:5} 成交{i['pos']:4} 普适{i['univ']:4.0f}({i['uvalid']}/{i['uyears']}年)")
all_sel[(market, regime)] = [c["version"] for c in sel]
# 保存建议
with open("/tmp/activation_suggestion.json", "w") as f:
json.dump({f"{m}:{r}": v for (m, r), v in all_sel.items()}, f, ensure_ascii=False, indent=1)
print("\n# 建议已存 /tmp/activation_suggestion.json")