好久没写东西了,还是老规矩:源码免费放送,关注后后台回复:夺笋针 获取整套源码。
一、引子:为什么是这个形态
每个老股民都见过这种 K 线组合:
一只票 4 天内急跌 16%,前一天带长上影、缩量;第二天突然带长下影、阳线、收在 30 日均线上方、还突破了前一天的高点。
这是某信价值10万的指标——"破杀螺旋夺筹针",背后的逻辑相当扎实:
• 急跌 → 恐慌盘出清:4 日累计跌 16% 以上,套牢盘割肉 • 缩量长上影 → 主力试盘前一天:上影线长但量缩,主力在试探上方抛压 • 长下影 → 主力接货:下影线长说明下方有承接 • 阳线 + 突破前高 → 反转确认:主力愿意拉,不是单纯止跌 • 站在 MA30 上方 → 趋势不破:不是超跌反弹,是趋势修复
这套组合的精髓在于:用 8 个简单条件把"主力在底部悄悄接货"的形态量化出来。
下面,我们把它从某信公式翻译成 Python,用 3 年 800 根日线数据,跑了全 A 沪深 2311 只股票的回测。
二、公式原理
2.1 原始通达信公式
{==== 破杀螺旋夺筹针 v1(原始版)====}A:=REF(C,4)/C >= 1.2; {4日累计跌≥16.7%}B:=(H-MAX(C,O))/(H-L) > 0.4; {上影线>40%}V1:=V < REF(V,1); {当日量缩}G:=C > O; {阳线}V2:=A AND B AND V1;D:=REF(V2,1); {昨日触发V2}E:=(MIN(C,O)-L)/(H-L) > 0.4; {下影线>40%}F:=MAX(C,O) > REF(MAX(C,O),1); {突破前高}MA30:=MA(C,30);S:=MIN(C,O) > MA30; {K线主体>MA30}XG: D AND E AND F AND G AND S;8 个条件,互相配合,缺一不可:
| A | REF(C,4)/C ≥ 1.2 | |
| B | (H-MAX(C,O))/(H-L) > 0.4 | |
| V1 | V < REF(V,1) | |
| V2 | A ∩ B ∩ V1 | |
| D | REF(V2,1) | |
| E | (MIN(C,O)-L)/(H-L) > 0.4 | |
| F | MAX(C,O) > REF(MAX(C,O),1) | |
| G | C > O | |
| S | MIN(C,O) > MA30 |
2.2 Python 转译(pandas 向量化)
把 某信的源码逐行循环翻译成 pandas 之后,2311 只股票的回测 7.8 秒跑完:
def signal_v1(df: pd.DataFrame) -> pd.Series: """原版(来自 TDX 代码)""" C, O, H, L, V = df['close'], df['open'], df['high'], df['low'], df['volume'] hl = (H - L).replace(0, np.nan) A = (C.shift(4) / C) >= 1.20 # 4日累计跌≥16.7% B = ((H - np.maximum(C, O)) / hl) > 0.4 V1 = V < V.shift(1) # 量缩 G = C > O # 阳线 V2 = (A & B & V1).fillna(False) D = V2.shift(1).fillna(False) # 昨日V2 E = ((np.minimum(C, O) - L) / hl) > 0.4 F = np.maximum(C, O) > np.maximum(C, O).shift(1) MA30 = C.rolling(30).mean() S = np.minimum(C, O) > MA30 return (D & E & F & G & S).fillna(False)2.3 优化版 V2:放宽 A + 加量 + 改 S
V1 信号太稀疏(2311 只票 3 年只出 27 个),我们做了 3 处微调:
| A | ≥ 1.20 | ≥ 1.10 | |
| F | |||
| S | MIN(C,O) > MA30 | C > MA30 |
改动之后,信号数从 27 暴增到 95,覆盖度 3.5 倍。
三、数据获取
3.1 候选池:2311 只沪深主板+创业板+科创板
def get_universe(): """剔除 ST、北交所、上市<1年""" df = client.stocks() # 通达信全市场清单 df = df[~df['name'].str.contains('ST', na=False)] df = df[df['code'].str.match(r'^(6|0|3)')] # 沪/深主板+创业板+科创板 df = df[~df['code'].str.startswith('8')] # 剔北交所 df = df[df['code'].str.len() == 6] df = df.drop_duplicates(subset='code') return df['code'].tolist()最终池:2680 只 → 活跃 2311 只(最近 1 年有数据)。
3.2 双路数据源
为了不被单一接口限流卡死,我们接了 2 路:
| mootdx | ||
| 腾讯财经 K 线接口 |
腾讯接口地址:
https://web.ifzq.gtimg.cn/appstock/app/fqkline/get?param=sz000001,day,,,800,qfq返回 6 字段:[日期, 开, 收, 高, 低, 成交量]。
3.3 并发拉取 + 本地缓存
def fetch_all(codes, max_workers=6, sleep_per_request=0.1): """6 线程并发 + 0.1s 间隔 + 缓存到本地""" with ThreadPoolExecutor(max_workers=max_workers) as ex: futures = {ex.submit(worker, c): c for c in codes} for fut in as_completed(futures): code, df = fut.result() # 已下载直接读缓存实际跑出来的速度:2311 只 159.4 秒,16.81 只/秒,每个工作日 1 拉全量。
本地缓存路径:cache/klines/{code}.pkl,首次拉取后 0 秒复用。
四、回测设计
4.1 评估维度
每检测到一个 XG 信号,记录"信号日 + T+1/T+3/T+5/T+10/T+20" 5 个未来收益:
for hd in [1, 3, 5, 10, 20]: ret[hd] = (df['close'].iloc[pos+hd] / df['close'].iloc[pos] - 1) * 100然后聚合出 胜率 / 均值 / 中位 / 盈亏比 / 累加收益 这 5 个核心指标。
4.2 关键差异:V1 vs V2
| A 阈值 | ||
| F 加量 | ||
| S 严格度 | ||
| 3 年信号数 |
五、回测结果
5.1 完整数据(5 年 2311 只票)
| 51.9% | +35.0% | |||
| 46.2% | +156.4% | |||
5.2 V2 信号时间分布
2021 年 1 个2023 年 14 个2024 年 29 个2025 年 27 个2026 年 24 个(截至 6/11)─────────────────────合计 95 个 / 92 只股票注:5 年内单只股票平均被识别 1 次,最多的也就 2-3 次。这个形态本身就稀有。
5.3 V2 T+10 表现最佳的 5 笔
| T+10 | ||||||
|---|---|---|---|---|---|---|
| +89.8% | ||||||
| +48.9% | ||||||
| +45.1% | ||||||
| +42.2% | ||||||
| +22.3% |
5.4 V2 T+10 表现最差的 5 笔
| T+10 | ||||||
|---|---|---|---|---|---|---|
| -30.4% | ||||||
| -26.6% | ||||||
| -20.0% | ||||||
| -17.3% | ||||||
| -17.2% |
5.5 5 个关键洞察
1. V1 适合 T+3 短线:胜率 51.9% + 累加 +35%,但 T+20 累加 -144.3%,信号珍稀 + 持有期短才能发挥威力 2. V2 优化版适合 T+10 中线:累加 +156.4% 是真正的亮点,单笔最大 +89.84%,这是能拿来用的版本 3. 两个版本 T+20 都崩:本质是短线反弹/中线反转,不是长线持有逻辑,别贪 4. V2 T+1 是负的(-35.6%):绝对不能次日开盘就跑,要么等 3 天要么拿 10 天 5. 亏损票的 T+1 多数是负的:从最差 5 笔看,入场即亏的概率不低,必须接受小止损
六、程序使用方法
6.1 目录结构
posha_backtest/├── 01_formula_test.py # 公式转译 + 单股验证├── 02_diagnose.py # 单股逐条件触发率诊断├── 03_full_backtest.py # 全 A 沪深批量回测(核心)├── 04_report.py # 生成最终报告├── requirements.txt # 依赖└── README.md # 本文件6.2 环境依赖
pip install -r requirements.txt# 依赖:pandas numpy mootdx6.3 推荐运行顺序
# Step 1: 单股快速验证(30 秒,确认公式转译正确)python 01_formula_test.py# Step 2: 单股逐条件诊断(10 秒,验证逻辑无误)python 02_diagnose.py# Step 3: 全 A 批量回测(首次 3 分钟,二次 30 秒)python 03_full_backtest.py# → 输出 cache/results.pkl + signals_v1.csv + signals_v2.csv# Step 4: 生成可读报告python 04_report.py6.4 自定义参数
03_full_backtest.pyget_universe() | |
signal_v1/v2() | |
signal_v2()F = F & (V > V.shift(1)) | |
evaluate_signals(hold_days_list=...) | |
fetch_all(max_workers=6, sleep_per_request=0.1) | |
_tencent_kline(),返回同样的 DataFrame 即可 |
6.5 输出文件说明
cache/universe.pkl | |
cache/klines/{code}.pkl | |
cache/results.pkl | |
cache/signals_v1.csv | |
cache/signals_v2.csv |
七、风险与展望
7.1 风险提示
• 历史回测 ≠ 未来表现:5 年 +156% 是回测结果,样本外 / 实盘 / 不同市况都可能有偏差 • 信号稀薄:95/2311 = 4.1% 命中率,5 年才 95 个,对资金容量有限制 • T+20 都崩:不是长线策略,持仓周期务必控制 • 科创板占优:盈利 5 笔里有 4 笔是 688 开头科创板,可能与样本期内科创板弹性大有关,未来注册制下需重新评估 • 未做大盘过滤:V2 没接沪深 300 指数的 MA20 过滤,牛熊市混合统计
7.2 后续可做工作
• 加大盘过滤: signal_v3()已留占位,要求"沪深 300 在 MA20 上方"才进场• 分行业 / 分市值回测:拆出 688(科创)/ 300(创业板)/ 主板分别看 • 接实盘监控:每日收盘后跑一遍 signal_v2(),把新触发票推到企业微信• 接 policy_trading_system:把每日新增的 V2 信号并入现有选股 cron • 多公式组合:V1(短线)+ V2(中线)组合持仓,覆盖更多市场环境
八、写在最后
"破杀螺旋夺筹针"这个形态,本身就是民间老股经验的总结。我们用 Python + pandas + 一份腾讯财经的免费 K 线接口,把它做成了可批量验证、可回测、可监控的量化公式。
整个项目不到 300 行代码,跑完全 A 沪深 2311 只票只要 3 分钟。
公式的精髓不在于"找黑马",而在于用 8 个简单条件把"主力在底部悄悄接货"这件模糊的事说清楚。
代码已开源,文章已开源,剩下的就是拿你自己的仓位去验证。后台回复:夺笋针 获取全部回测源码。
夜雨聆风