English Deutsch
preview
プライスアクション分析ツールキットの開発(第55回):CPIミニローソク足オーバーレイによるバー内圧力の可視化

プライスアクション分析ツールキットの開発(第55回):CPIミニローソク足オーバーレイによるバー内圧力の可視化

MetaTrader 5インディケータ |
23 2
Christian Benjamin
Christian Benjamin

内容


はじめに

多くのトレーダーは、ローソク足をシンプルな色分けで解釈することから学びます。一般的には、緑(陽線)は買い圧力、赤(陰線)は売り圧力を意味します。しかし実際の価格がこのような二元的な動きになることはほとんどありません。各ローソク足は買い手と売り手の継続的なせめぎ合いを表しており、終値はその攻防がどこで終了したかを示すだけであり、バー内でどちらがどれほど強く優勢だったかまでは示していません。その結果、見た目が似ている2本のローソク足でも、全く異なる情報を含む場合があります。たとえば、一方は高値付近で明確に引けており持続的な買い圧力を示すのに対し、もう一方は陽線であっても上昇の勢いが弱いまま終了する場合があります。

例えとして、サッカーの試合を考えてみます。最終スコア(たとえば1–0)は勝敗を示しますが、試合内容までは表しません。ボール支配率で優位に立ち、試合の大半を相手陣内でプレーし、多くのチャンスを作っていたチームが、1点差で敗れることもあります。ローソク足も同様です。色は終値が始値より上か下かを示すだけであり、バー内の値動きの大部分をどちらが支配していたのか、またその支配の強さがどれほどだったのかは見えにくくなります。

ローソク足の構造、圧力分類および可視化の仕組み、そして時間足や銘柄に依存せず一貫した動作を維持する、リペイントなしの遷移ベースアラートシステムに焦点を当てます。このインジケーターは、各バーの値幅内における終値の位置を測定し、その結果を中点を基準としたミニローソク足として表示します。各ミニローソク足の方向と大きさはバー内圧力を表しており、買いと売りのどちらが優勢だったかだけでなく、その優勢の強さも把握できます。ローソク足は中立、弱圧、強圧ゾーンに分類され、オプションとして強圧ゾーンのバーを示す矢印表示や、強圧ゾーンへの移行やCPIの方向変化を通知するイベントアラートも備えています。

この追加的な可視化により、CPIミニローソク足は、単純な色分けだけでは判断が難しい状況(たとえば「陰線だが強気(red-but-bullish)」のように、わずかに下落して引けたにもかかわらず、レンジの上部付近で終了しているケース)を明確に示します。また、同じ色のローソク足でも圧力の強さが大きく異なる場合を識別できます。このインジケーターはあくまで分析用途に限定されており、売買を実行するものではありません。価格の挙動を可視化することに特化しており、取引の推奨はおこないません。

続くセクションでは、CPIフレームワークの基盤となるローソク足構造を説明し、CLVベースの計算式を導出し、MQL5での可視化およびアラートロジックの実装について詳しく説明します。また、再現可能なテスト手順および実例を示し、各ユーザーが自身のプライスアクション分析に組み込めるようにします。


概念と理論

CPIおよびその計算方法を説明する前に、まずローソク足の基本構造を振り返ることが重要です。CPIはローソク足の構造を直接利用しており、ローソク足の全体レンジと終値の位置を利用することで、価格が高値付近で受け入れられたのか、安値付近で受け入れられたのか、あるいはその中間なのかを推定します。

ローソク足は一定の時間足におけるすべての取引を圧縮して表現したものです。ローソク足の色は、終値が始値より上か下かを示しますが、その構造(値幅、実体、ヒゲ)は、価格がどこまで到達し、どこで拒否され、最終的にどこで落ち着いたのかといった追加情報を提供します。CPIはローソク足の色ではなく、この内部構造に注目します。

ローソク足構造の基本

ローソク足は以下の4つの価格によって定義されます。

  • 始値 :ローソク足が開始する価格
  • 高値:期間内の最高価格
  • 安値:期間内の最低価格
  • 終値:ローソク足が終了する価格

これらの値から、次の3つの構造要素が導かれます。

レンジ

レンジは高値と安値の差を表します。

  • Range = High – Low

これは、そのローソク足の期間中に買い手と売り手が相互作用した全体の領域を定義します。

実体

実体は始値と終値の間の距離です。ローソク足の色はこの関係から決まります。

  • 終値 > 始値:陽線(買い優勢)
  • 終値 < 始値:陰線(売り優勢)

実体はそのローソク足の「結果」を示しますが、終値がレンジ内のどこに位置したかは示しません。

ヒゲ

上下のヒゲは価格の拒否を示します。

  • 上ヒゲ:到達したが維持されずに下落した価格
  • 下ヒゲ:到達したが維持されずに上昇した価格

ヒゲには、ローソク足の色だけでは分からない「拒否」や「吸収」に関する重要な情報が含まれます。

ローソク足構造からCPIへ

CPIは、[-1, +1]の範囲に正規化された値であり、ローソク足の高値と安値のレンジ内で終値がどこに位置するかを測定することで、価格が高値付近で終わった場合は強い買い圧力、安値付近で終わった場合は強い売り圧力を示します。ローソク足の色は終値が始値より上か下かを示すだけであり、終値がレンジ内のどこに位置したかまでは示しません。CPIはこの制約を補完するために、レンジ内での終値位置を定量化します。

レンジ内での終値位置

概念的には、

  • 高値付近でのクローズ → 買い圧力が強い
  • 安値付近でのクローズ → 売り圧力が強い
  • 中央付近でのクローズ → バランス状態または吸収状態

この概念を定量化するために、CPIはClosing Location Value (CLV)を用います。

CPI値の範囲

この式により結果は以下のように正規化されます。

  • +1:高値でクローズ(最大買い圧力)
  • 0:レンジ中央でクローズ(中立)
  • −1:安値でクローズ(最大売り圧力)

この正規化により、異なる銘柄や時間足でもCPI値を一貫して比較できます。

  • エッジケース:High = Low

高値と安値が等しい場合、そのローソク足にはレンジが存在しません。この場合CPIは未定義となるため、中立値(0)として扱います。これによりゼロ除算を防ぎ、方向性が存在しない状態を適切に表現します。

実践的な解釈(シナリオ)

CPIの価値は、実際のローソク足の挙動に適用したときに最も明確になります。以下は、ローソク足の色だけでは得られない情報をCPIがどのように補うかを示す例です。

陰線だが強い買い圧力(「Red-but-Bullish」)

ローソク足がわずかに始値より下で引けた(陰線)にもかかわらず、レンジの上部付近でクローズする場合があります。この場合、結果としては売り優勢ですが、バー内の大部分では買い圧力が支配していたことを意味します。CPIはこの状況を正の値(しばしば強い値)として表現します。

陽線だが弱い(「Green-but-Weak」)

ローソク足が始値より上で引けた(陽線)にもかかわらず、レンジの下部付近でクローズする場合があります。この場合、結果としては買い優勢ですが、セッション全体では売り圧力が強かったことを示します。CPIはこの弱さを低い値、場合によっては負の値として反映します。

中立CPIの例

終値がレンジの中央付近に位置する場合、CPIは0に近づきます。これは買いと売りのどちらも支配的ではなかった状態を示し、ローソク足の色に関係なくバランス状態を表します。

なぜこれが重要なのか

  • 従来のローソク足分析は「結果」を重視します。
  • 一方でCPIは「圧力」と「支配力」を重視します。

ローソク足の構造と終値位置を組み合わせることで、CPIは次の2点を評価可能にします。

  • 方向性:どちらがセッションを支配したか
  • 強度:どれほど強く支配したか


インジケーター設計

このインジケーターは、元の価格ローソク足を変更することなく、メインチャート上にコンパクトなCPI可視化を提供します。CPIは各バーごとに計算され、小さなローソク足状マーカー、任意の強圧ゾーン矢印、そして色分けされたゾーンによって迅速な解釈を可能にします。

可視化ルール

  • 確定足のみ:CPIはi ≥ 1のバーに対してのみ計算されます。形成中のバー(Bar 0)は、値が不安定であり、アラートの重複発生を避けるため除外されます。
  • 中央基準配置:各マーカーはローソク足の中点(レンジの中央値)を基準として配置されます。

この配置により、マーカーは高値と安値の影響から視覚的に独立した状態になります。

  • |CPI| に比例した高さ(固定):マーカーの高さはCPIの絶対値に応じて増加し、MinMarkerPointsMaxMarkerPoints(ポイント単位)の範囲内に制限されるため、異なる銘柄やボラティリティ環境でも視認性が維持されます。

ゾーンと色

CPIゾーンは2つの閾値によって5段階に分類されます。

  • StrongThreshold(例:0.60)
  • MildThreshold(例:0.20)

ゾーン定義:

  • 強い買い:CPI ≥ +StrongThreshold
  • 弱い買い:+MildThreshold ≤ CPI < +StrongThreshold
  • 中立:−MildThreshold < CPI < +MildThreshold
  • 弱い売り:−StrongThreshold < CPI ≤ −MildThreshold
  • 強い売り:CPI ≤ −StrongThreshold

各ゾーンには識別しやすいように異なる色が割り当てられます。

強圧ゾーン矢印(オプション)

有効な場合、強圧ゾーンにあるローソク足にのみ矢印が描画されます。

強い買い矢印:ローソク足の安値の下に表示されます。

  • Low—OffsetPoints × Point

強い売り矢印:ローソク足の高値の上に表示されます。

  • High + OffsetPoints × Point

OffsetPointsは、ローソク足およびミニローソク足マーカーとの重なりを防ぐために使用されます。

下図:CPIミニローソク足の可視化

CPIの値は、各確定足上にコンパクトなミニローソク足として表示されます。マーカーの方向は買いまたは売りの圧力を示し、マーカーの高さは圧力の強さを反映します。また、矢印は強圧ゾーンを強調表示します。ローソク足の色とCPIによる圧力は一致しない場合があり、その差分によって価格内部のダイナミクス(バー内構造)を可視化できます。


アラートとイベントロジック

アラートシステムは、「状態の継続」ではなく「状態の変化」を通知するというシンプルな原則に基づいています。CPI値は同一の圧力ゾーン内に複数バーにわたって留まることがあり、各ローソク足で繰り返しアラートを発生させると、実用的な価値を高めることなくノイズのみが増加します。そのため、アラートは定義された状態間の遷移が発生した場合にのみ、かつ確定足に対してのみ生成されます。各確定足は、設定された閾値に基づき、強い買い、弱い買い、中立、弱い売り、強い売りのいずれかのCPI状態に分類されます。アラートロジックは生のCPI値ではなく、現在の状態と直前の状態を比較し、意味のある遷移が発生した場合のみイベントをトリガーします。

最も重要なアラートは「強圧ゾーンへのエントリー」です。これはCPIが中立または「弱い」状態から強い尾買いまたは強い売りへ移行した場合に発生します。この変化はクローズ位置に基づく圧力の明確なシフトを示すため、最も選択的であり、通常は最も情報価値の高いシグナルとなります。オプションアラートとして、強圧ゾーンからの退出アラートおよびCPIの符号反転アラートが利用可能です。強圧ゾーンからの退出は方向性の圧力が弱まっていることを示します。一方、符号反転アラートはCPIの方向性が変化した場合に発生し、これは「弱い」ゾーンまたは「中立」ゾーン内でも起こる可能性があります。符号反転は比較的頻繁に発生するため、強圧ゾーン関連のアラートとは分離されており、必要に応じて無効にできます。

重複を防ぐため、すべてのイベントは新しく確定したバーごとに1回のみ評価され、1バーにつき最大1回のアラートのみが発生します。同一のイベントロジックはすべての通知チャネルに対して適用され、配信手段に依存しない一貫した動作が保証されます。


MQL5での実装

このセクションでは、CPIミニローソク足のコンセプトをMetaTrader 5上でどのように実装しているかについて説明します。特にレンダリング処理、計算フロー、およびアラート処理に焦点を当てます。また、リペイントなしの決定論的な動作と、視覚的な明瞭性を重視しています。

レンダリング手法(プロットとバッファ)

このインジケーターはメインチャートウィンドウ上に直接描画され、3つのプロットを使用します。それぞれが異なる視覚的役割を持ちます。CPIはコンパクトなローソク足状マーカーとして表示され、元の価格ローソク足を変更することなく重ねて描画されます。この設計により、従来のプライスアクションを維持しつつ、バー内圧力の情報を追加できます。

主要な可視化は最初のプロットでおこなわれ、DRAW_COLOR_CANDLES 描画モードを使用してCPIミニローソク足を描画します。このモードはローソク足形式の入力を必要とするため、5つのバッファによって構成されます。すなわち、各CPIマーカーの形状を定義するための合成Open、High、Low、Close値、および5種類の事前定義カラーパレットを選択するカラーインデックスバッファです。これらの合成OHLC値は実際の市場データから直接取得されるものではありません。CPI計算に基づいて構築されており、マーカーはローソク足の中点に配置され、圧力の強さに応じて高さがスケーリングされます。カラーインデックスバッファは各マーカーをゾーン(強い買い、弱い買い、中立、弱い売り、強い売り)に対応付けることで、方向性と強度の両方を視覚的に表現します。

さらに2つの追加プロットは、ミニローソク足レイヤーを乱さずに強いCPIイベントを表示するために使用されます。第2プロットは強い買いCPIバー専用のDRAW_ARROWプロットであり、第3プロットは強い売りバー専用のDRAW_ARROWプロットです。買いと売りの矢印を別プロットとして分離することで、それぞれ独立したスタイリングが可能となり、各バーに対して適切な矢印のみが表示されます。連続的な圧力情報(ミニローソク足)と離散的なイベントマーカー(矢印)を分離することで、レンダリングロジックは明確かつ決定論的になり、視覚的にもバランスの取れた構成になります。

#property indicator_chart_window
#property indicator_plots 3
#property indicator_buffers 7

#property indicator_label1  "CPI MiniCandles"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrForestGreen, clrSeaGreen, clrSilver, clrTomato, clrFireBrick
#property indicator_width1  2

#property indicator_label2  "CPI Strong Buy"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrForestGreen

#property indicator_label3  "CPI Strong Sell"
#property indicator_type3   DRAW_ARROW
#property indicator_color3  clrFireBrick

double BufOpen[], BufHigh[], BufLow[], BufClose[], BufColorIndex[];
double BufBuyArrow[], BufSellArrow[];

入力項目(視覚的なサイズ調整、ゾーン、矢印、アラート)

ユーザー入力は機能ごとにグループ化されており、直感的な設定インターフェースを維持すると同時に、視覚的な設定と分析ロジックを明確に分離しています。ビジュアル関連のパラメータは、CPIミニローソク足のスケーリングおよびクランプ(ポイント単位)を制御します。これにより、異なる銘柄やボラティリティ環境においてもマーカーの視認性が維持されます。また、レンジベースのノイズゲート(InpMinRangePoints)を導入することで、レンジが極端に小さいローソク足に対するCPI計算を抑制します。このようなケースではバー内圧力の意味が限定的であるため、不要なシグナルやノイズの発生を低減します。

ゾーン閾値は、CPI値を弱・強の圧力状態に分類するために使用され、カラー割り当ておよびアラート生成の基準となります。矢印マーカーおよびアラート機能はオプションとして実装されており、それぞれ独立して有効・無効を切り替えることができます。これにより、ユーザーはCPIの計算ロジックを変更することなく、視覚出力および通知動作を柔軟に調整できます。

input double InpMarkerScale        = 0.25;
input int    InpMinMarkerPoints    = 2;
input int    InpMaxMarkerPoints    = 200;
input int    InpMinRangePoints     = 5;
input bool   InpHideNeutralMarkers = false;
input bool   InpShowLatestInfo     = true;

input double InpStrongThreshold    = 0.60;
input double InpMildThreshold      = 0.20;

input bool InpShowStrongArrows     = true;
input int  InpArrowOffsetPoints    = 10;
input int  InpArrowCodeBuy         = 233;
input int  InpArrowCodeSell        = 234;

input bool   InpEnableAlerts       = true;
input bool   InpAlertOnExit        = false;
input bool   InpAlertOnSignFlip    = true;
input bool   InpPopupAlert         = true;
input bool   InpSoundAlert         = false;
input string InpSoundFile          = "alert.wav";
input bool   InpPushAlert          = false;
input bool   InpEmailAlert         = false;

CPI計算式と補助ユーティリティ

CPIは、Closing Location Value (CLV)型の正規化に基づいて計算されます。これは、終値がローソク足の全レンジ内のどこに位置しているかを測定するものであり、レンジを持つすべてのローソク足に対して[-1, +1]の範囲に収まる値を生成します。これにより、異なる銘柄や時間足間でも圧力の強さを一貫して比較することが可能になります。ローソク足のレンジが存在しない場合、すなわち高値と安値が等しい場合は、CPI値は中立(0.0)として扱われます。これはゼロ除算を回避するためであると同時に、方向性の圧力が存在しない状態を正しく反映するものです。

CPIの計算に加えて、レンダリング時には小さなクランプ用ユーティリティが使用されます。これはCPIミニローソク足の高さをユーザー定義の最小値および最大値の範囲内に制限するためのものです。これにより、マーカーは常に視認可能でありながら過度に目立つことがなくなり、ボラティリティの異なる環境でもチャートの視認性が維持されます。

double ClampVal(const double v, const double lo, const double hi)
{
   if(v < lo) return lo;
   if(v > hi) return hi;
   return v;
}

double CalcCPI(const double high, const double low, const double close)
{
   double range = high - low;
   if(range <= 0.0)
      return 0.0;
   return (2.0*close - high - low) / range;
}

ゾーン分類とカラーマッピング

CPIミニローソク足は、バー内圧力の方向性と強さを保ちながら解釈を簡素化するために、5つの離散的なビジュアルゾーンに分類されます。これらのゾーンは列挙型として実装されており、その数値順はindicator_color1で定義されたカラーパレットの順序と意図的に一致するよう設計されています。DRAW_COLOR_CANDLES プロットは色を名前ではなくインデックスで選択するため、この対応関係は重要です。ヘルパー関数ColorFromCPIは、計算されたCPI値を設定されたMildおよびStrongの閾値に基づいて適切なカラーパレットインデックスへマッピングします。これにより、各ミニローソク足は正しいゾーンカラーで描画されます。

並行して、関数ZoneNameFromCPI()は同じCPI値を人間が読めるラベルに変換し、チャート上のステータステキストやツールチップなどの情報出力に使用されます。同一のロジックからビジュアルカラーとテキスト分類の両方を導出することで、チャート上の表示内容とユーザーへの報告内容の一貫性が保証されます。

enum CPIColorIndex
{
   IDX_STRONG_BUY = 0,
   IDX_MILD_BUY   = 1,
   IDX_NEUTRAL    = 2,
   IDX_MILD_SELL  = 3,
   IDX_STRONG_SELL= 4
};

int ColorFromCPI(const double cpi)
{
   double a = MathAbs(cpi);

   if(a < InpMildThreshold)
      return IDX_NEUTRAL;

   if(cpi >= 0.0)
   {
      if(a >= InpStrongThreshold) return IDX_STRONG_BUY;
      return IDX_MILD_BUY;
   }
   else
   {
      if(a >= InpStrongThreshold) return IDX_STRONG_SELL;
      return IDX_MILD_SELL;
   }
}

string ZoneNameFromCPI(const double cpi)
{
   double a = MathAbs(cpi);
   if(a < InpMildThreshold) return "NEUTRAL";
   if(cpi >= 0.0)
   {
      if(a >= InpStrongThreshold) return "STRONG BUY";
      return "MILD BUY";
   }
   else
   {
      if(a >= InpStrongThreshold) return "STRONG SELL";
      return "MILD SELL";
   }
}

強状態抽出(遷移ベースのアラート用)

アラート処理のために、CPI値はさらに簡略化された「強状態(strong-state)」表現に変換されます。5段階のゾーン分類をそのまま評価するのではなく、CPIを以下の3つの離散状態にマッピングします。

  • +1:強い買い圧力
  • −1:強い売り圧力
  • 0:それ以外のすべて(弱いおよび中立)

この抽象化により、アラートロジックは意味のある優位性の変化のみに集中でき、通知の対象とならない軽微な変動を無視できます。現在の強状態と直前の強状態を比較することで、最小限の分岐ロジックで強圧ゾーンへのエントリーおよび退出を検出することが可能になります。またこの方式により、CPIが連続する複数のローソク足で同一の強状態に留まっている場合でも繰り返しアラートが発生することが防止されます。結果として、実際に状態遷移が発生した場合にのみ通知がトリガーされる設計となっています。

int StrongStateFromCPI(const double cpi)
{
   if(cpi >=  InpStrongThreshold) return +1;
   if(cpi <= -InpStrongThreshold) return -1;
   return 0;
}

アラートルーティング(通知チャネル)

すべてのアラートイベント(強圧ゾーンへのエントリー、強圧ゾーンからの退出、CPIの符号反転を含む)は、中央集約されたアラート処理関数を通じてルーティングされます。この設計により、ターミナルポップアップ、サウンドアラート、プッシュ通知、Eメールなど、すべての対応通知チャネルにおいて一貫した動作が保証されます。各通知手段は個別の入力フラグによって制御されており、ユーザーはアラートの基本ロジックを変更することなく、特定のチャネルを有効または無効にできます。この分離設計により、アラート検出ロジックの決定論的な性質を維持しつつ、通知設定の柔軟性を確保しています。

void FireAlert(const string msg)
{
   if(!InpEnableAlerts) return;

   if(InpPopupAlert) Alert(msg);
   if(InpSoundAlert) PlaySound(InpSoundFile);
   if(InpPushAlert)  SendNotification(msg);
   if(InpEmailAlert) SendMail("CPI Alert", msg);
}

初期化(OnInit):バッファバインディング、矢印設定、時系列モード

初期化処理では、OnInit()関数においてすべてのインジケーターバッファを必要な順序で各プロットにバインドします。これには、合成Open、High、Low、CloseおよびカラーインデックスバッファをDRAW_COLOR_CANDLESプロットに関連付ける処理が含まれます。また、矢印用バッファはそれぞれ対応するDRAW_ARROWプロットにリンクされます。この段階でWingdingsの矢印コードが割り当てられ、未使用時には矢印が表示されないように空値が設定されます。すべてのバッファは明示的に時系列モードに設定され、インデックス0が最新バーを指すようになります。これにより、OnCalculateへ渡される価格配列との整合性が正しく保たれます。

int OnInit()
{
   SetIndexBuffer(0, BufOpen,       INDICATOR_DATA);
   SetIndexBuffer(1, BufHigh,       INDICATOR_DATA);
   SetIndexBuffer(2, BufLow,        INDICATOR_DATA);
   SetIndexBuffer(3, BufClose,      INDICATOR_DATA);
   SetIndexBuffer(4, BufColorIndex, INDICATOR_COLOR_INDEX);

   PlotIndexSetInteger(0, PLOT_COLOR_INDEXES, 5);

   SetIndexBuffer(5, BufBuyArrow, INDICATOR_DATA);
   PlotIndexSetInteger(1, PLOT_ARROW, InpArrowCodeBuy);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   SetIndexBuffer(6, BufSellArrow, INDICATOR_DATA);
   PlotIndexSetInteger(2, PLOT_ARROW, InpArrowCodeSell);
   PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   IndicatorSetString(INDICATOR_SHORTNAME, "CPI MiniCandles (CLV) + Strong Arrows");

   ArraySetAsSeries(BufOpen, true);
   ArraySetAsSeries(BufHigh, true);
   ArraySetAsSeries(BufLow, true);
   ArraySetAsSeries(BufClose, true);
   ArraySetAsSeries(BufColorIndex, true);
   ArraySetAsSeries(BufBuyArrow, true);
   ArraySetAsSeries(BufSellArrow, true);

   return INIT_SUCCEEDED;
}

メイン計算(OnCalculate):確定足、ノイズゲート、マーカー構築

メイン計算ループは確定足のみに対して動作し、インデックスi ≥ 1のバーのみを処理します。形成中のローソク足(インデックス0)は、値が不安定になりやすくリペイントの原因となるため除外されます。処理対象となる各バーについては、前回計算の残留データを防ぐため、関連するすべてのバッファがまず初期化(クリア)されます。

double point    = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double minMarker= InpMinMarkerPoints * point;
double maxMarker= InpMaxMarkerPoints * point;
double minRange = InpMinRangePoints  * point;

for(int i = rates_total - 1; i >= 1; --i)
{
   BufOpen[i] = BufHigh[i] = BufLow[i] = BufClose[i] = EMPTY_VALUE;
   BufColorIndex[i] = EMPTY_VALUE;
   BufBuyArrow[i]  = EMPTY_VALUE;
   BufSellArrow[i] = EMPTY_VALUE;

   double range = high[i] - low[i];

   double cpi = 0.0;
   if(range > 0.0 && range >= minRange)
      cpi = CalcCPI(high[i], low[i], close[i]);

   int colorIdx = ColorFromCPI(cpi);

   if(!(InpHideNeutralMarkers && colorIdx == IDX_NEUTRAL))
   {
      double mid = 0.5 * (high[i] + low[i]);

      double height = range * InpMarkerScale * MathAbs(cpi);
      height = ClampVal(height, minMarker, maxMarker);

      double o = mid;
      double c = (cpi >= 0.0 ? mid + height : mid - height);

      BufOpen[i]  = o;
      BufClose[i] = c;
      BufHigh[i]  = MathMax(o, c);
      BufLow[i]   = MathMin(o, c);
      BufColorIndex[i] = (double)colorIdx;
   }
}

CPIは、ローソク足の総レンジが設定されたノイズゲートを上回る場合にのみ計算され、それを下回る場合はそのバーは中立として扱われます。CPIが有効な場合、ミニローソク足マーカーはローソク足の中点に配置されます。その高さはローソク足のレンジを基にし、CPIの絶対値およびユーザー定義のスケール係数によってスケーリングされます。その後、この高さは最小および最大マーカーサイズの範囲内にクランプされ、視覚的スケーリングの一貫性が保たれます。設定に応じて、中立マーカーはチャートの視認性を高めるために非表示にすることも可能です。

強圧矢印(オプションオーバーレイ)

強圧矢印は、この機能が有効化されている場合かつノイズゲートを通過したローソク足に対してのみ描画されます。これにより、CPIが強制的に中立扱いとなるような小さなローソク足に矢印が表示されることを防ぎます。強い買い圧力のイベントはローソク足の安値の下に矢印として表示され、強い売り圧力のイベントはローソク足の高値の上に矢印として表示されます。ローソク足およびCPIミニマーカーとの重なりを防ぐため、ポイントベースのオフセットが適用されます。

double arrowOff = InpArrowOffsetPoints * point;

if(InpShowStrongArrows && range >= minRange)
{
   if(cpi >= InpStrongThreshold)
      BufBuyArrow[i]  = low[i] - arrowOff;
   else if(cpi <= -InpStrongThreshold)
      BufSellArrow[i] = high[i] + arrowOff;
}

バー0(形成中ローソク足)の非表示処理

リペイントなしの安定した出力を保証するため、形成中のローソク足(インデックス0)はCPIミニローソク足および矢印プロットの両方で明示的に非表示にされます。この設計により、可視化結果は確定足のみを対象とするアラートロジックと完全に一致します。その結果、表示とアラートの両方が確定済みデータのみに基づいて動作します。

BufOpen[0] = BufHigh[0] = BufLow[0] = BufClose[0] = EMPTY_VALUE;
BufColorIndex[0] = EMPTY_VALUE;
BufBuyArrow[0] = EMPTY_VALUE;
BufSellArrow[0] = EMPTY_VALUE;

最新バー情報パネル(オプション)

有効な場合、直近で確定したローソク足(バー1)のCPI値、その対応ゾーンラベル、および現在のノイズゲート設定を表示するコンパクトな情報パネルがチャート上に表示されます。このパネルは主にテストおよび検証用途を想定しており、CPIの動作を内部バッファを確認せずに検証するために使用されます。描画処理、アラートロジック、およびパフォーマンスには影響を与えません。

if(InpShowLatestInfo)
{
   double cpi1 = 0.0;
   double range1 = high[1] - low[1];
   if(range1 > 0.0 && range1 >= minRange)
      cpi1 = CalcCPI(high[1], low[1], close[1]);

   Comment(StringFormat("CPI (closed bar): %.3f | %s | MinRange=%d pts",
                        cpi1, ZoneNameFromCPI(cpi1), InpMinRangePoints));
}
else
{
   Comment("");
}

アラート:新規バーゲート、強圧遷移、オプション退出、符号反転

アラートは、新しく確定したバーごとに1回のみ評価されます。重複通知を防ぐために、lastAlertBarTimeとしてタイムスタンプを保持し、同一バー内での複数発火を防止します。強圧ゾーンへのエントリーアラートは、現在の強状態が非ゼロであり、かつ直前に保存された強状態と異なる場合に発生します。オプションの退出アラートは、直前の状態が強状態(+1または−1)であり、現在の状態が非強状態(0)に戻った場合にトリガーされます。

datetime lastAlertBarTime = 0;
int      lastStrongState  = 0;

if(InpEnableAlerts)
{
   datetime barTime = time[1];
   if(barTime != lastAlertBarTime)
   {
      double cpi1 = 0.0, cpi2 = 0.0;

      double r1 = high[1] - low[1];
      double r2 = high[2] - low[2];

      if(r1 > 0.0 && r1 >= minRange) cpi1 = CalcCPI(high[1], low[1], close[1]);
      if(r2 > 0.0 && r2 >= minRange) cpi2 = CalcCPI(high[2], low[2], close[2]);

      int strongState = StrongStateFromCPI(cpi1);

      if(strongState != 0 && strongState != lastStrongState)
      {
         string side = (strongState > 0 ? "STRONG BUY" : "STRONG SELL");
         FireAlert(StringFormat("%s %s | CPI=%.3f | TF=%s | %s",
                                _Symbol, side, cpi1,
                                EnumToString((ENUM_TIMEFRAMES)_Period),
                                TimeToString(barTime, TIME_DATE|TIME_MINUTES)));
      }
      else if(InpAlertOnExit && lastStrongState != 0 && strongState == 0)
      {
         FireAlert(StringFormat("%s EXIT STRONG | CPI=%.3f | TF=%s | %s",
                                _Symbol, cpi1,
                                EnumToString((ENUM_TIMEFRAMES)_Period),
                                TimeToString(barTime, TIME_DATE|TIME_MINUTES)));
      }

      if(InpAlertOnSignFlip)
      {
         if(r1 >= minRange && r2 >= minRange)
         {
            if((cpi2 < 0.0 && cpi1 > 0.0) || (cpi2 > 0.0 && cpi1 < 0.0))
               FireAlert(StringFormat("%s CPI SIGN FLIP | prev=%.3f now=%.3f | TF=%s | %s",
                                      _Symbol, cpi2, cpi1,
                                      EnumToString((ENUM_TIMEFRAMES)_Period),
                                      TimeToString(barTime, TIME_DATE|TIME_MINUTES)));
         }
      }

      lastStrongState  = strongState;
      lastAlertBarTime = barTime;
   }
}

オプションの符号反転アラートは、CPI値がゼロラインをクロスした場合に生成されますが、その条件として現在および直前の両方のローソク足がノイズゲートを通過している必要があります。この制約により、レンジが小さいローソク足によって発生する可能性のある不要なシグナルを排除し、方向性の解釈が歪むことを防ぎます。

パフォーマンスに関する注意(prev_calculated)

簡素化とバッファの残留データ回避のため、本実装では各ティックごとにすべての確定足を再計算しています。バー単位の計算コストは軽量であるため、この方式は多くの実用的なユースケースにおいて問題なく動作し、開発およびテスト段階での堅牢性も向上します。

// current approach (simple, stable):
for(int i = rates_total - 1; i >= 1; --i) { /* ... */ }

// optimized approach (conceptual):
// if(prev_calculated == 0) start = rates_total - 1; else start = 2;
// for(int i = start; i >= 1; --i) { /* ... */ }

パフォーマンス最適化が必要となった場合は、prev_calculatedパラメータを使用して計算ループを最適化し、直近で確定したバーのみを処理するように改良できます。ただし、いかなる最適化をおこなう場合でも、再計算対象バーのバッファクリア、ノイズゲートの適用、形成中バーの除外というコアルールは必ず維持する必要があります。


テスト

安定性と一貫した動作を保証するため、すべてのテストは多様な取引環境において実施する必要があります。具体的には、主要な外国為替ペアと非FX銘柄(インデックスやコモディティなど)の少なくとも2種類の異なる銘柄タイプ、および複数の時間足(例:M5、M15、H1)で評価をおこなうことが推奨されます。異なる市場構造および時間圧縮条件でのテストは、ポイントベースのスケーリング、レンジベースのノイズゲート、CPI閾値ロジックが、異なるボラティリティプロファイル、価格スケール、桁数フォーマットに対して安定して機能することを検証する上で重要です。

ビジュアルレイヤー、情報パネル、およびアラートエンジンが確定足ベースで一貫して動作することを確認するため、MetaTrader 5のライブチャートでインジケーターをテストしました。下図はEURUSDのH1チャートにデフォルト設定(MinRange = 5 pts)でインジケーターを適用したものです。

チャート情報パネルは、以下の3点を1つの表示で正しく示しています。

CPI (closed bar):0.767 | STRONG BUY | MinRange = 5 pts

この出力は以下を検証しています。

  1. CPIがバー1(直近の確定足)で計算されていること
  2. ゾーン分類が正しく機能しており、CPI 0.767がSTRONG BUYゾーンにマッピングされていること
  3. ノイズゲート設定が有効であり、ローソク足のレンジに基づいて計算が制御されていること

[操作ログ]タブでは、定義された遷移ルールに従ってアラートが発生します。このテストでは、STRONG BUYアラートとCPI SIGN FLIPアラートが同一のバークローズ時刻で同時に記録されています。これは、CPIが以下の両条件を満たした場合の想定通りの動作です。

  • 強い正のゾーンへ移行したこと
  • 直前バーから現在バーにかけて負から正へと符号が反転したこと

これらの結果は、このインジケーターのアラートシステムがティック単位で連続的に反応するのではなく、新しく確定したローソク足ごとに遷移ベースで評価されていることを示しています。視覚的な出力とアラートは完全に同期しており、リペイントなしの決定論的動作が維持されています。下図はStep Index (H4)における追加テストであり、バー内圧力が交互に変化するCPIミニローソク足を示しています。直近の確定足ではオンチャート情報パネルにより弱い売りCPIが報告されており、強圧ゾーンの矢印は、閾値およびノイズゲート設定を変更することなく、優勢な圧力が発生した期間を強調しています。




結論

Candle Pressure Index (CPI)は、正規化された終値位置モデルを通じてバー内の買い圧力と売り圧力を可視化するプライスアクション分析ツールです。構造化されたゾーン分類、カラー割り当て、および状態遷移ベースのアラートを組み合わせることで、リペイントなしで市場圧力表現を実現し、ローソク足の色だけでは見えない内部ダイナミクスを明らかにします。

MetaTrader 5の実装では、確定足のみを対象とする厳密な評価、明示的なバッファ管理、そして可視化ロジックとアラートロジックの明確な分離によって、決定論的な動作を重視しています。このインジケーターは、銘柄や時間足を問わず一貫した挙動を示すように設計および調整可能であり、これらの設計方針により、CPIはテクニカル分析およびバー内圧力概念の教育的デモンストレーションの両面において有用なツールとなります。

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/20949

添付されたファイル |
最後のコメント | ディスカッションに移動 (2)
Mfoka Gatsheni
Mfoka Gatsheni | 21 1月 2026 において 01:18
矢印はローソク足が開いている時に表示されますか?
Christian Benjamin
Christian Benjamin | 24 1月 2026 において 08:37
Mfoka Gatsheni #:
矢印はローソク足が開いているときに表示されるのですか?
いいえ、矢印はローソク足の終値で評価される内圧計算の結果です。
古典的な戦略を再構築する(第21回):ボリンジャーバンドとRSIのアンサンブル戦略の発見 古典的な戦略を再構築する(第21回):ボリンジャーバンドとRSIのアンサンブル戦略の発見
EURUSD市場を対象としたアンサンブル型アルゴリズム取引戦略の開発について説明します。この戦略は、ボリンジャーバンドとRSI(相対力指数)を組み合わせたものです。初期のルールベース戦略は高品質なシグナルを生成した一方で、取引頻度が低く、収益性にも限界がありました。その後、複数の戦略バリエーションを反復的に評価した結果、市場に対する理解の誤り、ノイズの増加、パフォーマンスの劣化といった問題が明らかになりました。これらの課題に対し、統計的学習アルゴリズムを適切に活用し、モデリング対象をテクニカル指標へと再定義し、適切なスケーリングを適用したうえで、機械学習による予測と従来の取引ルールを組み合わせることで、最終的には許容可能なシグナル品質を維持しながら、収益性と取引頻度の大幅な改善を達成しました。
MQL5取引ツール(第12回):相関行列ダッシュボードのインタラクティブ機能の強化 MQL5取引ツール(第12回):相関行列ダッシュボードのインタラクティブ機能の強化
MQL5における相関行列ダッシュボードを強化し、パネルのドラッグ操作、最小化と最大化、ボタンや時間足に対するホバー効果、マウスイベント処理などを追加することで、ユーザー体験の向上を図ります。さらに、相関の強さに基づく銘柄の並び替え(昇順、降順)、相関値表示とp値表示の切り替え、ライトテーマとダークテーマの切り替え、動的なカラー更新も実装します。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
ラリー・ウィリアムズの『市場の秘密』(第8回):ボラティリティ、ストラクチャー、時間フィルターの組み合わせ ラリー・ウィリアムズの『市場の秘密』(第8回):ボラティリティ、ストラクチャー、時間フィルターの組み合わせ
MQL5における、ラリー・ウィリアムズに着想を得たボラティリティブレイクアウト型エキスパートアドバイザーの構築についての詳細な解説です。本手法は、スイング構造、ボラティリティベースのエントリー、曜日フィルター、時間フィルター、柔軟なリスク管理を組み合わせ、完全な実装と再現性のあるテスト環境を備えています。