From 4af4bda7b32662fcac66f52c9f76c4d3a822de85 Mon Sep 17 00:00:00 2001 From: xxm Date: Tue, 18 Aug 2026 21:30:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20strategy=5Fdefs=E8=A1=A5trend=E5=AE=B6?= =?UTF-8?q?=E6=97=8F(v=5Fnext/v8.1=3D=E8=B6=8B=E5=8A=BF=E5=B8=82=E9=BE=99?= =?UTF-8?q?=E5=A4=B4=E5=9B=9E=E8=B0=83,=E6=98=A0=E5=B0=84leader=5Fscanner)?= =?UTF-8?q?+=E5=80=99=E9=80=89=E7=9B=AE=E5=BD=95=E6=9B=B4=E6=96=B0(600262?= =?UTF-8?q?=E5=88=87=E7=AD=96=E7=95=A5=E6=9C=89=E7=9B=AE=E6=A0=87=E4=BA=86?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/profile-scripts/add_trend_defs.py | 75 ++++++++++++++++++++++++ deploy/profile-scripts/batch_reassess.py | 4 +- 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 deploy/profile-scripts/add_trend_defs.py diff --git a/deploy/profile-scripts/add_trend_defs.py b/deploy/profile-scripts/add_trend_defs.py new file mode 100644 index 00000000..5c8c136a --- /dev/null +++ b/deploy/profile-scripts/add_trend_defs.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""补 strategy_defs 的 trend 家族卡(2026-08-18) +v_next / v8.1(trend_up 激活的顺势策略,映射 leader_scanner.py) +执行:/home/hmo/MoFin/venv/bin/python add_trend_defs.py +""" +import sqlite3, json, hashlib + +DB = "/home/hmo/MoFin/data/mofin.db" +conn = sqlite3.connect(DB, timeout=30) +conn.execute("PRAGMA busy_timeout=30000") + +def upsert(s): + hp = hashlib.md5((s.get("key_params") or "").encode()).hexdigest()[:12] + s2 = dict(s); s2["params_hash"] = hp; s2["updated_at"] = "2026-08-18 21:20:00"; s2["updated_by"] = "add_trend_defs.py" + cols = ",".join(s2.keys()); ph = ",".join("?" * len(s2)) + conn.execute( + f"INSERT INTO strategy_defs ({cols}) VALUES ({ph}) ON CONFLICT(strategy_name) DO UPDATE SET " + f"display_name=excluded.display_name, aliases=excluded.aliases, market=excluded.market, " + f"regime=excluded.regime, family=excluded.family, summary=excluded.summary, " + f"entry_logic=excluded.entry_logic, exit_logic=excluded.exit_logic, " + f"key_params=excluded.key_params, review_focus=excluded.review_focus, " + f"holding_style=excluded.holding_style, status=excluded.status, " + f"retired_reason=excluded.retired_reason, superseded_by=excluded.superseded_by, " + f"params_hash=excluded.params_hash, needs_review=excluded.needs_review, " + f"doc_ref=excluded.doc_ref, scanner_file=excluded.scanner_file, " + f"version=excluded.version, updated_at=excluded.updated_at, updated_by=excluded.updated_by", + [s2[k] for k in s2]) + print(f" + {s['strategy_name']} 已写入/更新") + +# v_next:趋势市龙头回调(trend_up 激活,映射 leader_scanner.py) +upsert(dict( + strategy_name="v_next", + display_name="趋势市龙头回调买点", + aliases=json.dumps(["v_next", "v_next4", "leader", "bull_trend"]), + market="A", regime="trend_up", + family="trend_momentum", + summary="牛市(trend_up)买入龙头股回调到 MA20 附近的健康回调(dist_ma20∈[-3%,+2%]、RSI 45-65 强势未超买、量能配合、行业趋势共振),不追高。", + entry_logic="dist_ma20∈[-3%,+2%](回调到MA20附近,不追高);RSI 45-65(强势但未超买);mcap_q>0.3(龙头非小盘);vol_ratio>0.8(量能配合);trend_aligned=1(趋势共振)。", + exit_logic="跌破 MA20 下方 8% 止损(MA20×0.92);+15% 止盈(MA20×1.15,压力位);买入区 MA20×0.98~MA20。", + key_params=json.dumps({"dist_ma20_range": [-3,2], "rsi_range": [45,65], "mcap_q_min": 0.3, "vol_ratio_min": 0.8, "trend_aligned": 1, "tp_pct": 0.15, "sl_pct": 0.08, "regime": "trend_up"}), + review_focus="①牛市温区是否延续(trend_up→其他=龙头策略失效,需切换)②回调买点是否仍成立(价格还在MA20附近吗)③RSI是否仍强势未超买④量能是否配合⑤跌破MA20止损位合理性", + holding_style="趋势持有(龙头主升浪,回调即买点)", + status="active", version="v_next", + scanner_file="leader_scanner.py", + doc_ref="", +)) + +# v8.1:波段先出再进(trend_up 激活的顺势波段) +upsert(dict( + strategy_name="v8.1", + display_name="顺势波段先出再进", + aliases=json.dumps(["v8.1", "v8"]), + market="A", regime="trend_up", + family="trend_momentum", + summary="趋势市顺势波段:上升趋势中回调买入,波段顶部先出、回踩再进(波段先出再进),吃趋势主升的波段利润。", + entry_logic="上升趋势(trend_up)中的回调买点;趋势共振 + 技术位支撑买入;避免追高。", + exit_logic="波段顶部先出(压力位/超买);回踩支撑再进;跌破趋势线止损。", + key_params=json.dumps({"regime": "trend_up", "mode": "swing_band"}), + review_focus="①趋势是否延续(trend_up)②当前在波段哪个位置(顶部该出/回调该进)③止损/止盈空间④温区切换风险", + holding_style="波段(顺势,先出再进)", + status="active", version="v8.1", + scanner_file="leader_scanner.py", + doc_ref="", +)) + +# 更新候选目录引用(batch_reassess 的 _CANDIDATES 加 v_next/v8.1) +# 在 parse_response 的 _CANDIDATES 里补 + +conn.commit() +n = conn.execute("SELECT COUNT(*) FROM strategy_defs").fetchone()[0] +print(f"\nstrategy_defs 共 {n} 张卡:") +for r in conn.execute("SELECT strategy_name, display_name, regime, status FROM strategy_defs ORDER BY strategy_name").fetchall(): + print(f" {r[0]} | {r[1]} | {r[2]} | {r[3]}") +conn.close() diff --git a/deploy/profile-scripts/batch_reassess.py b/deploy/profile-scripts/batch_reassess.py index d266d994..36b47937 100644 --- a/deploy/profile-scripts/batch_reassess.py +++ b/deploy/profile-scripts/batch_reassess.py @@ -378,7 +378,7 @@ def build_prompt(data): _strategy_sec = """【策略定义】(无策略记录) 来源策略未知。请基于技术形态/估值/资金特征判断该股当前最接近的策略画像,并在【策略判断】中说明; 若判断需要归类到某个策略,从以下候选中选择(禁止自创): -accumulation(主力建仓) / b_td1_v3(超跌原池优选) / v_mr(弱市深超跌) / hk_pe_mom(港股动量) / hk_pe_oversold(港股超卖) / p_oversold(预测超跌) / s2_panic(恐慌买强势) / leader(龙头回调)""" +accumulation(主力建仓) / b_td1_v3(超跌原池优选) / v_mr(弱市深超跌) / hk_pe_mom(港股动量) / hk_pe_oversold(港股超卖) / p_oversold(预测超跌) / s2_panic(恐慌买强势) / leader(龙头回调) / v_next(趋势龙头回调) / v8.1(顺势波段)""" _sit_sec = f"""【情势体检】(系统确定性计算,非LLM估计) 当前温区(A股): {_sit.get('regime_a','unknown')}(7天前: {_sit.get('regime_7d_ago','unknown')}) 温区突变: {'是' if _sit.get('regime_a') != _sit.get('regime_7d_ago') and _sit.get('regime_a') not in ('unknown','') else '否'} @@ -604,7 +604,7 @@ def parse_response(text): result["strategy_reason"] = re.sub(r'^\s*【策略失效理由】\s*', '', _sr).strip()[:200] # 更换策略归类(封闭集校验:必须在候选列表内) result["strategy_switch_to"] = "" - _CANDIDATES = ("accumulation", "b_td1_v3", "v_mr", "hk_pe_mom", "hk_pe_oversold", "p_oversold", "s2_panic", "leader") + _CANDIDATES = ("accumulation", "b_td1_v3", "v_mr", "hk_pe_mom", "hk_pe_oversold", "p_oversold", "s2_panic", "leader", "v_next", "v8.1") _st = _section_line("更换策略归类") if _st: _body = re.sub(r'^\s*【更换策略归类】\s*', '', _st).strip().lower()