English Русский Deutsch
preview
ダイナミックマルチペアEAの形成(第4回):ボラティリティとリスク調整

ダイナミックマルチペアEAの形成(第4回):ボラティリティとリスク調整

MetaTrader 5 |
89 3
Hlomohang John Borotho
Hlomohang John Borotho

はじめに

マルチペア取引において、トレーダーが直面する課題の1つは、通貨ペアごとのボラティリティの違いによるパフォーマンスの不安定さです。前回の記事で述べたように、EUR/USDでうまく機能する戦略でも、GBP/JPYではリスクが過小または過大となり、パフォーマンスが低下する場合があります。固定ロットサイズや静的なストップロスを使用すると、ボラティリティの高い市場ではポジションが大きくなりすぎ、安定した市場ではチャンスを逃すことがあります。適応性が欠如していると、リスクのばらつきやドローダウンが増加し、特に重要なニュースや急激な相場変動時の結果が予測不能になります。

この問題を解決するため、EAにはボラティリティとリスク調整の仕組みを導入します。ATR (Average True Range)などのインジケーターとリスクベースの動的サイズ調整を組み込むことで、EAは市場状況に応じて取引パラメータを自動で調整します。これにより、各ポジションはボラティリティに応じて適切に調整され、一貫したリスク管理が可能になり、すべての取引ペアにおけるEAのパフォーマンスが向上します。



EA開発計画

取引ロジック

1. 複数銘柄ハンドラ

  • 銘柄リストの解析
  • 事前銘柄データの追跡
  • 同時ポジション管理


2. エントリー条件

3. ボラティリティに基づくリスク階層

4. ポジションサイズ管理

Risk Amount = Account Equity * Risk %
Position Size = Risk Amount / (SL Distance * Point Value)

5. Vストップターゲットの特定

[  Current Price  ]
        |
        v
[ Resistance Area ]  <-- Previous V-Stop Upper (TP for sells)
        |
        v
[  Current Price  ]
        |
        v
[  Support Area   ]  <-- Previous V-Stop Lower (TP for buys)

6. 取引ライフサイクルのタイムライン



導入手順

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

いつも通り、まずはエキスパートアドバイザー(EA)が注文を実行しポジションを管理するために必要な取引ライブラリをインポートします。

//--- Input settings
input string   Symbols = "XAUUSD,GBPUSD,USDCAD,USDJPY";  // Symbols to trade
input int      RSI_Period = 14;                           // RSI Period
input double   RSI_Overbought = 70.0;                     // RSI Overbought Level
input double   RSI_Oversold = 30.0;                       // RSI Oversold Level
input uint     ATR_Period = 14;                           // ATR Period for Volatility
input double   ATR_Multiplier = 2.0;                      // ATR Multiplier for SL
input double   RiskPercent_High = 0.02;                   // Risk % High Volatility
input double   RiskPercent_Mod = 0.01;                    // Risk % Moderate Volatility
input double   RiskPercent_Low = 0.005;                   // Risk % Low Volatility
input int      Min_Bars = 50;                             // Minimum Bars Required
input double   In_Lot = 0.01;                             // Default lot size
input int      StopLoss = 100;                            // SL in points
input int      TakeProfit = 100;                          // TP in points

このブロックでは、複数銘柄でEAを動作させるための入力設定を定義します。Symbols入力パラメータはトレーダーが取引対象の通貨ペアや銘柄を指定するためのもので、RSI関連の入力(RSI_Period、RSI_Overbought、RSI_Oversold)は買われ過ぎまたは売られすぎ状態に基づきエントリーポイントを特定するために使用されます。ボラティリティはATR (Average True Range)を用いて考慮され、ATRの期間やストップロスを動的に調整するための倍率も設定可能です。

EAはボラティリティレベルに応じてリスクエクスポージャーを調整します。高・中・低ボラティリティの状況で異なるリスク比率を設定できます。その他の入力パラメータには、十分な過去データを確保するためのMin_Bars、デフォルトのロットサイズIn_Lot、および固定ポイントベースのStopLossとTakeProfitがあり、動的なレベルが適用されない場合のフォールバックとして機能します。

//--- Global variables
string symb_List[];
int    Num_symbs = 0;
int    ATR_Handles[];
int    RSI_Handles[];
double Prev_ATR[];
double Prev_RSI[];
datetime LastBarTime[];
CTrade trade;

このセクションでは、EA全体で使用されるグローバル変数を宣言します。Symb_List[]は取引対象銘柄のリストを格納する配列で、Num_symbsはその銘柄数を保持します。ATR_Handles[]やRSI_Handles[]のような配列は、複数銘柄のATRおよびRSI計算用のインジケーターハンドルを管理し、EAが同時に複数銘柄を処理できるようにします。

Prev_ATR[]およびPrev_RSI[]は各銘柄の最新のインジケーター値を保持し、意思決定ロジックで使用されます。LastBarTime[]は各銘柄の最後に処理したバーの時間を追跡し、同じローソク足での重複処理を防ぎます。最後に、CTrade取引オブジェクトはMQL5組み込みの取引関数にアクセスでき、注文の実行やポジション管理を可能にします。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    //--- Split symbol list
    ushort separator = StringGetCharacter(",", 0);
    StringSplit(Symbols, separator, symb_List);
    Num_symbs = ArraySize(symb_List);
    
    //--- Resize arrays
    ArrayResize(ATR_Handles, Num_symbs);
    ArrayResize(RSI_Handles, Num_symbs);
    ArrayResize(Prev_ATR, Num_symbs);
    ArrayResize(Prev_RSI, Num_symbs);
    ArrayResize(LastBarTime, Num_symbs);
    ArrayInitialize(Prev_ATR, 0.0);
    ArrayInitialize(Prev_RSI, 50.0);
    ArrayInitialize(LastBarTime, 0);
    
    //--- Create indicator handles
    for(int i = 0; i < Num_symbs; i++)
    {
        string symbol = symb_List[i];
        ATR_Handles[i] = iATR(symbol, PERIOD_CURRENT, ATR_Period);
        RSI_Handles[i] = iRSI(symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE);
        
        if(ATR_Handles[i] == INVALID_HANDLE || RSI_Handles[i] == INVALID_HANDLE)
        {
            Print("Error creating indicator handles for ", symbol, " - Error: ", GetLastError());
            return INIT_FAILED;
        }
    }
    
    return INIT_SUCCEEDED;
}

OnInit()関数は、EAがチャートに読み込まれたときの初期化処理を担当します。まず、カンマ区切りのSymbols入力パラメータを分割して配列Symb_Listに格納し、管理対象のシンボル総数を計算します。次に、ATRやRSIのハンドル、前回のインジケーター値、最後に処理したバーの時間を格納するグローバル配列などをサイズ変更または初期化し、各銘柄が専用のストレージと追跡情報を持つようにします。初期値を設定することで、EAの最初の実行サイクルで未定義の動作が発生するのを防ぎます。

その後、関数は各銘柄をループ処理し、iATRおよびiRSIを使ってATRとRSIのインジケーターハンドルを作成します。これらのハンドルは、取引中にリアルタイムのインジケーター値を取得するために不可欠です。いずれかのハンドルの作成に失敗した場合(INVALID_HANDLEが返された場合)、エラーメッセージが出力され、INIT_FAILEDを返して初期化を中断します。すべてのインジケーターが正常に準備された場合、INIT_SUCCEEDEDを返し、EAが実行準備完了であることを示します。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    //--- Release indicator handles
    for(int i = 0; i < Num_symbs; i++)
    {
        if(ATR_Handles[i] != INVALID_HANDLE) 
            IndicatorRelease(ATR_Handles[i]);
        if(RSI_Handles[i] != INVALID_HANDLE) 
            IndicatorRelease(RSI_Handles[i]);
    }
}

OnDeinit()関数は、EAがチャートから削除されるか再初期化されるときに呼び出されます。主な目的は、各銘柄に関連付けられたインジケーターハンドルを解放してリソースをクリーンアップすることです。IndicatorRelease()をATRおよびRSIのハンドルに対して、ハンドルが有効な場合のみ呼び出すことで、システムメモリが適切に解放され、リークや不要なリソース消費を防ぎます。これにより、複数のEAやインジケーターを同時に実行している場合でもプラットフォームの安定性が維持されます。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    for(int i = 0; i < Num_symbs; i++)
    {
        string symbol = symb_List[i];
        
        //--- Check for new bar
        datetime currentBarTime = iTime(symbol, PERIOD_CURRENT, 0);
        if(LastBarTime[i] == currentBarTime) continue;
        LastBarTime[i] = currentBarTime;
        
        //--- Get indicator values
        double atr[2] = {0.0, 0.0};
        double rsi[2] = {50.0, 50.0};
        
        if(CopyBuffer(ATR_Handles[i], 0, 1, 2, atr) < 2 || 
           CopyBuffer(RSI_Handles[i], 0, 1, 2, rsi) < 2)
            continue;
        
        Prev_ATR[i] = atr[0];  // Previous bar's ATR
        Prev_RSI[i] = rsi[0];  // Previous bar's RSI
        
        //--- Get current prices
        MqlTick lastTick;
        if(!SymbolInfoTick(symbol, lastTick)) continue;
        double ask = lastTick.ask;
        double bid = lastTick.bid;
        
        //--- Check existing positions
        bool hasLong = false, hasShort = false;
        CheckExistingPositions(symbol, hasLong, hasShort);
        
        //--- Calculate volatility-based risk
        double riskPercent = CalculateRiskLevel(symbol);
        
        //--- Get V-Stop levels
        double vStopUpper = CalculateVStop(symbol, 1, true);  // Previous bar's upper band
        double vStopLower = CalculateVStop(symbol, 1, false); // Previous bar's lower band
        
        //--- Trade Entry Logic
        if(!hasLong && !hasShort)
        {
            //--- Sell signal: RSI overbought + price below V-Stop upper band
            if(rsi[0] > RSI_Overbought && bid < vStopUpper)
            {
                double tp = GetProfitTarget(symbol, false);  // Previous V-Stop level
                double sl = ask + ATR_Multiplier * atr[0];
                double lots = CalculatePositionSize(symbol, riskPercent, sl, ask);
                
                if(lots > 0)
                    ExecuteTrade(ORDER_TYPE_SELL, symbol);
            }
            //--- Buy signal: RSI oversold + price above V-Stop lower band
            else if(rsi[0] < RSI_Oversold && ask > vStopLower)
            {
                double tp = GetProfitTarget(symbol, true);   // Previous V-Stop level
                double sl = bid - ATR_Multiplier * atr[0];
                double lots = CalculatePositionSize(symbol, riskPercent, sl, bid);
                
                if(lots > 0)
                    ExecuteTrade(ORDER_TYPE_BUY, symbol);
            }
        }
        
        //--- Trailing Stop Logic
        UpdateTrailingStops(symbol, atr[0]);
    }
}

OnTick()関数は、市場が更新されるたびに実行され、取引リスト内の各銘柄を順に処理してリアルタイムの分析と取引管理をおこないます。まず、現在のバーのタイムスタンプと最後に記録したバーの時間を比較して、新しいバーが形成されたかどうかを確認します。新しいバーであれば、CopyBuffer()を使用して最新のATRおよびRSI値を取得し、グローバル配列に格納して意思決定に使用します。また、SymbolInfoTick()で現在のBidおよびAsk価格も取得し、正確なエントリーおよび決済レベルを確保します。

次に、CheckExistingPositions()関数を用いて、現在の銘柄に対してロングまたはショートのポジションが既に存在するかどうかを確認します。その後、CalculateRiskLevel()で銘柄のボラティリティに応じた適切なリスクレベルを算出し、最新のVストップレベルを取得してエントリーやトレーリングロジックに活用します。これらの情報に基づき、EAは取引エントリールールを適用します。具体的には、RSIが買われすぎを示し価格が上部Vストップを下回った場合は売り、RSIが売られすぎを示し価格が下部Vストップを上回った場合は買いを実行します。両方の場合において、ATRとVストップを用いて動的なストップロスとテイクプロフィットを計算し、ポジションサイズは定義されたリスクレベルに合わせて調整されます。

最後に、新規ポジションが開かれたかに関わらず、UpdateTrailingStops()を呼び出して既存ポジションを管理します。この関数は、最新のボラティリティデータに基づきストップロスを調整し、利益を確保しつつ損失を最小限に抑えることを目的としています。こうした動的アプローチにより、EAは複数銘柄に対してリアルタイムで柔軟かつ適応的に戦略を実行できます。

//+------------------------------------------------------------------+
//| Execute trade with risk parameters                               |
//+------------------------------------------------------------------+
void ExecuteTrade(ENUM_ORDER_TYPE tradeType, string symbol)
{
   double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
   double price = (tradeType == ORDER_TYPE_BUY) ? SymbolInfoDouble(symbol, SYMBOL_ASK) :
                                                SymbolInfoDouble(symbol, SYMBOL_BID);

   // Convert StopLoss and TakeProfit from pips to actual price distances
   double sl_distance = StopLoss * point;
   double tp_distance = TakeProfit * point;
   
   double sl = (tradeType == ORDER_TYPE_BUY) ? price - sl_distance :
                                             price + sl_distance;
   
   double tp = (tradeType == ORDER_TYPE_BUY) ? price + tp_distance :
                                             price - tp_distance;

   trade.PositionOpen(symbol, tradeType, In_Lot, price, sl, tp, NULL);
}

ExecuteTrade()関数は、指定された注文タイプ(買いまたは売り)と銘柄に基づいて実際の取引を実行します。まず、取引方向に応じて現在のAskまたはBidを使用して正しいエントリー価格を決定します。次に、ユーザーが指定したポイント値を銘柄のポイントサイズに基づいて実際の価格距離に変換し、ストップロス(SL)とテイクプロフィット(TP)レベルを計算します。取引が買いか売りかに応じて、SLとTPを適切な方向に設定し、最後にCTradeオブジェクトを使用して、定義されたロットサイズと価格パラメータでポジションを開きます。

//+------------------------------------------------------------------+
//| Calculate V-Stop levels                                          |
//+------------------------------------------------------------------+
double CalculateVStop(string symbol, int shift, bool isUpper)
{
    double atr[1];
    double high[1], low[1], close[1];
    
    if(CopyBuffer(ATR_Handles[GetSymbolIndex(symbol)], 0, shift, 1, atr) < 1 ||
       CopyHigh(symbol, PERIOD_CURRENT, shift, 1, high) < 1 ||
       CopyLow(symbol, PERIOD_CURRENT, shift, 1, low) < 1 ||
       CopyClose(symbol, PERIOD_CURRENT, shift, 1, close) < 1)
        return 0.0;
    
    double price = (high[0] + low[0] + close[0]) / 3.0;  // Typical price
    return isUpper ? price + ATR_Multiplier * atr[0] : price - ATR_Multiplier * atr[0];
}

CalculateVStop()関数は、指定された銘柄とバーシフトに対して、ボラティリティに応じた動的なVストップレベルを計算します。まず、CopyBuffer()および価格系列関数を使用して、指定バーのATR、高値、安値、終値を取得します。次に、典型価格(Typical Price)を、高値、安値、終値の平均として計算します。必要なVストップが上側か下側か(isUpperフラグ)に応じて、ATRの倍率をこの典型価格に加算または減算し、ボラティリティ調整されたサポートまたはレジスタンスレベルを生成します。このレベルは、取引フィルタリングやトレーリングロジックに使用されます。

//+------------------------------------------------------------------+
//| Get profit target from V-Stop history                            |
//+------------------------------------------------------------------+
double GetProfitTarget(string symbol, bool forLong)
{
    int bars = 50;  // Look back 50 bars
    double target = 0.0;
    
    for(int i = 1; i <= bars; i++)
    {
        double vStop = forLong ? 
            CalculateVStop(symbol, i, false) :  // For longs, find support levels
            CalculateVStop(symbol, i, true);     // For shorts, find resistance levels
        
        if(vStop != 0.0)
        {
            target = vStop;
            break;
        }
    }
    
    // Fallback: Use fixed multiplier if no V-Stop found
    MqlTick lastTick;
    SymbolInfoTick(symbol, lastTick);
    return (target == 0.0) ? 
        (forLong ? lastTick.ask + 5*Prev_ATR[GetSymbolIndex(symbol)] : 
                   lastTick.bid - 5*Prev_ATR[GetSymbolIndex(symbol)]) : 
        target;
}

GetProfitTarget()関数は、過去のV-Stop値に基づき動的なTPレベルを決定します。最大50本分のバーを遡り、ロングポジションの場合は下側バンド(サポートとして機能)、ショートポジションの場合は上側バンド(レジスタンスとして機能)の最も近い有効なVストップレベルを検索します。有効なレベルが見つかった場合、テイクプロフィットレベルとして使用します。もし指定した範囲内に有効なレベルがない場合は、現在のBidまたはAsk価格からATRの5倍距離をデフォルトのターゲットとして設定し、過去のVストップデータがなくてもEAが論理的な利確レベルを持つようにします。

//+------------------------------------------------------------------+
//| Calculate risk level based on volatility                         |
//+------------------------------------------------------------------+
double CalculateRiskLevel(string symbol)
{
    double atrValues[20];
    if(CopyBuffer(ATR_Handles[GetSymbolIndex(symbol)], 0, 1, 20, atrValues) < 20)
        return RiskPercent_Mod;
    
    double avgATR = 0.0;
    for(int i = 0; i < 20; i++) avgATR += atrValues[i];
    avgATR /= 20.0;
    
    double currentATR = atrValues[0];  // Most recent ATR
    
    if(currentATR > avgATR * 1.5)
        return RiskPercent_High;
    else if(currentATR < avgATR * 0.5)
        return RiskPercent_Low;
    
    return RiskPercent_Mod;
}

CalculateRiskLevel()関数は、現在の市場ボラティリティに基づきEAのリスクエクスポージャーを動的に調整します。指定された銘柄の直近20本のATR値を取得し、その平均を基準値として計算します。最新のATR値をこの平均と比較し、1.5倍以上であれば市場は高ボラティリティと判断し高リスク%を適用、0.5倍以下であれば低リスク%を適用、それ以外は中リスクレベルを選択します。これにより、取引のポジションサイズが市場状況に応じて適応されるようになります。

//+------------------------------------------------------------------+
//| Calculate position size based on risk                            |
//+------------------------------------------------------------------+
double CalculatePositionSize(string symbol, double riskPercent, double sl, double entryPrice)
{
    double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
    double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
    double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
    
    if(tickSize == 0 || point == 0) return 0.0;
    
    double riskAmount = AccountInfoDouble(ACCOUNT_EQUITY) * riskPercent;
    double slDistance = MathAbs(entryPrice - sl) / point;
    double moneyRisk = slDistance * tickValue / (tickSize / point);
    
    if(moneyRisk <= 0) return 0.0;
    double lots = riskAmount / moneyRisk;
    
    // Normalize and validate lot size
    double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
    double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
    double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
    
    lots = MathMax(minLot, MathMin(maxLot, lots));
    lots = MathRound(lots / step) * step;
    
    return lots;
}

CalculatePositionSize()関数は、指定されたリスク%とストップロス距離に基づき、取引に適切なロットサイズを算出します。まず、銘柄のティックサイズ、ティック値、ポイントサイズなどの必要な取引パラメータを取得します。口座資金と選択したリスク%を用いて、トレーダーがリスクを取る総額を計算します。

次に、ストップロス距離(ポイント単位)を考慮して1ロットあたりの金額リスクを算出し、ティックパラメータに基づき価値に変換します。関数は総リスク額を1ロットあたりのリスクで割ることで最適ロットサイズを求め、最後にブローカーの最小ロット、最大ロット、ステップサイズの要件に合わせてロットサイズを調整し、実行可能で適切に丸められたポジションサイズを確保します。

//+------------------------------------------------------------------+
//| Update trailing stops                                            |
//+------------------------------------------------------------------+
void UpdateTrailingStops(string symbol, double currentATR)
{
    double newSL = 0.0;
    for(int pos = PositionsTotal()-1; pos >= 0; pos--)
    {
        if(PositionGetSymbol(pos) != symbol) continue;
        
        ulong ticket = PositionGetInteger(POSITION_TICKET);
        double currentSL = PositionGetDouble(POSITION_SL);
        double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
        double currentProfit = PositionGetDouble(POSITION_PROFIT);
        double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
        
        if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
        {
            newSL = currentPrice - ATR_Multiplier * currentATR;
            if(newSL > currentSL && newSL > openPrice && currentProfit > 0)
                ModifySL(ticket, newSL);
        }
        else
        {
            newSL = currentPrice + ATR_Multiplier * currentATR;
            if((newSL < currentSL || currentSL == 0) && newSL < openPrice && currentProfit > 0)
                ModifySL(ticket, newSL);
        }
    }
}

UpdateTrailingStops()関数は、現在の市場ボラティリティに基づいてポジションのストップロスを調整し、積極的に管理します。指定された銘柄に一致する各ポジションについて、最新のATRに事前定義された係数を掛けて新しいトレーリングストップレベルを計算します。ロングポジションの場合、新しいレベルが現在のストップロスより高く、かつ建値より上であればストップロスを上方に移動させ、取引が利益圏に入った際に利益を確保します。ショートポジションでは、同様の条件でストップロスを下方に調整します。この動的トレーリングストップ方式により、利益を守りつつボラティリティの高い市場でも取引に余裕を持たせることが可能です。

//+------------------------------------------------------------------+
//| Modify stop loss                                                 |
//+------------------------------------------------------------------+
bool ModifySL(ulong ticket, double newSL)
{
    MqlTradeRequest request = {};
    MqlTradeResult result = {};
    
    request.action = TRADE_ACTION_SLTP;
    request.position = ticket;
    request.sl = newSL;
    request.deviation = 5;
    
    if(!OrderSend(request, result))
    {
        Print("Modify SL error: ", GetLastError());
        return false;
    }
    return true;
}

ModifySL()関数は、チケット番号で識別される既存ポジションのストップロスレベルを更新します。関数内で、ストップロスを変更するための取引リクエスト (TRADE_ACTION_SLTP)を作成し、新しいストップロス価格を設定、さらに許容される最大価格偏差を指定します。その後、OrderSend()を使ってリクエストを送信し、変更が失敗した場合は対応するエラーコードとともにエラーメッセージを出力します。関数は成功時にtrue、ストップロス更新が失敗した場合はfalseを返し、EAがエラーを適切に処理できるようにしています。

//+------------------------------------------------------------------+
//| Check existing positions                                         |
//+------------------------------------------------------------------+
void CheckExistingPositions(string symbol, bool &hasLong, bool &hasShort)
{
    hasLong = false;
    hasShort = false;
    
    for(int pos = PositionsTotal()-1; pos >= 0; pos--)
    {
        if(PositionGetSymbol(pos) == symbol)
        {
            if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                hasLong = true;
            else
                hasShort = true;
        }
    }
}

このコードブロックでは、CheckExistingPositions()関数がすべてのポジションをスキャンし、指定された銘柄に対してアクティブな買いポジションまたは売りポジションが存在するかどうかを判定します。出力フラグhasLongとhasShortは最初にfalseに初期化され、すべてのポジションを順にチェックします。銘柄が一致するポジションに対しては、買いポジションであればhasLongをtrueに、売りポジションであればhasShortをtrueに設定します。

//+------------------------------------------------------------------+
//| Get symbol index                                                 |
//+------------------------------------------------------------------+
int GetSymbolIndex(string symbol)
{
    for(int i = 0; i < Num_symbs; i++)
        if(symb_List[i] == symbol)
            return i;
    return -1;
}

最後に、GetSymbolIndex()関数は、symb_List配列から指定された銘柄のインデックスを取得するためのシンプルなユーティリティです。格納されたすべての銘柄を順にチェックし、銘柄が見つかれば対応するインデックスを返します。リストに存在しない場合は-1を返します。この関数は、EA全体で使用される配列間で、インジケーターハンドルや保存された値など、銘柄固有のデータを同期させる際に不可欠です。

バックテスト結果

バックテストは1時間足(1H)で、約2か月間のテスト期間(2025年6月11日~2025年8月1日)で評価されました。以下の入力設定を使用しました。

  • RSI期間 = 60
  • RSI買われすぎレベル = 70
  • RSI売られすぎレベル = 45
  • ボラティリティのATR期間 = 62
  • SLのATR乗数 = 3.2
  • リスク%高ボラティリティ = 0.19
  • リスク%中ボラティリティ = 0.064
  • リスク%低ボラティリティ = 0.0335
  • 必要な最小バー数 = 50
  • デフォルトのロットサイズ = 0.01
  • SLポイント = 610
  • TPポイント = 980



結論

まとめると、私たちはボラティリティに基づくリスク管理を統合し、市場状況の変化に適応するダイナミックなマルチペア向けEAを開発しました。本システムは複数の銘柄を同時に処理し、ATRやRSIなどのテクニカル指標を用いて取引機会を特定します。Vストップレベルを計算してエントリーをフィルタリングし、決済管理をおこないます。また、リアルタイムのボラティリティに応じてロットサイズやストップロスを動的に調整します。モジュール化された関数により、EAは取引の実行、トレーリングストップ、リスク分類(高、中、低)を管理し、各取引が銘柄の現在の挙動に沿うよう設計されています。

このアプローチにより、トレーダーはさまざまな通貨ペアで安定したパフォーマンスを維持する堅牢で適応的なツールを手に入れることができます。ボラティリティに基づいてリスクと取引パラメータを調整することで、ボラティリティの高い市場での過剰エクスポージャーを抑え、静かな市場でのパフォーマンス低下を回避できます。

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

添付されたファイル |
最後のコメント | ディスカッションに移動 (3)
CapeCoddah
CapeCoddah | 11 8月 2025 において 11:29

こんにちは、


非常に興味深い記事ですね。 ざっと目を通しただけですが、私が開発しているEAに似ているので、詳しく評価してみようと思います。

最初の質問ですが、すべてのトレードが買いで、売りがないのは何か理由があるのでしょうか? また、このテーマでさらに記事を書く予定はありますか?

あなたのコードをざっと見たところ、GetSymbolIndex や point のような他の変数はシンボルループの先頭に移動して変数に代入し、冗長性を減らして効率を向上させることをお勧めします。 より多くのシンボルがペアリストに追加されるにつれて、指数関数的に 多くの時間が冗長なコードの実行に費やされることになります。 また、配列に PairCode インデックスを追加して、直接アクセスできるようにすることも検討してはいかがでしょうか。


ケープコッダ

Hlomohang John Borotho
Hlomohang John Borotho | 12 8月 2025 において 15:52
CapeCoddah 指数関数的に 多くの時間が冗長なコードの実行に費やされることになります。 また、配列に PairCode インデックスを追加して、直接アクセスできるようにすることも検討してはいかがでしょうか。


ケープコッダ



もしバックテストで私と同じ設定を使用したのであれば、私が EA を最適化したためです。

ご指摘ありがとうございます。覚えておきます。

JohnHlomohang
Ademir Temoteo De Vasco
Ademir Temoteo De Vasco | 6 10月 2025 において 01:28

こんばんは!

まず最初に、あなたの素晴らしい仕事を祝福したいと思います。

私はテストを行っており、その結果には非常に満足しているのですが、正直に告白すると、各資産ごとに設定が異なるため、私はテストを行うために1つの通貨しか使用していません。

複数の通貨を同時に配置するこのロジックがどのように機能するのか理解できません。

ご説明いただければ幸いです。

モデレーターによる自動翻訳が適用されました。英語フォーラムでは、英語で書き込んでください。

MQL5とデータ処理パッケージの統合(第5回):適応学習と柔軟性 MQL5とデータ処理パッケージの統合(第5回):適応学習と柔軟性
今回は、過去のXAU/USDデータを用いて柔軟で適応的な取引モデルを構築し、ONNX形式でのエクスポートや実際の取引システムへの統合に備えることに焦点を当てます。
取引システムの構築(第2回):ポジションサイズ管理の科学 取引システムの構築(第2回):ポジションサイズ管理の科学
期待値がプラスのシステムであっても、ポジションサイズ管理の決定次第で取引が成功するか破綻するかが決まります。ポジションサイズ管理はリスク管理の中心であり、統計的な優位性を現実の利益に変換しつつ、資本を守る役割を担います。
MQL5での取引戦略の自動化(第25回):最小二乗法と動的シグナル生成を備えたTrendline Trader MQL5での取引戦略の自動化(第25回):最小二乗法と動的シグナル生成を備えたTrendline Trader
本記事では、最小二乗法を用いてサポートおよびレジスタンスのトレンドラインを検出し、価格がこれらのラインに触れた際に動的な売買シグナルを生成するTrendline Traderプログラムを開発します。また、生成されたシグナルに基づきポジションをオープンする仕組みも構築します。
MQL5取引ツール(第8回):ドラッグ&最小化可能な拡張情報ダッシュボード MQL5取引ツール(第8回):ドラッグ&最小化可能な拡張情報ダッシュボード
本記事では、前回のダッシュボードを拡張し、ドラッグ&最小化機能を追加し、ユーザー操作性を向上させながら、複数銘柄のポジションや口座指標のリアルタイム監視を維持する情報ダッシュボードを開発します。