English Русский Deutsch 日本語
preview
开发一款波段交易入场监控智能交易系统(EA)

开发一款波段交易入场监控智能交易系统(EA)

MetaTrader 5示例 |
39 0
Clemence Benjamin
Clemence Benjamin

内容:


概述

比特币兑美元(BTCUSD)是具有显著长期前景的主要交易对之一。今天,我们选择它作为范例,来指导我们的开发流程。

比特币的价格受市场情绪、宏观经济因素和不断变化的监管环境影响,波动性极高。在这些波动中识别出有利可图的入场点位极具挑战性,尤其是对于仅依赖手动分析的交易者而言。例如,在过去两年中,比特币价格从约16,000美元的低点一路飙升,至2024年11月创下99,645.39美元的历史新高。在此期间,围绕100周期指数移动平均线(EMA 100)出现了多个关键入场机会,为长期交易策略提供了宝贵见解。

解决上述挑战的方案是开发一款用于监测长期交易入场信号的MQL5监控EA。该EA将具备以下功能:

  • 持续监控特定交易对的价格走势。
  • 利用EMA 100作为动态支撑或阻力位,识别潜在的入场机会。
  • 当满足特定条件(如价格在EMA 100处反弹)时,向交易者发出警报。

该工具将实现分析自动化,使交易者能够专注于决策制定,而非持续监控市场。

下图展示了比特币与100日均线对应的关键支撑位。

比特币价格在100日均线处反弹

BTCUSD,H4:比特币兑美元:价格在EMA 100处反弹



比特币兑美元(BTCUSD)2021-2024年概况

2021年至2024年间,比特币经历了剧烈的价格波动。2022年,受利率上升和市场低迷影响,比特币价格大幅下跌,收盘价低于20,000美元;2023年则出现反弹,年初价格为16,530美元,年末升至42,258美元。2024年,随着比特币现货ETF获批和美联储降息,比特币价格飙升,11月达到99,645.39美元的峰值。

在撰写本文时,该交易对价格约为97,300美元,与一年前的价格相比,涨幅高达146.5%。根据CoinGecko的数据,其历史最高价为99,645.39美元,于2024年12月初创下。

比特币日线图

比特币兑美元日线图价格走势(2021-2024年)


开发一款波段交易入场监控智能交易系统(EA)

我将开发流程分为三个步骤,以便我们能够有条不紊地推进,最终打造出一款功能完备的EA。第一步是深入了解我们所使用的移动平均线,这是开发自定义指标的基础。该指标将作为一个独立工具使用,同时也为后续步骤中构建我们的监控型EA奠定基础。

第一步:了解EMA100策略


指数移动平均线(EMA)是一种被广泛应用的指标,它对近期数据赋予了更大的权重。尤其是100周期指数移动平均线(EMA 100),在交易对中是一个关键水平,常常充当强支撑位或阻力位。从历史上看,当价格在EMA 100处反弹时,尤其是波动时期,比特币往往会涌现出许多盈利的入场机会。

第二步:监控指标


由于我们使用的是MetaTrader 5交易终端自带的EMA功能,我决定首先开发一个利用这些内置工具的监控指标。这种方法可以简化在价格走势中识别关键关注区域的过程。根据我的经验,该指标能够通过终端通知、推送通知甚至电子邮件发送警报。

然而,它无法处理用于高级警报服务的网络请求 ,例如与热门社交网络的集成。为了摆脱这一限制,我们将进入第三步,将该指标升级为EA。这一转变将实现更强大的功能,包括通过Telegram实现无缝通信,确保构建一个全面且高效的监控系统。

以下是我们监控指标的开发分解:

属性与基本数据:

本部分定义了EA的基本数据,包括版权所有者、作者个人资料链接、版本号以及指标用途的简要描述。这些信息对于文件存档至关重要,有助于用户快速了解创建者及指标的预期功能。

#property copyright "Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.0"
#property description "EMA 100 Monitoring Indicator"

指标设置

在这一部分中,我们将对交易图表上显示的视觉呈现方式进行配置。#property indicator_chart_window 指令表明,该EA将在主图表窗口内运行。通过定义两个指标缓冲区(indicator_buffers 2),我们为显示两种不同的信号做好了准备。属性中的indicator_type1和indicator_type2指定这两个指标均将以箭头形式呈现,且分别采用橙色和蓝色两种不同颜色,并标注“寻找 EMA 100 反弹机会”。这种设置通过为交易者提供基于价格与EMA交互的即时视觉提示,增强了交易机会判断的清晰度。

///Properties and Settings

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2

#property indicator_type1 DRAW_ARROW
#property indicator_width1 5
#property indicator_color1 0xFFAA00
#property indicator_label1 "Look for EMA 100 bounce "

#property indicator_type2 DRAW_ARROW
#property indicator_width2 5
#property indicator_color2 0x0000FF
#property indicator_label2 "Look for EMA 100 bounce "

缓冲区定义

本部分对EA生成的信号所存储的指标缓冲区进行定义。Buffer1和Buffer2被声明为双精度型数组,用于存储与价格走势和EMA相关的两种信号的数据。常量PLOT_MAXIMUM_BARS_BACK和OMIT_OLDEST_BARS被设定用于管理处理的历史K线数量,确保程序高效运行,避免因处理过时数据而给系统造成负担。这种设计选择有助于在保持系统性能的同时,为用户提供相关且及时的信息。

#define PLOT_MAXIMUM_BARS_BACK 5000
#define OMIT_OLDEST_BARS 50

//--- indicator buffers
double Buffer1[];
double Buffer2[];

输入参数:

在这一部分,我们定义了用户可配置的输入参数,以增强EA的灵活性。输入参数 int EMA_Period = 100; 允许用户指定计算EMA的周期,使得EA能够适应各种交易策略。此外,诸如 Audible_Alerts(声音警报)和 Push_Notifications(推送通知)等标识默认设置为 true,以便在发生重大市场事件时启用实时警报和通知。其他变量,如Low、High和MA_handle,它们将存储价格数据并处理移动平均线计算,在EA的运行中发挥关键作用。

input int EMA_Period = 100;
datetime time_alert; //used when sending alert
bool Audible_Alerts = true;
bool Push_Notifications = true;
double myPoint; //initialized in OnInit
double Low[];
int MA_handle;
double MA[];
double High[];

警报函数:

myAlert 函数旨在实现EA内警报管理的集中化。该函数接收两个参数:type,用于指定警报的性质(例如“打印”“错误”“指标”);message,用于包含警报文本。根据type的不同,该函数要么打印调试信息,要么发送与市场变化相关的警报。这种方法提高了代码的组织性和可读性,便于后续维护。通过提供声音警报和推送通知,该函数确保用户能够及时了解重要的市场动态,这样对于做出及时的交易决策至关重要。

void myAlert(string type, string message)
  {
   if(type == "print")
      Print(message);
   else if(type == "error")
     {
      Print(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
     }
   else if(type == "order")
     {
     }
   else if(type == "modify")
     {
     }
   else if(type == "indicator")
     {
      Print(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
      if(Audible_Alerts) Alert(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
      if(Push_Notifications) SendNotification(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
     }
  }

初始化函数:

OnInit函数是EA加载时的入口点。该函数会对指标缓冲区进行初始化,并设置EMA的计算。使用SetIndexBuffer函数可将已定义的缓冲区与指标绘图关联起来,确保相关数值能够在图表上显示。此外,该函数还会检查移动平均线句柄(MA_handle)是否成功创建,并提供了错误处理机制,增强了系统的健壮性。若初始化过程中出现任何问题,系统将打印清晰的错误信息,便于用户排查故障。这种全面的设置对于确保EA从一开始就能正常运行至关重要。

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {   
   SetIndexBuffer(0, Buffer1);
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
   PlotIndexSetInteger(0, PLOT_ARROW, 233);
   SetIndexBuffer(1, Buffer2);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
   PlotIndexSetInteger(1, PLOT_ARROW, 234);
   //initialize myPoint
   myPoint = Point();
   if(Digits() == 5 || Digits() == 3)
     {
      myPoint *= 10;
     }
   MA_handle = iMA(NULL, PERIOD_CURRENT, EMA_Period, 0, MODE_SMA, PRICE_CLOSE);
   if(MA_handle < 0)
     {
      Print("The creation of iMA has failed: MA_handle=", INVALID_HANDLE);
      Print("Runtime error = ", GetLastError());
      return(INIT_FAILED);
     }
   
   return(INIT_SUCCEEDED);
  }

计算函数:

OnCalculate函数是EA的核心所在,其中包含了计算指标值的逻辑。该函数会处理传入的市场数据,获取价格信息,并计算移动平均线。函数启动时,会根据总数据量和已计算的数据量来确定需要处理的数据量(即K线数量)。随后,它会初始化缓冲区,并获取必要的数据(如最低价和最高价),然后进入主循环。在该循环中,函数会遍历价格数据,根据价格与EMA之间的关系检查生成交易信号的条件。此函数的效率至关重要,因为它使EA能够实时动态响应市场变化。

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime& time[],
                const double& open[],
                const double& high[],
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])
  {
   int limit = rates_total - prev_calculated;
   //--- counting from 0 to rates_total
   ArraySetAsSeries(Buffer1, true);
   ArraySetAsSeries(Buffer2, true);
   //--- initial zero
   if(prev_calculated < 1)
     {
      ArrayInitialize(Buffer1, EMPTY_VALUE);
      ArrayInitialize(Buffer2, EMPTY_VALUE);
     }
   else
      limit++;
   datetime Time[];
   
   if(CopyLow(Symbol(), PERIOD_CURRENT, 0, rates_total, Low) <= 0) return(rates_total);
   ArraySetAsSeries(Low, true);
   if(BarsCalculated(MA_handle) <= 0) 
      return(0);
   if(CopyBuffer(MA_handle, 0, 0, rates_total, MA) <= 0) return(rates_total);
   ArraySetAsSeries(MA, true);
   if(CopyHigh(Symbol(), PERIOD_CURRENT, 0, rates_total, High) <= 0) return(rates_total);
   ArraySetAsSeries(High, true);
   if(CopyTime(Symbol(), Period(), 0, rates_total, Time) <= 0) return(rates_total);
   ArraySetAsSeries(Time, true);

主要逻辑:

OnCalculate函数中,主逻辑循环会分析与EMA相关的价格走势。它会检查特定条件,例如最低价下穿EMA的情况,或者最高价上穿EMA的情况。当满足这些条件时,相应的缓冲区(低于EMA的反弹情况使用Buffer1缓冲区,高于EMA的反弹情况使用Buffer2缓冲区)会被填充当前的最低价或最高价,同时会为用户触发警报。这一机制是提供可操作交易信号的基础,使交易者能够基于技术分析做出明智的决策。这种设置指标值的清晰且有条理的方法,确保了用户能够及时收到关于潜在交易机会的相关信息。

//--- main loop
   for(int i = limit-1; i >= 0; i--)
     {
      if (i >= MathMin(PLOT_MAXIMUM_BARS_BACK-1, rates_total-1-OMIT_OLDEST_BARS)) continue; //omit some old rates to prevent "Array out of range" or slow calculation   
      
      //Indicator Buffer 1
      if(Low[i] < MA[i]
      && Low[i+1] > MA[i+1] //Candlestick Low crosses below Moving Average
      )
        {
         Buffer1[i] = Low[i]; //Set indicator value at Candlestick Low
         if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Look for EMA 100 bounce "); //Alert on next bar open
         time_alert = Time[1];
        }
      else
        {
         Buffer1[i] = EMPTY_VALUE;
        }
      //Indicator Buffer 2
      if(High[i] > MA[i]
      && High[i+1] < MA[i+1] //Candlestick High crosses above Moving Average
      )
        {
         Buffer2[i] = High[i]; //Set indicator value at Candlestick High
         if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Look for EMA 100 bounce "); //Alert on next bar open
         time_alert = Time[1];
        }
      else
        {
         Buffer2[i] = EMPTY_VALUE;
        }
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+

关于指标完整无误的代码如下:

#property copyright "Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.0"
#property description "EMA 100 Monitoring Indicator"

//--- indicator settings
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2

#property indicator_type1 DRAW_ARROW
#property indicator_width1 5
#property indicator_color1 0xFFAA00
#property indicator_label1 "Look for EMA 100 bounce "

#property indicator_type2 DRAW_ARROW
#property indicator_width2 5
#property indicator_color2 0x0000FF
#property indicator_label2 "Look for EMA 100 bounce "

#define PLOT_MAXIMUM_BARS_BACK 5000
#define OMIT_OLDEST_BARS 50

//--- indicator buffers
double Buffer1[];
double Buffer2[];

input int EMA_Period = 100;
datetime time_alert; //used when sending alert
bool Audible_Alerts = true;
bool Push_Notifications = true;
double myPoint; //initialized in OnInit
double Low[];
int MA_handle;
double MA[];
double High[];

void myAlert(string type, string message)
  {
   if(type == "print")
      Print(message);
   else if(type == "error")
     {
      Print(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
     }
   else if(type == "order")
     {
     }
   else if(type == "modify")
     {
     }
   else if(type == "indicator")
     {
      Print(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
      if(Audible_Alerts) Alert(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
      if(Push_Notifications) SendNotification(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
     }
  }

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {   
   SetIndexBuffer(0, Buffer1);
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
   PlotIndexSetInteger(0, PLOT_ARROW, 233);
   SetIndexBuffer(1, Buffer2);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
   PlotIndexSetInteger(1, PLOT_ARROW, 234);
   //initialize myPoint
   myPoint = Point();
   if(Digits() == 5 || Digits() == 3)
     {
      myPoint *= 10;
     }
   MA_handle = iMA(NULL, PERIOD_CURRENT, EMA_Period, 0, MODE_SMA, PRICE_CLOSE);
   if(MA_handle < 0)
     {
      Print("The creation of iMA has failed: MA_handle=", INVALID_HANDLE);
      Print("Runtime error = ", GetLastError());
      return(INIT_FAILED);
     }
   
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime& time[],
                const double& open[],
                const double& high[],
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])
  {
   int limit = rates_total - prev_calculated;
   //--- counting from 0 to rates_total
   ArraySetAsSeries(Buffer1, true);
   ArraySetAsSeries(Buffer2, true);
   //--- initial zero
   if(prev_calculated < 1)
     {
      ArrayInitialize(Buffer1, EMPTY_VALUE);
      ArrayInitialize(Buffer2, EMPTY_VALUE);
     }
   else
      limit++;
   datetime Time[];
   
   if(CopyLow(Symbol(), PERIOD_CURRENT, 0, rates_total, Low) <= 0) return(rates_total);
   ArraySetAsSeries(Low, true);
   if(BarsCalculated(MA_handle) <= 0) 
      return(0);
   if(CopyBuffer(MA_handle, 0, 0, rates_total, MA) <= 0) return(rates_total);
   ArraySetAsSeries(MA, true);
   if(CopyHigh(Symbol(), PERIOD_CURRENT, 0, rates_total, High) <= 0) return(rates_total);
   ArraySetAsSeries(High, true);
   if(CopyTime(Symbol(), Period(), 0, rates_total, Time) <= 0) return(rates_total);
   ArraySetAsSeries(Time, true);
   //--- main loop
   for(int i = limit-1; i >= 0; i--)
     {
      if (i >= MathMin(PLOT_MAXIMUM_BARS_BACK-1, rates_total-1-OMIT_OLDEST_BARS)) continue; //omit some old rates to prevent "Array out of range" or slow calculation   
      
      //Indicator Buffer 1
      if(Low[i] < MA[i]
      && Low[i+1] > MA[i+1] //Candlestick Low crosses below Moving Average
      )
        {
         Buffer1[i] = Low[i]; //Set indicator value at Candlestick Low
         if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Look for EMA 100 bounce "); //Alert on next bar open
         time_alert = Time[1];
        }
      else
        {
         Buffer1[i] = EMPTY_VALUE;
        }
      //Indicator Buffer 2
      if(High[i] > MA[i]
      && High[i+1] < MA[i+1] //Candlestick High crosses above Moving Average
      )
        {
         Buffer2[i] = High[i]; //Set indicator value at Candlestick High
         if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Look for EMA 100 bounce "); //Alert on next bar open
         time_alert = Time[1];
        }
      else
        {
         Buffer2[i] = EMPTY_VALUE;
        }
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+

第三步:EA开发

在这部分中,我们将指导您使用MQL5开发一款简单却高效的波段交易入场监控EA。该EA旨在监控市场价格,尤其以比特币为例,并根据预设条件发送警报。

为简化流程,我们将省略基本数据详情,因为在指标开发阶段已对这些内容进行了详尽解释。让我们逐步拆解代码,确保每一部分都易于理解和实现。

1. 输入参数:

input string IndicatorName = "ema100_monitoring_indicator"; // Name of the custom indicator
input bool EnableTerminalAlerts = true;
input bool EnablePushNotifications = true;
input bool EnableTelegramAlerts = false;
input string TelegramBotToken = "YOUR_BOT_TOKEN"; // Replace with your bot token
input string TelegramChatID = "YOUR_CHAT_ID";     // Replace with your chat ID

在本部分中,我们定义了多种可自定义的输入参数,这些参数增强了EA的灵活性和可用性。IndicatorName参数使我们能够指定想要监控的自定义指标名称,默认设置为“比特币监控器”。布尔型标识——EnableTerminalAlertsEnablePushNotificationsEnableTelegramAlerts——允许我们根据个人偏好定制警报通知方式。例如,我们可以选择在交易终端、移动设备上直接接收通知,或者通过Telegram接收。此外,我们必须输入Telegram机器人令牌和聊天ID,以启用Telegram警报功能。这种自定义设置使得其他用户能够根据自己的偏好和需求优化交易体验。

2. 指标句柄:

int indicatorHandle = INVALID_HANDLE;
int emaHandle = INVALID_HANDLE;

本部分声明了用于管理该EA中所使用指标的必要变量。indicatorHandleemaHandle 变量将分别存储自定义指标和EMA的引用句柄。这两个句柄均初始化为INVALID_HANDLE,表示它们尚未被赋值。这一设置对于EA的功能实现至关重要,因为它使程序能够与指定的指标进行交互,并获取相关的市场数据以供分析。

3. 警报函数:

void AlertMessage(string message) {
   if (EnableTerminalAlerts) Alert(message);
   if (EnablePushNotifications) SendNotification(message);
   if (EnableTelegramAlerts) SendTelegramMessage(message);
}

AlertMessage 函数在EA的警报通知管理中起着至关重要的作用。该函数接受一个字符串参数message,其中包含要发送的警报文本。它会检查用户对警报类型的偏好——终端警报、推送通知和Telegram消息,并据此发送消息。通过将警报管理集中在此函数中,代码变得更加有条理且更易于维护。对于那些依赖及时通知、以便根据市场走势做出明智决策的交易者而言,这一功能尤为重要。

4. Telegram警报通知: 

void SendTelegramMessage(string message) {
   if (EnableTelegramAlerts) {
      string url = "https://api.telegram.org/bot" + TelegramBotToken + "/sendMessage?chat_id=" + TelegramChatID + "&text=" + message;
      int timeout = 5000;
      ResetLastError();

      char postData[];
      uchar result[];
      string response;

      int res = WebRequest("GET", url, NULL, timeout, postData, result, response);

      if (res != 200) {
         Print("Telegram WebRequest failed. Error: ", GetLastError(), ", HTTP Code: ", res);
      } else {
         Print("Telegram message sent successfully: ", response);
      }
   }
}

为了便于与用户通信,系统实现了SendTelegramMessage函数。该函数会构建一个利用Telegram应用程序接口(API)向指定聊天群组发送消息的统一资源定位符(URL)。它首先检查Telegram警报功能是否已启用。如果已启用,该函数会利用构建好的包含机器人令牌(bot token)和聊天群组ID(chat ID)的URL,向Telegram服务器发送一个 GET请求。此外,该函数还会处理请求过程中可能出现的错误,如果消息发送失败,会向用户提供反馈。这一功能使用户能够直接在Telegram上接收警报,提高了信息获取的便捷性和易用性。

5. 初始化函数:

int OnInit() {
   Print("Bitcoin Monitoring EA started.");

   // Attach the custom indicator to the chart
   indicatorHandle = iCustom(_Symbol, _Period, IndicatorName);
   if (indicatorHandle == INVALID_HANDLE) {
      Print("Failed to attach indicator: ", IndicatorName, ". Error: ", GetLastError());
      return(INIT_FAILED);
   }

   // Attach built-in EMA 100 to the chart
   emaHandle = iMA(_Symbol, _Period, 100, 0, MODE_EMA, PRICE_CLOSE);
   if (emaHandle == INVALID_HANDLE) {
      Print("Failed to create EMA 100. Error: ", GetLastError());
      return(INIT_FAILED);
   }

   // Add EMA 100 to the terminal chart
   if (!ChartIndicatorAdd(0, 0, emaHandle)) {
      Print("Failed to add EMA 100 to the chart. Error: ", GetLastError());
   }

   return(INIT_SUCCEEDED);
}

OnInit函数在EA首次加载时执行。它负责设置必要的指标,并确保EA已做好运行准备。在此函数中,通过指标名称将自定义指标附加到图表上,并检查句柄以确认是否成功附加。如果附加失败,则会打印错误信息以帮助诊断问题。此外,该函数还会创建一个周期为100的EMA,并在将其添加到图表之前验证其是否创建成功。正确的初始化对于EA的功能实现至关重要,因为它确保所有组件均已正确设置并准备好处理市场数据。

6. 反初始化函数:

void OnDeinit(const int reason) {
   Print(" EA stopped.");
   if (indicatorHandle != INVALID_HANDLE) {
      IndicatorRelease(indicatorHandle);
   }
   if (emaHandle != INVALID_HANDLE) {
      IndicatorRelease(emaHandle);
   }
}

当EA从图表中移除或终端关闭时,会调用OnDeinit函数。其主要目的是清理资源并防止内存泄漏。该函数会检查指标句柄是否有效,如果有效则释放它们以清理系统资源。此外,该函数还会打印一条消息,表明EA已停止运行,从而为用户提供关于EA状态的明确反馈。正确的反初始化对于保持最优性能以及确保交易环境整洁无冗余至关重要。

7. 主要逻辑:

void OnTick() {
   static datetime lastAlertTime = 0; // Prevent repeated alerts for the same signal

   if (indicatorHandle == INVALID_HANDLE || emaHandle == INVALID_HANDLE) return;

   double buffer1[], buffer2[];
   ArraySetAsSeries(buffer1, true);
   ArraySetAsSeries(buffer2, true);

   // Read data from the custom indicator
   if (CopyBuffer(indicatorHandle, 0, 0, 1, buffer1) < 0) {
      Print("Failed to copy Buffer1. Error: ", GetLastError());
      return;
   }
   if (CopyBuffer(indicatorHandle, 1, 0, 1, buffer2) < 0) {
      Print("Failed to copy Buffer2. Error: ", GetLastError());
      return;
   }

   // Check for signals in Buffer1
   if (buffer1[0] != EMPTY_VALUE && TimeCurrent() != lastAlertTime) {
      string message = "Signal detected: Look for EMA 100 bounce (Low). Symbol: " + _Symbol;
      AlertMessage(message);
      lastAlertTime = TimeCurrent();
   }

   // Check for signals in Buffer2
   if (buffer2[0] != EMPTY_VALUE && TimeCurrent() != lastAlertTime) {
      string message = "Signal detected: Look for EMA 100 bounce (High). Symbol: " + _Symbol;
      AlertMessage(message);
      lastAlertTime = TimeCurrent();
   }

   // Debugging EMA 100 value
   double emaValueArray[];
   ArraySetAsSeries(emaValueArray, true); // Ensure it's set as series
   if (CopyBuffer(emaHandle, 0, 0, 1, emaValueArray) > 0) {
      Print("EMA 100 Current Value: ", emaValueArray[0]);
   } else {
      Print("Failed to read EMA 100 buffer. Error: ", GetLastError());
   }
}

OnTick函数是EA的核心逻辑所在,每当市场接收到新的数据报价(tick)时,都会执行该函数。在执行计算之前,此函数会先检查指标句柄是否有效。它会初始化数组,用于存储来自自定义指标的数据,并从指标缓冲区中获取最新值。如果在任一缓冲区中检测到交易信号,该函数会通过AlertMessage函数触发警报,通知用户可能存在的交易机会。此外,为便于调试,该函数还会获取EMA的当前值,从而提供关于EA运行情况的透明信息。这种实时分析使EA能够快速响应市场变化,成为交易者宝贵的工具。

EA完整代码如下:

//+------------------------------------------------------------------+
//| Bitcoin Monitoring Expert Advisor                                |
//| Copyright 2024, Clemence Benjamin                                |
//| https://www.mql5.com/en/users/billionaire2024/seller            |
//+------------------------------------------------------------------+
#property copyright "Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.0"
#property description "BTCUSD Monitoring Expert Advisor"

//--- Input parameters
input string IndicatorName = "ema100_monitoring_indicator"; // Name of the custom indicator
input bool EnableTerminalAlerts = true;
input bool EnablePushNotifications = true;
input bool EnableTelegramAlerts = false;
input string TelegramBotToken = "YOUR_BOT_TOKEN";         // Replace with your bot token
input string TelegramChatID = "YOUR_CHAT_ID";             // Replace with your chat ID

//--- Indicator handles
int indicatorHandle = INVALID_HANDLE;
int emaHandle = INVALID_HANDLE;

//--- Alert function
void AlertMessage(string message) {
   if (EnableTerminalAlerts) Alert(message);
   if (EnablePushNotifications) SendNotification(message);
   if (EnableTelegramAlerts) SendTelegramMessage(message);
}

//--- Telegram Alerting
void SendTelegramMessage(string message) {
   if (EnableTelegramAlerts) {
      string url = "https://api.telegram.org/bot" + TelegramBotToken + "/sendMessage?chat_id=" + TelegramChatID + "&text=" + message;
      int timeout = 5000;
      ResetLastError();

      char postData[];
      uchar result[];
      string response;

      int res = WebRequest("GET", url, NULL, timeout, postData, result, response);

      if (res != 200) {
         Print("Telegram WebRequest failed. Error: ", GetLastError(), ", HTTP Code: ", res);
      } else {
         Print("Telegram message sent successfully: ", response);
      }
   }
}

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   Print("Bitcoin Monitoring EA started.");

   // Attach the custom indicator to the chart
   indicatorHandle = iCustom(_Symbol, _Period, IndicatorName);
   if (indicatorHandle == INVALID_HANDLE) {
      Print("Failed to attach indicator: ", IndicatorName, ". Error: ", GetLastError());
      return(INIT_FAILED);
   }

   // Attach built-in EMA 100 to the chart
   emaHandle = iMA(_Symbol, _Period, 100, 0, MODE_EMA, PRICE_CLOSE);
   if (emaHandle == INVALID_HANDLE) {
      Print("Failed to create EMA 100. Error: ", GetLastError());
      return(INIT_FAILED);
   }

   // Add EMA 100 to the terminal chart
   if (!ChartIndicatorAdd(0, 0, emaHandle)) {
      Print("Failed to add EMA 100 to the chart. Error: ", GetLastError());
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   Print("Bitcoin Monitoring EA stopped.");
   if (indicatorHandle != INVALID_HANDLE) {
      IndicatorRelease(indicatorHandle);
   }
   if (emaHandle != INVALID_HANDLE) {
      IndicatorRelease(emaHandle);
   }
}

void OnTick() {
   static datetime lastAlertTime = 0; // Prevent repeated alerts for the same signal

   if (indicatorHandle == INVALID_HANDLE || emaHandle == INVALID_HANDLE) return;

   double buffer1[], buffer2[];
   ArraySetAsSeries(buffer1, true);
   ArraySetAsSeries(buffer2, true);

   // Read data from the custom indicator
   if (CopyBuffer(indicatorHandle, 0, 0, 1, buffer1) < 0) {
      Print("Failed to copy Buffer1. Error: ", GetLastError());
      return;
   }
   if (CopyBuffer(indicatorHandle, 1, 0, 1, buffer2) < 0) {
      Print("Failed to copy Buffer2. Error: ", GetLastError());
      return;
   }

   // Check for signals in Buffer1
   if (buffer1[0] != EMPTY_VALUE && TimeCurrent() != lastAlertTime) {
      string message = "Signal detected: Look for EMA 100 bounce (Low). Symbol: " + _Symbol;
      AlertMessage(message);
      lastAlertTime = TimeCurrent();
   }

   // Check for signals in Buffer2
   if (buffer2[0] != EMPTY_VALUE && TimeCurrent() != lastAlertTime) {
      string message = "Signal detected: Look for EMA 100 bounce (High). Symbol: " + _Symbol;
      AlertMessage(message);
      lastAlertTime = TimeCurrent();
   }

   // Debugging EMA 100 value
   double emaValueArray[];
   ArraySetAsSeries(emaValueArray, true); // Ensure it's set as series
   if (CopyBuffer(emaHandle, 0, 0, 1, emaValueArray) > 0) {
      Print("EMA 100 Current Value: ", emaValueArray[0]);
   } else {
      Print("Failed to read EMA 100 buffer. Error: ", GetLastError());
   }
}


测试和优化

在成功编译代码后,我们在MetaTrader 5交易终端中使用策略测试器(Strategy Tester)对其进行了测试。部分展示测试过程和结果的图片如下。

比特币监控EA

比特币监控EA:在策略测试器中运行

在策略测试器运行期间,我们成功展示了该EA的实时价格监测能力。以下是一张展示该过程以及EA实际运行表现的图片说明。

策略测试器中的比特币监控EA的价格监测情况

监测到的每个tick比特币价格变动情况:2022年


结果和分析

我们通过观察该指标在较高时间框架(尤其是H4和D1)上与100周期指数移动平均线(EMA 100)的交互情况,成功可视化该指标的表现。此系统能够通过三种不同方式发送警报,其中包括Telegram通知。从我们分享的图表中可以看出,价格始终遵循所选的EMA。下图展示了在MetaTrader 5终端上同时启动EA和指标的情景,彰显了它们的集成性与功能性。

在图表上添加EA和指标

在图表上添加EA和指标



结论

我们在本文中开发的这款监控型EA,对每位交易者而言都是极具价值的工具。它通过自动化价格监控,并集成如EMA 100等策略,减少了人工识别交易机会所需的工作量。尽管我们是为比特币兑美元开发的这款EA,但它同样可扩展至其他交易品种,或根据需求定制添加其他指标。该项目为初学者提供了一个激励性的基础模板,便于他们入门上手。发展前景无限广阔,所以大胆尝试不同的方法吧。

下载所附的EA和指标,使用您偏好的参数进行回测,并根据您的交易策略对其进行优化调整。通过将技术分析与自动化相结合,在瞬息万变的交易世界中保持领先地位。请注意,本系统目前仅专为监控和警报功能而设计,尚未集成交易功能。有关Telegram账号凭证信息,请访问以下文章:链接1链接2

附件文件列表:

文件 描述
ema100_monitoring_indicator.mq5 基于EMA 100反弹策略的自定义指标
bitcoin_monitoring_expert.mq5 通过WebRequest实现Telegram警报功能并持续监控的EA。


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

流动性攫取交易策略 流动性攫取交易策略
流动性攫取交易策略是智能资金概念(SMC)的核心组成部分,旨在识别并利用市场中机构投资者的操作行为。该策略聚焦于高流动性区域(如支撑位或阻力位),在这些区域,大额订单可引发价格波动,随后市场恢复原有趋势。本文将详细阐释流动性攫取的概念,并概述如何在MQL5中开发流动性攫取交易策略的智能交易系统(EA)。
构建K线趋势约束模型(第十部分):战略均线金叉与死叉(智能交易系统EA) 构建K线趋势约束模型(第十部分):战略均线金叉与死叉(智能交易系统EA)
您是否知道,基于移动平均线交叉的金叉和死叉策略,是识别长期市场趋势最为可靠的指标之一?当短期移动平均线上穿长期移动平均线时,金叉发出看涨趋势信号;而当短期移动平均线下穿长期移动平均线时,死叉则表明看跌趋势。尽管这些策略简单且有效,但手动运用时往往会导致错失机会或延迟交易。
迁移至 MQL5 Algo Forge(第 1 部分):创建主存储库 迁移至 MQL5 Algo Forge(第 1 部分):创建主存储库
在 MetaEditor 中处理项目时,开发人员经常需要管理代码版本。MetaQuotes 最近宣布迁移到 GIT,并推出具有代码版本控制和协作功能的 MQL5 Algo Forge。在本文中,我们将讨论如何更有效地使用新的和以前存在的工具。
开发先进的 ICT 交易系统:在指标中实现订单区块 开发先进的 ICT 交易系统:在指标中实现订单区块
在本文中,我们将学习如何创建一个指标来检测、绘制订单区块并提醒订单块的缓解。我们还将详细研究如何在图表上识别这些区块,设置准确的提醒,并使用矩形可视化它们的位置,以更好地了解价格行为。该指标将成为遵循聪明钱概念和内圈交易者(ICT,Inner Circle Trader)方法的交易者的关键工具。