English Русский Español Deutsch 日本語 Português
preview
价格行为分析工具包开发(第六部分):均值回归信号捕捉器

价格行为分析工具包开发(第六部分):均值回归信号捕捉器

MetaTrader 5交易系统 |
610 3
Christian Benjamin
Christian Benjamin

概述

均值回归是一种颇具吸引力的交易策略,许多技术娴熟的交易者都运用它来优化对市场的分析。这一概念的核心是,资产价格往往会回归其历史平均值,从而为策略性地把握交易时机创造机会。然而,手动分析价格走势既耗时又容易疏忽。这时,自动化就能显著提升交易体验。

在本文中,我们将开发一款基于MQL5的EA,旨在识别基于均值回归原理的交易机会。通过运用50周期的指数移动平均线(EMA)和相对强弱指数(RSI),我们将生成精确的入场信号,以便利用市场波动获利。为了使交易过程更加直观,我们的EA将在图表上直接以可视箭头显示买入和卖出信号,同时还会显示一个信息摘要,详细说明这些信号。

如果您渴望简化交易流程并利用自动化的强大功能,那么请与我们一同探索如何创建一款能体现均值回归精髓的MQL5的EA。让我们深入细节,看一下目录内容。



什么是均值回归

均值回归是一个金融概念,它认为随着时间的推移,资产价格、收益率或其他市场指标往往会回归到其历史平均值或“均值”。这一均值可以通过不同方法计算得出,包括指定时间段内的平均价格、移动平均线或标准基准收益率。

该理论基于这样的理念:市场中的极端波动通常是短暂的,价格最终会趋于稳定。交易者和分析师们利用这一原则来发现潜在的交易机会,尤其是当价格显著偏离其历史平均值时。

简要的历史与起源

均值回归是一个源于统计学和金融学的概念,最早在20世纪初弗朗西斯·高尔顿(Francis Galton)等数学家的著作中得以确认。该概念最初应用于研究自然现象,例如种群中极端特征(如身高)在几代人中回归到平均水平的趋势,后来,均值回归成为金融理论的一个基石。
在金融市场中,经济学家们例如约翰·梅纳德·凯恩斯(John Maynard Keynes)等推广了这一理念,他们观察到资产价格常常围绕其基本面价值波动。这样就催生了利用这些趋势的交易策略。例如,在上世纪90年代末的互联网泡沫期间,许多被高估的科技股最终都回归到了其基本面价值,展现了均值回归的实际作用。

均值回归的实际案例

  • 股票市场: 在新冠疫情期间,许多股票的价格与其历史平均值出现了大幅偏离。例如,旅游业股票大幅下跌至平均值以下,但随着市场恢复正常,后来又出现了反弹。
  • 货币对: 由于中央银行的政策和经济基本面因素,货币汇率常常会回归到其长期均值。例如,美元/日元汇率历史上会在一段时间内在一个可预测的范围内波动。
  • 大宗商品: 黄金和石油价格经常表现出均值回归的特性,在因地缘政治事件导致价格出现极端上涨或下跌后,往往会回归到历史水平。

结合我们的EA阐述均值回归的定义

在本项目中,均值回归指的是资产价格在显著高于或低于其平均值(均值)后,回归到该平均水平的趋势。这一原则构成了EA将生成的交易信号的基础。

  • 均值(50周期指数移动平均线):代码使用50周期的EMA来表示均值。EMA会根据近期价格数据动态调整,为当前市场趋势提供一个可靠的基准。当价格与EMA出现显著偏离时,就预示着可能会出现回归。 
  • 回归现象实操:当价格低于50周期EMA且RSI显示超卖时,EA会生成买入信号,预测价格将向上回归至EMA水平。相反,当价格高于50 EMA且RSI显示超买时,EA会生成卖出信号,预期价格将向下修正。


策略概述

本策略基于均值回归概念构建,该概念认为价格在显著偏离平均水平后,往往会回归至平均水平。结合50 EMA和RSI等可靠指标,这种行为为交易创造了机会。

  • 策略运作方式

该策略使用两个关键指标:

  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. 冷却机制。为避免重复信号和过度交易,策略设置了冷却期。触发信号后,策略会等待指定数量的K线后,才考虑新的信号。这一功能有助于在市场波动剧烈时减少干扰。 

平均真实波幅(ATR)用于设定动态的止盈(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)

将EA加载到图表上时,OnInit 函数便会执行。其主要作用是使用诸如用于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的核心,负责处理每个新的市场报价(tick)。它首先会检查自上次信号发出以来冷却期是否已过。如果冷却期未过,函数将提前退出,以避免发出冗余信号。接着,它会使用相应的指标句柄获取EMA、RSI和ATR的最新值。利用这些值,EA会评估买入或卖出信号的条件。

当价格低于EMA且RSI处于超卖区域时,会触发买入信号。相反,当价格高于EMA且RSI处于超买区域时,会触发卖出信号。对于每个信号,函数会在图表上绘制一个箭头,并显示包含价格、止损和止盈水平的文字摘要。同时,会更新信号的时间戳,以确保冷却机制得以执行。

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方法从指标句柄中提取数据。如果因任何原因导致数据获取失败,这些函数将返回0值,表明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);
}
  • 显示止盈/止损汇总信息

DisplayTextSummary函数会提供信号的简要汇总,包括价格、止损和止盈水平。这些信息会以标签的形式显示在图表的左上角。该函数会根据ATR及其乘数来计算止盈和止损水平,提供可随市场波动性动态调整的水平。如果已存在汇总标签,则会在创建新标签之前将其删除。这样可以确保仅显示最新的信号信息,减少图表杂乱,提高可读性。

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函数则计算自上次信号发出以来经过的K线数量。如果尚未生成任何信号,则返回一个较大数值,以保证系统正常运行。这一机制确保信号间隔合理,为交易者提供更清晰、更可靠的交易参考。
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秒)指数在两个不同时间框架下所进行的测试。以下,我们将两个测试的结果以表格的形式呈现。

信号生成与表现表

  • 表格1

信号类型  时间框架  生成的总信号数
有效信号(信号发出后价格上涨的情况)
买入信号准确率(%)
买入 M30 41 33 80.5%
卖出 M30 21 15 71.4%

  • 表格2

信号类型  时间框架  生成的总信号数
有效信号(信号发出后价格上涨的情况)
买入信号准确率(%)
 买入 H1 19 14 73.6% 
 卖出 H1  13 61.5% 

以下是该EA实时测试结果的图示说明。

实时信号

图例3. 测试结果2

让我们再来看一下下面的这张动图。

测试结果

图例4. 测试结果3

参考信息:


结论

均值回归收割器EA基于均值回归原理,提供了一套可靠的信号生成系统,其买入信号准确率至少可达70%,表现令人瞩目。该系统利用ATR设定止损和止盈水平,使系统能够适应各种市场波动状况,在震荡市场中尤为有效。

然而,需要注意的是,在强劲的趋势市场中,该EA的效果可能会打折扣,这也是均值回归策略的典型特征。为提升交易体验,建议对参数进行微调,并探索为卖出信号增设附加的过滤器。由于该EA不会自动执行交易,因此其发出的信号对希望利用短期反转获利的手动交易者尤为有益。

简而言之,对于关注市场反转形态的交易者而言,均值回归收割器EA可以成为得力的助手。密切关注市场状况,并随时准备调整交易策略,您就能充分发挥这一工具在交易中的潜力。

日期 工具名  描述 版本  更新  备注
01/10/24 图表展示器 以重影效果覆盖前一日价格走势的脚本 1.0 初始版本 Lynnchris工具箱的第一个工具
18/11/24 分析评论 以表格形式提供前一日的信息,并预测市场的未来方向 1.0 初始版本 Lynnchris工具箱的第二个工具
27/11/24 分析大师 每两小时定期更新市场指标  1.01 第二个版本 Lynnchris工具箱的第三个工具
02/12/24 Analytics Forecaster  集成Telegram通知功能,每两小时定时更新市场指标 1.1 第三个版本 工具数4
09/12/24 波动率导航仪 该EA通过布林带、RSI和ATR三大指标综合分析市场状况 1.0 初始版本 工具数5
19/12/24 均值回归信号收割器  运用均值回归策略分析市场并提供交易信号  1.0  初始版本  工具数6 

本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/16700

附加的文件 |
Mean_Reversion.mq5 (16.16 KB)
最近评论 | 前往讨论 (3)
linfo2
linfo2 | 7 1月 2025 在 03:33
谢谢你,克里斯蒂安,作为模板非常有用
Roman Shiredchenko
Roman Shiredchenko | 3 9月 2025 在 18:58
是....谢谢。有趣的材料 - 我将把它带入我的工作中,并在策略测试器 和实际交易中寻找交易方法的变体......
khanatd
khanatd | 15 10月 2025 在 06:10

您好,请问 mql4 版本在当前蜡烛上有箭头吗?

谢谢

khan

从基础到中级:数组(四) 从基础到中级:数组(四)
在本文中,我们将看看如何做一些与 C、C++ 和 Java 等语言中实现的非常相似的事情。我说的是在函数或过程中传递几乎无限数量的参数。虽然这似乎是一个相当高级的主题,但在我看来,任何理解了前面概念的人都可以很容易地实现这里展示的内容。只要它们真的被正确理解。
基于Python与MQL5的多模块交易机器人(第一部分):构建基础架构与首个模块 基于Python与MQL5的多模块交易机器人(第一部分):构建基础架构与首个模块
我们将开发一个模块化交易系统,该系统结合了 Python 进行数据分析,并使用 MQL5 执行交易。四个独立模块并行监控市场的不同方面:成交量、套利、经济指标和风险,并使用包含400棵树的随机森林( RandomForest )。特别强调风险管理,因为即使是最先进的交易算法,如果没有适当的风险管理,也是毫无用处的。
重构经典策略(第十三部分):最小化均线交叉的滞后性 重构经典策略(第十三部分):最小化均线交叉的滞后性
在我们交易者社区中,均线交叉策略已是广为人知,然而,自该策略诞生以来,其核心思想却几乎一成未变。在本次讨论中,我们将为您呈现对原策略的一项微调,其目的在于最小化该交易策略中存在的滞后性。所有原策略的爱好者们,不妨根据我们今天将要探讨的见解,来重新审视并改进这一策略。通过使用两条周期相同的移动平均线,我们可以在不违背策略基本原则的前提下,显著减少交易策略的滞后。
交易中的神经网络:降低锐度强化变换器效率(SAMformer) 交易中的神经网络:降低锐度强化变换器效率(SAMformer)
训练变换器模型需要大量数据,并且往往很困难,因为模型不擅长类推到小型数据集。SAMformer 框架通过避免糟糕的局部最小值来帮助解决这个问题。即使在有限的训练数据集上,也能提升模型的效率。