diff --git a/deploy/profile-scripts/b_td1_v3_gen.py b/deploy/profile-scripts/b_td1_v3_gen.py new file mode 100644 index 00000000..bde21577 --- /dev/null +++ b/deploy/profile-scripts/b_td1_v3_gen.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +"""b_td1_v3 生成器(基于 DB 原版信号 + merge 查 score + 每日top-N截断)""" +import sys, json +sys.path.insert(0, "/home/hmo/MoFin") +import pandas as pd +import numpy as np +import sqlite3 + +TOP_N = 5 +SRC_VERSION = "b_td1" + + +def score_row(b, r, s, r5): + sc = 0 + if b is not None and not pd.isna(b): sc += 40 if b < -30 else 32 if b < -20 else 20 if b < -10 else 8 + if r is not None and not pd.isna(r): sc += 30 if r < 30 else 24 if r < 40 else 14 if r < 50 else 6 + if s is not None and not pd.isna(s): sc += 20 if s < -20 else 14 if s < -10 else 8 if s < 0 else 3 + if r5 is not None and not pd.isna(r5): sc += 10 if r5 < -25 else 7 if r5 < -15 else 4 if r5 < -8 else 1 + return sc + + +def gen_trades(start, end, top_n=TOP_N): + conn = sqlite3.connect("/home/hmo/MoFin/data/mofin.db", timeout=30) + r = conn.execute("SELECT results_json FROM strategy_research WHERE version=? AND period_tag='2y'", + (SRC_VERSION,)).fetchone() + conn.close() + if not r: + return [] + d = json.loads(r[0]) + trades = [t for t in d["trades"] if start <= t["entry_date"] <= end] + if not trades: + return [] + + # panel 特征用 merge(快) + panel = pd.read_pickle("/tmp/panel_12d.pkl") + pcols = ["bias60", "rsi", "sec_ret20", "ret5"] + pf = panel[["code", "date"] + pcols].copy() + pf["code"] = pf["code"].astype(str).str.zfill(6) + pf["date"] = pf["date"].astype(str) + + tdf = pd.DataFrame(trades) + tdf["code"] = tdf["code"].astype(str).str.zfill(6) + tdf["date"] = tdf["entry_date"].astype(str) + merged = tdf[["code", "date"]].merge(pf, on=["code", "date"], how="left") + merged["score"] = merged.apply(lambda r: score_row(r["bias60"], r["rsi"], r["sec_ret20"], r["ret5"]), axis=1) + score_map = dict(zip(zip(merged["code"], merged["date"]), merged["score"])) + for t in trades: + t["score"] = score_map.get((str(t["code"]).zfill(6), t["entry_date"]), 50) + t.setdefault("boost", 1.0) + + # 每日 top-N + from collections import defaultdict + by_day = defaultdict(list) + for t in trades: + by_day[t["entry_date"]].append(t) + chosen = [] + for dt in sorted(by_day): + chosen.extend(sorted(by_day[dt], key=lambda x: -x["score"])[:top_n]) + return chosen + + +def build_results(trades): + import copy, random + from strategy_lab import portfolio_sim + n = len(trades) + wins = [t for t in trades if t["profit_pct"] > 0] + wr = len(wins) / n * 100 if n else 0 + avg = sum(t["profit_pct"] for t in trades) / n if n else 0 + sim = portfolio_sim(trades, 1000000, max_positions=10) or {} + rets = [] + for seed in range(5): + t2 = copy.deepcopy(trades); rng = random.Random(seed); rng.shuffle(t2) + rets.append((portfolio_sim(t2, 1000000, max_positions=10) or {}).get("total_return_pct") or 0) + return {"summary": {"total_trades": n, "win_rate": round(wr, 1), "avg_profit_pct": round(avg, 2)}, + "portfolio": {"positions_taken": sim.get("positions_taken"), "positions_skipped": sim.get("positions_skipped"), + "total_return_pct": sim.get("total_return_pct"), "cagr_pct": sim.get("cagr_pct"), + "portfolio_max_dd_pct": sim.get("portfolio_max_dd_pct"), "capital_final": sim.get("capital_final")}, + "robustness": {"shuffle_total_return": rets, "spread_pp": round(max(rets) - min(rets), 1)}, + "trades": trades} + + +if __name__ == "__main__": + from datetime import datetime + DB = "/home/hmo/MoFin/data/mofin.db" + VERSION = "b_td1_v3" + windows = {"1y": ("2025-07-01", "2026-07-01"), "2y": ("2024-07-01", "2026-07-01"), + "5y": ("2021-07-01", "2026-07-01"), "10y": ("2016-01-01", "2026-07-01")} + conn = sqlite3.connect(DB, timeout=30) + now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + for pt, (s, e) in windows.items(): + trades = gen_trades(s, e) + if not trades: + print(f"[{pt}] 无信号") + continue + results = build_results(trades) + exist = conn.execute("SELECT id FROM strategy_research WHERE version=? AND period_tag=?", (VERSION, pt)).fetchone() + if exist: + conn.execute("UPDATE strategy_research SET results_json=? WHERE version=? AND period_tag=?", + (json.dumps(results, ensure_ascii=False), VERSION, pt)) + else: + conn.execute("""INSERT INTO strategy_research (version, name, summary, hypothesis, parent, config_json, results_json, period, created_at, market, period_tag, deprecated) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?)""", + (VERSION, "B组超跌·原池优选", "b_td1原池+score每日top5截断", + "老莫指正: v2超跌强化错误砍成交。改原池+score每日top5 → 信号-89%/成交-9%/比1.8", + "B组", json.dumps({"top_n": TOP_N, "src": SRC_VERSION}, ensure_ascii=False), + json.dumps(results, ensure_ascii=False), None, now, "a", pt, None)) + s2 = results["summary"]; p = results["portfolio"] or {} + _pt2 = p.get("positions_taken") or 0 + print(f"[{pt}] 信号{s2['total_trades']} 成交{_pt2} 比{s2['total_trades']/max(_pt2,1):.1f} " + f"胜率{s2['win_rate']}% 年化{p.get('cagr_pct') or 0}% 回撤{p.get('portfolio_max_dd_pct') or 0}% 洗牌差{results['robustness']['spread_pp']}pp") + conn.commit(); conn.close() + print(f"\n写入完成 (version={VERSION})")