English
preview
分析型ボリュームプロファイル取引(AVPT):流動性アーキテクチャ、市場メモリ、アルゴリズム実行

分析型ボリュームプロファイル取引(AVPT):流動性アーキテクチャ、市場メモリ、アルゴリズム実行

MetaTrader 5 |
27 9
Hlomohang John Borotho
Hlomohang John Borotho

目次

  1. はじめに
  2. システムの概要と理解
  3. 導入手順
  4. バックテスト結果
  5. 結論


はじめに

機関投資家のオーダーフローが絶えず流動性構造を書き換える中で、トレーダーは価格そのもののノイズではなく、市場参加構造をマッピングする手法に依存する必要があります。分析型ボリュームプロファイル取引(AVPT, Analytical Volume Profile Trading)は、単なる指標の域を超え、市場が実際にどの価格で取引されたかを詳細に分析します。高ボリュームノード(HVN, High-Volume Nodes)、低ボリュームノード(LVN, Low-Volume Nodes)、バリューエリア、そして最も重要なポイントオブコントロール(POC, Point of Control)の相互作用を通じて、AVPTは市場記憶の隠れた層を明らかにし、機関投資家がどこで同意し、どこで意見が分かれ、どこでポジションを構築したかを示します。

アルゴリズム取引が市場の大部分を占める現代では、価格レベルにおける取引量の分布を理解することは決定的なアドバンテージとなります。AVPTはマイクロストラクチャー分析と自動化を組み合わせることで、トレーダーが流動性のアンバランスをリアルタイムで解釈し、精密に取引を実行できるようにします。生のボリュームを実行可能な構造に変換することで、この手法は混沌とした市場活動をトレンド検出、ブレイクアウト予測、反転タイミングのための明確な分析フレームワークへと変換します。 


システムの概要と理解

ボリュームプロファイルの主要構成要素は以下のとおりです。

コンポーネント  説明 
コントロールポイント(POC) 最も取引量が多い価格レベルで、プライスアクションのピボットポイントとして機能することが多いです。
高ボリュームノード(HVN) 取引が集中した価格帯で、強いサポートまたはレジスタンスとして機能します。
低ボリュームノード(LVN) 取引がほとんどない価格帯で、流動性が低いため価格が急速に移動する可能性があります。
バリューエリア 指定された期間の取引量の70%程度が集中した価格帯で、「公正価値」の範囲を示します。

AVPTは、特定の価格レベルにおける取引量という重要な次元に着目することで、従来の価格ベースのテクニカル指標を超えた高度な市場分析を提供します。AVPTでは、市場をプライスアクションがボリュームベースのサポートおよびレジスタンスゾーンと相互作用する動的な地形として可視化します。ボリュームプロファイルは通常、チャートの右側に水平ヒストグラムとして表示され、指定期間における重要な取引活動の場所を示します。この可視化により、機関投資家や大口トレーダーがポジションを蓄積した価格帯(HVN)と、参加がほとんどなかった価格帯(LVN)が明確に示されます。POCはこの構造の重心として機能し、最も取引量の多い価格レベルを示し、価格を引き寄せる強い磁石のような役割を果たします。

AVPTの戦略的枠組みは、価格がこれらのボリュームベース構造とどのように相互作用するかを理解することに基づいています。価格がHVNに接近すると、これらのゾーンは機関の関心と流動性が集中しているため、強いサポートやレジスタンスとして作用します。一方、LVNは、意味のある取引参加が存在しないために価格が急速に移動しやすい加速ゾーンとして機能します。バリューエリアは、その期間の取引量の約70%を含む範囲であり、最も取引がおこなわれた「公正価値」の価格帯を定義します。これらの要素を可視化することで、トレーダーはバリューエリアの境界付近での反転ゾーン、低ボリューム帯を通過するブレイクアウトのチャンス、そしてPOCにおける利確目標を特定できます。価格、ボリューム、時間という3次元的な視点を取り入れることで、従来の2次元的な価格分析よりも優位性が得られます。

AVPTの実務的な適用では、現在のプライスアクションが既存のボリューム構造とどのように関連しているかを観察します。レンジ相場では、トレーダーはバリューエリア下限付近で買い、近くのLVNの下方にストップを置き、POCで利確する平均回帰戦略を採用することがあります。トレンド相場では、価格がLVNを勢いよく突破した場合にブレイクアウト戦略が有効で、バリューエリアの先までターゲットを設定することが可能です。可視化により、リスク管理も直感的になります。ストップロスはLVNの外側に置くのが自然で、これにより取引前提が無効になる価格変動から保護されます。また、ポジションサイズはバリューエリアの幅や主要ボリュームノードまでの距離に応じて調整できます。分析、エントリータイミング、リスク管理を統合したこのアプローチは、機関投資家の資金フローが実際に市場動向を形成する方法に沿った体系的な手法を提供します。



導入手順

//+------------------------------------------------------------------+
//|                                                         AVPT.mq5 |
//|                        GIT under Copyright 2025, MetaQuotes Ltd. |
//|                     https://www.mql5.com/ja/users/johnhlomohang/ |
//+------------------------------------------------------------------+
#property copyright "GIT under Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/ja/users/johnhlomohang/"
#property version   "1.00"
#include <Trade/Trade.mqh>


//+------------------------------------------------------------------+
//| Input Parameters                                                 |
//+------------------------------------------------------------------+
input group "=== Volume Profile Settings ==="
input int      VP_LookbackBars = 500;        // Volume Profile Lookback Bars
input double   VA_Percentage = 70.0;         // Value Area Percentage
input double   HVN_Threshold = 1.5;          // HVN Volume Threshold (x median)
input double   LVN_Threshold = 0.3;          // LVN Volume Threshold (x median)

input group "=== Trading Settings ==="
input double   LotSize = 0.1;                // Trade Lot Size
input bool     UseAtrSL = true;              // Use ATR for Stop Loss
input double   AtrMultiplier = 2.0;          // ATR Multiplier for SL
input int      AtrPeriod = 14;               // ATR Period
input double   RiskRewardRatio = 1.5;        // Risk/Reward Ratio
input bool     UseTrailingStop = true;       // Enable Trailing Stop
input double   TrailingStep = 0.0010;        // Trailing Stop Step

input group "=== Strategy Settings ==="
input bool     EnableReversion = true;       // Enable Reversion Strategy
input bool     EnableBreakout = false;       // Enable Breakout Strategy
input int      MinBarsBetweenTrades = 3;     // Minimum Bars Between Trades

まず、ボリュームプロファイルエンジンおよび売買ロジックの動作を形作るすべての設定可能な入力項目を定義することから始めます。最初のグループでは、プロファイルに関連するパラメータを指定します。具体的には、分析する過去バーの数、バリューエリアを算出する際に使用する割合、そしてHVNやLVNを識別するための閾値などです。次に、取引設定のセクションでは、リスクおよびポジション管理の制御を確立します。ここには、ロットサイズ、ATRに基づくストップロス、リスク・リワード比率、トレーリングストップの挙動などが含まれ、EAがボラティリティに応じて柔軟に対応できるようにします。最後に、戦略設定のグループでは、平均回帰やブレイクアウト取引といった主要なAVPTモードの有効化または無効化を可能にするとともに、取引間に最小間隔を設けることで、よりクリーンな実行とシグナルのノイズ軽減を実現します。

//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+
enum ENUM_LEVEL_POSITION{
   BELOW,
   ABOVE
};

enum ENUM_DIRECTION{
   UP,
   DOWN
};

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
CTrade trade;

// Volume Profile Arrays
double volumeProfile[];
double priceLevels[];
int profileBins;

// Key Levels
double pocPrice;
double vahPrice;
double valPrice;
double hvnLevels[];
double lvnLevels[];

// Trading
datetime lastTradeTime;
int atrHandle;
double currentAtr;

次に、EAのロジックやデータ構造を支える列挙型とグローバル変数を定義します。列挙型は、戦略全体で使用される明確な方向性およびポジション分類を提供します。一方、グローバル変数は、CTradeオブジェクト、動的に構築されるボリュームプロファイル配列、POC、VAH、VAL、HVN、LVNといった主要レベルなど、取引のコアコンポーネントを保持します。さらに、追加の変数は取引の管理情報を追跡します。具体的には、直近の取引実行のタイムスタンプの管理や、ATRなどの指標へアクセスできるようにすることで、EAがストップロスやリスク管理の判断のためにボラティリティデータをリアルタイムで参照できるようにします。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    // Initialize ATR indicator
    atrHandle = iATR(_Symbol, _Period, AtrPeriod);
    if(atrHandle == INVALID_HANDLE)
    {
        Print("Error creating ATR indicator");
        return(INIT_FAILED);
    }
    
    // Initialize volume profile arrays
    InitializeVolumeProfile();
    
    // Set up timer for periodic updates
    EventSetTimer(60); // Update every minute
    
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    IndicatorRelease(atrHandle);
    EventKillTimer();
    CleanUpChartObjects();
}

初期化フェーズでは、EAはまずボラティリティに基づくストップロス計算に使用するATRインジケーターを作成し、それが正しく読み込まれていることを検証することで、すべての必須コンポーネントをセットアップします。続いて、専用の初期化関数を通じてボリュームプロファイル配列を準備し、さらにシステムが1分ごとにプロファイルとロジックを更新できるよう、繰り返し実行されるタイマーを設定します。初期化解除処理では、EAはATRインジケーターを安全に解放し、タイマーを停止し、実行中に作成されたすべてのチャートオブジェクトを削除します。これにより、クリーンなシャットダウンを実現し、リソースリークの発生を防止します。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // Update ATR value
    UpdateAtrValue();
    
    // Update volume profile every new bar
    if(IsNewBar())
    {
        UpdateVolumeProfile();
        CalculateKeyLevels();
        UpdateChartObjects();
        
        // Check for trading signals
        CheckTradingSignals();
    }
    
    // Manage open positions
    ManagePositions();
}

//+------------------------------------------------------------------+
//| Volume Profile Engine                                           |
//+------------------------------------------------------------------+
void InitializeVolumeProfile()
{
    profileBins = 200; // Number of price bins
    ArrayResize(volumeProfile, profileBins);
    ArrayResize(priceLevels, profileBins);
    ArrayInitialize(volumeProfile, 0);
}

メインループでは、EAはすべてのティックごとにATRの値を継続的に更新し、ボラティリティ指標を常に正確な状態に保ちます。新しいバーが形成されると、コアとなる処理がトリガーされます。具体的には、ボリュームプロファイルの更新、POCやバリューエリアの境界など主要レベルの再計算、そしてチャート上のビジュアルオブジェクトの更新です。構造分析の更新後、EAは最新のボリュームプロファイルシグナルに基づいて取引条件が満たされているかを評価します。その後、現在開いているすべてのポジションを個別に管理し、市場状況の変化に応じてストップ、トレーリングロジック、エグジット条件が適切に調整されるようにします。

ボリュームプロファイルエンジンは、価格レベルごとに取引されたボリュームの分布を保存するためのデータ構造を初期化するところから始まります。一定数のビンを定義し、それに合わせて配列のサイズを調整することで、EAは実行中にボリュームデータを収集するためのクリーンなコンテナを準備します。これにより、新しいバーが形成されるたびに、それを一貫したボリュームヒストグラムにマッピングできるようになり、後の処理でHVN、LVN、そしてPOCといった重要レベルを正確に検出できるようになります。

void UpdateVolumeProfile()
{
    // Clear previous profile
    ArrayInitialize(volumeProfile, 0);
    
    // Get price range for current lookback
    double highPrice = GetHighestPrice(VP_LookbackBars);
    double lowPrice = GetLowestPrice(VP_LookbackBars);
    double priceRange = highPrice - lowPrice;
    double binSize = priceRange / profileBins;
    
    // Initialize price levels
    for(int i = 0; i < profileBins; i++)
    {
        priceLevels[i] = lowPrice + (i * binSize);
    }
    
    // Distribute volume across price bins
    for(int bar = 0; bar < VP_LookbackBars; bar++)
    {
        double open = iOpen(_Symbol, _Period, bar);
        double high = iHigh(_Symbol, _Period, bar);
        double low = iLow(_Symbol, _Period, bar);
        double close = iClose(_Symbol, _Period, bar);
        long volume = iVolume(_Symbol, _Period, bar);
        
        DistributeVolumeToBins(open, high, low, close, volume, lowPrice, binSize);
    }
}

void DistributeVolumeToBins(double open, double high, double low, double close, 
                           long volume, double basePrice, double binSize)
{
    double bodyLow = MathMin(open, close);
    double bodyHigh = MathMax(open, close);
    
    for(int i = 0; i < profileBins; i++)
    {
        double binLow = basePrice + (i * binSize);
        double binHigh = binLow + binSize;
        
        // Check if price bin was touched by this candle
        if(!(high < binLow || low > binHigh))
        {
            // Weight volume by time spent in price zone (simplified)
            double overlap = MathMin(high, binHigh) - MathMax(low, binLow);
            double candleRange = high - low;
            
            if(candleRange > 0)
            {
                double weight = overlap / candleRange;
                // Additional weight for body area
                if(binLow <= bodyHigh && binHigh >= bodyLow)
                {
                    double bodyOverlap = MathMin(bodyHigh, binHigh) - MathMax(bodyLow, binLow);
                    weight += (bodyOverlap / candleRange) * 0.3;
                }
                
                volumeProfile[i] += volume * weight;
            }
        }
    }
}

UpdateVolumeProfile()関数は、まずヒストグラムをリセットし、その後プロファイリングする必要がある価格レンジを特定することで、指定されたルックバック期間におけるボリューム分布全体を再構築します。この価格レンジに基づいてビンサイズを計算し、それぞれのボリュームビンに対応する価格レベルを初期化します。こうした構造的な境界を確立した後、関数はルックバック期間内の過去バーを順に走査し、各ローソク足のOHLC値と出来高を取得して、それらを分布計算ロジックへ渡します。これにより、ボリュームプロファイルは常に市場における最新かつ最も関連性の高い活動を反映する状態に保たれます。

DistributeVolumeToBins()関数は、ローソク足の出来高を適切な価格ビンへ割り当てる中核ロジックを担います。各ビンについて、その価格帯がローソク足の値動きレンジと相互作用しているかどうかを確認し、該当する場合には、その価格ゾーン内でどの程度の取引活動が発生した可能性が高いかを推定します。この推定は、ビンとローソク足の高値から安値までのレンジとの比例的な重なり具合に基づく加重手法を用いておこなわれます。さらに、取引がより集中して発生する傾向のあるローソク足の実体部分には追加の重みが与えられます。これらの加重された寄与を累積することにより、EAは価格階層全体における機関投資家のフットプリントを捉える、より現実的で詳細なボリュームプロファイルを生成します。

//+------------------------------------------------------------------+
//| Key Levels Calculation                                           |
//+------------------------------------------------------------------+
void CalculateKeyLevels()
{
    CalculatePOC();
    CalculateValueArea();
    CalculateHVNLVN();
}

void CalculatePOC()
{
    double maxVolume = 0;
    int pocIndex = 0;
    
    for(int i = 0; i < profileBins; i++)
    {
        if(volumeProfile[i] > maxVolume)
        {
            maxVolume = volumeProfile[i];
            pocIndex = i;
        }
    }
    
    pocPrice = priceLevels[pocIndex];
}

void CalculateValueArea()
{
    double totalVolume = 0;
    for(int i = 0; i < profileBins; i++)
    {
        totalVolume += volumeProfile[i];
    }
    
    double targetVolume = totalVolume * (VA_Percentage / 100.0);
    int pocIndex = FindPriceIndex(pocPrice);
    
    double currentVolume = volumeProfile[pocIndex];
    int upperIndex = pocIndex;
    int lowerIndex = pocIndex;
    
    while(currentVolume < targetVolume && (upperIndex < profileBins - 1 || lowerIndex > 0))
    {
        bool canExpandUp = (upperIndex < profileBins - 1);
        bool canExpandDown = (lowerIndex > 0);
        
        double upVolume = canExpandUp ? volumeProfile[upperIndex + 1] : 0;
        double downVolume = canExpandDown ? volumeProfile[lowerIndex - 1] : 0;
        
        if(upVolume > downVolume && canExpandUp)
        {
            upperIndex++;
            currentVolume += upVolume;
        }
        else if(canExpandDown)
        {
            lowerIndex--;
            currentVolume += downVolume;
        }
        else if(canExpandUp)
        {
            upperIndex++;
            currentVolume += upVolume;
        }
    }
    
    vahPrice = priceLevels[upperIndex];
    valPrice = priceLevels[lowerIndex];
}

void CalculateHVNLVN()
{
    ArrayResize(hvnLevels, 0);
    ArrayResize(lvnLevels, 0);
    
    // Calculate median volume
    double tempVolumes[];
    ArrayCopy(tempVolumes, volumeProfile);
    ArraySort(tempVolumes);
    double medianVolume = tempVolumes[profileBins / 2];
    
    double hvnThreshold = medianVolume * HVN_Threshold;
    double lvnThreshold = medianVolume * LVN_Threshold;
    
    for(int i = 0; i < profileBins; i++)
    {
        if(volumeProfile[i] >= hvnThreshold)
        {
            int size = ArraySize(hvnLevels);
            ArrayResize(hvnLevels, size + 1);
            hvnLevels[size] = priceLevels[i];
        }
        else if(volumeProfile[i] <= lvnThreshold && volumeProfile[i] > 0)
        {
            int size = ArraySize(lvnLevels);
            ArrayResize(lvnLevels, size + 1);
            lvnLevels[size] = priceLevels[i];
        }
    }
}

主要レベルの計算エンジンは、まずボリュームプロファイルの3つの中核要素を統括的に処理することから始まります。すなわち、POCの決定、バリューエリアの境界計算、そしてHVNとLVNの識別です。このモジュール化されたアプローチにより、それぞれのメソッドは特定の構造要素に集中でき、EAが流動性レベルの正確で整理された階層構造を維持できるようになります。ロジックを分離することで、システムは新しい戦略やフィルターが追加された場合でもデバッグ、拡張、最適化が容易な構造を保つことができます。

POCとValue Areaの計算は、市場の「重心」を定義するために相互に連携して機能します。POCは、ボリュームヒストグラムを走査して最も取引量が多い価格ビンを特定することで求められます。この価格レベルは、市場参加者による受容が最も強かったポイントを示します。その後、Value AreaはそのPOCを起点として両方向へ拡張され、累積ボリュームが指定された割合に達するまでボリュームが加算されていきます。この拡張ロジックでは、取引量の多い価格レベルが優先的に取り込まれるため、EAはValue Area High (VAH)およびValue Area Low (VAL)を、取引量の大部分が集中した領域の境界として定義することができます。

最後に、HVNおよびLVNの計算では、中央値の出来高と比較して参加度が著しく高いゾーンと、極端に低いゾーンを特定します。まず、ボリュームプロファイルのコピーをソートして中央値を算出し、その後ユーザー定義の倍率を適用して、意味のあるボリュームの集中(HVN)と拒否ゾーン(LVN)を検出します。HVNは強い受容が存在する領域を表し、価格が停滞したり反転したりしやすいポイントです。一方、LVNは流動性が低いポケットを示し、価格が加速したりブレイクアウトを起こしたりすることが多い領域を示します。これらのレベルは総合的にEAの意思決定の基盤となり、流動性構造と市場記憶に基づいた適応的な戦略の実行を可能にします。

//+------------------------------------------------------------------+
//| Trading Signal Logic                                             |
//+------------------------------------------------------------------+
void CheckTradingSignals()
{
    if(!IsTradeAllowed()) return;
    
    // Check minimum bars between trades
    if(Bars(_Symbol, _Period, lastTradeTime, TimeCurrent()) < MinBarsBetweenTrades) 
        return;
    
    double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_LAST);
    
    if(EnableReversion)
    {
        CheckReversionSignals(currentPrice);
    }
    
    if(EnableBreakout)
    {
        CheckBreakoutSignals(currentPrice);
    }
}

void CheckReversionSignals(double currentPrice)
{
    MqlRates currentRates[];
    CopyRates(_Symbol, _Period, 0, 3, currentRates);
    
    // Buy signal: Price rejects VAL with bullish confirmation
    if(IsNearLevel(currentPrice, valPrice) && 
       IsBullishRejection(currentRates))
    {
        double sl = FindNearestLVN(BELOW);
        double tp = pocPrice;
        OpenTrade(ORDER_TYPE_BUY, sl, tp);
    }
    
    // Sell signal: Price rejects VAH with bearish confirmation
    if(IsNearLevel(currentPrice, vahPrice) && 
       IsBearishRejection(currentRates))
    {
        double sl = FindNearestLVN(ABOVE);
        double tp = pocPrice;
        OpenTrade(ORDER_TYPE_SELL, sl, tp);
    }
}

void CheckBreakoutSignals(double currentPrice)
{
    // Breakout above VAH with confirmation
    if(currentPrice > vahPrice && IsBreakoutConfirmed(UP))
    {
        double sl = FindNearestHVN(BELOW);
        double tp = CalculateBreakoutTP(UP);
        OpenTrade(ORDER_TYPE_BUY, sl, tp);
    }
    
    // Breakout below VAL with confirmation
    if(currentPrice < valPrice && IsBreakoutConfirmed(DOWN))
    {
        double sl = FindNearestHVN(ABOVE);
        double tp = CalculateBreakoutTP(DOWN);
        OpenTrade(ORDER_TYPE_SELL, sl, tp);
    }
}

売買シグナルのロジックでは、まず実行条件が適切であることを確認します。具体的には、取引が可能かどうかを確認し、前回の取引から十分なバー数が経過しているかを検証し、現在の市場価格を取得します。これらの安全確認が満たされた後、EAはどの戦略モードが有効になっているかを評価し、プライスアクションをそれぞれのロジックへ振り分けます。平均回帰モデルが有効な場合、価格がバリューエリアの境界とどのように相互作用しているかを確認し、拒否を示唆する動きがあるかを判定します。ブレイクアウトモデルが有効な場合には、価格がバリューレベルを明確に突破しているかどうかを確認し、さらに構造的な裏付けが伴っているかを評価します。

各戦略の内部では、取引セットアップを検証するためにより詳細なチェックがおこなわれます。平均回帰戦略では、VALまたはVAH付近での強気または弱気のリジェクションパターンを探し、その後、最も近いLVNとPOCを基準としてストップロスとテイクプロフィットのレベルを計算します。ここでPOCは自然な価格の引力点として機能します。ブレイクアウト戦略では、価格がVAHまたはVALを明確に突破していることを確認したうえで、近くのHVNを保護的なストップとして使用し、ブレイクアウトの強さに基づいて方向性のあるターゲットを算出します。このような階層的な構造により、シグナルはボリュームプロファイルによって定義された流動性アーキテクチャに基づくものとなり、市場構造とボリュームコンテキストの両方に整合した取引が生成されます。

//+------------------------------------------------------------------+
//| Trade Execution & Risk Management                                |
//+------------------------------------------------------------------+
void OpenTrade(ENUM_ORDER_TYPE type, double sl, double tp)
{
    double price = (type == ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) 
                                           : SymbolInfoDouble(_Symbol, SYMBOL_BID);
    
    if(UseAtrSL)
    {
        double atrSl = currentAtr * AtrMultiplier;
        sl = (type == ORDER_TYPE_BUY) ? price - atrSl : price + atrSl;
        
        // Calculate TP based on risk/reward
        double risk = MathAbs(price - sl);
        tp = (type == ORDER_TYPE_BUY) ? price + (risk * RiskRewardRatio) 
                                     : price - (risk * RiskRewardRatio);
    }
    
    // Execute trade using CTrade
    if(trade.PositionOpen(_Symbol, type, LotSize, price, sl, tp, "VolumeProfileEA"))
    {
        lastTradeTime = iTime(_Symbol, _Period, 0);
        Print("Trade executed: ", EnumToString(type), " Price: ", price, " SL: ", sl, " TP: ", tp);
    }
    else
    {
        Print("Trade failed: ", trade.ResultRetcodeDescription());
    }
}

void ManagePositions()
{
    for(int i = PositionsTotal() - 1; i >= 0; i--)
    {
        ulong ticket = PositionGetTicket(i);
        if(PositionSelectByTicket(ticket) && PositionGetString(POSITION_COMMENT) == "VolumeProfileEA")
        {
            if(UseTrailingStop)
            {
                ApplyTrailingStop(ticket);
            }
            
            // Check exit condition: price re-enters Value Area
            if(ShouldExitEarly(ticket))
            {
                ClosePosition(ticket);
            }
        }
    }
}

void ApplyTrailingStop(ulong ticket)
{
    if(PositionSelectByTicket(ticket))
    {
        double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_LAST);
        double currentSL = PositionGetDouble(POSITION_SL);
        ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
        
        double newSL = currentSL;
        double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
        
        if(type == POSITION_TYPE_BUY)
        {
            newSL = currentPrice - (currentAtr * TrailingStep);
            if(newSL > currentSL + point && newSL < currentPrice - point)
            {
                if(trade.PositionModify(ticket, newSL, PositionGetDouble(POSITION_TP)))
                {
                    Print("Trailing SL updated for BUY: ", newSL);
                }
            }
        }
        else if(type == POSITION_TYPE_SELL)
        {
            newSL = currentPrice + (currentAtr * TrailingStep);
            if(newSL < currentSL - point && newSL > currentPrice + point)
            {
                if(trade.PositionModify(ticket, newSL, PositionGetDouble(POSITION_TP)))
                {
                    Print("Trailing SL updated for SELL: ", newSL);
                }
            }
        }
    }
}

void ClosePosition(ulong ticket)
{
    if(PositionSelectByTicket(ticket))
    {
        ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
        double volume = PositionGetDouble(POSITION_VOLUME);
        
        if(type == POSITION_TYPE_BUY)
        {
            trade.PositionClose(ticket);
        }
        else if(type == POSITION_TYPE_SELL)
        {
            trade.PositionClose(ticket);
        }
        
        Print("Position closed early: ", ticket);
    }
}

取引実行ロジックは、シグナルの種類、現在の市場価格、そして動的なボラティリティ条件に基づいて、リスク管理されたクリーンな注文を構築することから始まります。ATRベースのストップロスが有効になっている場合、EAはATR倍率と設定されたリスクリワード比率を用いてSLとTPの両方を再計算します。これにより、すべてのポジションが固定距離に依存するのではなく、現在のボラティリティに適応する形になります。最終的なパラメータが確定すると、EAはCTradeオブジェクトを使用して注文をサーバーへ送信し、実行が成功したか失敗したかをログに記録します。この構造化されたワークフローにより、取引は一貫したポジションサイズ、ボラティリティに整合した保護、そして戦略アーキテクチャに基づく合理的な利益目標を備えた状態でエントリーされます。

その後、ポジション管理エンジンがすべてのアクティブなVolumeProfileEAの取引を監視し、動的なトレーリングストップを適用するとともに、早期決済が必要かどうかを評価します。トレーリングストップはATRに基づく価格の動きに応じて自動的に調整されるため、取引を過度に早く締め付けることなく、効率的にストップロスを価格に追従させることができます。一方で、早期エグジットロジックは価格が再びValue Areaへ戻るかどうかを監視します。これは元の取引の前提が弱まりつつある可能性を示すシグナルであり、その場合には資本保全のためにポジションを決済します。このように、トレーリングロジックと構造的な無効化チェックを組み合わせた二層のエグジットシステムにより、各ポジションは市場状況に応じて賢く変化しながら管理され、リスクを抑えつつ利益を確保できるようになります。


バックテスト結果

バックテストは4時間足(H4)で実施し、約2か月間の検証期間(2025年7月7日から2025年11月3日)を対象としました。使用した設定は以下のとおりです。


以下はエクイティカーブとバックテストの結果です。



結論

Analytical Volume Profile Trading (AVPT)を開発するにあたり、流動性アーキテクチャ、出来高分布、市場記憶がどのように価格変動の背後にある本当の市場構造を形成しているのかを分解して分析しました。ボリュームノード、高流動性の棚状ゾーン、そして低ボリュームの非効率領域が、市場がどこで停滞し、拡張し、あるいは反転するのかを示す再現性のある設計図をどのように形成するのかを検討しました。さらに、この構造的ロジックをアルゴリズム実行へと翻訳し、EAがボリュームプロファイルのゾーンを読み取り、流動性イベントを予測し、表面的な価格アクションではなく最も深い機関投資家のフットプリントに合わせてエントリーをおこなう方法を示しました。

結論として、AVPTはトレーダーにとってより知的でコンテキストを理解した執行アプローチを提供します。取引はもはや単なるパターンやインジケーターのみによってトリガーされるのではなく、実際の流動性がどこに存在しているのか、そして市場が過去のアンバランスをどのように「記憶」しているのかを理解することに基づいて実行されます。市場記憶、出来高のクラスタリング、そしてアルゴリズムによる意思決定を統合することで、トレーダーは精度を高め、リスク配置を改善し、ノイズに起因するシグナルを減らすためのフレームワークを得ることができます。

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

添付されたファイル |
AVPT.mq5 (25.64 KB)
最後のコメント | ディスカッションに移動 (9)
Hlomohang John Borotho
Hlomohang John Borotho | 28 11月 2025 において 14:55
Cédric Olivier サポート/レジスタンス・レベル です

- 価格はこのレベルまでリトレースする傾向がある。

- VAH(バリューエリア高値)***:バリューゾーンの上限。
- VAL(バリュー・エリア・ロー)***:VAL(バリュー・エリア・ロー)**: **下側の緑の点線** - バリュー・ゾーンの下側の境界線
- VAHとVALの間は、出来高の70%が取引されたエリアを表す。

- これは分析期間中の市場の「公正価値」とみなされる。

- HVN(High Volume Nodes)**:青い破線** - 出来高が非常に多い価格水準(中央値の1.5倍の出来高)。

- 機関投資家の関心が高いエリア

- 非常に優れた支持/抵抗水準

- LVN (Low Volume Nodes)**:黄色の破線** - 出来高が非常に少ない価格水準(中央値の0.3倍の出来高)

- 機関投資家の関心が低いエリア

- 一般的に、価格はこれらの水準を素早く突破する。

## このインジケータの使用方法

1.**重要なエリアの特定**:

- POCは監視すべき最も重要なレベルです。

- バリューゾーン(VAHとVALの間)は "フェアバリュー "エリア。

2.**取引戦略

- 買い**:市場が上昇トレンドにある場合、バリューゾーン(VAL)またはポイント・オブ・チェンジ(POC)付近で買う。

- 売り**:売り**:市場が下降トレンドにある場合、平均値ゾーン(AVT)または変化点(POC)付近
- ターゲット平均値水準(LVN)を超えた場合、次の高値水準(HVN)まで上昇する傾向がある。
- 損切り**:重要なHVNを超えたところに置かれる。

3.**確認

- 一旦バリューゾーンを離れた後、価格がバリューゾーンに戻った場合、多くの場合、"適正価格 "に戻ったことを示す良いシグナルとなる。

- 価格がAVTを上回れば買い圧力が、VALを下回れば売り圧力があることを示す。

## カスタマイズ可能なパラメーター(インジケーター内)
- バリューゾーンのパーセンテージを調整できます。
- HVNとLVNのしきい値を好みに応じて変更できます。
- 必要に応じてヒストグラム表示を有効/無効にできます。

このインディケータは特に便利で、機関投資家が最も活発に取引した場 所(HVN)、取引が少なかった場所(LVN)、そして「適正」価格がある場所 (バリューゾーン)を特定するのに役立ちます。スマートマネーのコンセプトに従うトレーダーに高く評価されている。

改めてありがとうございます。コミュニティとして一緒に、私たちはよりスマートに!

セドリック・オリビエ、 インジケーターをありがとう。

Hlomohang John Borotho
Hlomohang John Borotho | 28 11月 2025 において 14:56
Ryan L Johnson #:

なんと壮大な記事とディスカッションだろう...しかもすべてがたった3日間のうちに。

ジョン・ボロトさん、ありがとうございます。

セドリック・オリビエ、ありがとう。


テストされたシンボルについては、特に心配はしていません。参考までに、iRealVolume()がコードから除外されているため、技術的な意味でのセキュリティではない可能性が高い。このコードでは、iTickVolume()と同じ値を返すiVolume()を使用しています。iVolume()は一般的に、FX、CFDなどのOTC市場で使用されます。

ライアン、そして皆さん、フィードバックをありがとう。
Hlomohang John Borotho
Hlomohang John Borotho | 28 11月 2025 において 14:59
Bryan John Aldridge バックテストが 実行された証券を特定していただければ幸いです。

テストされたシンボルはXAUUSDで、同じ結果を得るために使用されたテスト期間と設定を参照してください。

DecentOne
DecentOne | 1 12月 2025 において 07:43
私はコーダーではありません。
Ryan L Johnson
Ryan L Johnson | 1 12月 2025 において 13:11
DecentOne #:
私はコーダーではありません。

このディスカッションの一番上にリンクされている記事の最後にあります:

分析的出来高プロファイル取引(AVPT):流動性アーキテクチャ、マーケットメモリー、アルゴリズム執行

(これが、フォーラムに自動的に投稿されるすべての記事のディスカッションの仕組みです)

EA

MQL5入門(第28回):MQL5のAPIとWebRequest関数の習得(II) MQL5入門(第28回):MQL5のAPIとWebRequest関数の習得(II)
本記事では、APIとMQL5のWebRequest関数を使用して、外部プラットフォームから価格データを取得および抽出する方法を解説します。URLの構造、APIレスポンスの形式、サーバーデータを可読な文字列へ変換する方法、そしてJSONレスポンスから特定の値を識別および抽出する方法を学びます。
MetaTrader 5機械学習の設計図(第6回):実務で使えるキャッシュシステムの設計 MetaTrader 5機械学習の設計図(第6回):実務で使えるキャッシュシステムの設計
進捗バーを眺めるだけで、取引戦略のテストに時間を浪費していませんか。従来のキャッシュ手法では金融機械学習には対応できず、計算の無駄や再実行によるフラストレーションに悩まされます。私たちは、金融データ特有の課題、時間的依存関係、複雑なデータ構造、そして先読みバイアスのリスクを理解した洗練されたキャッシュアーキテクチャを設計しました。この三層構造のシステムにより、計算速度は劇的に向上し、古い結果の自動無効化やコストの高いデータリークの防止も可能です。もう計算待ちに時間を費やす必要はありません。市場が要求するペースで、迅速に反復作業をおこなえます。
MQL5での取引戦略の自動化(第42回):セッションベースのオープニングレンジブレイクアウト(ORB)システム MQL5での取引戦略の自動化(第42回):セッションベースのオープニングレンジブレイクアウト(ORB)システム
MQL5で完全にカスタマイズ可能なセッションベースのオープニングレンジブレイクアウト(ORB)システムを作成します。このシステムでは、任意のセッション開始時刻とレンジの期間を設定でき、指定したオープニング期間の高値と安値を自動計算し、かつ動きの方向に沿った確定ブレイクアウトのみを取引します。
MQL5での取引戦略の自動化(第41回):ローソク足レンジ理論(CRT)-蓄積・操作・分配(AMD) MQL5での取引戦略の自動化(第41回):ローソク足レンジ理論(CRT)-蓄積・操作・分配(AMD)
MQL5で動作するローソク足レンジ理論(CRT)取引システムを開発します。このシステムは、指定した時間足での蓄積のレンジを特定し、操作の深さフィルタリングを用いてブレイクを検知し、分配フェーズにおける反転確認を経てエントリーをおこないます。また、リスクリワード比に基づく動的または静的なストップロスとテイクプロフィット設定、任意のトレーリングストップ、方向ごとのポジション上限設定などによるリスク管理にも対応しています。