OBVとドンチャンチャネルを組み合わせる考え方 — 価格と出来高の「同時更新」でブレイクを確かめる
「直近20本の高値を終値が超えた」という事実は、コードにすれば真偽値ひとつで表せます。けれども、その更新に市場参加者の厚みが伴っていたのか、薄い時間帯に価格だけが押し出されたのかは、真偽値からは読み取れません。本記事では、水準の更新を検知する ドンチャンチャネル と、出来高を方向つきで累積する OBV(オンバランスボリューム) を組み合わせる設計を整理します。
特徴は、OBVを「傾き」で読むのではなく、OBV自身にもドンチャンチャネルと同じ「直近N本の高値・安値」の判定を当てる点です。価格とOBVがそろって自分の直近高値を更新したかを見ることで、ブレイクアウトの候補を「裏付けあり」と「保留」に仕分けます。ブレイクアウト系のcBotを組んでいて、ダマシの扱いに悩んでいる方に向けた内容です。特定の設定が成果を約束するものではなく、判定の組み立て方を共有することが目的です。
ドンチャンチャネルの役割 — 更新を検知する層
ドンチャンチャネルは、直近N本の最高値と最安値、その中点で構成されるチャネルです。基礎はドンチャンチャネルとはで扱っています。
この構成でチャネルに任せるのは、「価格が水準を抜けたか」を確定させることだけです。計算に平滑化を含まず、価格の最大値・最小値から直接求まるため、判定条件にあいまいさが残りません。いつ候補が立ったのかを後から検証しやすいのも、ブレイクアウト系ロジックの土台として扱いやすい理由です。
単独使用時の限界は、更新が起きた「理由」を持たない点にあります。まとまった注文が続いて水準を押し上げたのか、流動性の薄い時間帯に数ティックはみ出しただけなのか、チャネルからは区別がつきません。後者のような更新はすぐに押し戻されやすく、いわゆるダマシの典型的な形になります。この構造はブレイクアウトとダマシの基本でも整理しています。
OBVの役割 — 参加度を累積する層
OBVは、終値が前の足より高ければその足の出来高を加算し、低ければ減算する、という規則で積み上げた累積値です。価格の値幅ではなく、どちら向きの足にどれだけ出来高が乗ったかを線として残します。
この構成でOBVに任せるのは、価格の更新に参加度の裏付けがあるかを確かめる係です。価格が高値を更新した場面で、OBVも同じように積み上がっていれば、その更新に出来高が伴っていたと読めます。
限界は3つあります。第一に、OBVの値そのものには意味がありません。累積の起点によって水準が変わるため、「いくつ以上なら強い」という読み方はできません。第二に、足の方向は終値の比較だけで決まるため、大きな出来高を伴う1本が以後の形を長く支配します。第三に、cTraderを含むFX/CFD環境で使えるのは多くの場合ティックボリューム(一定時間内の値動きの回数)であり、取引所の実出来高とは性質が異なります。ブローカーによって値の出方も変わるため、参加度の近似として控えめに扱うのが妥当です。OBVを傾きで読む使い方はOBVと移動平均の組み合わせで解説しています。
なぜこの組み合わせか
両者をつなぐ鍵は、同じ物差しを2つの系列に当てることです。ドンチャンチャネルの「直近N本の高値を超えたか」という判定は、価格にしか使えないものではありません。OBVの系列に同じ判定を当てれば、「参加度が直近N本で最も積み上がった状態になったか」を真偽値で得られます。
この2つの真偽値を並べると、上方向の更新は次の4通りに分類できます。
- 価格もOBVも更新: 水準の更新に参加度の積み上がりが伴っている状態。裏付けのある候補として扱います
- 価格だけ更新: 値は抜けたが、出来高の後押しが直近の山に届いていない状態。保留としてログに残します
- OBVだけ更新: 価格はまだ水準の内側だが、参加度が先に積み上がっている状態。判定には使わず、観察の材料に留めます
- どちらも更新なし: 判定の対象外です
下方向は安値側で同じ構造になります。価格側の更新を入口の条件、OBV側の更新を通過条件とする2段の関係で、チャネル単独では区別できなかった更新を仕分けるノイズフィルターとして機能することが期待できます。
この判定には、OBVの弱点をひとつ打ち消す性質もあります。累積の起点がずれても、同じ窓の中のすべての値に同じ量が上乗せされるだけなので、「直近N本の中で最大か」という比較の結果は変わりません。絶対値に意味がないというOBVの性質と、相対比較だけを行うチャネルの判定は、相性がよいと考えられます。
一方で、トレードオフも明記しておきます。ティックボリュームは値動きが活発なほど増えやすく、価格の更新とOBVの更新はもともと同時に起きやすい関係にあります。2つの情報源が完全に独立しているわけではないため、確認を1つ足したというより、既存の材料に別の角度から条件をかけたと理解するのが実態に近いと考えられます。また、OBVの更新が1〜2本遅れて付いてくる場面もあるため、「同じ足で同時」を厳密に要求すると候補が極端に減ります。どこまでのずれを許容するかは設計上の論点になります。
パラメータをどう考えるか
決めるのは、価格側のチャネル期間、OBV側のチャネル期間、そして両者の更新のずれを許容する本数の3つです。いずれも唯一の正解はなく、方向性だけを整理します。
価格側のチャネル期間は、短くすれば更新が頻繁に起き、長くすれば回数が減る代わりに1回の意味が重くなる傾向があります。OBV側のチャネル期間は、まず価格側と同じ値にそろえるのが素直な出発点です。同じ窓で比べることで、「同じ期間の中で価格も参加度も山を越えた」という読み方が一貫します。OBV側を短くすると確認が緩くなり、長くすると厳しくなります。
許容本数は、ゼロなら同じ足での同時更新だけを通し、増やすほどOBVの先行や遅行を許す形になります。広げすぎると、関係の薄い過去のOBV更新まで裏付けとして拾ってしまう点に注意が必要です。
時間足の違いも無視できません。短い時間足ほどティックボリュームの偏りが時間帯の影響を受けやすく、流動性の薄い時間帯ではOBVの山が形成されにくくなります。どの組み合わせを採るにしても、バックテストでは3つを同時に動かさず、1つずつ固定して挙動を確かめる進め方が現実的です。
cBot化する際の考慮点
cTraderのcBotとして実装する場合の急所を挙げます。
当該足を除いた水準で比べる: 判定する足を含めて直近N本の最大値を取ると、その足の値が最大値そのものになり、比較の意味が崩れます。価格側もOBV側も、判定する足の手前N本で確定した水準と比べる形にそろえます。
OBVの取得方法を決める: cTraderには組み込みの Indicators.OnBalanceVolume があり、終値系列を渡すだけで利用できます。自前で累積する場合は、確定足ごとに1回だけ加減算し、二重加算や取りこぼしが起きないよう更新をOnBarに一本化します。どちらの場合も、読み込んだ履歴の長さで絶対値が変わる点は前提として扱います。
許容本数を状態として持つ: OBVの更新が価格より先に起きた場合に備え、「最後にOBVが上方向・下方向に更新した足」をそれぞれ記録しておきます。価格の更新が起きた足で、その記録が許容本数以内かを確認します。
履歴とデータ品質のガード: チャネル期間の長いほうに数本の余裕を加えた本数が揃うまで、判定を保留します。週明けの窓開けや、ティックボリュームが極端に少ない足が続く時間帯は、除外するフィルターを別に用意しておくと挙動が読みやすくなります。
// 抜粋: 価格とOBVに同じ「手前N本の高値」判定を当てる(上方向のみ)
private OnBalanceVolume _obv;
private int _lastObvBreakUpBar = -1;
protected override void OnStart()
{
_obv = Indicators.OnBalanceVolume(Bars.ClosePrices);
}
// 判定対象の足(Last(1))を含めず、その手前N本の最大値を返す
private static double PriorHigh(DataSeries series, int period)
{
double high = double.MinValue;
for (int i = 2; i <= period + 1; i++)
high = Math.Max(high, series.Last(i));
return high;
}
protected override void OnBar()
{
if (Bars.Count < Math.Max(PriceChannelPeriod, ObvChannelPeriod) + 5) return;
double obvNow = _obv.Result.Last(1);
if (double.IsNaN(obvNow)) return;
int bar = Bars.Count - 2; // 判定対象の確定足
if (obvNow > PriorHigh(_obv.Result, ObvChannelPeriod))
_lastObvBreakUpBar = bar; // 先行したOBV更新も記録
bool priceBreakUp = Bars.ClosePrices.Last(1) > PriorHigh(Bars.HighPrices, PriceChannelPeriod);
bool obvConfirmed = _lastObvBreakUpBar >= 0 && bar - _lastObvBreakUpBar <= SyncWindow;
// priceBreakUp && obvConfirmed → 裏付けあり候補
// priceBreakUp && !obvConfirmed → 保留としてログに残す
}
計算負荷はチャネル期間ぶんのループが2本増える程度で、OnBarでの運用なら問題になりにくい水準です。保留に回った更新もログに残しておくと、後から許容本数を見直す材料になります。
実装の流れ
処理は、更新する → 記録する → 検知する → 確かめる → 出力する、の一方向に整理できます。
- データ更新: 確定足を受け取り、価格系列とOBVを1本ぶん進めます
- OBVの記録: OBVが自身の手前N本の高値・安値を更新していれば、その足を方向ごとに記録します
- 価格の検知: 終値が手前N本の高値・安値を抜けたかを判定します。抜けていなければ対象外として終了します
- 参加度の確認: 同じ方向のOBV更新が許容本数以内にあるかを確認します。なければ保留としてログに残します
- 出力: 通過した更新だけを候補として通知や発注へ渡します。損切り幅とロットは、ATRなど別系統の指標で決める構成が扱いやすくなります
OBVの記録を価格の検知より前に置いているのは、同じ足での同時更新を取りこぼさないためです。最初から発注まで自動化せず、候補と保留の件数を通知で並べて観察する段階を挟むと、フィルターがどの局面で効いているのかを把握しやすくなります。
どう活用するか
「同じ判定を別の系列に当てる」という型は、OBVとドンチャンチャネルに限らず応用が利きます。自作派の方は、Claude Codeを使ったcBot開発の練習として、本記事の PriorHigh のような判定関数を1つ用意し、価格・OBV・他のオシレーターへ使い回す構成を試してみてください。
委託派の方は、ai-programming.xyz への開発相談の際に「OBV側のチャネル期間」と「更新のずれを何本まで許すか」の2点を先に決めておくと、要件のすり合わせが早く進みます。教育派の方は、同じブレイクアウトのロジックをOBV確認あり・なしで実装し、保留に回った更新がその後どう推移したかを比べる課題として扱うと、フィルターの意味を手を動かしながら確認できます。変動幅の設計まで含めたい場合は、ドンチャンチャネルとATRの組み合わせの考え方を三層目として重ねる拡張も検討できます。
本記事は一般的な情報提供を目的としています。投資助言ではありません。 取引判断はご自身の責任で行ってください。