From effed7d7cd158934f272d2a66c1e53e08cd1a6dc Mon Sep 17 00:00:00 2001 From: hmo Date: Wed, 29 Jul 2026 02:58:53 +0800 Subject: [PATCH] =?UTF-8?q?test:=20v9=E5=A4=9A=E5=91=A8=E6=9C=9F=E6=B6=88?= =?UTF-8?q?=E8=9E=8D(=E5=90=A6=E5=86=B3)=20=E2=80=94=20=E5=91=A8=E7=BA=BF?= =?UTF-8?q?=E8=B6=8B=E5=8A=BF=E8=BF=87=E6=BB=A4=E4=B8=8E=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E4=B9=B0=E5=85=A5=E9=80=BB=E8=BE=91=E5=86=B2=E7=AA=81(weekly?= =?UTF-8?q?=5Fup=3DTrue=E8=83=9C=E7=8E=8760.9%=20vs=20False=2078.4%)?= =?UTF-8?q?=EF=BC=8C=E8=AF=81=E6=8D=AE=E5=AD=98=E6=A1=A3=E4=B8=8D=E5=B9=B6?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- strategy_lab.py | 78 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/strategy_lab.py b/strategy_lab.py index 6221b87b..8021d6c4 100644 --- a/strategy_lab.py +++ b/strategy_lab.py @@ -242,6 +242,17 @@ STRATEGIES.update({ entry_overrides={"vol_ratio_min": 0.9, "vol_ratio_max": 2.0, "sector_slope_max": 1.0, "hl_only": True, "rsi_delta_min": 6}, exit_overrides={"tp_pct": None, "exit_mode": "swing", "sl_atr": 1.5, "max_hold_days": 40, "reentry_days": 10}), + # H组: 多周期维度 + "v9.0": _v40_branch("v9.0", "周线趋势过滤", + "v7.1 + 周线收盘须站上周线MA10(中期趋势向上才买)", + "L2多周期维度消融:日线级的回调买点若周线趋势已坏则是下跌中继;周线MA10上方=中期趋势完好", + entry_overrides={"vol_ratio_min": 0.9, "vol_ratio_max": 2.0, "sector_slope_max": 1.0, + "hl_only": True, "rsi_delta_min": 6, "weekly_up": True}), + "v9.1": _v40_branch("v9.1", "周线多头排列", + "v7.1 + 周线MA10>MA20(周线多头排列,更严的中期趋势要求)", + "比weekly_up更严的变体:不仅要求价在线上,还要求周线均线本身多头排列", + entry_overrides={"vol_ratio_min": 0.9, "vol_ratio_max": 2.0, "sector_slope_max": 1.0, + "hl_only": True, "rsi_delta_min": 6, "weekly_aligned": True}), }) @@ -396,6 +407,61 @@ def flow_ctx(code, date, bars_dates, idx): return f +# ══════════════════════════════════════════════════════ +# 多周期(周线)上下文 +# ══════════════════════════════════════════════════════ +_WEEKLY_CTX = {} # code -> sorted [(date, close, ma10w, ma20w)] + +def prepare_weekly_context(start_date, end_date): + """加载 stock_weekly,计算每周 MA10/MA20""" + global _WEEKLY_CTX + _WEEKLY_CTX = {} + conn = sqlite3.connect(DB_PATH) + try: + rows = conn.execute(""" + SELECT code, date, close FROM stock_weekly + WHERE date >= ? AND date <= ? ORDER BY code, date + """, (start_date, end_date)).fetchall() + except sqlite3.OperationalError: + rows = [] + finally: + conn.close() + from collections import defaultdict + by_code = defaultdict(list) + for code, d, close in rows: + by_code[code].append((d, close)) + for code, series in by_code.items(): + closes = [c for _, c in series] + out = [] + for i, (d, close) in enumerate(series): + ma10 = sum(closes[max(0, i-9):i+1]) / len(closes[max(0, i-9):i+1]) if i >= 4 else None + ma20 = sum(closes[max(0, i-19):i+1]) / len(closes[max(0, i-19):i+1]) if i >= 10 else None + out.append((d, close, ma10, ma20)) + _WEEKLY_CTX[code] = out + +def weekly_ctx(code, date): + """取 date 之前最近一根完整周线的状态""" + series = _WEEKLY_CTX.get(code) + if not series: + return {} + last = None + for d, close, ma10, ma20 in series: + if d < date: # 只用已完成的周线(不含当周) + last = (d, close, ma10, ma20) + else: + break + if not last: + return {} + _, close, ma10, ma20 = last + f = {} + if ma10: + f['weekly_up'] = close > ma10 # 周线站上MA10 = 中期趋势向上 + f['weekly_dist'] = round((close - ma10) / ma10 * 100, 2) + if ma10 and ma20: + f['weekly_aligned'] = ma10 > ma20 # 周线多头排列 + return f + + # ══════════════════════════════════════════════════════ # 入场过滤器 # ══════════════════════════════════════════════════════ @@ -437,6 +503,10 @@ def pass_filters(factors, filters): if not chk('flow_pct', filters.get('flow_pct_min'), filters.get('flow_pct_max')): return False if not chk('flow_5d', filters.get('flow_5d_min'), filters.get('flow_5d_max')): return False if not chk('flow_delta', filters.get('flow_delta_min'), filters.get('flow_delta_max')): return False + # 多周期 + if filters.get('weekly_up') and factors.get('weekly_up') is not True: return False + if filters.get('weekly_aligned') and factors.get('weekly_aligned') is not True: return False + if not chk('weekly_dist', filters.get('weekly_dist_min'), filters.get('weekly_dist_max')): return False return True @@ -500,6 +570,7 @@ def run_backtest(strategy_version, start_date, end_date, capital=1000000, save=T prepare_market_context(fetch_start, end_date) prepare_sector_context(start_date, end_date) prepare_flow_context(fetch_start, end_date) + prepare_weekly_context(fetch_start, end_date) conn = sqlite3.connect(DB_PATH) stocks = conn.execute(""" @@ -552,6 +623,8 @@ def run_backtest(strategy_version, start_date, end_date, capital=1000000, save=T # 资金面因子 fl = flow_ctx(code, date, None, i) factors.update(fl) + # 周线因子 + factors.update(weekly_ctx(code, date)) if pass_filters(factors, filters): atr_val = last.get('atr') or 0 @@ -866,9 +939,10 @@ def calc_summary(trades, capital): # ══════════════════════════════════════════════════════ ANALYZE_FACTORS = ['rsi', 'adx', 'macd_hist', 'roc', 'atr_pct', 'dist_ma20', 'vol_ratio', 'ma20_slope', 'macd_hist_delta', 'rsi_delta', 'mkt_slope', 'mkt_roc', - 'sector_change', 'sector_rank_pct', 'sector_slope', 'flow_pct', 'flow_5d', 'flow_delta', 'score'] + 'sector_change', 'sector_rank_pct', 'sector_slope', 'flow_pct', 'flow_5d', 'flow_delta', + 'weekly_dist', 'score'] BOOL_FACTORS = ['trend_aligned', 'hh_structure', 'hl_structure', 'adx_rising', - 'mkt_above_ma20', 'near_high_20d', 'sector_above_ma20'] + 'mkt_above_ma20', 'near_high_20d', 'sector_above_ma20', 'weekly_up', 'weekly_aligned'] def analyze_trades(strategy_version): conn = sqlite3.connect(DB_PATH)