English
preview
プライスアクション分析ツールキットの開発(第6回):Mean Reversion Signal Reaper

プライスアクション分析ツールキットの開発(第6回):Mean Reversion Signal Reaper

MetaTrader 5トレーディングシステム | 22 4月 2025, 10:10
17 0
Christian Benjamin
Christian Benjamin

はじめに

平均回帰は、多くの熟練トレーダーが市場分析の精度を高めるために活用している、非常に興味深い取引戦略です。この戦略は、資産価格が過去の平均値へと回帰する傾向があるという考えに基づいており、その動きを活かしたタイミングの良い取引が可能になります。しかしながら、こうした価格の動きを手作業で分析するのは時間がかかり、見落としも生じやすくなります。ここで、自動化によって取引体験が大幅に向上します。

この記事では、平均回帰の原則に基づき、取引のチャンスを見極めるMQL5 EAを開発していきます。50期間の指数移動平均(EMA)と相対力指数(RSI)を活用することで、相場の変動に合わせた的確なエントリーシグナルを生成します。また、取引プロセスをより直感的にするため、EAはシグナルの要点をまとめた情報とともに、チャート上に売買シグナルを矢印で視覚的に表示します。

取引の効率化や自動化の力を活用したいとお考えなら、平均回帰戦略の本質をとらえたMQL5 EAの作り方を学べるこの旅に、ぜひご参加ください。では、詳細と目次を見ていきましょう。



平均回帰とは何か

平均回帰とは、資産価格やリターン、その他の市場指標が時間の経過とともに過去の平均、すなわち「平均値」へと回帰する傾向があるとする金融理論です。この平均は、一定期間の平均価格、移動平均、あるいは標準的なベンチマークリターンなど、さまざまな手法で算出されます。

この理論は、市場における極端な変動は一時的であり、価格はやがて安定し平均値へと戻るという考えに基づいています。トレーダーやアナリストはこの原則を活用し、特に価格が過去の平均から大きく乖離している局面で取引機会を見出します。

簡単な歴史と起源

平均回帰の概念は、統計学と金融の分野にルーツがあり、20世紀初頭にフランシス・ゴルトンなどの数学者によって初めて提唱されました。当初は、集団の極端な特性(例:身長)が世代を経ることで平均に回帰するという自然現象の研究に用いられていましたが、のちにこの考え方は金融市場にも応用されるようになります。
経済学者ジョン・メイナード・ケインズは、資産価格が本質的な価値を中心に上下動する傾向に注目し、この理論を金融市場で普及させました。たとえば、1990年代後半のドットコム・バブルでは、多くのテクノロジー株が過大評価された後、最終的に本来の価値に戻り、平均回帰の概念が現実に確認されました。

平均回帰の実例

  • 株式市場:COVID-19パンデミック時、多くの株価が歴史的平均値から大きく乖離しました。たとえば旅行関連株は大幅に下落しましたが、その後、市場の正常化に伴い回復しました。
  • 通貨ペア:為替相場は中央銀行の政策や経済の基礎的要因により、長期的には平均的な水準へと戻る傾向があります。たとえば、USD/JPYは歴史的に一定のレンジ内で推移しています。
  • コモディティ:金や原油などの商品価格も、地政学的リスクや需給の変化により急騰・急落した後、しばしば過去の水準へと戻る動きを見せます。

EAを用いた平均回帰の定義

本プロジェクトにおける「平均回帰」とは、資産価格が平均(平均値)から大きく乖離した後、再びその平均に戻るという性質を指します。この原則に基づき、EAは取引シグナルを生成します。

  • 平均(50 EMA):本EAでは、50期間の指数移動平均(EMA)を「平均」として採用します。EMAは直近の価格に敏感に反応し、現在の市場トレンドを捉える指標として非常に有効です。価格がこのEMAから大きく乖離したとき、それは反転の可能性を示唆します。 
  • 復帰の実施: 価格が50 EMAを下回り、RSIが売られすぎの状態にある場合、EAは価格がEMA方向へ反発すると予測し、買いシグナルを生成します。価格が50 EMAを上回り、RSIが買われすぎの状態にある場合、EAは価格が下落に転じると判断し、売りシグナルを出します。


戦略の概要

この戦略は、価格が一時的に大きく乖離した後、平均水準へと回帰する傾向があるという「平均回帰」の概念に基づいて構築されています。このような市場の動きは、50期間の指数移動平均(EMA)や相対力指数(RSI)といった信頼性の高いテクニカル指標と組み合わせることで、有効な取引機会を生み出します。

  • 仕組み

本戦略では、以下の2つの主要な指標を活用します。

  1. 50 EMAは特定の期間における価格の動的な平均値を示し、相場の基準線(ベンチマーク)として機能します。 
  2. RSIは市場の「買われすぎ」または「売られすぎ」の状態を検出します。

価格が50 EMAから大きく乖離し、同時にRSIが極端な水準を示している場合、本戦略では価格が平均水準へと回帰する可能性が高いと判断し、反転を狙ったエントリーを行います。

平均回帰概念価格 

図1:平均回帰の概念

上の図(図1)は、指数移動平均(EMA)に対する価格の動きを視覚化したものであり、相対力指数(RSI)の極端な水準で発生する取引シグナルを強調しています。

買いシグナルは次の場合に生成されます
価格が50 EMAを下回っていて、価格が平均から下方に乖離していることを示唆します。 RSIが30未満で、市場が売られ過ぎの状態にあることを示します。この状態は、価格が平均値に向かって反発する可能性があることを示しています。

売りシグナルは次の場合に生成されます
価格が50 EMAを上回っていて、価格が平均から上方に乖離していることを示唆します。 RSIが70を超えていて、市場が買われ過ぎの状態にあることを示します。 この状態では、価格が平均値に戻る修正を予測します。

  • 視覚的なヒントとテキストの要約
緑の矢印は買いシグナルを示します。 赤い矢印は売りシグナルを示します。 チャートの右上隅に、シグナルの種類(買い/売り)を示すテキストサマリーが表示されます。 この戦略には明確なストップロスとテイクプロフィットのルールが組み込まれています。
  1. ストップロス:買いポジションの場合は最近の安値より下に、売りポジションの場合は最近の高値より上に配置します。
  2. テイクプロフィット:価格の反転が予想される50 EMA付近をターゲットとします。
  3. クールダウンのメカニズム:同じ条件下でのシグナルが連続して発生しないように、クールダウン期間を設けます。シグナル発生後、この戦略は新しいシグナルを検討する前に指定された数のバーを待機します。この機能は、不安定な市場状況でのノイズを軽減するのに役立ちます。 

ATR (Average True Range)は、テイクプロフィット(TP)値とストップロス(SL)値を設定するための動的なレベルを確立するために使用されます。

  • 戦略の主なメリット

メリット
説明
シンプルながら効果的なロジック
EMAとRSIを組み合わせて、信頼性の高い平均回帰の機会を実現
視覚シグナル 
矢印とテキストの要約により、シグナルが明確になり、実行可能
リスク管理の統合 
明確なストップロスとテイクプロフィットのルールにより、取引を保護し、リスクを管理 
ノイズ低減 クールダウン期間は、不安定な市場における冗長なシグナルを除外するのに有用
設定可能なパラメータ EMA期間、RSIレベル、クールダウン設定を簡単にカスタマイズ

  • EAのMQL5コード

//+------------------------------------------------------------------+
//|                                        Mean Reversion Reaper.mq5 |
//|                              Copyright 2024, Christian Benjamin. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Christian Benjamin"
#property link      "https://www.mql5.com"
#property version   "1.00"

#property strict
#property indicator_chart_window

//--- Input Parameters
input int EMA_Period = 50;                     // EMA Period
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 int CooldownBars = 3;                    // Cooldown bars between signals
input double ATR_Multiplier = 2.0;             // ATR Multiplier for TP and SL
input int ATR_Period = 14;                     // ATR Period
input color BuySignalColor = clrGreen;         // Buy signal arrow color
input color SellSignalColor = clrRed;          // Sell signal arrow color
input int ArrowSize = 2;                       // Arrow size
input color TextColor = clrDodgerBlue;         // Color for TP/SL text summary

//--- Global Variables
int EMA_Handle, RSI_Handle, ATR_Handle;
datetime lastSignalTime = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Create handles for indicators
   EMA_Handle = iMA(NULL, 0, EMA_Period, 0, MODE_EMA, PRICE_CLOSE);
   RSI_Handle = iRSI(NULL, 0, RSI_Period, PRICE_CLOSE);
   ATR_Handle = iATR(NULL, 0, ATR_Period);

   if(EMA_Handle == INVALID_HANDLE || RSI_Handle == INVALID_HANDLE || ATR_Handle == INVALID_HANDLE)
     {
      Print("Failed to create indicator handles. Error: ", GetLastError());
      return INIT_FAILED;
     }

   Print("Mean Reversion EA initialized.");
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
// Clear the Signal Summary text object upon deinitialization
   ObjectDelete(0, "SignalSummary");
   Print("Mean Reversion EA deinitialized.");
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Avoid repeating signals on cooldown
   if(BarsSinceLastSignal() < CooldownBars)
      return;

   double EMA_Value = GetEMA();
   double RSI_Value = GetRSI();
   double ATR_Value = GetATR();

   if(EMA_Value == 0 || RSI_Value == 0 || ATR_Value == 0)
      return;

   double closePrice = iClose(NULL, 0, 0); // Current close price
   double highPrice = iHigh(NULL, 0, 1);   // Previous bar high
   double lowPrice = iLow(NULL, 0, 1);     // Previous bar low

// Check for Buy Signal
   if(closePrice < EMA_Value && RSI_Value <= RSI_Oversold)
     {
      DrawSignalArrow("BuySignal", closePrice, BuySignalColor);
      DisplayTextSummary("BUY", closePrice, lowPrice, ATR_Value);
      UpdateSignalTime();
     }
// Check for Sell Signal
   else
      if(closePrice > EMA_Value && RSI_Value >= RSI_Overbought)
        {
         DrawSignalArrow("SellSignal", closePrice, SellSignalColor);
         DisplayTextSummary("SELL", closePrice, highPrice, ATR_Value);
         UpdateSignalTime();
        }
  }

//+------------------------------------------------------------------+
//| Get EMA Value                                                    |
//+------------------------------------------------------------------+
double GetEMA()
  {
   double emaValues[1];
   if(CopyBuffer(EMA_Handle, 0, 0, 1, emaValues) <= 0)
      return 0;
   return emaValues[0];
  }

//+------------------------------------------------------------------+
//| Get RSI Value                                                    |
//+------------------------------------------------------------------+
double GetRSI()
  {
   double rsiValues[1];
   if(CopyBuffer(RSI_Handle, 0, 0, 1, rsiValues) <= 0)
      return 0;
   return rsiValues[0];
  }

//+------------------------------------------------------------------+
//| Get ATR Value                                                    |
//+------------------------------------------------------------------+
double GetATR()
  {
   double atrValues[1];
   if(CopyBuffer(ATR_Handle, 0, 0, 1, atrValues) <= 0)
      return 0;
   return atrValues[0];
  }

//+------------------------------------------------------------------+
//| Draw signal arrow on the chart                                   |
//+------------------------------------------------------------------+
void DrawSignalArrow(string signalType, double price, color arrowColor)
  {
   string arrowName = signalType + "_" + TimeToString(TimeCurrent(), TIME_MINUTES);
// Delete the existing arrow if it exists
   if(ObjectFind(0, arrowName) != -1)  // If the object exists
     {
      ObjectDelete(0, arrowName); // Delete the existing object
     }
   ObjectCreate(0, arrowName, OBJ_ARROW, 0, TimeCurrent(), price);
   ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, (signalType == "BuySignal") ? 233 : 234);
   ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor);
   ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, ArrowSize);
  }

//+------------------------------------------------------------------+
//| Display TP and SL as text summary                                |
//+------------------------------------------------------------------+
void DisplayTextSummary(string signalType, double price, double refPrice, double ATR)
  {
   string objectName = "SignalSummary"; // Unique object name for the summary

// Delete the existing summary if it exists
   if(ObjectFind(0, objectName) != -1)  // If the object exists
     {
      ObjectDelete(0, objectName); // Delete the existing object
     }

   double SL = (signalType == "BUY") ? refPrice - (ATR * ATR_Multiplier) : refPrice + (ATR * ATR_Multiplier);
   double TP = (signalType == "BUY") ? price + (ATR * ATR_Multiplier) : price - (ATR * ATR_Multiplier);

   string summary = signalType + " Signal\n" +
                    "Price: " + DoubleToString(price, 5) + "\n" +
                    "TP: " + DoubleToString(TP, 5) + "\n" +
                    "SL: " + DoubleToString(SL, 5);

   ObjectCreate(0, objectName, OBJ_LABEL, 0, 0, 0);
   ObjectSetString(0, objectName, OBJPROP_TEXT, summary);
   ObjectSetInteger(0, objectName, OBJPROP_COLOR, TextColor);
   ObjectSetInteger(0, objectName, OBJPROP_FONTSIZE, 10); // Adjust font size if needed

// Position the label at the left upper corner
   ObjectSetInteger(0, objectName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, objectName, OBJPROP_XDISTANCE, 10); // 10 pixels from the left
   ObjectSetInteger(0, objectName, OBJPROP_YDISTANCE, 10); // 10 pixels from the top
  }

//+------------------------------------------------------------------+
//| Update signal time to prevent frequent signals                   |
//+------------------------------------------------------------------+
void UpdateSignalTime()
  {
   lastSignalTime = iTime(NULL, 0, 0);
  }

//+------------------------------------------------------------------+
//| Calculate bars since the last signal                             |
//+------------------------------------------------------------------+
int BarsSinceLastSignal()
  {
   datetime currentBarTime = iTime(NULL, 0, 0);
   if(lastSignalTime == 0)
      return INT_MAX; // If no signal has been generated return a large number.
   return (int)((currentBarTime - lastSignalTime) / PeriodSeconds());
  }
//+------------------------------------------------------------------+


コードの内訳

ヘッダー情報

ヘッダーには、名前、作成者、バージョンなど、EAに関する基本的なメタデータが提供されます。この情報は、EAを識別し、帰属を保証するのに役立ちます。#propertyディレクティブは、EAの使用時に表示される著作権、著者リンク、バージョンなどのメタデータを指定します。さらに、「#property indicator_chart_window」ディレクティブは、このEAが別のサブウィンドウではなくメインチャートウィンドウで動作することを示します。
//+------------------------------------------------------------------+
//| Mean Reversion Reaper.mq5                                        |
//| Author: Christian Benjamin                                       |
//| Website: https://www.mql5.com                                    |
//+------------------------------------------------------------------+
#property copyright "Christian Benjamin"
#property link      "https://www.mql5.com"
#property version   "1.00"

// Indicator operates in the main chart window
#property indicator_chart_window

入力パラメータ

入力パラメータを使用すると、ユーザーはコード自体を変更することなくEAの動作をカスタマイズできます。これらのパラメータには、EMA、RSI、ATR期間などの重要な設定や、買われすぎと売られすぎのRSIレベルの閾値が含まれます。クールダウン期間を設定することで、頻繁なシグナル生成を防ぎ、市場のノイズを減らして明確さを確保することができます。ユーザーは、売買シグナルの矢印の色やサイズ、チャートに表示される概要のテキストの色などの視覚要素を指定することもできます。これらのパラメータを調整することで、トレーダーはEAを自分の取引戦略や市場状況に合わせてカスタマイズできます。

input int EMA_Period = 50;                     // EMA Period
input int RSI_Period = 14;                     // RSI Period
input double RSI_Overbought = 75.0;            // RSI Overbought level
input double RSI_Oversold = 25.0;              // RSI Oversold level
input int CooldownBars = 3;                    // Cooldown bars between signals
input double ATR_Multiplier = 2.0;             // ATR Multiplier for TP and SL
input int ATR_Period = 14;                     // ATR Period
input color BuySignalColor = clrGreen;         // Buy signal arrow color
input color SellSignalColor = clrRed;          // Sell signal arrow color
input int ArrowSize = 2;                       // Arrow size
input color TextColor = clrDodgerBlue;         // Color for TP/SL text summary

グローバル変数

グローバル変数セクションでは、EAで使用されるインジケーターの重要な状態追跡変数と参照を定義します。EMA、RSI、ATRインジケーターのハンドルは、最新のシグナルのタイムスタンプを格納するlastSignalTimeとともにここで宣言されます。これらの変数により、EAがインジケーター データに効率的にアクセスし、シグナル間のクールダウン期間などのロジックを適用できるようになります。

int EMA_Handle, RSI_Handle, ATR_Handle;        // Handles for EMA, RSI, and ATR
datetime lastSignalTime = 0;                   // Tracks last signal time
初期化(OnInit)

OnInit関数は、EAがチャートにロードされたときに実行されます。その主な役割は、EMAの場合はiMA、RSIの場合はiRSI、ATRの場合はiATRなどの関数を使用してインジケーターハンドルを初期化することです。これらのハンドルは、実行時にインジケーターの値を取得するために不可欠です。この関数は、すべてのハンドルが正常に作成されたことを確認するためにエラーチェックも実行します。ハンドルの作成に失敗した場合、エラーメッセージが記録され、EAの実行が停止します。初期化が成功すると、EAが動作する準備ができていることを知らせる確認メッセージが操作ログに出力されます。

int OnInit()
{
    EMA_Handle = iMA(NULL, 0, EMA_Period, 0, MODE_EMA, PRICE_CLOSE);
    RSI_Handle = iRSI(NULL, 0, RSI_Period, PRICE_CLOSE);
    ATR_Handle = iATR(NULL, 0, ATR_Period);

    if (EMA_Handle == INVALID_HANDLE || RSI_Handle == INVALID_HANDLE || ATR_Handle == INVALID_HANDLE) {
        Print("Failed to create indicator handles. Error: ", GetLastError());
        return INIT_FAILED; // Stops EA if initialization fails
    }

    Print("Mean Reversion EA initialized.");
    return INIT_SUCCEEDED;
}

クリーンアップ(OnDeinit)

EAがチャートから削除されるか再初期化されると、OnDeinit関数が自動的に呼び出されます。その主な役割は、EAの実行中に作成されたチャートオブジェクトをクリーンアップすることです。具体的には、SignalSummaryラベルを削除して、チャートが整理された状態になるようにします。この関数は、EAの初期化解除を確認するメッセージもログに記録し、終了プロセスを明確にします。

void OnDeinit(const int reason)
{
    ObjectDelete(0, "SignalSummary"); // Remove any signal summary text
    Print("Mean Reversion EA deinitialized.");
}

シグナル処理(OnTick)

OnTick関数はEAの中核として機能し、新しいティックを処理します。最後のシグナルからクールダウン期間が経過したかどうかを確認することから始まります。経過していない場合、関数は冗長なシグナルを回避するために早期に終了します。次に、それぞれのハンドルを使用して、EMA、RSI、およびATRインジケーターの最新の値を取得します。これらの値を使用して、EAは買いまたは売りのシグナルの条件を評価します。

価格がEMAを下回り、RSIが売られ過ぎ領域にある場合、買いシグナルがトリガーされます。逆に、価格がEMAを上回り、RSIが買われすぎている場合は、売りシグナルが発生します。この関数は、シグナルごとにチャート上に矢印を描画し、価格、ストップロス(SL)、テイクプロフィット(TP)レベルを含むテキストサマリーを表示します。クールダウン メカニズムを強制するために、シグナルのタイムスタンプが更新されます。

void OnTick()
{
    if (BarsSinceLastSignal() < CooldownBars) return; // Skip if cooldown is active

    double EMA_Value = GetEMA();   // Fetch EMA value
    double RSI_Value = GetRSI();   // Fetch RSI value
    double ATR_Value = GetATR();   // Fetch ATR value

    double closePrice = iClose(NULL, 0, 0); // Current bar's close price

    if (closePrice < EMA_Value && RSI_Value <= RSI_Oversold) {
        // Buy Signal
        DrawSignalArrow("BuySignal", closePrice, BuySignalColor);
        DisplayTextSummary("BUY", closePrice, iLow(NULL, 0, 1), ATR_Value);
        UpdateSignalTime(); // Update last signal time
    } else if (closePrice > EMA_Value && RSI_Value >= RSI_Overbought) {
        // Sell Signal
        DrawSignalArrow("SellSignal", closePrice, SellSignalColor);
        DisplayTextSummary("SELL", closePrice, iHigh(NULL, 0, 1), ATR_Value);
        UpdateSignalTime(); // Update last signal time
    }
}

ユーティリティ関数
  • インジケーターの値を取得する

GetEMA、GetRSI、GetATR などのユーティリティ関数は、それぞれのインジケーターから最新の値を取得するように設計されています。これらの関数は、CopyBufferメソッドを使用してインジケーター ハンドルからデータを抽出します。何らかの理由でデータの取得に失敗した場合、関数はゼロを返し、EAが現在のティックの処理をスキップする必要があることを通知します。これらの関数は軽量かつモジュール化されており、コードをクリーンかつ保守しやすい状態に保つことができます。

double GetEMA()
{
    double emaValues[1];
    if (CopyBuffer(EMA_Handle, 0, 0, 1, emaValues) <= 0)
        return 0;
    return emaValues[0];
}

double GetRSI()
{
    double rsiValues[1];
    if (CopyBuffer(RSI_Handle, 0, 0, 1, rsiValues) <= 0)
        return 0;
    return rsiValues[0];
}

double GetATR()
{
    double atrValues[1];
    if (CopyBuffer(ATR_Handle, 0, 0, 1, atrValues) <= 0)
        return 0;
    return atrValues[0];
}
  • シグナル矢印を描く

DrawSignalArrow関数は、買いまたは売りのシグナルを示す視覚的なヒントをチャートに追加します。シグナルの種類と現在の時刻を使用して、各矢印に一意の名前を動的に生成し、既存のオブジェクトと重複しないようにします。同じ名前の矢印がすでに存在する場合は、新しい矢印を作成する前に削除されます。矢印の色、サイズ、タイプなどのプロパティは、ユーザー定義の入力パラメータによって決定され、明確でカスタマイズ可能な視覚表現が可能になります。

void DrawSignalArrow(string signalType, double price, color arrowColor)
{
    string arrowName = signalType + "_" + TimeToString(TimeCurrent(), TIME_MINUTES);
    if (ObjectFind(0, arrowName) != -1) ObjectDelete(0, arrowName);

    ObjectCreate(0, arrowName, OBJ_ARROW, 0, TimeCurrent(), price);
    ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, (signalType == "BuySignal") ? 233 : 234);
    ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor);
    ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, ArrowSize);
}
  • TP/SLサマリーを表示する

DisplayTextSummary関数は、価格、ストップロス、テイクプロフィットのレベルなど、シグナルの簡潔な概要を提供します。この情報はグラフの左上隅にラベルとして表示されます。この関数は、ATRとその乗数に基づいてTPレベルとSLレベルを計算し、市場のボラティリティに適応する動的なレベルを提供します。サマリーラベルがすでに存在する場合は、新しいラベルを作成する前に削除されます。これにより、最新のシグナル情報のみが表示されるようになり、混乱が軽減され、読みやすさが向上します。

void DisplayTextSummary(string signalType, double price, double refPrice, double ATR)
{
    string objectName = "SignalSummary";
    if (ObjectFind(0, objectName) != -1) ObjectDelete(0, objectName);

    double SL = (signalType == "BUY") ? refPrice - (ATR * ATR_Multiplier) : refPrice + (ATR * ATR_Multiplier);
    double TP = (signalType == "BUY") ? price + (ATR * ATR_Multiplier) : price - (ATR * ATR_Multiplier);

    string summary = signalType + " Signal\n" +
                     "Price: " + DoubleToString(price, 5) + "\n" +
                     "TP: " + DoubleToString(TP, 5) + "\n" +
                     "SL: " + DoubleToString(SL, 5);

    ObjectCreate(0, objectName, OBJ_LABEL, 0, 0, 0);
    ObjectSetString(0, objectName, OBJPROP_TEXT, summary);
    ObjectSetInteger(0, objectName, OBJPROP_COLOR, TextColor);
    ObjectSetInteger(0, objectName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
    ObjectSetInteger(0, objectName, OBJPROP_XDISTANCE, 10);
    ObjectSetInteger(0, objectName, OBJPROP_YDISTANCE, 10);
}
  • クールダウン管理
EAは、不安定な市場状況での過剰取引を防ぐために、シグナル間にクールダウン期間を強制します。UpdateSignalTime関数は、最新のシグナルのタイムスタンプでlastSignalTime変数を更新し、BarsSinceLastSignal関数は最後のシグナルから経過したバーの数を計算します。まだシグナルが生成されていない場合は、正常な動作を保証するために大きな値が返されます。このメカニズムにより、シグナルが適切な間隔で配置されるようになり、トレーダーにはより明確で信頼性の高い洞察が提供されます。
void UpdateSignalTime()
{
    lastSignalTime = iTime(NULL, 0, 0); // Store time of the current bar
}

int BarsSinceLastSignal()
{
    datetime currentBarTime = iTime(NULL, 0, 0);
    if (lastSignalTime == 0) return INT_MAX; // No signals generated yet
    return (int)((currentBarTime - lastSignalTime) / PeriodSeconds());
}


テストと結果

EAを評価する上で重要なステップであるバックテストについて説明します。基本的に、バックテストでは、EAを過去の市場データで実行し、過去のパフォーマンスを確認します。このプロセスは、戦略がさまざまな市場状況と一致しているかどうかを判断し、全体的な堅牢性に関する洞察を得るために重要です。
バックテストをおこなう際に留意すべき重要なポイントは次のとおりです。
  • 履歴データの範囲:必ず、トレンド市場やレンジ市場など、さまざまな市場状況にまたがるデータを使用してください。これは、EAの適応性を評価するのに役立ちます。
  • 主な指標:利益率、勝率、最大ドローダウン、平均取引期間などの重要な指標を通じてパフォーマンスを測定することに重点を置きます。これにより、戦略がどの程度効果的であるかをより明確に把握できるようになります。
  • 最適化:EMA期間やRSIレベルなどの入力パラメータをためらわずに試して、特定の市場商品や時間枠に最も効果的な設定を見つけてください。
  • 視覚的検証:最後に、EAによって生成された矢印とシグナルが意図した取引ロジックと一致しているかどうかを常に確認してください。これは、戦略が設計どおりに機能していることを確認するための鍵となります。

これらの考慮事項を踏まえてEAを徹底的にバックテストすることで、より信頼性が高く効果的な取引戦略を構築できます。

テストシグナル

図2:テスト結果1

上の図は、29日間にわたってボラティリティ75(1秒)インデックスの2つの異なる時間枠で実行されたテストを示しています。以下に、 2つのテストの結果を表にまとめました。

シグナル生成とパフォーマンス表

  • 表1

シグナルの種類  時間枠  生成シグナル総数
真のシグナル(シグナル後に価格が上昇)
買いシグナル精度(%)
買い M30 41 33 80.5%
売り M30 21 15 71.4%

  • 表2

シグナルの種類  時間枠  生成シグナル総数
真のシグナル(シグナル後に価格が上昇)
買いシグナル精度(%)
 買い H1 19 14 73.6% 
 売り H1  13 61.5% 

以下は、EAのリアルタイムテスト結果の図です。

リアルタイムシグナル

図3:テスト結果2

下のGIFも見てみましょう。

テスト結果

図4:テスト結果3

参照文献


結論

Mean Reversion Reaper EAは、平均回帰の原則に基づいた信頼性の高いシグナル生成システムを提供し、特に買いシグナルにおいて70%以上の高精度を実現しています。ストップロス(SL)およびテイクプロフィット(TP)の設定にはATR (Average True Range)を用いることで、市場のボラティリティに応じた柔軟な対応が可能となり、レンジ相場において高い効果を発揮します。

一方で、これは平均回帰戦略全般に見られる傾向ですが、強いトレンドが発生している相場ではパフォーマンスが低下する可能性がある点に注意が必要です。取引精度と安定性をさらに高めるためには、パラメータの微調整や売りシグナル向けの追加フィルターの導入を検討すると良いでしょう。また、本EAは自動売買はおこなわず、シグナルの提示に特化しているため、短期的な反転を狙う裁量トレーダーにとって特に有用です。

総じて、Mean Reversion Reaper EAは平均回帰パターンを活用したいトレーダーにとって心強いツールとなり得ます。市場環境に常に注意を払い、必要に応じて戦略を柔軟に調整することで、本EAの可能性を最大限に引き出すことができるでしょう。

日付 ツール名  詳細 バージョン  アップデート  備考
01/10/24 ChartProjector 前日のプライスアクションをゴースト効果でオーバーレイするスクリプト 1.0 初回リリース Lynnchris Tool Chestの最初のツール
18/11/24 Analytical Comment 前日の情報を表形式で提供し、市場の将来の方向性を予測する 1.0 初回リリース Lynnchris Tool Chestの2番目のツール
27/11/24 Analytics Master 2時間ごとに市場指標を定期的に更新  1.01 v.2 Lynnchris Tool Chestの3番目のツール
2024年2月12日 Analytics Forecaster  Telegram統合により、2時間ごとに市場指標を定期的に更新 1.1 v.3 ツール番号4
09/12/24 Volatility Navigator ボリンジャーバンド、RSI、ATR指標を使用して市場の状況を分析するEA 1.0 初回リリース ツール番号5
19/12/24 Mean Reversion Signal Reaper  平均回帰戦略を用いて市場を分析し、シグナルを提供する  1.0  初回リリース  ツール番号6 

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

添付されたファイル |
Mean_Reversion.mq5 (16.16 KB)
MQL5での取引戦略の自動化(第3回):ダイナミック取引管理のためのZone Recovery RSIシステム MQL5での取引戦略の自動化(第3回):ダイナミック取引管理のためのZone Recovery RSIシステム
この記事では、MQL5を使ってZone Recovery RSI EAシステムを構築し、RSIシグナルによって取引を開始し、損失を管理するためのリカバリーストラテジーを実装します。取引エントリー、リカバリーロジック、ポジション管理を自動化するために、ZoneRecoveryクラスを作成します。この記事の最後では、EAのパフォーマンスを最適化し、その有効性を高めるためのバックテストの洞察を紹介します。
スイングエントリーモニタリングEAの開発 スイングエントリーモニタリングEAの開発
年末が近づくと、多くの長期トレーダーは市場の過去を振り返り、その動きや傾向を分析して、将来の動向を予測しようとします。この記事では、MQL5を用いて長期エントリーの監視をおこなうエキスパートアドバイザー(EA)の開発について解説します。手動取引や自動監視システムの不在によって、長期的な取引チャンスを逃してしまうという課題に取り組むことが本稿の目的です。今回は、特に取引量の多い通貨ペアの一つを例に挙げ、効果的な戦略を立案しながらソリューションを構築していきます。
古典的な戦略を再構築する(第13回):移動平均線のクロスオーバーにおける遅延の最小化 古典的な戦略を再構築する(第13回):移動平均線のクロスオーバーにおける遅延の最小化
移動平均クロスオーバーは、私たちのコミュニティにおけるトレーダーの間で広く知られている戦略ですが、その基本的な仕組みは誕生以来ほとんど変化していません。本稿では、この戦略に存在する“遅延”を最小限に抑えることを目的とした、わずかながらも重要な改良について紹介します。元の戦略を愛用しているトレーダーの方々にも、今回ご紹介する洞察をもとに、戦略の見直しを検討していただければ幸いです。同一の期間を持つ2つの移動平均を使用することで、戦略の根本的な原則を損なうことなく、遅延を大幅に削減することが可能になります。
出来高ベースの取引システムを構築し最適化する方法(チャイキンマネーフロー:CMF) 出来高ベースの取引システムを構築し最適化する方法(チャイキンマネーフロー:CMF)
この記事では、出来高ベースの指標であるチャイキンマネーフロー(CMF)の構築方法、計算方法、使用方法を説明した上で、その概要を説明します。カスタムインジケーターの構築方法を理解します。使用できるいくつかの簡単な戦略を共有し、それらをテストしてどれが優れているかを理解します。