English Русский Deutsch
preview
MQL5における高度な注文執行アルゴリズム:TWAP、VWAP、アイスバーグ注文

MQL5における高度な注文執行アルゴリズム:TWAP、VWAP、アイスバーグ注文

MetaTrader 5トレーディングシステム |
33 6
N Soumik
N Soumik
  1. はじめに
  2. 執行アルゴリズムの理解
  3. MQL5での実装
  4. パフォーマンスアナライザーの実装
  5. アルゴリズムのパフォーマンスの比較
  6. 執行アルゴリズムと取引戦略の統合
  7. 統合例
  8. 統合戦略
  9. バックテスト結果
  10. 結論



はじめに

想像してみてください。あなたは取引フロアのk片隅に立ち、心臓が高鳴る中で価格がリアルタイムに動くのを見ています。一度の誤った判断、あるいは過大な注文で、その優位性は一瞬にして消えてしまいます。ここでは、執行の質は単なる「便利な機能」ではなく、勝者とその他のトレーダーを分ける秘密兵器なのです。

何十年もの間、機関投資家は高度なアルゴリズムを駆使して、注文を巧みに分割・管理し、市場への影響やスリッページを最小限に抑えてきました。そして今、MQL5の柔軟性のおかげで、その強力な手法が、意欲的な個人トレーダーの手元でも利用可能になっています。

何が重要なのか

たとえば、絶好のチャンスを見つけて大きく攻めるとします。フルサイズの成行注文を投入した途端、自分の取引の重みで価格が滑り、理想のエントリーが不安定な妥協に変わってしまう。これが市場影響の厄介な作用です。流動性の高い市場でも、この現象は起こります。

そこで登場するのが執行アルゴリズムです。大きな注文を小さな戦略的スライスに分割して順次発注することで、オーダーブック上での自分の足跡を滑らかにできます。その結果、スリッページの低減、約定の改善、平均執行価格の向上が実現します。

個人トレーダーでも恩恵を受けられる理由

「でも自分は機関投資家のような大口を動かしているわけではない」と思うかもしれません。肝心なのは、そうする必要がないということです。0.5ロットや数ミニロットの取引でも、ボラティリティ次第で執行に影響が出ます。これらのツールは次のような利点を提供します。

  • スリッページを抑える:小さな注文でも乱高下する市場で価格のブレを抑制
  • 優位性を強化:分割発注により、単発の一括注文より有利な平均価格を実現
  • 冷静さを維持:自動化によりパニック的な売買の誘惑を排除
  • スムーズなスケーリング:口座規模が拡大しても執行精度は維持
  • 存在を隠す:特にアイスバーグ注文は、真の注文サイズを隠して他アルゴリズムからの追跡を回避

今日では、かつて数百万ドル規模の予算が必要だった執行テクノロジーが、個人の取引環境でも利用可能です。TWAP、VWAP、アイスバーグ戦略用の洗練されたMQL5コードをプラットフォームに組み込むことで、個人トレーダーでも機関投資家レベルの執行力を手に入れられます。

執行プロセスを刷新する準備をしましょう。市場は変化しています。これらのアルゴリズムをツールキットに加えることで、勝つための取引を仕掛けられるようになります。


執行アルゴリズムの理解

実装の詳細に入る前に、各執行アルゴリズムの理論と、異なる市場環境でなぜ効果的なのかを理解しておくことが重要です。

  1. 時間加重平均価格(TWAP:Time Weighted Average Price)TWAPはシンプルな執行アルゴリズムで、大口注文を等分して、設定された期間にわたり一定の時間間隔で発注します。目的は、その期間の平均価格に近い価格で執行することです。
    • 仕組み

      • 開始から終了までの一定時間間隔で注文を送信
      • 通常は同じサイズの注文(必要に応じてランダムサイズも可能)
      • 価格変動に関係なく、あらかじめ決められたタイムテーブルに従う
      • 市場への影響を時間的に分散させ、スリッページを抑える

    • 使用する場合

      • 特定の時間軸で平均執行価格を求めたい
      • 取引期間中の流動性が安定している
      • 注文を完了する固定されたウィンドウがある
      • シンプルで予測可能な方法を好む

  2. 出来高加重平均価格(VWAP: Volume-Weighted Average Price)VWAPはTWAPを改良したもので、注文サイズを予想出来高に応じて加重します。等分ではなく、出来高が高い時間帯に大きな注文を出すことで、市場への影響を抑えます。
    • 仕組み

      • 過去の出来高パターンに応じて注文サイズを割り当て
      • 過去の取引量を分析して、将来の出来高分布を予測
      • 実装によってはリアルタイムの出来高変化に適応可能
      • 出来高が多い時間帯に注文を集中させることで市場への影響を軽減

    • 使用する場合

      • パフォーマンスの評価基準がVWAPである
      • 出来高が日中のパターンに従う市場
      • セッション中に流動性が変動する市場で取引する
      • 市場の自然なフローに沿った執行を目指す

  3. アイスバーグ注文アイスバーグ注文は、大口注文の実際のサイズを隠すことに特化しています。常に少量だけが見えるように表示され、執行されると次の一部が現れます。
    • 仕組み

      • 注文全体の一部だけを表示
      • 各ティップが約定した後に次の可視部分を公開
      • 表示される数量を固定またはランダムにすることで、他の市場参加者に察知されるのを防ぐ
      • 価格制御のためにリミット注文として配置されることが多い

    • 使用する場合

      • 注文の全サイズを隠したい
      • 流動性が低く、大口取引で価格が動きやすい市場
      • 特定の価格水準で執行を維持したい
      • 他のトレーダーに注文を察知され、先回りされることを避けたい


MQL5での実装

    これらの執行アルゴリズムの理論を理解した上で、実際にMQL5で実装していきます。モジュール化され、オブジェクト指向のフレームワークを作成し、各アルゴリズムを個別に使用したり、統合された執行システムとして組み合わせたりできるようにします。

    基盤クラス:CExecutionAlgorithm

    まず、すべての実行アルゴリズムに共通の機能を提供する基盤クラスを定義します。

    //+------------------------------------------------------------------+
    //| Base class for all execution algorithms                          |
    //+------------------------------------------------------------------+
    class CExecutionAlgorithm
    {
    protected:
       string            m_symbol;              // Symbol to trade
       double            m_totalVolume;         // Total volume to execute
       double            m_executedVolume;      // Volume already executed
       double            m_remainingVolume;     // Volume remaining to execute
       datetime          m_startTime;           // Start time for execution
       datetime          m_endTime;             // End time for execution
       bool              m_isActive;            // Flag indicating if the algorithm is active
       int               m_totalOrders;         // Total number of orders placed
       int               m_filledOrders;        // Number of filled orders
       double            m_avgExecutionPrice;   // Average execution price
       double            m_executionValue;      // Total value of executed orders
       int               m_slippage;            // Allowed slippage in points
       
    public:
       // Constructor
       CExecutionAlgorithm(string symbol, double volume, datetime startTime, datetime endTime, int slippage = 3);
       
       // Destructor
       virtual ~CExecutionAlgorithm();
       
       // Common methods
       virtual bool      Initialize();
       virtual bool      Execute() = 0;
       virtual bool      Update() = 0;
       virtual bool      Terminate();
       
       // Utility methods
       bool              PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price);
       bool              CancelOrder(ulong ticket);
       void              UpdateAverageExecutionPrice(double price, double volume);
       
       // Getters
       string            GetSymbol() const { return m_symbol; }
       double            GetTotalVolume() const { return m_totalVolume; }
       double            GetExecutedVolume() const { return m_executedVolume; }
       double            GetRemainingVolume() const { return m_remainingVolume; }
       datetime          GetStartTime() const { return m_startTime; }
       datetime          GetEndTime() const { return m_endTime; }
       bool              IsActive() const { return m_isActive; }
       int               GetTotalOrders() const { return m_totalOrders; }
       int               GetFilledOrders() const { return m_filledOrders; }
       double            GetAverageExecutionPrice() const { return m_avgExecutionPrice; }
    };
    

    PlaceOrderメソッドは特に重要で、実際の注文執行をおこない、注文量の追跡を更新する役割を担います。

    bool CExecutionAlgorithm::PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price)
    {
       // Prepare the trade request
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       ZeroMemory(result);
       
       request.action = TRADE_ACTION_DEAL;
       request.symbol = m_symbol;
       request.volume = volume;
       request.type = orderType;
       request.price = price;
       request.deviation = m_slippage;
       request.magic = 123456; // Magic number for identification
       
       // Send the order
       bool success = OrderSend(request, result);
       
       if(!success)
       {
          Print("OrderSend error: ", GetLastError());
          return false;
       }
       
       // Check the result
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("OrderSend failed with code: ", result.retcode);
          return false;
       }
       
       // Update statistics
       m_totalOrders++;
       m_filledOrders++;
       m_executedVolume += volume;
       m_remainingVolume -= volume;
       UpdateAverageExecutionPrice(price, volume);
       
       // Store the order ticket for future reference
       ulong ticket = result.order;
       
       return true;
    }
    

    この関数は、MqlTradeRequestとMqlTradeResultをゼロ初期化した上で、銘柄、注文数量、注文種類、価格、スリッページ、マジックナンバーなどを設定し、OrderSendを呼び出して成行注文を作成・送信します。もし送信に失敗した場合や、ブローカーからの返却コードがTRADE_RETCODE_DONEでない場合は、エラーをログに記録してfalseを返します。成功した場合は、内部カウンタ(総注文数や約定済み/残り数量)を更新し、平均価格を再計算し、チケットIDを記録した上でtrueを返します。

    TWAPの実装

    TWAPアルゴリズムは、執行期間を等しい時間間隔に分割し、各間隔で同じサイズ(またはランダム化されたサイズ)の注文を発注します。

    //+------------------------------------------------------------------+
    //| Time-Weighted Average Price (TWAP) Algorithm                     |
    //+------------------------------------------------------------------+
    class CTWAP : public CExecutionAlgorithm
    {
    private:
       int               m_intervals;           // Number of time intervals
       int               m_currentInterval;     // Current interval
       datetime          m_nextExecutionTime;   // Next execution time
       double            m_intervalVolume;      // Volume per interval
       bool              m_useRandomization;    // Whether to randomize order sizes
       double            m_randomizationFactor; // Factor for randomization (0-1)
       ENUM_ORDER_TYPE   m_orderType;           // Order type (buy or sell)
       bool              m_firstOrderPlaced;    // Flag to track if first order has been placed
       int               m_initialDelay;        // Initial delay in seconds before first execution
       datetime          m_lastCheckTime;       // Last time order status was checked
       int               m_checkInterval;       // How often to check order status (seconds)
       
    public:
       // Constructor
       CTWAP(string symbol, double volume, datetime startTime, datetime endTime, 
             int intervals, ENUM_ORDER_TYPE orderType, bool useRandomization = false, 
             double randomizationFactor = 0.2, int slippage = 3, int initialDelay = 10);
       
       // Implementation of virtual methods
       virtual bool      Initialize() override;
       virtual bool      Execute() override;
       virtual bool      Update() override;
       virtual bool      Terminate() override;
       
       // TWAP specific methods
       void              CalculateIntervalVolume();
       datetime          CalculateNextExecutionTime();
       double            GetRandomizedVolume(double baseVolume);
       bool              IsTimeToExecute();
    };
    

    主要メソッド:CalculateNextExecutionTime

    このメソッドは、注文が時間的に適切に分散されるようにし、最初の注文には初期遅延を設けます。
    datetime CTWAP::CalculateNextExecutionTime()
    {
       // Calculate the duration of each interval
       int totalSeconds = (int)(m_endTime - m_startTime);
       int intervalSeconds = totalSeconds / m_intervals;
       
       // Calculate the next execution time
       datetime nextTime;
       
       if(m_currentInterval == 0) {
          // First interval - start at the defined start time plus initial delay
          nextTime = m_startTime + m_initialDelay;
          
          Print("TWAP: First execution time calculated with ", m_initialDelay, 
                " seconds delay: ", TimeToString(nextTime));
       } else {
          // For subsequent intervals, ensure proper spacing from current time
          datetime currentTime = TimeCurrent();
          nextTime = currentTime + intervalSeconds;
          
          // Make sure we don't exceed the end time
          if(nextTime > m_endTime)
             nextTime = m_endTime;
             
          Print("TWAP: Next execution time calculated: ", TimeToString(nextTime),
                " (interval: ", intervalSeconds, " seconds)");
       }
       
       return nextTime;
    }
    

    このメソッドは、m_startTimeからm_endTimeまでの期間をm_intervals個の等しいセグメントに分割し、次の取引を実行すべき時刻を返します。最初の呼び出しでは単純にm_startTime + m_initialDelayを返し、それ以降の呼び出しでは TimeCurrent() + 1セグメント分の秒数を返しますが、m_endTimeを超えることはありません。

    Executeメソッド:注文を発注するタイミングかどうかをチェックし、実際の注文発注処理を担当します。

    bool CTWAP::Execute()
    {
       if(!m_isActive)
          return false;
       
       // Check if it's time to execute the next order
       if(!IsTimeToExecute())
          return true; // Not time yet
       
       // Calculate the volume for this execution
       double volumeToExecute = m_useRandomization ? 
                               GetRandomizedVolume(m_intervalVolume) : 
                               m_intervalVolume;
                               
       // Ensure we don't exceed the remaining volume
       if(volumeToExecute > m_remainingVolume)
          volumeToExecute = m_remainingVolume;
          
       // Get current market price
       double price = 0.0;
       if(m_orderType == ORDER_TYPE_BUY)
          price = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
       else
          price = SymbolInfoDouble(m_symbol, SYMBOL_BID);
          
       Print("TWAP: Placing order for interval ", m_currentInterval,
             ", Volume: ", DoubleToString(volumeToExecute, 2),
             ", Price: ", DoubleToString(price, _Digits));
             
       // Place the order using OrderSend directly for more control
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       ZeroMemory(result);
       
       request.action = TRADE_ACTION_DEAL;
       request.symbol = m_symbol;
       request.volume = volumeToExecute;
       request.type = m_orderType;
       request.price = price;
       request.deviation = m_slippage;
       request.magic = 123456; // Magic number for identification
       
       // Send the order
       bool success = OrderSend(request, result);
       
       if(!success)
       {
          Print("TWAP: OrderSend error: ", GetLastError());
          return false;
       }
       
       // Check the result
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("TWAP: OrderSend failed with code: ", result.retcode);
          return false;
       }
       
       // Update statistics
       m_totalOrders++;
       m_filledOrders++;
       m_executedVolume += volumeToExecute;
       m_remainingVolume -= volumeToExecute;
       
       // Update interval counter
       m_currentInterval++;
       m_firstOrderPlaced = true;
       
       // Calculate the time for the next execution
       if(m_currentInterval < m_intervals && m_remainingVolume > 0)
          m_nextExecutionTime = CalculateNextExecutionTime();
       else
          m_isActive = false; // All intervals completed or no volume left
          
       Print("TWAP: Executed ", DoubleToString(volumeToExecute, 2), 
             " at price ", DoubleToString(price, _Digits), 
             ". Remaining: ", DoubleToString(m_remainingVolume, 2),
             ", Next execution: ", TimeToString(m_nextExecutionTime));
             
       return true;
    }
    

    このExecuteメソッドは、TWAPの1スライス分の処理を管理します。まず、戦略がアクティブでない場合や、まだ取引のタイミングでない場合は処理を中断します。取引可能な場合には、残りの注文量の中から固定またはランダム化されたスライス量を選択し(残量を超えない範囲で)、現在の買値(Buyの場合)または売値(Sellの場合)を取得します。次に、間隔、注文量、価格をログに記録し、シンボル、数量、注文種類、価格、スリッページ、マジックナンバーを設定したMqlTradeRequestを構築してOrderSendを呼び出します。送信に失敗した場合や、ブローカーの返却コードがTRADE_RETCODE_DONEでない場合は、エラーを出力してfalseを返します。

    注文が成功した場合は、注文カウンタを増加させ、実行済みおよび残りの注文量を調整し、間隔カウントを更新、初回注文送信フラグを立てます。その後、次の実行時刻をスケジュールするか、間隔や残量が尽きた場合は戦略を非アクティブ化します。最後に、処理結果をログに記録し、trueを返します。

    VWAPの実装

    VWAPアルゴリズムはTWAPに似ていますが、注文サイズを過去の出来高パターンに基づいて分配する点が異なります。
    //+------------------------------------------------------------------+
    //| Volume-Weighted Average Price (VWAP) Algorithm                   |
    //+------------------------------------------------------------------+
    class CVWAP : public CExecutionAlgorithm
    {
    private:
       int               m_intervals;           // Number of time intervals
       int               m_currentInterval;     // Current interval
       datetime          m_nextExecutionTime;   // Next execution time
       double            m_volumeProfile[];     // Historical volume profile
       double            m_intervalVolumes[];   // Volume per interval based on profile
       bool              m_adaptiveMode;        // Whether to adapt to real-time volume
       ENUM_ORDER_TYPE   m_orderType;           // Order type (buy or sell)
       int               m_historyDays;         // Number of days to analyze for volume profile
       bool              m_profileLoaded;       // Flag indicating if profile was loaded
       bool              m_firstOrderPlaced;    // Flag to track if first order has been placed
       int               m_initialDelay;        // Initial delay in seconds before first execution
       datetime          m_lastCheckTime;       // Last time order status was checked
       int               m_checkInterval;       // How often to check order status (seconds)
       
    public:
       // Constructor
       CVWAP(string symbol, double volume, datetime startTime, datetime endTime, 
             int intervals, ENUM_ORDER_TYPE orderType, int historyDays = 5,
             bool adaptiveMode = true, int slippage = 3, int initialDelay = 10);
       
       // Implementation of virtual methods
       virtual bool      Initialize() override;
       virtual bool      Execute() override;
       virtual bool      Update() override;
       virtual bool      Terminate() override;
       
       // VWAP specific methods
       bool              LoadVolumeProfile();
       void              CalculateIntervalVolumes();
       void              AdjustToRealTimeVolume();
       datetime          CalculateNextExecutionTime();
       double            GetCurrentVWAP();
       bool              IsTimeToExecute();
    };
    
    TWAPと同様に、VWAPもCalculateNextExecutionTimeメソッドを実装しており、注文が適切な間隔で発注されるようにしています。
    datetime CVWAP::CalculateNextExecutionTime()
    {
       // Calculate the duration of each interval
       int totalSeconds = (int)(m_endTime - m_startTime);
       int intervalSeconds = totalSeconds / m_intervals;
       
          // Calculate the next execution time
       datetime nextTime;
       
       if(m_currentInterval == 0) {
          // First interval - start at the defined start time plus initial delay
          nextTime = m_startTime + m_initialDelay;
          
          Print("VWAP: First execution time calculated with ", m_initialDelay, 
                " seconds delay: ", TimeToString(nextTime));
       } else {
          // For subsequent intervals, ensure proper spacing from current time
          datetime currentTime = TimeCurrent();
          nextTime = currentTime + intervalSeconds;
          
          // Make sure we don't exceed the end time
          if(nextTime > m_endTime)
             nextTime = m_endTime;
             
          Print("VWAP: Next execution time calculated: ", TimeToString(nextTime),
                " (interval: ", intervalSeconds, " seconds)");
       }
       
       return nextTime;
    }
    

    アイスバーグ注文の実装

    アイスバーグ注文は、大口注文の実際のサイズを隠し、常に市場に一部の注文のみを表示することで実現されます。

    //+------------------------------------------------------------------+
    //| Iceberg Order Implementation                                     |
    //+------------------------------------------------------------------+
    class CIcebergOrder : public CExecutionAlgorithm
    {
    private:
       double            m_visibleVolume;          // Visible portion of the order
       double            m_minVisibleVolume;       // Minimum visible volume
       double            m_maxVisibleVolume;       // Maximum visible volume
       bool              m_useRandomVisibleVolume; // Whether to randomize visible volume
       int               m_orderPlacementDelay;    // Delay between order placements (ms)
       bool              m_avoidRoundNumbers;      // Whether to avoid round numbers in price
       double            m_limitPrice;             // Limit price for the orders
       ulong             m_currentOrderTicket;     // Current active order ticket
       ENUM_ORDER_TYPE   m_orderType;              // Order type (buy or sell)
       bool              m_orderActive;            // Flag indicating if an order is currently active
       int               m_priceDeviation;         // Price deviation to avoid round numbers (in points)
       datetime          m_lastCheckTime;          // Last time order status was checked
       int               m_checkInterval;          // How often to check order status (seconds)
       int               m_maxOrderLifetime;       // Maximum lifetime for an order in seconds
       datetime          m_orderPlacementTime;     // When the current order was placed
       
    public:
       // Constructor
       CIcebergOrder(string symbol, double volume, double limitPrice, ENUM_ORDER_TYPE orderType,
                     double visibleVolume, double minVisibleVolume = 0.0, double maxVisibleVolume = 0.0,
                     bool useRandomVisibleVolume = true, int orderPlacementDelay = 1000, 
                     bool avoidRoundNumbers = true, int priceDeviation = 2, int slippage = 3);
       
       // Implementation of virtual methods
       virtual bool      Initialize() override;
       virtual bool      Execute() override;
       virtual bool      Update() override;
       virtual bool      Terminate() override;
       
       // Iceberg specific methods
       double            GetRandomVisibleVolume();
       double            AdjustPriceToAvoidRoundNumbers(double price);
       bool              CheckAndReplaceOrder();
       bool              IsOrderFilled(ulong ticket);
       bool              IsOrderPartiallyFilled(ulong ticket, double &filledVolume);
       bool              IsOrderCancelled(ulong ticket);
       bool              IsOrderExpired(ulong ticket);
       bool              IsOrderTimeout();
       ulong             GetCurrentOrderTicket() { return m_currentOrderTicket; }
       bool              IsOrderActive() { return m_orderActive; }
    };
    
    Executeメソッドは、注文の新しい可視部分を市場に発注します。
    bool CIcebergOrder::Execute()
    {
       if(!m_isActive)
       {
          Print("Iceberg: Execute called but algorithm is not active");
          return false;
       }
          
       // If an order is already active, check its status
       if(m_orderActive)
       {
          Print("Iceberg: Execute called with active order ", m_currentOrderTicket);
          return CheckAndReplaceOrder();
       }
       
       // Calculate the volume for this execution
       double volumeToExecute = m_useRandomVisibleVolume ? 
                               GetRandomVisibleVolume() : 
                               m_visibleVolume;
                               
       // Ensure we don't exceed the remaining volume
       if(volumeToExecute > m_remainingVolume)
          volumeToExecute = m_remainingVolume;
          
       Print("Iceberg: Placing order for ", DoubleToString(volumeToExecute, 2), 
             " at price ", DoubleToString(m_limitPrice, _Digits));
             
       // Place the order using OrderSend directly for more control
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       ZeroMemory(result);
       
       request.action = TRADE_ACTION_PENDING;
       request.symbol = m_symbol;
       request.volume = volumeToExecute;
       request.type = m_orderType;
       request.price = m_limitPrice;
       request.deviation = m_slippage;
       request.magic = 123456; // Magic number for identification
       
       // Send the order
       bool success = OrderSend(request, result);
       
       if(!success)
       {
          Print("Iceberg: OrderSend error: ", GetLastError());
          return false;
       }
       
       // Check the result
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("Iceberg: OrderSend failed with code: ", result.retcode);
          return false;
       }
       
       // Store the order ticket
       m_currentOrderTicket = result.order;
       m_orderActive = true;
       m_orderPlacementTime = TimeCurrent();
       
       Print("Iceberg: Order placed successfully. Ticket: ", m_currentOrderTicket,
             ", Volume: ", DoubleToString(volumeToExecute, 2), 
             ", Remaining: ", DoubleToString(m_remainingVolume, 2));
             
       return true;
    }
    

    Executeが実行されると、まずアルゴリズムがアクティブであることを確認します。すでに生きているアイスバーグの子注文がある場合は、CheckAndReplaceOrderを呼び出して、キャンセルや再発注が必要かどうかを確認します。そうでなければ、可視スライス(固定またはランダム)を選び、残りの総量で上限を設定し、サイズと価格をログに記録します。

    その後、銘柄、数量、リミット価格、スリッページ、マジックナンバーを指定した予約注文リクエスト(TRADE_ACTION_PENDING)を構築し、OrderSendを呼び出します。送信エラーやTRADE_RETCODE_DONE以外の返却コードが返された場合はログに記録してfalseを返します。成功した場合は、新しいチケット番号を保存し、注文をアクティブとしてマーク、発注時刻を記録し、詳細をログに残してtrueを返します。

    Updateメソッドでは、注文が無期限にアクティブな状態にならないよう、タイムアウト検出機能が組み込まれています。

    bool CIcebergOrder::Update()
    {
       if(!m_isActive)
       {
          Print("Iceberg: Update called but algorithm is not active");
          return false;
       }
          
       // Check if all volume has been executed
       if(m_remainingVolume <= 0)
       {
          Print("Iceberg: All volume executed. Terminating algorithm.");
          return Terminate();
       }
       
       // Check if it's time to check order status
       datetime currentTime = TimeCurrent();
       if(currentTime >= m_lastCheckTime + m_checkInterval)
       {
          m_lastCheckTime = currentTime;
          
          // Log current market conditions
          double currentBid = SymbolInfoDouble(m_symbol, SYMBOL_BID);
          double currentAsk = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
          
          Print("Iceberg: Market update - Bid: ", DoubleToString(currentBid, _Digits),
                ", Ask: ", DoubleToString(currentAsk, _Digits),
                ", Limit Price: ", DoubleToString(m_limitPrice, _Digits));
          
          // If an order is active, check its status
          if(m_orderActive)
          {
             // Check if the order has been active too long
             if(IsOrderTimeout())
             {
                Print("Iceberg: Order ", m_currentOrderTicket, " has timed out. Replacing it.");
                
                // Cancel the current order
                if(!CancelOrder(m_currentOrderTicket))
                {
                   Print("Iceberg: Failed to cancel timed out order ", m_currentOrderTicket);
                }
                
                // Reset order tracking
                m_orderActive = false;
                m_currentOrderTicket = 0;
                
                // Place a new order after a delay
                Sleep(m_orderPlacementDelay);
                return Execute();
             }
             
             return CheckAndReplaceOrder();
          }
          else
          {
             // If no order is active, execute a new one
             Print("Iceberg: No active order, executing new order");
             return Execute();
          }
       }
       
       return true;
    }
    

    Updateメソッドは、m_checkIntervalで定義された間隔で市場および注文の状態を定期的に監視します。すべての注文量が完了していれば処理を終了します。そうでない場合、チェック時刻になると、現在の買値/売値とリミット価格をログに記録します。注文がアクティブな場合はタイムアウトを確認し、期限切れであれば注文をキャンセルして状態をリセットし、m_orderPlacementDelay秒待機してから新しいスライスを発注するために再実行します。期限切れでない場合は CheckAndReplaceOrderに処理を委ねます。アクティブな注文がない場合は、単純にExecuteを呼び出して次の可視スライスを発注します。


    パフォーマンスアナライザーの実装

    パフォーマンスアナライザークラスは、各種指標を追跡し、アルゴリズムのパフォーマンスを分析・比較するためのメソッドを提供します。
    //+------------------------------------------------------------------+
    //| Performance Analyzer for Execution Algorithms                    |
    //+------------------------------------------------------------------+
    class CPerformanceAnalyzer
    {
    private:
       string            m_symbol;              // Symbol being analyzed
       datetime          m_startTime;           // Analysis start time
       datetime          m_endTime;             // Analysis end time
       double            m_decisionPrice;       // Price at decision time
       double            m_avgExecutionPrice;   // Average execution price
       double            m_totalVolume;         // Total volume executed
       double            m_implementationShortfall; // Implementation shortfall
       double            m_marketImpact;        // Estimated market impact
       double            m_slippage;            // Average slippage
       int               m_executionTime;       // Total execution time in seconds
       double            m_priceImprovement;    // Total price improvement
       
    public:
       // Constructor
       CPerformanceAnalyzer(string symbol, double decisionPrice);
       
       // Analysis methods
       void              RecordExecution(datetime time, double price, double volume);
       void              CalculateMetrics();
       void              CompareAlgorithms(CPerformanceAnalyzer &other);
       
       // Reporting methods
       void              PrintReport();
       void              SaveReportToFile(string filename);
       
       // Getters
       double            GetImplementationShortfall() const { return m_implementationShortfall; }
       double            GetMarketImpact() const { return m_marketImpact; }
       double            GetSlippage() const { return m_slippage; }
       int               GetExecutionTime() const { return m_executionTime; }
       double            GetPriceImprovement() const { return m_priceImprovement; }
    };
    

    CPerformanceAnalyzerは、すべての取引後指標を一箇所にまとめて管理するクラスです。インスタンス生成時に銘柄と意思決定時の基準価格を渡すと、現在時刻を開始時刻として記録します。各子注文が約定するたびに RecordExecution(time, price, volume)を呼び出すことで、累積出来高、加重平均約定価格、タイムスタンプなどの集計が更新されます。戦略の終了時(または定期的に) CalculateMetrics() を呼ぶと、以下の指標が計算されます。

    • 実装ショートフォール(意思決定価格と実際の約定価格の損益差)
    • 公開価格に対する平均スリッページ
    • 自身の市場フットプリントによる推定市場影響
    • 総執行時間(終了時刻−開始時刻)
    • ベンチマークに対する価格改善の有無

    さらに、CompareAlgorithms(otherAnalyzer)を使えば、2回の実行を比較してどちらの戦略が有利だったかを確認できます。PrintReport()は主要統計をログに出力し、SaveReportToFile(filename)を使えば外部ファイルにフルレポートを保存できます。また、各指標を取得する軽量なゲッターも用意されており、カスタムダッシュボードや追加分析に活用可能です。


    アルゴリズムのパフォーマンスの比較

    市場環境によって最適な実行アルゴリズムは異なります。以下は一般的な目安(経験則)です。
    1. TWAP
      • 最適な市場環境:流動性が安定している市場
      • 長所:単純で予測可能な執行パターン
      • 短所:市場状況の変化に適応できない
    2. VWAP
      • 最適な市場環境:出来高パターンが予測可能な市場
      • 長所:市場の自然なリズムに合わせられるため、より良い価格で約定することが多い
      • 短所:過去の出来高データが必要であり、実装がより複雑
    3. アイスバーグ注文
      • 最適な市場環境:流動性が低い市場、または価格への影響を最小化したい場合
      • 長所:市場インパクトを抑えつつ価格コントロールを維持できる
      • 短所:執行時間が予測しにくく、部分的な約定のリスクがある


    執行アルゴリズムと取引戦略の統合

    実行アルゴリズムの本当の強みは、それらを取引戦略に統合したときに発揮されます。このセクションでは、作成した実行アルゴリズムを完全な売買システムへ組み込む方法を示します。

    実行マネージャー

    統合をシンプルにするために、すべての実行アルゴリズムを一括管理する実行マネージャークラス(CExecutionManager)を用意します。

    //+------------------------------------------------------------------+
    //| Execution Manager - Facade for all execution algorithms          |
    //+------------------------------------------------------------------+
    class CExecutionManager
    {
    private:
       CExecutionAlgorithm* m_algorithm;       // Current execution algorithm
       CPerformanceAnalyzer* m_analyzer;       // Performance analyzer
       
    public:
       // Constructor
       CExecutionManager();
       
       // Destructor
       ~CExecutionManager();
       
       // Algorithm creation methods
       bool              CreateTWAP(string symbol, double volume, datetime startTime, datetime endTime, 
                                   int intervals, ENUM_ORDER_TYPE orderType, bool useRandomization = false, 
                                   double randomizationFactor = 0.2, int slippage = 3);
                                   
       bool              CreateVWAP(string symbol, double volume, datetime startTime, datetime endTime, 
                                   int intervals, ENUM_ORDER_TYPE orderType, int historyDays = 5,
                                   bool adaptiveMode = true, int slippage = 3);
                                   
       bool              CreateIcebergOrder(string symbol, double volume, double limitPrice, ENUM_ORDER_TYPE orderType,
                                           double visibleVolume, double minVisibleVolume = 0.0, double maxVisibleVolume = 0.0,
                                           bool useRandomVisibleVolume = true, int orderPlacementDelay = 1000, 
                                           bool avoidRoundNumbers = true, int priceDeviation = 2, int slippage = 3);
       
       // Execution methods
       bool              Initialize();
       bool              Execute();
       bool              Update();
       bool              Terminate();
       
       // Performance analysis
       void              EnablePerformanceAnalysis(double decisionPrice);
       void              PrintPerformanceReport();
       
       // Getters
       CExecutionAlgorithm* GetAlgorithm() { return m_algorithm; }
    };
    

    このクラスはファサードとして機能し、戦略コードから直接アルゴリズムの詳細を扱わずに済むようにします。内部的には、現在選択されている CExecutionAlgorithm(TWAP、VWAP、または Iceberg)のポインタと、発注のパフォーマンスを追跡するためのCPerformanceAnalyzerを保持しています。

    ユーザーは、Create...メソッドのいずれかを呼び出して戦略を選択します。その際には、銘柄、総発注量、開始・終了時間(TWAP/VWAP 用)、インターバル数またはスライスサイズ、注文タイプ、そしてアルゴリズム固有のパラメータ(ランダム化、出来高履歴ウィンドウ、指値価格など)を渡します。アルゴリズムが生成された後は、通常のライフサイクルに従って実行を進めていきます。

    1. Initialize()は必要な状態やデータを準備します。
    2. Execute()は次のスライスや子注文を発注します。
    3. Update()は約定状況、市場データ、あるいはタイムアウトを監視します。
    4. Terminate()はすべての発注が完了した、または手動で停止したいときにクリーンアップをおこないます。

    パフォーマンス分析を有効化するにはEnablePerformanceAnalysis()を呼び出します。するとマネージャーは意思決定時の基準価格に対して実際の執行価格を記録し、PrintPerformanceReport()によって簡潔な損益およびスリッページのレポートを出力できます。さらに必要であれば、GetAlgorithm()を用いて生のアルゴリズムオブジェクトを取得し、独自の検証やメトリクス抽出に利用することも可能です。


    統合例

    以下は、異なるタイプの取引戦略に本稿の執行アルゴリズムを統合する方法の例です。

    1. トレンドフォロー戦略 × TWAP執行

      //+------------------------------------------------------------------+
      //| Trend-Following Strategy with TWAP Execution                     |
      //+------------------------------------------------------------------+
      void OnTick()
      {
         // Strategy parameters
         int maPeriodFast = 20;
         int maPeriodSlow = 50;
         double volume = 1.0;
         int executionIntervals = 5;
         
         // Calculate indicators
         double maFast = iMA(Symbol(), PERIOD_CURRENT, maPeriodFast, 0, MODE_SMA, PRICE_CLOSE, 0);
         double maSlow = iMA(Symbol(), PERIOD_CURRENT, maPeriodSlow, 0, MODE_SMA, PRICE_CLOSE, 0);
         
         // Check for entry conditions
         static bool inPosition = false;
         static CExecutionManager executionManager;
         
         if(!inPosition)
         {
            // Buy signal: Fast MA crosses above Slow MA
            if(maFast > maSlow)
            {
               // Create TWAP execution algorithm
               datetime startTime = TimeCurrent();
               datetime endTime = startTime + 3600; // 1 hour execution window
               
               if(executionManager.CreateTWAP(Symbol(), volume, startTime, endTime, 
                                             executionIntervals, ORDER_TYPE_BUY))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  Print("Buy signal detected. Starting TWAP execution.");
               }
            }
         }
         else
         {
            // Update the execution algorithm
            if(executionManager.Update())
            {
               // Check if execution is complete
               if(!executionManager.GetAlgorithm().IsActive())
               {
                  inPosition = false;
                  Print("TWAP execution completed.");
               }
            }
         }
      }
      
    2. 平均回帰戦略 × VWAP執行

      //+------------------------------------------------------------------+
      //| Mean-Reversion Strategy with VWAP Execution                      |
      //+------------------------------------------------------------------+
      void OnTick()
      {
         // Strategy parameters
         int rsiPeriod = 14;
         int rsiOversold = 30;
         int rsiOverbought = 70;
         double volume = 1.0;
         int executionIntervals = 5;
         
         // Calculate indicators
         double rsi = iRSI(Symbol(), PERIOD_CURRENT, rsiPeriod, PRICE_CLOSE, 0);
         
         // Check for entry conditions
         static bool inPosition = false;
         static bool isLong = false;
         static CExecutionManager executionManager;
         
         if(!inPosition)
         {
            // Buy signal: RSI oversold
            if(rsi < rsiOversold)
            {
               // Create VWAP execution algorithm
               datetime startTime = TimeCurrent();
               datetime endTime = startTime + 3600; // 1 hour execution window
               
               if(executionManager.CreateVWAP(Symbol(), volume, startTime, endTime, 
                                             executionIntervals, ORDER_TYPE_BUY))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  isLong = true;
                  Print("Buy signal detected. Starting VWAP execution.");
               }
            }
            // Sell signal: RSI overbought
            else if(rsi > rsiOverbought)
            {
               // Create VWAP execution algorithm
               datetime startTime = TimeCurrent();
               datetime endTime = startTime + 3600; // 1 hour execution window
               
               if(executionManager.CreateVWAP(Symbol(), volume, startTime, endTime, 
                                             executionIntervals, ORDER_TYPE_SELL))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  isLong = false;
                  Print("Sell signal detected. Starting VWAP execution.");
               }
            }
         }
         else
         {
            // Update the execution algorithm
            if(executionManager.Update())
            {
               // Check if execution is complete
               if(!executionManager.GetAlgorithm().IsActive())
               {
                  inPosition = false;
                  Print("VWAP execution completed.");
               }
            }
            
            // Check for exit conditions
            if(isLong && rsi > rsiOverbought)
            {
               executionManager.Terminate();
               inPosition = false;
               Print("Exit signal detected. Terminating VWAP execution.");
            }
            else if(!isLong && rsi < rsiOversold)
            {
               executionManager.Terminate();
               inPosition = false;
               Print("Exit signal detected. Terminating VWAP execution.");
            }
         }
      }
      
    3. ブレイクアウト戦略 x アイスバーグ注文
      //+------------------------------------------------------------------+
      //| Breakout Strategy with Iceberg Orders                            |
      //+------------------------------------------------------------------+
      void OnTick()
      {
         // Strategy parameters
         int channelPeriod = 20;
         double volume = 1.0;
         double visibleVolume = 0.1;
         
         // Calculate indicators
         double upperChannel = iHigh(Symbol(), PERIOD_CURRENT, iHighest(Symbol(), PERIOD_CURRENT, MODE_HIGH, channelPeriod, 1));
         double lowerChannel = iLow(Symbol(), PERIOD_CURRENT, iLowest(Symbol(), PERIOD_CURRENT, MODE_LOW, channelPeriod, 1));
         
         double currentPrice = SymbolInfoDouble(Symbol(), SYMBOL_BID);
         
         // Check for entry conditions
         static bool inPosition = false;
         static CExecutionManager executionManager;
         
         if(!inPosition)
         {
            // Buy signal: Price breaks above upper channel
            if(currentPrice > upperChannel)
            {
               // Create Iceberg Order
               double limitPrice = upperChannel; // Place limit order at breakout level
               
               if(executionManager.CreateIcebergOrder(Symbol(), volume, limitPrice, ORDER_TYPE_BUY,
                                                    visibleVolume, visibleVolume * 0.8, visibleVolume * 1.2,
                                                    true, 1000, true, 2, 3))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  Print("Buy breakout detected. Starting Iceberg Order execution.");
               }
            }
            // Sell signal: Price breaks below lower channel
            else if(currentPrice < lowerChannel)
            {
               // Create Iceberg Order
               double limitPrice = lowerChannel; // Place limit order at breakout level
               
               if(executionManager.CreateIcebergOrder(Symbol(), volume, limitPrice, ORDER_TYPE_SELL,
                                                    visibleVolume, visibleVolume * 0.8, visibleVolume * 1.2,
                                                    true, 1000, true, 2, 3))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  Print("Sell breakout detected. Starting Iceberg Order execution.");
               }
            }
         }
         else
         {
            // Update the execution algorithm
            if(executionManager.Update())
            {
               // Check if execution is complete
               if(!executionManager.GetAlgorithm().IsActive())
               {
                  inPosition = false;
                  Print("Iceberg Order execution completed.");
               }
            }
         }
      }
      

    すべての例に共通して、統合は次のような高レベルのパターンに従います。

    1. 状態管理

      • ブール値inPositionで現在注文を執行中かどうかを管理します。
      • CExecutionManager executionManagerは静的に保持され、ティックをまたいで選択したアルゴリズムのライフサイクルを管理します。

    2. エントリーロジック

      • エントリーシグナル(MAクロスオーバー、RSI閾値、チャネルブレイク)が発生したら、executionManagerの適切な生成メソッド(TWAP, VWAP, Iceberg)を呼び出し、銘柄シン、合計注文量、時間ウィンドウまたは指値価格、スライスのパラメータ、注文種別を渡します。
      • 生成に成功したら、すぐに executionManager.Initialize()を呼び、inPosition=true に設定し、開始をログに残します。

    3. 実行の継続

      • inPositionがtrueの間は、毎回OnTick()内で executionManager.Update()を呼びます。
      • Update()の内部では、必要に応じてExecute()を呼び、約定状況をチェックし、タイムアウトやマーケット更新を処理し、次のスライスをスケジューリングします(Icebergの場合は子注文のキャンセル・差し替えもおこないます)。

    4. 完了と終了

      • 各アップデート後にexecutionManager.GetAlgorithm()->IsActive()を確認します。これがfalseを返したら(すべてのインターバルが終了したか、残量が尽きた状態)、inPosition=falseにして実行完了をログに残します。
      • VWAPを用いた平均回帰(逆張り)の例では、追加の退出条件があります。もし実行途中で価格がRSIの閾値を超えて反転した場合、executionManager.Terminate()を呼び、残りのスライスを中止します。

    それぞれのルールは次のとおりです。
    1. トレンドフォロー + TWAP
      エントリ:短期MAが長期MAを上抜けたら発動
      実行:1ロットの買い注文を1時間で5つの等分スライスに分割

    2. 平均回帰 + VWAP
      エントリ:RSI < 30 で買い、RSI > 70 で売り
      実行:過去の出来高に基づき、1時間にわたって5スライスに分配して1ロットを執行
      早期終了:シグナルが反転(例:買い途中で RSI > 70)したら executionManager.Terminate() で残りを中止

    3. ブレイクアウト + アイスバーグ
      エントリ:価格がチャネル高値を上抜け(買い)、安値を下抜け(売り)したとき
      実行:ブレイクアウト価格に指値を置き、常に約0.1ロットだけを板に見せ、全1ロットが完了するまで補充を繰り返す

    CreateTWAP、CreateVWAP、CreateIcebergOrderを入れ替えるだけで、注文管理ロジックを重複させることなく、任意の執行アルゴリズムをシグナルロジックに組み込むことができます。


    統合戦略

    以下が完全なコードです。

    //+------------------------------------------------------------------+
    //|                                  IntegratedStrategy.mq5          |
    //|                        Copyright 2025, MetaQuotes Software Corp. |
    //|                                    https://www.metaquotes.net    |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2025, MetaQuotes Software Corp."
    #property link      "https://www.metaquotes.net"
    #property version   "1.00"
    
    #include "ExecutionAlgorithm.mqh"
    #include "TWAP.mqh"
    #include "VWAP.mqh"
    #include "IcebergOrder.mqh"
    #include "PerformanceAnalyzer.mqh"
    #include "ExecutionManager.mqh"
    
    // Input parameters
    input int FastMA = 20;                  // Fast moving average period
    input int SlowMA = 50;                  // Slow moving average period
    input double TradingVolume = 0.1;       // Trading volume
    input bool UseAdaptiveExecution = true; // Use adaptive execution based on market conditions
    
    // Global variables
    CExecutionManager *g_executionManager = NULL;
    int g_maHandle1 = INVALID_HANDLE;
    int g_maHandle2 = INVALID_HANDLE;
    
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
    {
       // Initialize execution manager
       g_executionManager = new CExecutionManager(Symbol(), 3, UseAdaptiveExecution);
       
       // Initialize indicators
       g_maHandle1 = iMA(Symbol(), Period(), FastMA, 0, MODE_SMA, PRICE_CLOSE);
       g_maHandle2 = iMA(Symbol(), Period(), SlowMA, 0, MODE_SMA, PRICE_CLOSE);
       
       if(g_maHandle1 == INVALID_HANDLE || g_maHandle2 == INVALID_HANDLE)
       {
          Print("Failed to create indicator handles");
          return INIT_FAILED;
       }
       
       return(INIT_SUCCEEDED);
    }
    
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
       // Clean up
       if(g_executionManager != NULL)
       {
          delete g_executionManager;
          g_executionManager = NULL;
       }
       
       // Release indicator handles
       IndicatorRelease(g_maHandle1);
       IndicatorRelease(g_maHandle2);
    }
    
    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick()
    {
       // Update execution algorithms
       if(g_executionManager != NULL)
          g_executionManager.UpdateAlgorithms();
       
       // Only process at bar open
       if(iVolume(_Symbol, PERIOD_CURRENT, 0) > 1)
          return;
          
       // Get indicator values
       double fastMA[2], slowMA[2];
       
       if(CopyBuffer(g_maHandle1, 0, 0, 2, fastMA) <= 0 ||
          CopyBuffer(g_maHandle2, 0, 0, 2, slowMA) <= 0)
       {
          Print("Failed to copy indicator buffers");
          return;
       }
       
       // Check for trend signals
       bool buySignal = (fastMA[0] > slowMA[0]) && (fastMA[1] <= slowMA[1]);
       bool sellSignal = (fastMA[0] < slowMA[0]) && (fastMA[1] >= slowMA[1]);
       
       // Execute signals using the execution manager
       if(buySignal)
       {
          Print("Buy signal detected");
          
          if(UseAdaptiveExecution)
          {
             // Let the execution manager select the best algorithm
             g_executionManager.ExecuteSignal(SIGNAL_TYPE_BUY, TradingVolume);
          }
          else
          {
             // Manually create a TWAP algorithm
             datetime currentTime = TimeCurrent();
             CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime, 
                                                       currentTime + 3600, 6, 
                                                       ORDER_TYPE_BUY, true);
          }
       }
       else if(sellSignal)
       {
          Print("Sell signal detected");
          
          if(UseAdaptiveExecution)
          {
             // Let the execution manager select the best algorithm
             g_executionManager.ExecuteSignal(SIGNAL_TYPE_SELL, TradingVolume);
          }
          else
          {
             // Manually create a TWAP algorithm
             datetime currentTime = TimeCurrent();
             CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime, 
                                                       currentTime + 3600, 6, 
                                                       ORDER_TYPE_SELL, true);
          }
       }
    }
    //+------------------------------------------------------------------+
    

    IntegratedStrategy.mq5 EAは、まずメタデータ(著作権、リンク、バージョン)を宣言し、すべての執行アルゴリズムクラスおよびパフォーマンスアナライザーのヘッダをインクルードするところから始まります。次に、ユーザーが調整可能な4つの入力を定義します。すなわち、短期・長期のSMA期間、シグナルごとの総取引量、そして「アダプティブ」執行をオン/オフするブールフラグです(アダプティブモードでは、マネージャが内部的にTWAP, VWAP, Icebergのどれを使うかを自動選択します)。さらに、CExecutionManager へのグローバルポインタと、ティックをまたいで保持される2つのインジケーターハンドルも宣言されます。

    OnInit()では、現在の銘柄、同時最大3アルゴリズム、そしてアダプティブフラグを渡して執行マネージャをインスタンス化し、2つのSMAインジケーターハンドルを作成します。もしどちらかのハンドル作成に失敗した場合は初期化を中断します。OnDeinit()は単にマネージャを削除し、インジケータハンドルを解放して、EAを削除したりプラットフォームを終了したときにメモリやハンドルがリークしないようにします。

    コアロジックは OnTick()内にあります。最初に、執行マネージャのUpdateAlgorithms()を呼び出し、既存の子注文(TWAPスライス、VWAPバケット、Icebergレッグ)が必要に応じて処理・キャンセル・補充されるようにします。次に、新しいバーを待つために、ティックボリュームがまだ積み上がっている場合はスキップします。バーが開始すると、短期・長期それぞれのSMA値を直近2本取得します。短期が長期を下から上へクロスすれば買いシグナル、逆なら売りシグナルが発生します。

    アダプティブ執行が有効な場合は、シグナルと取引量を g_executionManager.ExecuteSignal() に渡し、適切なアルゴリズムの選択を任せます。そうでなければ、1時間のウィンドウと6スライスでTWAPインスタンスを手動で起動します。このパターンにより、エントリーロジック(トレンド検出)と注文管理ロジックを明確に分離でき、同じファサードから複数の執行スタイルを重複なく扱えるようになります。

    ここにテイクプロフィットを組み込むと、コードは次のように変更されます。

    //+------------------------------------------------------------------+
    //|                                  IntegratedStrategy.mq5          |
    //|                        Copyright 2025, MetaQuotes Software Corp. |
    //|                                    https://www.metaquotes.net    |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2025, MetaQuotes Software Corp."
    #property link      "https://www.metaquotes.net"
    #property version   "1.00"
    
    #include "ExecutionAlgorithm.mqh"
    #include "TWAP.mqh"
    #include "VWAP.mqh"
    #include "IcebergOrder.mqh"
    #include "PerformanceAnalyzer.mqh"
    #include "ExecutionManager.mqh"
    #include <Trade\Trade.mqh>
    
    // Input parameters
    input int    FastMA              = 20;    // Fast moving average period
    input int    SlowMA              = 50;    // Slow moving average period
    input double TradingVolume       = 0.1;   // Trading volume
    input bool   UseAdaptiveExecution = true; // Use adaptive execution based on market conditions
    input double EquityTPPercent     = 10.0;  // Equity Take Profit in percent
    input double EquitySLPercent     = 5.0;   // Equity Stop Loss in percent
    
    // Global variables
    CExecutionManager *g_executionManager = NULL;
    int                g_maHandle1       = INVALID_HANDLE;
    int                g_maHandle2       = INVALID_HANDLE;
    double             g_initialEquity   = 0.0;
    CTrade             trade;
    
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit() {
        // Record initial equity
        g_initialEquity = AccountInfoDouble(ACCOUNT_EQUITY);
    
        // Initialize execution manager
        g_executionManager = new CExecutionManager(Symbol(), 3, UseAdaptiveExecution);
    
        // Initialize indicators
        g_maHandle1 = iMA(Symbol(), Period(), FastMA, 0, MODE_SMA, PRICE_CLOSE);
        g_maHandle2 = iMA(Symbol(), Period(), SlowMA, 0, MODE_SMA, PRICE_CLOSE);
    
        if(g_maHandle1 == INVALID_HANDLE || g_maHandle2 == INVALID_HANDLE) {
            Print("Failed to create indicator handles");
            return INIT_FAILED;
        }
    
        return(INIT_SUCCEEDED);
    }
    
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason) {
        // Clean up
        if(g_executionManager != NULL) {
            delete g_executionManager;
            g_executionManager = NULL;
        }
    
        // Release indicator handles
        IndicatorRelease(g_maHandle1);
        IndicatorRelease(g_maHandle2);
    }
    
    //+------------------------------------------------------------------+
    //| Check equity-based TP and SL, then reset baseline                |
    //+------------------------------------------------------------------+
    void CheckEquityTPandSL() {
        double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
        double tpEquity      = g_initialEquity * (1.0 + EquityTPPercent / 100.0);
        double slEquity      = g_initialEquity * (1.0 - EquitySLPercent / 100.0);
    
        if(currentEquity >= tpEquity) {
            Print("Equity Take Profit reached: ", currentEquity);
            CloseAllPositions();
            g_initialEquity = currentEquity;
            Print("Equity baseline reset to: ", g_initialEquity);
        } else if(currentEquity <= slEquity) {
            Print("Equity Stop Loss reached: ", currentEquity);
            CloseAllPositions();
            g_initialEquity = currentEquity;
            Print("Equity baseline reset to: ", g_initialEquity);
        }
    }
    
    //+------------------------------------------------------------------+
    //| Close all open positions                                         |
    //+------------------------------------------------------------------+
    void CloseAllPositions() {
        CPositionInfo  m_position;
        CTrade m_trade;
        for(int i = PositionsTotal() - 1; i >= 0; i--) // loop all Open Positions
            if(m_position.SelectByIndex(i)) { // select a position
                m_trade.PositionClose(m_position.Ticket()); // then delete it --period
            }
    }
    
    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick() {
        // Check and reset equity thresholds
        CheckEquityTPandSL();
    
        // Update execution algorithms
        if(g_executionManager != NULL)
            g_executionManager.UpdateAlgorithms();
    
        // Only process at bar open
        if(iVolume(_Symbol, PERIOD_CURRENT, 0) > 1)
            return;
    
        // Get indicator values
        double fastMA[2], slowMA[2];
    
        if(CopyBuffer(g_maHandle1, 0, 0, 2, fastMA) <= 0 ||
                CopyBuffer(g_maHandle2, 0, 0, 2, slowMA) <= 0) {
            Print("Failed to copy indicator buffers");
            return;
        }
    
        // Check for trend signals
        bool buySignal  = (fastMA[0] > slowMA[0]) && (fastMA[1] <= slowMA[1]);
        bool sellSignal = (fastMA[0] < slowMA[0]) && (fastMA[1] >= slowMA[1]);
    
        // Execute signals using the execution manager
        if(buySignal) {
            Print("Buy signal detected");
    
            if(UseAdaptiveExecution) {
                // Let the execution manager select the best algorithm
                g_executionManager.ExecuteSignal(SIGNAL_TYPE_BUY, TradingVolume);
            } else {
                datetime currentTime = TimeCurrent();
                CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime,
                              currentTime + 3600, 6,
                              ORDER_TYPE_BUY, true);
            }
        } else if(sellSignal) {
            Print("Sell signal detected");
    
            if(UseAdaptiveExecution) {
                // Let the execution manager select the best algorithm
                g_executionManager.ExecuteSignal(SIGNAL_TYPE_SELL, TradingVolume);
            } else {
                datetime currentTime = TimeCurrent();
                CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime,
                              currentTime + 3600, 6,
                              ORDER_TYPE_SELL, true);
            }
        }
    }
    //+------------------------------------------------------------------+
    


    バックテスト結果

    1. 資産曲線と残高曲線

    緑の「Balance(残高)」はポジションをクローズしたタイミングでの口座資産を階段状に描き、青の「Equity」(資産)」は未実現損益を含んだ推移を滑らかに示しています。1月から3月初旬にかけて明確な上昇トレンドが見られ、いくつかの押し目が確認できます。各ドローダウンは約10〜16%で止まり、その後の勝ちトレードの連続で利益を回復しています。このパターンから、このシステムはトレンド相場で優位性を発揮する一方で、許容範囲内のドローダウンを抱えることがわかります。

    2. ボリュームとリスクの活用

    下部の「Deposit Load(三角形)」は時間の経過とともに徐々に縮小しています。これは、エクイティに対するポジションサイズの割合を示しています。開始時は残高の約10%でしたが、固定ロット運用のためエクイティが増えるにつれて割合が低下していきます。そのため、口座残高が増えても取引ごとのリスクは減少し、ドローダウンがドルベースでは大きくなっても割合としては同程度に収まります。

    3. 主要な収益性指標

    • 初回入金額:1,000ドル

    • 純利益: +$703(約2か月で70%のリターン)

    • プロフィットファクター:2.34(1ドルの損失に対して2.34ドルの利益)

    • 期待利益:平均1取引あたり2.34ドル

    • シャープレシオ:5.47(非常に高く、リスク調整後のリターンが優秀)

    これらの数値は、この戦略が単に利益を出すだけでなく、ボラティリティを上回る健全な収益性を確保していることを示しています。

    4. ドローダウンと回復

    • 最大残高ドローダウン:156ポイント(9.99%)

    • 最大エクイティドローダウン:228ポイント(15.89%)

    • リカバリーファクター:3.08(純利益 ÷ 最大ドローダウン)

    リカバリーファクターは2以上で良好とされますが、この場合3.08で、最悪の損失に対して3倍以上の利益を生み出しています。

    5. 取引分布

    • 総取引数:300(600ディール、エントリー+エグジットを1セットとしてカウント)

    • 勝率:76%(228勝、72敗)

    • 平均利益:$5.39

    • 平均損失:–$7.31

    勝率とプロフィットファクターは優秀ですが、平均損失が平均利益を上回っている点には注意が必要です。相場環境が逆転した際にリスクになる可能性があります。

    6. 連勝・連敗と一貫性

    • 最大連勝:87取引、+$302

    • 最大連敗:23取引、–$156

    • 平均連勝:57取引

    • 平均連敗:18取引

    長期の連勝が右肩上がりの成績を支えていますが、最大の連敗でも口座の約15%に留まっています。


    結論

    混雑した市場アリーナで孤独なトレーダーを想像してください。すべてのティックが重要であり、すべての約定価格が利益か損失をささやきます。TWAP、VWAP、そしてアイスバーグ注文をツールキットに組み込めば、単に値動きに反応するだけではなく、それを演奏する側に回れるのです。かつては機関投資家専用の高度なアルゴリズムが、今やあなたの指先ひとつで使え、流動性をレーザーのように切り裂き、混沌とした板情報を機会へと変えていきます。

    TWAPはあなたの安定したメトロノームとなり、設定した時間間隔で均等にサイズを刻んでいきます。これは、相場が落ち着いていて、ただ滑らかに約定させたいときに最適です。VWAPは市場のリズムを読み取り、出来高が最も集中する瞬間を狙って仕掛け、マーケットの鼓動に合わせて売買します。そして、自分の意図を隠したいときには、アイスバーグ注文が本当のサイズを水面下に隠し、約定に必要な分だけを見せ、大口プレイヤーを警戒させることなく取引を成立させます。

    しかし、これらは単なる個別のテクニックではありません。私たちのモジュール型MQL5フレームワークによって、どんな戦略(トレンドフォロー、逆張り、ブレイクアウト狙い)にも簡単に組み込むことができます。統合されたExecutionManagerによってアルゴリズムをスワップ、組み合わせ、さらには取引中にレイヤーとして重ねることも可能です。同時に、PerformanceAnalyzerが隙なく監視し、スリッページやショートフォール、市場インパクトをピップ単位で計測します。

    次のステップは、執行を生き物のように進化させることです。TWAP にボラティリティの急変から学習させ、VWAPをより深い流動性プールへとルーティングし、アイスバーグには捕食者が潜む場を感知させ、さらに奥深く身を隠す力を持たせるのです。そして、それで終わりではありません。機械学習を注入して最適な発注タイミングを予測させたり、複数の注文タイプを組み合わせて自分だけのハイブリッド執行を創り出すこともできます。

    取引の世界は決して止まりません。あなたの注文執行も同じです。果敢に試し、積極的に実験し、あらゆるスライス、あらゆる約定を計算された優位性へと変えてください。あなたのアドバンテージは、コードの中に眠っています。

    便宜上、この記事に含まれるファイルの概要を以下に示します。

    ファイル名 説明
    ExecutionAlgorithm.mqh すべての実行アルゴリズムの基盤クラス 
    TWAP.mqh 時間加重平均価格の実装
    VWAP.mqh 出来高加重平均価格の導入
    IcebergOrder.mqh アイスバーグ注文の実装
    PerformanceAnalyzer.mqh 実行パフォーマンスを分析するためのツール
    ExecutionManager.mqh 取引戦略との簡単な統合のためのファサード
    IntegratedStrategy.mq5
    取引戦略との統合を示すEAの例
    IntegratedStrategy - Take Profit.mq5 口座残高のパーセンテージで利益確定と損切りを設定する取引戦略との統合を示すEAの例
    これらの高度な執行アルゴリズムをトレーディングツールキットに組み込むことで、よりプロフェッショナルで効率的な取引へと大きな一歩を踏み出すことができます。大口取引の市場インパクトを最小化したい場合でも、平均約定価格を改善したい場合でも、あるいは取引手法にさらなる洗練を加えたい場合でも、これらのアルゴリズムは貴重な解決策を提供し、今日の競争の激しい市場においてあなたの取引パフォーマンスを向上させることができます。

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

    添付されたファイル |
    TWAP.mqh (9.35 KB)
    VWAP.mqh (15.91 KB)
    IcebergOrder.mqh (15.33 KB)
    最後のコメント | ディスカッションに移動 (6)
    i_vergo
    i_vergo | 16 5月 2025 において 07:44
    あなたのアルゴをテストしています。
    ファイル、ExecutionAlgorithm.mqh に、注文を出す際に request.type_filling = ORDER_FILLING_IOC; という行を追加し、注文を出す際の問題を修正しました。

    H1 でテストしたところ、SL も TP も適用されず、すべての取引が損失で終了しました。

    また、コンパイル中に警告が発生しました。
    long'から'double'へのタイプ変換によるデータ損失の可能性 VWAP.mqh 271 41
    long'から'double'への型変換によるデータ損失の可能性 VWAP.mqh 272 22
    long' から 'double' への型変換によるデータ損失の可能性 VWAP.mqh 449 17
    long' から 'double' への型変換によるデータ損失の可能性 PerformanceAnalyzer.mqh 222 17
    long' から 'double' への型変換によるデータ損失の可能性 ExecutionManager.mqh 418 17


    アルゴのテスト方法、
    時間枠、その他推奨事項があれば教えてください。
    i_vergo
    i_vergo | 16 5月 2025 において 07:57
    警告を修正する
    また、コンパイル中に警告が発生する
    long'から'double'への型 変換による データ損失の可能性 VWAP .mqh 271 41
    long'から'double'への型変換によるデータ損失の可能性 VWAP .mqh 272 22
    long」から「double」への型変換によるデータ損失の可能性 VWAP .mqh 449 17
    long」から「double」への型変換によるデータ損失の可能性 PerformanceAnalyzer .mqh 222 17
    long' から 'double' への型変換によるデータ損失の可能性 ExecutionManager .mqh 418 17


    コード行
    m_volumeProfile[intervalIndex] += rates[i].tick_volu


    に変更した。
    m_volumeProfile[intervalIndex] += (double)rates[i].tick_volume;

    それは警告を修正しました
    今、私の他のクエリに関する指導が必要です。
    時間枠
    また、
    バックテスト中のすべての取引が損失となる理由
    あなたのこの偉大な仕事をテストする方法。
    Dominic Michael Frehner
    Dominic Michael Frehner | 16 5月 2025 において 07:57
    i_vergo データ損失の可能性 VWAP.mqh 271 41
    long'から'double'への型変換によるデータ損失の可能性 VWAP.mqh 272 22
    long' から 'double' への型変換によるデータ損失の可能性 VWAP.mqh 449 17
    long' から 'double' への型変換によるデータ損失の可能性 PerformanceAnalyzer.mqh 222 17
    long' から 'double' への型変換によるデータ損失の可能性 ExecutionManager.mqh 418 17


    アルゴのテスト方法、
    時間枠、その他推奨事項があれば教えてください。

    警告は問題ではないが、すぐに修正できるだろう。しかし、作者がバックテストに使用した設定と入力をステップ・バイ・ステップで示すことができれば最高だ。

    CapeCoddah
    CapeCoddah | 16 5月 2025 において 12:01

    I_Virgoの結果は、おそらく彼が間違った時間枠と通貨ペアを使用したためです。 バックテストレポートを見る限り、約2000バーのうち、時間枠はM1かM5で、ペアは不明でした。

    MQがバックテストレポートに時間枠と通貨ペアを追加し、さらにペアの結果をより詳細に分けてくれれば、著者のバックテスト結果を より忠実に再現でき、FXペアへの適用性も判断できるのにと思います。また、EAが実行中にチャートにテキストを投稿できれば非常に助かります。


    私もこの記事はすばらしいと思いますし、彼のテクニックを他のEAに応用することを期待して、徹底的に研究するつもりです。


    ケープコッダ

    Shashin Wijewardhane
    Shashin Wijewardhane | 11 7月 2025 において 08:40
    //+------------------------------------------------------------------+
    //| すべての実行アルゴリズムの基本クラス|
    //+------------------------------------------------------------------+
    class CExecutionAlgorithm
    {
    protected:
       string            m_symbol;           // 取引シンボル
       double            m_totalVolume;      // 実行する総量
       double            m_executedVolume;   // ボリュームはすでに実行済み
       double            m_remainingVolume;  // 実行可能な残量
       datetime          m_startTime;        // 実行開始時刻
       datetime          m_endTime;          // 実行終了時刻
       int               m_slippage;         // 許容されるスリップポイント
       bool              m_isActive;         // アルゴリズムは現在アクティブか
       
       // 統計
       double            m_avgExecutionPrice; // 平均執行価格
       int               m_totalOrders;       // 注文総数
       int               m_filledOrders;      // 埋まった注文の数
       
    public:
       // コンストラクタ
       CExecutionAlgorithm(string symbol, double volume, datetime startTime, datetime endTime, int slippage);
       
       // デストラクタ
       virtual ~CExecutionAlgorithm();
       
       // 派生クラスが実装する仮想メソッド
       virtual bool      Initialize();
       virtual bool      Execute() = 0;
       virtual bool      Update() = 0;
       virtual bool      Terminate() = 0;
       
       // 共通メソッド
       bool              IsActive() { return m_isActive; }
       double            GetExecutedVolume() { return m_executedVolume; }
       double            GetRemainingVolume() { return m_remainingVolume; }
       double            GetAverageExecutionPrice() { return m_avgExecutionPrice; }
       
       // ヘルパーメソッド
       bool              PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price = 0.0);
       bool              ModifyOrder(ulong ticket, double price, double sl, double tp);
       bool              CancelOrder(ulong ticket);
       void              UpdateAverageExecutionPrice(double price, double volume);
       
       // 適切な充填モードを取得するヘルパーメソッド
       ENUM_ORDER_TYPE_FILLING GetFillingMode();
    };
    
    //+------------------------------------------------------------------+
    //| コンストラクタ|
    //+------------------------------------------------------------------+
    CExecutionAlgorithm::CExecutionAlgorithm(string symbol, double volume, 
                                           datetime startTime, datetime endTime, 
                                           int slippage)
    {
       m_symbol = symbol;
       m_totalVolume = volume;
       m_executedVolume = 0.0;
       m_remainingVolume = volume;
       m_startTime = startTime;
       m_endTime = endTime;
       m_slippage = slippage;
       m_isActive = false;
       
       m_avgExecutionPrice = 0.0;
       m_totalOrders = 0;
       m_filledOrders = 0;
    }
    
    //+------------------------------------------------------------------+
    //| デストラクタ|
    //+------------------------------------------------------------------+
    CExecutionAlgorithm::~CExecutionAlgorithm()
    {
       // 必要に応じてリソースをクリーンアップする
    }
    
    //+------------------------------------------------------------------+
    //| アルゴリズムの初期化|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::Initialize()
    {
       // 入力を検証する
       if(m_symbol == "" || m_totalVolume <= 0.0)
       {
          Print("Invalid inputs for execution algorithm");
          return false;
       }
       
       // シンボルが存在するかチェックする
       if(!SymbolSelect(m_symbol, true))
       {
          Print("Symbol not found: ", m_symbol);
          return false;
       }
       
       // 統計をリセットする
       m_executedVolume = 0.0;
       m_remainingVolume = m_totalVolume;
       m_avgExecutionPrice = 0.0;
       m_totalOrders = 0;
       m_filledOrders = 0;
       
       return true;
    }
    
    //+------------------------------------------------------------------+
    //| シンボルの適切な充填モードを取得する。
    //+------------------------------------------------------------------+
    ENUM_ORDER_TYPE_FILLING CExecutionAlgorithm::GetFillingMode()
    {
       // シンボルの充填モードを取得する
       int filling_modes = (int)SymbolInfoInteger(m_symbol, SYMBOL_FILLING_MODE);
       
       // 利用可能な充填モードを優先順にチェックする
       if((filling_modes & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK)
          return ORDER_FILLING_FOK;
       else if((filling_modes & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC)
          return ORDER_FILLING_IOC;
       else
          return ORDER_FILLING_RETURN;
    }
    
    //+------------------------------------------------------------------+
    //| 注文する|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price = 0.0)
    {
       // 入力を検証する
       if(volume <= 0.0)
       {
          Print("Invalid order volume");
          return false;
       }
       
       // リクエストを準備する
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       
       request.symbol = m_symbol;
       request.volume = volume;
       request.type = orderType;
       request.deviation = m_slippage;
       request.magic = 123456; // 識別のためのマジックナンバー
       
       // 注文の種類に応じて、適切なアクションと価格を設定する。
       if(orderType == ORDER_TYPE_BUY || orderType == ORDER_TYPE_SELL)
       {
          // 市場注文
          request.action = TRADE_ACTION_DEAL;
          request.type_filling = GetFillingMode();
          
          if(orderType == ORDER_TYPE_BUY)
             request.price = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
          else
             request.price = SymbolInfoDouble(m_symbol, SYMBOL_BID);
       }
       else
       {
          // 未決注文
          request.action = TRADE_ACTION_PENDING;
          if(price <= 0.0)
          {
             Print("Price must be specified for pending orders");
             return false;
          }
          request.price = price;
       }
       
       // 注文を送信する
       if(!OrderSend(request, result))
       {
          Print("OrderSend error: ", GetLastError());
          return false;
       }
       
       // 結果をチェックする
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("OrderSend failed with code: ", result.retcode, " - ", result.comment);
          return false;
       }
       
       // 統計を更新する
       m_totalOrders++;
       
       // 成行注文の場合、即座に執行統計を更新する
       if(orderType == ORDER_TYPE_BUY || orderType == ORDER_TYPE_SELL)
       {
          m_filledOrders++;
          UpdateAverageExecutionPrice(request.price, volume);
          m_executedVolume += volume;
          m_remainingVolume -= volume;
       }
       
       Print("Order placed successfully. Ticket: ", result.order, " Volume: ", volume, " Price: ", request.price);
       
       return true;
    }
    
    //+------------------------------------------------------------------+
    //| 既存の注文を変更する|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::ModifyOrder(ulong ticket, double price, double sl, double tp)
    {
       // リクエストを準備する
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       
       request.action = TRADE_ACTION_MODIFY;
       request.order = ticket;
       request.price = price;
       request.sl = sl;
       request.tp = tp;
       
       // 修正リクエストを送信する
       if(!OrderSend(request, result))
       {
          Print("OrderModify error: ", GetLastError());
          return false;
       }
       
       // 結果をチェックする
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("OrderModify failed with code: ", result.retcode, " - ", result.comment);
          return false;
       }
       
       Print("Order modified successfully. Ticket: ", ticket);
       
       return true;
    }
    
    //+------------------------------------------------------------------+
    //| 既存の注文をキャンセルする|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::CancelOrder(ulong ticket)
    {
       // リクエストを準備する
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       
       request.action = TRADE_ACTION_REMOVE;
       request.order = ticket;
       
       // キャンセルリクエストを送信する
       if(!OrderSend(request, result))
       {
          Print("OrderCancel error: ", GetLastError());
          return false;
       }
       
       // 結果をチェックする
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("OrderCancel failed with code: ", result.retcode, " - ", result.comment);
          return false;
       }
       
       Print("Order cancelled successfully. Ticket: ", ticket);
       
       return true;
    }
    
    //+------------------------------------------------------------------+
    //| 平均約定価格の更新|
    //+------------------------------------------------------------------+
    void CExecutionAlgorithm::UpdateAverageExecutionPrice(double price, double volume)
    {
       // 新しい平均執行価格を計算する
       if(m_executedVolume > 0.0)
       {
          // 新旧価格の加重平均
          m_avgExecutionPrice = (m_avgExecutionPrice * m_executedVolume + price * volume) / 
                               (m_executedVolume + volume);
       }
       else
       {
          // 最初の実行
          m_avgExecutionPrice = price;
       }
    }
    //+------------------------------------------------------------------+
    EAのサンプル EAのサンプル
    一般的なMACDを使ったEAを例として、MQL4開発の原則を紹介します。
    データサイエンスとML(第40回):機械学習データにおけるフィボナッチリトレースメントの利用 データサイエンスとML(第40回):機械学習データにおけるフィボナッチリトレースメントの利用
    フィボナッチリトレースメントはテクニカル分析で人気のツールであり、トレーダーが潜在的な反転ゾーンを特定するのに役立ちます。本記事では、これらのリトレースメントレベルを機械学習モデルの目的変数に変換し、この強力なツールを使用して市場をより深く理解できるようにする方法について説明します。
    エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
    この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
    データサイエンスとML(第39回):ニュース × 人工知能、それに賭ける価値はあるか データサイエンスとML(第39回):ニュース × 人工知能、それに賭ける価値はあるか
    ニュースは金融市場を動かす力を持っており、特に非農業部門雇用者数(NFP)のような主要指標の発表は大きな影響を与えます。私たちは、単一のヘッドラインが急激な価格変動を引き起こす様子を何度も目にしてきました。本記事では、ニュースデータと人工知能(AI)の強力な融合について探っていきます。