この記事でわかること
- レギュラーダイバージェンスとヒドゥンダイバージェンスの定義と検出ロジック
- ta.pivothigh / ta.pivotlowを使ったスイングポイントベースの実装パターン
- RSIダイバージェンス検出器の完成コードとClaudeへのプロンプト設計
ダイバージェンス検出はPine Scriptの中でも実装難易度が高い部類に入ります。「価格とオシレーターが逆方向に動く」という概念をコードで表現するには、スイングポイントの追跡と比較が必要です。正しい構造のプロンプトを書けばClaudeは機能するコードを出してきます。
ダイバージェンスの4種類
| 種類 | 価格の動き | オシレーターの動き | シグナル |
|---|---|---|---|
| レギュラー弱気 | 高値が切り上がる | 高値が切り下がる | 上昇トレンドの弱体化・転換 |
| レギュラー強気 | 安値が切り下がる | 安値が切り上がる | 下降トレンドの弱体化・転換 |
| ヒドゥン弱気 | 高値が切り下がる | 高値が切り上がる | 下降トレンドの継続 |
| ヒドゥン強気 | 安値が切り上がる | 安値が切り下がる | 上昇トレンドの継続 |
実装の考え方:ピボットポイントを使う
ダイバージェンスの検出には「価格の直近スイングハイ/ロー」と「オシレーターの直近スイングハイ/ロー」を比較する必要があります。Pine Scriptのta.pivothigh()・ta.pivotlow()がこれに使えます。
重要:ピボットポイントのタイムラグta.pivothigh(high, 5, 5)は「左5本・右5本を見て判定する」ため、判定結果が返ってくるのは実際の高値から5本後のバーです。シグナルには必ずleftBarsの本数分の遅延があります。
Claudeへのプロンプト例
Pine Script v5で書いてください。インジケーター名は「RSI Divergence Detector」。overlay=false。
RSI(14)のダイバージェンスをta.pivothigh / ta.pivotlowで検出する。
スイング期間はinput.intで設定(デフォルト:左5・右5)
【検出するダイバージェンス】
1. レギュラー弱気:価格の直近2ピボットハイが切り上がり、RSIの対応するピボットハイが切り下がる
2. レギュラー強気:価格の直近2ピボットローが切り下がり、RSIの対応するピボットローが切り上がる
3. ヒドゥン弱気:価格↓+RSI↑(ピボットハイ)
4. ヒドゥン強気:価格↑+RSI↓(ピボットロー)
ダイバージェンス検出時にRSIチャートのピボット間をline.newで結ぶ(レギュラー弱気=赤、強気=緑、ヒドゥン弱気=橙、強気=ライム)
max_lines_count=500、max_labels_count=500。var変数はグローバルスコープで定義。
完成コード
//@version=5
indicator("RSI Divergence Detector", overlay=false, max_lines_count=500, max_labels_count=500)
rsiLen = input.int(14, "RSI期間", minval=1)
lbLeft = input.int(5, "左スイング期間", minval=1)
lbRight = input.int(5, "右スイング期間", minval=1)
showReg = input.bool(true, "レギュラーダイバージェンス", group="表示")
showHid = input.bool(true, "ヒドゥンダイバージェンス", group="表示")
rsiVal = ta.rsi(close, rsiLen)
priceH = ta.pivothigh(high, lbLeft, lbRight)
priceL = ta.pivotlow(low, lbLeft, lbRight)
rsiH = ta.pivothigh(rsiVal, lbLeft, lbRight)
rsiL = ta.pivotlow(rsiVal, lbLeft, lbRight)
var float prevPH = na
var float curPH = na
var float prevRL_h = na
var float curRL_h = na
var int prevPH_idx = na
var int curPH_idx = na
var float prevPL = na
var float curPL = na
var float prevRL_l = na
var float curRL_l = na
var int prevPL_idx = na
var int curPL_idx = na
if not na(priceH) and not na(rsiH)
prevPH := curPH
curPH := priceH
prevRL_h := curRL_h
curRL_h := rsiH
prevPH_idx := curPH_idx
curPH_idx := bar_index - lbRight
if not na(priceL) and not na(rsiL)
prevPL := curPL
curPL := priceL
prevRL_l := curRL_l
curRL_l := rsiL
prevPL_idx := curPL_idx
curPL_idx := bar_index - lbRight
bearDiv = showReg and not na(prevPH) and not na(curPH) and curPH > prevPH and curRL_h < prevRL_h
bullDiv = showReg and not na(prevPL) and not na(curPL) and curPL < prevPL and curRL_l > prevRL_l
hidBear = showHid and not na(prevPH) and not na(curPH) and curPH < prevPH and curRL_h > prevRL_h
hidBull = showHid and not na(prevPL) and not na(curPL) and curPL > prevPL and curRL_l < prevRL_l
plot(rsiVal, "RSI", color=color.new(color.blue, 0), linewidth=2)
hline(70, "過熱圏", color=color.red, linestyle=hline.style_dashed)
hline(50, "中立線", color=color.gray, linestyle=hline.style_dotted)
hline(30, "売られすぎ", color=color.green, linestyle=hline.style_dashed)
if bearDiv
line.new(prevPH_idx, prevRL_h, curPH_idx, curRL_h, color=color.red, width=2, style=line.style_solid)
label.new(curPH_idx, curRL_h, "Bearish Div", color=color.red, textcolor=color.white, style=label.style_label_down, size=size.small)
if bullDiv
line.new(prevPL_idx, prevRL_l, curPL_idx, curRL_l, color=color.green, width=2, style=line.style_solid)
label.new(curPL_idx, curRL_l, "Bullish Div", color=color.green, textcolor=color.white, style=label.style_label_up, size=size.small)
if hidBear
line.new(prevPH_idx, prevRL_h, curPH_idx, curRL_h, color=color.orange, width=1, style=line.style_dashed)
label.new(curPH_idx, curRL_h, "Hidden Bear", color=color.orange, textcolor=color.white, style=label.style_label_down, size=size.tiny)
if hidBull
line.new(prevPL_idx, prevRL_l, curPL_idx, curRL_l, color=color.lime, width=1, style=line.style_dashed)
label.new(curPL_idx, curRL_l, "Hidden Bull", color=color.lime, textcolor=color.black, style=label.style_label_up, size=size.tiny)
MACDダイバージェンスへの応用
RSIをMACDに置き換えるときは、ピボット検出に使うソースを変えるだけです。MACDラインをソースにする方が安定します。
[macdLine, signalLine, histLine] = ta.macd(close, 12, 26, 9)
macdH = ta.pivothigh(macdLine, lbLeft, lbRight)
macdL = ta.pivotlow(macdLine, lbLeft, lbRight)
精度を上げるATRフィルター
atrVal = ta.atr(14)
minSwing = input.float(0.5, "最小スイング幅(ATR倍率)")
bullDivFiltered = bullDiv and (curPL - prevPL) > atrVal * minSwing
「ダイバージェンスの有効判定にATRフィルターを追加して、価格スイングの幅がATR(14)の〇倍未満のときはシグナルを無視する」とClaudeに指示するだけで追加できます。
まとめ
ダイバージェンス検出のコードが複雑になる理由は「2つのスイングポイントを時間をまたいで比較する」操作にあります。var変数で直近2点のスイング値を保持し続け、新しいスイングが確認されるたびに更新する構造をプロンプトで明示すればClaudeは正確に実装してくれます。
ダイバージェンスシグナルをバックテストで検証したい場合はClaudeでTradingViewストラテジーを本格設計するを参照してください。SMC分析との組み合わせはPine Script v5でSMCを自動化するも参考にしてください。
