feat: v_weak/v_oversold改mr引擎config——用run_mr_backtest高效回测(O(N)扫描替代compute_single_score), 扩展run_mr_backtest支持外部因子过滤(mkt_rsi/mkt_dd60/mcap_q/pe_q/news3/sec_ret20)

This commit is contained in:
hmo
2026-08-12 00:40:18 +08:00
parent 41847c8d3a
commit 614b4570d6
+52 -17
View File
@@ -334,15 +334,14 @@ STRATEGIES.update({
"hypothesis": "弱市深超跌反弹,六步方法论+12维框架定稿",
"parent": "v_mr",
"config": {
"entry": {"min_score": 45, "min_momentum": 8, "filters": {
"bias60_min": -35, "bias60_max": -20,
"rsi_max": 25,
"r5f_max": -3,
"dist_lo20_max": 5,
"require_close_up": True,
"vol_shrink_max": 1.0,
"mkt_above_ma20": False,
"mkt_adx_min": 25, "mkt_adx_max": 30,
"entry": {"min_score": 0, "min_momentum": 0, "filters": {}, "mr": {
# run_mr_backtest 原生支持:bias60/RSI/60日跌幅/20日动量/成交额/RSI回升/大盘模式
"bias_min": -35, "bias_max": -20, # 深超跌(MA60下方35%~20%
"rsi_max": 25, # 极度超卖
"ret_max": -5, # 60日跌超5%(单边下跌)
"mom20_max": 0, # 20日动量<=0(低动量)
"rsi_delta_min": 0, # RSI止跌回升
"mkt_mode": "sideways", # 大盘震荡市(ADX<25 非趋势)
}},
"exit": {"tp_pct": 0.30, "sl_pct": 0.12, "max_hold_days": 40},
"sizing": {"kelly": False},
@@ -356,14 +355,19 @@ STRATEGIES.update({
"hypothesis": "由果及因:预测超跌反弹",
"parent": "v_weak",
"config": {
"entry": {"min_score": 45, "min_momentum": 8, "filters": {
"mkt_rsi_max": 50,
"mkt_dd60_max": -5,
"mcap_q_max": 0.2,
"pe_q_max": 0.2,
"news3_min": 1,
"sec_ret20_max": 0,
"bias60_max": -20,
"entry": {"min_score": 0, "min_momentum": 0, "filters": {}, "mr": {
# run_mr_backtest 原生 + 外部因子(mkt_rsi/mkt_dd60/mcap_q/pe_q/news3/sec_ret20
"bias_max": -20, # 深跌
"rsi_max": 50, # 超卖
"ret_max": -10, # 60日跌超10%
"mom20_max": 0, # 低动量
"mkt_mode": "bear", # 弱市
"mkt_rsi_max": 50, # 大盘RSI<50(弱)
"mkt_dd60_max": -5, # 大盘距60日高点回撤>5%
"mcap_q_max": 0.2, # 小市值(全市场后20%分位)
"pe_q_max": 0.2, # 低PE(全市场后20%分位)
"news3_min": 1, # 3日内有新闻
"sec_ret20_max": 0, # 行业20日动量<=0(行业弱)
}},
"exit": {"tp_pct": None, "sl_pct": 0.05, "max_hold_days": 40},
"sizing": {"kelly": False},
@@ -2260,6 +2264,33 @@ def run_mr_backtest(strategy_version, start_date, end_date, capital=913000,
if mkt_above is True:
skip_stats['mkt'] += 1; i += 1; continue
# 8. 大盘RSI/回撤 + 外部因子(mcap_q/pe_q/news3/sec_ret202026-08-11 支持 v_oversold
if mr.get('mkt_rsi_max') is not None:
_mkt_rsi = mk.get('rsi')
if _mkt_rsi is None or _mkt_rsi > mr['mkt_rsi_max']:
skip_stats['mkt'] += 1; i += 1; continue
if mr.get('mkt_dd60_max') is not None:
_mkt_dd60 = mk.get('mkt_dd60')
if _mkt_dd60 is None or _mkt_dd60 > mr['mkt_dd60_max']:
skip_stats['mkt'] += 1; i += 1; continue
_ext = _get_external_factors(code, date)
if mr.get('mcap_q_max') is not None:
_mcap_q = _ext.get('mcap_q')
if _mcap_q is None or _mcap_q > mr['mcap_q_max']:
skip_stats['amount'] += 1; i += 1; continue
if mr.get('pe_q_max') is not None:
_pe_q = _ext.get('pe_q')
if _pe_q is None or _pe_q > mr['pe_q_max']:
skip_stats['amount'] += 1; i += 1; continue
if mr.get('news3_min') is not None:
_news3 = _ext.get('news3', 0)
if _news3 < mr['news3_min']:
skip_stats['amount'] += 1; i += 1; continue
if mr.get('sec_ret20_max') is not None:
_sec_ret20 = _ext.get('sec_ret20')
if _sec_ret20 is None or _sec_ret20 > mr['sec_ret20_max']:
skip_stats['amount'] += 1; i += 1; continue
# 次日开盘入场
if i + 1 >= len(bars):
skip_stats['next_open'] += 1; i += 1; continue
@@ -2299,6 +2330,10 @@ def run_mr_backtest(strategy_version, start_date, end_date, capital=913000,
'mkt_above_ma20': mkt_above,
'rsi_delta': round(rsi_delta, 2),
})
# 2026-08-11: 外部因子记录(v_oversold 12维因子可分析)
factors['mkt_rsi'] = mk.get('rsi')
factors['mkt_dd60'] = mk.get('mkt_dd60')
factors.update(_ext)
sc_ctx = sector_ctx(code, date)
factors['sector_change'] = sc_ctx.get('change')
factors['sector_rank_pct'] = sc_ctx.get('rank_pct')