第 4 章 · 03 RSI:头肩顶形态识别


文档摘要

第 4 章 · 03 RSI:头肩顶形态识别 本节摘要:本节精读 RSI 策略——但作者对 RSI 这个指标本身毫不客气,开篇就表白"i believe its kinda bull shit(我其实觉得它有点扯)"。本节提供两套玩法:① 最朴素的 30/70 超买超卖阈值;② 在 RSI 序列上直接做头肩顶(head-shoulder)形态识别,找 7 个节点 m-n-l-j-k-o-i。我们重点讲清楚 递归实现的 Wilder 原版 smoothed moving average、 的 7 节点查找,以及那个让作者果断放弃背离策略的公案——发明人 Wilder 与其门徒 Cardwell 在"熊背离"上互相矛盾。

第 4 章 · 03 RSI:头肩顶形态识别

本节摘要:本节精读 RSI 策略——但作者对 RSI 这个指标本身毫不客气,开篇就表白"i believe its kinda bull shit(我其实觉得它有点扯)"。本节提供两套玩法:① 最朴素的 30/70 超买超卖阈值;② 在 RSI 序列上直接做头肩顶(head-shoulder)形态识别,找 7 个节点 m-n-l-j-k-o-i。我们重点讲清楚 smma 递归实现的 Wilder 原版 smoothed moving average、pattern_recognition 的 7 节点查找,以及那个让作者果断放弃背离策略的公案——发明人 Wilder 与其门徒 Cardwell 在"熊背离"上互相矛盾。还有个作者的小脾气:他故意把"head and shoulders"简写成"head-shoulder",因为**"拒绝给洗发水 Head&Shoulders 免费广告,而且那洗发水根本没用"**。

涉及源码:原项目 RSI Pattern Recognition backtest.py(约 390 行),数据 yfinance 拉取 FCAU(菲亚特克莱斯勒)2016-01-01 至 2018-01-01 日线。

⚠️ 注意:本节策略的形态识别是直接在 RSI 上做,不是在价格上。看代码前务必记住这一点,否则会把 df['Close'] 的比较误以为是"价格头肩",其实它只是用来定位节点坐标,真正的形态判据落在 RSI 曲线上。

学习目标

阅读完本节,你应当能够:

  1. 说清 RSI 的计算:14 日向上/向下变动分别平滑均线,RS=up/down,RSI=100-100/(1+RS)。
  2. 手写 smma 递归 smoothed moving average(Wilder 原版)的公式。
  3. 讲清楚 30/70 超买超卖阈值与背离策略的区别,以及为什么作者放弃背离。
  4. 还原 pattern_recognition 的 7 节点查找:m-n-l-j-k-o-i 各自的定位与条件。
  5. 解释 delta=0.2 与 head/shoulder multiplier=1.1 两个阈值的作用。
  6. 讲清楚"RSI 升 4 或持仓 5 天"的懒人退出法。

金融/数学原理

RSI:动量强弱的相对指标

RSI(Relative Strength Index,相对强弱指数)反映价格动量的强弱。作者推荐的资料是 TradingView Wiki——"its not like i work there and try to promote it, trading view wiki is a very detailed encyclopedia"。

计算逻辑(以默认 14 日为例):

  1. 取每日价格变动 delta = Close_t - Close_{t-1}。
  2. 把 delta 分成"向上"(delta>0,否则 0)和"向下"(delta<0 取绝对值,否则 0)两组。
  3. 分别对 up 和 down 做 14 日平滑均线(SMA / EMA / SMMA,版本不同结果略异)。
  4. RS = up 的均线 / down 的均线。
  5. RSI = 100 - 100 / (1 + RS)

RSI 恒在 0 到 100 之间。RS 越大(涨得多跌得少),RSI 越接近 100;RS 越小,RSI 越接近 0。

smma:Wilder 原版平滑

本节用的是 smoothed moving average(SMMA),作者特别标注"the authentic way(正宗做法)"。smma 函数(L43-51)是一个递归:

def smma(series, n): output = [series[0]] for i in range(1, len(series)): temp = output[-1] * (n-1) + series[i] output.append(temp / n) return output

写成递推公式:

SMMA_t = (SMMA_{t-1} * (n-1) + series_t) / n

这等价于一个 α=1/n 的指数加权移动平均(EMA),但种子值用 series[0] 而非前 n 项的 SMA。它是 RSI 发明人 J. Welles Wilder Jr. 在 1978 年《New Concepts in Technical Trading Systems》里用的原始平滑法,所以叫"正宗"。相比简单移动平均,SMMA 对近端数据更敏感、对远端数据缓慢衰减,更贴合"动量"的语义。

三种用法与一个被放弃的背离

TradingView Wiki 列了 RSI 的几种玩法:

  1. 超买超卖(overbought/oversold):最朴素。RSI > 70 视为超买(该回调,做空);RSI < 30 视为超卖(该反弹,做多)。本节的 signal_generation 就是这个。
  2. 背离(divergence):RSI 往上但价格往下(熊背离),或反过来(牛背离)。
  3. 失败摆动(failure swing):类似价格的双底/双顶,但在 RSI 上。

关于背离,有一段让作者彻底放弃它的公案:

"the inventor of rsi called wilder believes bearish rsi divergence creates a selling opportunity. but his protege cardwell believes bearish divergence only occurs in a bullish trend. so their ideas basically contradict to each other, i would undoubtedly give up on this bs divergence strategy."

发明人 Wilder 认为熊背离是卖出时机;他的门徒 Cardwell 却认为熊背离只在多头趋势中出现。师徒俩直接打架。作者的结论很干脆:"我果断放弃这个扯淡的背离策略。"——这是一个值得记住的方法论:当一个指标的"正确用法"连发明人和嫡系门徒都吵不清时,别用它

头肩顶:故意叫 head-shoulder

本节的主菜是头肩顶形态识别——但直接在 RSI 上做,不在价格上。头肩顶本来叫"head and shoulders",作者却全程写成"head-shoulder",理由是个冷幽默:

"i refused to do free marketing for the shampoo, cuz that shampoo doesnt work at all!"(我拒绝给那个洗发水做免费广告,因为那洗发水根本没用!)

Head & Shoulders 是宝洁旗下的知名洗发水品牌,与"头肩顶"英文同名。作者的小脾气让本节代码里所有变量名(headshoulder)都成了"洗发水梗"的载体。

算法与代码精读

策略①:30/70 超买超卖

最简单的版本在 signal_generation(L81-90):

def signal_generation(df, method, n=14): df['rsi'] = 0.0 df['rsi'][n:] = method(df['Close'], n=14) df['positions'] = np.select([df['rsi']<30, df['rsi']>70], [1, -1], default=0) df['signals'] = df['positions'].diff() return df[n:]

np.select 是关键:条件列表 [rsi<30, rsi>70] 对应值列表 [1, -1],都不满足则 default=0(空仓)。signals = positions.diff() 把"持仓方向"转成"执行动作"。这套写法极其紧凑,适合阈值类策略。

策略②:pattern_recognition 找 7 节点头肩顶

真正的主角是 pattern_recognition(L151-307)。它直接在 RSI 上找头肩顶,7 个节点 m-n-l-j-k-o-i:

n j o ← 左肩 头 右肩 / \ / \ / \ / \ / \ / \ m / \ l / \ k / \ i ← 左底 左颈底 颈底 右底 起点(右端)

(注:i 是迭代起点,在 RSI 序列右端;m 在最左端;头 j 是最高点;左右肩 n、o 次高;左右底及颈底 m、l、k 与 i 近似等高。)

关键超参:

period = 25 # 形态查找的回溯视野 delta = 0.2 # 两价"近似相等"的阈值 head = 1.1 # 头节点显著高于其他节点的倍数 shoulder = 1.1 # 肩节点显著高于底、又显著低于头的倍数

外层循环从 i = period+lag 开始,i 是右端起点。查找思路与 Bollinger Bands 的 W 底如出一辙——从 i 往回找,用 moveon 开关链短路:

第 1 步 找头节点 j(L220-226):在 [i-period, i) 区间取 Close 的最大值点作为头 j。要求"头显著高于起点":|Close[j] - Close[i]| > head*delta(即头比 i 高出 1.1×0.2=0.22 以上)。把 Close[i] 记为 bottom(底的参考价)。

第 2 步 找节点 k(L231-236):在 (j, i) 之间找一个价格近似等于 bottom 的点 k(|Close[k]-bottom| < delta)——这是右颈底。

第 3 步 找节点 l(L244-249):从 j 向左,找一个近似等于 bottom 的点 l——这是左颈底。注意方向是从 j 向左,为后续在 l 之左找肩节点 n 留空间。

第 4 步 找节点 m(L255-260):在 [i-period, l) 之间(从左往右)找一个近似等于 bottom 的点 m——这是左底。

第 5 步 找左肩 n(L267-273):在 [m, l) 之间取 Close 最大值点作为左肩 n。要求"肩显著高于底且显著低于头":

if (df['Close'][n] - bottom > shoulder*delta) and \ (df['Close'][j] - df['Close'][n] > shoulder*delta):

即 n 比 bottom 高出 0.22 以上,且 j 比 n 高出 0.22 以上——保证"头>肩>底"的层级。

第 6 步 找右肩 o(L281-289):在 (k, i) 之间找一个价格近似等于左肩 n 的点 o(|Close[o]-top| < delta,top=Close[n])。全部凑齐,在 i 处发 signals=-1(看空入场),并把 7 个节点行号存进 coordinates

懒人退出:RSI 升 4 或持仓 5 天

头肩顶是看空形态,入场后需要退出条件。作者的退出逻辑很"懒"(L298-305):

if entry_rsi != 0 and moveon == False: counter += 1 if (df['rsi'][i] - entry_rsi > exit_rsi) or (counter > exit_days): df.at[df.index[i], 'signals'] = 1 # 做多平仓 df['cumsum'] = df['signals'].cumsum() counter = 0 entry_rsi = 0

两个退出条件,任一触发就用 signals=1(做多)平掉空头:

  • exit_rsi=4:RSI 自入场以来上升超过 4(动量回升,看空失效)。
  • exit_days=5:持仓满 5 天(时间止损)。

作者很坦白:"this is a lazy way to exit the trade, cuz i dont wanna import indicators from other scripts……exiting trades based on rsi is definitely inefficient and unprofitable(这是懒人退场法,因为我不想从别的脚本引入指标……单靠 RSI 退场肯定低效又不赚钱)。"——他建议实战中引入 MACD 或布林带做退出。这种"把局限写在注释里"的诚实是本仓库一贯的风格。

图: RSI头肩

上图是 RSI 曲线上的头肩顶可视化:黄色 RSI 线、黑色 30/70 超买超卖区间、红色粗折线把 7 个节点 m-n-l-j-k-o-i 连起来,标注了 Shoulder / Head / Shoulder。红色倒三角是看空入场,绿色三角是平仓。

图: RSI震荡

第二张图是简单 30/70 策略的 RSI 振荡器:紫色 RSI 线在 30/70 区间内外游走,破 70 做空、破 30 做多。

图: RSI持仓

第三张图是持仓叠加在价格上,可对照两种策略的入场离场节奏。

🎯 反直觉发现:本节的反直觉不在公式,而在两个"作者脾气"。第一,发明人与门徒互相矛盾时,果断放弃该用法——Wilder 说熊背离是卖点,Cardwell 说熊背离只在多头趋势,作者一句"bs divergence"直接弃用。这是一个朴素却强大的方法论:当一个信号的语义连嫡系都吵不清,它就不该进你的策略。第二,指标可以"半信半疑地用"——作者嘴上说 RSI"kinda bull shit",却仍然为它写了 390 行的形态识别;这种"不迷信但不抛弃"的务实态度,正是量化交易者该有的工具观。

关键技巧

  1. SMMA 是 Wilder 原版:递推 SMMA_t = (SMMA_{t-1}*(n-1) + series_t)/n,种子用 series[0],等价于 α=1/n 的 EMA。
  2. np.select 写阈值策略极紧凑:条件列表 + 值列表 + default,一行实现 30/70 双向,比 if-else 链清爽。
  3. 7 节点查找用 moveon 开关链:与 Bollinger Bands 的 W 底同构,任一层失败则后续全跳过。
  4. delta=0.2 是"近似相等"阈值:|price_a - price_b| < delta 判定两节点等高;head/shoulder multiplier=1.1 是"显著高低"阈值,乘以 delta 得到 0.22 的显著差。
  5. 头节点用 idxmax 定位:df.index.get_loc(df['Close'][...].idxmax()) 把时间戳索引转成数值索引,便于后续 range 循环。
  6. 懒人退出靠 RSI 升幅 + 持仓天数:exit_rsi=4exit_days=5,作者自陈低效,建议实战引入 MACD/布林带。
  7. 形态识别在 RSI 上做,不在价格上:coordinates 存的是 RSI 序列的节点,但画图时叠加在 RSI 曲线上,别误读。
  8. cumsum 防重复入场:cumsum[i]==0 保证空仓才找形态,避免叠加仓位。

本节要点

  1. RSI = 100 - 100/(1+RS),RS = up 的 SMMA / down 的 SMMA;SMMA 是 Wilder 原版递归平滑。
  2. 最朴素玩法:30/70 超买超卖,np.select 一行实现。
  3. 背离策略被作者放弃——发明人 Wilder 与门徒 Cardwell 在熊背离上互相矛盾,"俩人打架,我果断弃用"。
  4. pattern_recognition 在 RSI 上找头肩顶 7 节点 m-n-l-j-k-o-i,用 delta=0.2 判近似相等、head/shoulder multiplier=1.1 判显著高低。
  5. 懒人退出:RSI 升 4 或持仓 5 天,作者自陈低效,建议引入别的指标。
  6. 方法论高潮:发明人与门徒矛盾时果断弃用;指标可"半信半疑地用"——不迷信但不抛弃。

第 4 章到此结束。我们学了三个策略,但最重要的不是公式,而是 Pair Trading 那句"不存在无风险的统计套利",以及 Bollinger Bands 那句"为什么用机器学习,当算术更快更简单"。下一章,我们将从线性的股票/期货世界,跨入非线性的期权世界——先看 Options Straddle 跨式期权如何从双向不确定中获利,再看 VIX Calculator 如何用黎曼和 + 泰勒展开把连续方差互换离散化。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U