feat: 择优激活规则(strategy_activation_selector)——质量分(综合分×普适有效年占比)排序+家族去重+出手上限,输出各温区应激活策略
This commit is contained in:
@@ -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")
|
||||
Reference in New Issue
Block a user