使用 MQL5 向导生成的“EA 交易”只能以距当前价格的固定距离建立挂单。这意味着如果市场情况发生变化(例如市场波动中的变化),“EA 交易”不得不以新的参数重新运行。
这对于许多交易系统而言是不合适的。在大多数情形中,挂单的价格水平由交易系统动态决定。而且与当前价格的距离一直都在变化。在本文中,我们将探讨如何修改使用 MQL5 向导生成的“EA 交易”,使其能够以与当前价格的可变距离建立挂单。
生成的“EA 交易”的头文件将具有与下述代码大致相同的代码:
//+------------------------------------------------------------------+ //| Inputs | //+------------------------------------------------------------------+ //--- inputs for expert input string Expert_Title="ExpertMySignalEnvelopes.mq5"; // Document name ulong Expert_MagicNumber =3915; // bool Expert_EveryTick =false; // //--- inputs for main signal input int Signal_ThresholdOpen =10; // Signal threshold value to open [0...100] input int Signal_ThresholdClose =10; // Signal threshold value to close [0...100] input double Signal_PriceLevel =0.0; // Price level to execute a deal input double Signal_StopLevel =85.0; // Stop Loss level (in points) input double Signal_TakeLevel =195.0; // Take Profit level (in points) input int Signal_Expiration =0; // Expiration of pending orders (in bars) input int Signal_Envelopes_PeriodMA =13; // Envelopes(13,0,MODE_SMA,...) Period of averaging input int Signal_Envelopes_Shift =0; // Envelopes(13,0,MODE_SMA,...) Time shift input ENUM_MA_METHOD Signal_Envelopes_Method =MODE_SMA; // Envelopes(13,0,MODE_SMA,...) Method of averaging input ENUM_APPLIED_PRICE Signal_Envelopes_Applied =PRICE_CLOSE; // Envelopes(13,0,MODE_SMA,...) Prices series input double Signal_Envelopes_Deviation=0.2; // Envelopes(13,0,MODE_SMA,...) Deviation input double Signal_Envelopes_Weight =1.0; // Envelopes(13,0,MODE_SMA,...) Weight [0...1.0] //--- inputs for money input double Money_FixLot_Percent =10.0; // Percent input double Money_FixLot_Lots =0.1; // Fixed volume //+------------------------------------------------------------------+
请注意 Signal_PriceLevel 参数。在默认情况下,“EA 交易”由 Signal_PriceLevel=0 生成。此参数定义与当前价格的距离。如果其值等于零,将会以当前市场价格建立订单。要建立挂单,您需要为 Signal_PriceLevel 参数设置非零值,即 Signal_PriceLevel 的值可以是正数也可以是负数。
Signal_PriceLevel 参数的值通常是一个相当大的数字。负值与正值之间的差异如下所示:
Signal_PriceLevel=-50:
图 1. Signal_PriceLevel=-50
Signal_PriceLevel=50:
图 2. Signal_PriceLevel=50
因此,如果 Signal_PriceLevel=-50,将以较当前价格不太有利的价格建立挂单,而如果 Signal_PriceLevel=50,将以较当前价格更优的价格建立挂单。
这个版本的“EA 交易”建立卖出止损和买入止损订单。
我们先来看看下面的图,然后是注释部分:
图 3. 存储与当前价格的距离的数据
解读上图。
Expert Advisor 是使用 MQL5 向导生成的“EA 交易”。
因此,在 Expert Advisor 中声明的用于存储与当前价格距离的 Signal_PriceLevel 参数被传递至 CExpertSignal 类的 signal 对象。
CExpertSignal 类在以受保护的类的范围声明的 m_price_level 变量中存储与当前价格的距离的值:
class CExpertSignal : public CExpertBase { protected: //--- variables double m_base_price; // base price for detection of level of entering (and/or exit?) //--- variables for working with additional filters CArrayObj m_filters; // array of additional filters (maximum number of fileter is 64) //--- Adjusted parameters double m_weight; // "weight" of a signal in a combined filter int m_patterns_usage; // bit mask of using of the market models of signals int m_general; // index of the "main" signal (-1 - no) long m_ignore; // bit mask of "ignoring" the additional filter long m_invert; // bit mask of "inverting" the additional filter int m_threshold_open; // threshold value for opening int m_threshold_close;// threshold level for closing double m_price_level; // level of placing a pending orders relatively to the base price double m_stop_level; // level of placing of the "stop loss" order relatively to the open price double m_take_level; // level of placing of the "take profit" order relatively to the open price int m_expiration; // time of expiration of a pending order in bars
“EA 交易”由不同的功能块组成。
图 4.“EA 交易”的结构
解读上图:
正如您在使用 MQL5 向导生成的“EA 交易”的结构中看到的,“EA 交易”包含一些基类块。基类是标准库的一部分。
这些类从本质上而言是其他基类的后代,反过来它们又可以组成一个或多个基类。下面给出了 CExpert 和 CExpertSignal 两个类的前几行代码:
//+------------------------------------------------------------------+ //| Expert.mqh | //| Copyright 2009-2013, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "ExpertBase.mqh" #include "ExpertTrade.mqh" #include "ExpertSignal.mqh" #include "ExpertMoney.mqh" #include "ExpertTrailing.mqh" //+------------------------------------------------------------------+ . . . class CExpert : public CExpertBase
以及
//+------------------------------------------------------------------+ //| ExpertSignal.mqh | //| Copyright 2009-2013, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "ExpertBase.mqh" . . . class CExpertSignal : public CExpertBase
对于基类的任何修改,笔者持强烈反对态度:
作为替代,最好是修改“EA 交易”的块和交易信号生成器模块,尤其是考虑到我们的交易系统已经有一个在用的修改模块 - 轨道线指标的交易信号生成器。
所以,就这么定了:我们将修改“EA 交易”和交易信号生成器的块。
指针将从“EA 交易”传递至交易信号生成器。
为此,我们需要额外声明一个具有受保护范围的变量,并编写一个在内部变量中存储来自“EA 交易”的指针的方法:
图 5. 实现逻辑
图表的时间表为 D1。使用的指标是采用平均周期为 13 和指数平均法的轨道线。“EA 交易”可建立的订单类型为卖出止损和买入止损。
如果上一个柱为牛势,我们设置一个卖出止损订单。如果上一个柱为熊势,我们设置一个买入止损订单。换言之,我们期待回调:
图 6. 交易系统
要生成交易系统要求的交易信号,需要修改交易信号生成器 SignalEnvelopes.mqh 的标准模块。
注意,在此您可以使用标准库中的任意交易信号生成器。
那么,让我们开始。我得说,我更喜欢将程序保存在 MQL5 存储中。
要开始修改交易信号生成器,我们要做的第一件事就是创建一个空白的包含文件,删除其所有内容并粘贴轨道线指标的标准交易信号生成器的全部内容。
默认情况下交易信号生成器必定位于 ...MQL5\Include\Expert\Signal 中。不要在标准库的 ...\Signal 文件夹内放入太多的信息,我们在 ...\Expert 文件夹下创建一个新文件夹,并将其命名为 \MySignals:
图 7. 创建 MySignals 文件夹
接下来,我们使用 MQL5 向导来创建一个包含文件。
在 MetaEditor 中选择 File(文件)菜单下的 "New"(新建),再选择 "Include file (*.mqh)" (包含文件 (*.mqh))。
图 8. MQL5 向导创建包含文件
我们的信号生成器类的名称将是 MySignalEnvelopes。
并且它将位于:Include\Expert\MySignals\MySignalEnvelopes 。我们来指定它:
图 9. MQL5 向导包含文件的位置
单击 "Finish"(完成)后,MQL5 向导就会生成一个空模板。
生成的 MySignalEnvelopes.mqh 文件接下来必须添加至 MQL5 存储:
图 10. MQL5 存储添加文件
添加文件后,我们需要将更改提交至 MQL5 存储:
图 11. MQL5 存储提交更改
完成上述步骤后,我们可以继续修改我们的信号生成器了。
由于生成器基于 \Include\Expert\Signal\SignalEnvelopes.mqh 文件,我们复制该文件的全部内容并粘贴至生成器文件中,只留下原来的头文件:
//+------------------------------------------------------------------+ //| MySignalEnvelopes.mqh | //| Copyright © 2013, Vladimir Karputov | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2013, Vladimir Karputov" #property link "http://wmua.ru/slesar/" #include <Expert\ExpertSignal.mqh> // wizard description start //+------------------------------------------------------------------+ //| Description of the class | //| Title=Signals of indicator 'Envelopes' | //| Type=SignalAdvanced | //| Name=Envelopes | //| ShortName=Envelopes | //| Class=CSignalEnvelopes | //| Page=signal_envelopes | //| Parameter=PeriodMA,int,45,Period of averaging | //| Parameter=Shift,int,0,Time shift | //| Parameter=Method,ENUM_MA_METHOD,MODE_SMA,Method of averaging | //| Parameter=Applied,ENUM_APPLIED_PRICE,PRICE_CLOSE,Prices series | //| Parameter=Deviation,double,0.15,Deviation | //+------------------------------------------------------------------+ // wizard description end //+------------------------------------------------------------------+ //| Class CSignalEnvelopes. | //| Purpose: Class of generator of trade signals based on | //| the 'Envelopes' indicator. | //| Is derived from the CExpertSignal class. | //+------------------------------------------------------------------+ class CSignalEnvelopes : public CExpertSignal { protected: CiEnvelopes m_env; // object-indicator //--- adjusted parameters int m_ma_period; // the "period of averaging" parameter of the indicator int m_ma_shift; // the "time shift" parameter of the indicator ENUM_MA_METHOD m_ma_method; // the "method of averaging" parameter of the indicator ENUM_APPLIED_PRICE m_ma_applied; // the "object of averaging" parameter of the indicator double m_deviation; // the "deviation" parameter of the indicator double m_limit_in; // threshold sensitivity of the 'rollback zone' double m_limit_out; // threshold sensitivity of the 'break through zone' //--- "weights" of market models (0-100) int m_pattern_0; // model 0 "price is near the necessary border of the envelope" int m_pattern_1; // model 1 "price crossed a border of the envelope" public: CSignalEnvelopes(void); ~CSignalEnvelopes(void); //--- methods of setting adjustable parameters void PeriodMA(int value) { m_ma_period=value; } void Shift(int value) { m_ma_shift=value; } void Method(ENUM_MA_METHOD value) { m_ma_method=value; } void Applied(ENUM_APPLIED_PRICE value) { m_ma_applied=value; } void Deviation(double value) { m_deviation=value; } void LimitIn(double value) { m_limit_in=value; } void LimitOut(double value) { m_limit_out=value; } //--- methods of adjusting "weights" of market models void Pattern_0(int value) { m_pattern_0=value; } void Pattern_1(int value) { m_pattern_1=value; } //--- method of verification of settings virtual bool ValidationSettings(void); //--- method of creating the indicator and timeseries virtual bool InitIndicators(CIndicators *indicators); //--- methods of checking if the market models are formed virtual int LongCondition(void); virtual int ShortCondition(void); protected: //--- method of initialization of the indicator bool InitMA(CIndicators *indicators); //--- methods of getting data double Upper(int ind) { return(m_env.Upper(ind)); } double Lower(int ind) { return(m_env.Lower(ind)); } }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CSignalEnvelopes::CSignalEnvelopes(void) : m_ma_period(45), m_ma_shift(0), m_ma_method(MODE_SMA), m_ma_applied(PRICE_CLOSE), m_deviation(0.15), m_limit_in(0.2), m_limit_out(0.2), m_pattern_0(90), m_pattern_1(70) { //--- initialization of protected data m_used_series=USE_SERIES_OPEN+USE_SERIES_HIGH+USE_SERIES_LOW+USE_SERIES_CLOSE; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CSignalEnvelopes::~CSignalEnvelopes(void) { } //+------------------------------------------------------------------+ //| Validation settings protected data. | //+------------------------------------------------------------------+ bool CSignalEnvelopes::ValidationSettings(void) { //--- validation settings of additional filters if(!CExpertSignal::ValidationSettings()) return(false); //--- initial data checks if(m_ma_period<=0) { printf(__FUNCTION__+": period MA must be greater than 0"); return(false); } //--- ok return(true); } //+------------------------------------------------------------------+ //| Create indicators. | //+------------------------------------------------------------------+ bool CSignalEnvelopes::InitIndicators(CIndicators *indicators) { //--- check pointer if(indicators==NULL) return(false); //--- initialization of indicators and timeseries of additional filters if(!CExpertSignal::InitIndicators(indicators)) return(false); //--- create and initialize MA indicator if(!InitMA(indicators)) return(false); //--- ok return(true); } //+------------------------------------------------------------------+ //| Initialize MA indicators. | //+------------------------------------------------------------------+ bool CSignalEnvelopes::InitMA(CIndicators *indicators) { //--- check pointer if(indicators==NULL) return(false); //--- add object to collection if(!indicators.Add(GetPointer(m_env))) { printf(__FUNCTION__+": error adding object"); return(false); } //--- initialize object if(!m_env.Create(m_symbol.Name(),m_period,m_ma_period,m_ma_shift,m_ma_method,m_ma_applied,m_deviation)) { printf(__FUNCTION__+": error initializing object"); return(false); } //--- ok return(true); } //+------------------------------------------------------------------+ //| "Voting" that price will grow. | //+------------------------------------------------------------------+ int CSignalEnvelopes::LongCondition(void) { int result=0; int idx =StartIndex(); double close=Close(idx); double upper=Upper(idx); double lower=Lower(idx); double width=upper-lower; //--- if the model 0 is used and price is in the rollback zone, then there is a condition for buying if(IS_PATTERN_USAGE(0) && close<lower+m_limit_in*width && close>lower-m_limit_out*width) result=m_pattern_0; //--- if the model 1 is used and price is above the rollback zone, then there is a condition for buying if(IS_PATTERN_USAGE(1) && close>upper+m_limit_out*width) result=m_pattern_1; //--- return the result return(result); } //+------------------------------------------------------------------+ //| "Voting" that price will fall. | //+------------------------------------------------------------------+ int CSignalEnvelopes::ShortCondition(void) { int result =0; int idx =StartIndex(); double close=Close(idx); double upper=Upper(idx); double lower=Lower(idx); double width=upper-lower; //--- if the model 0 is used and price is in the rollback zone, then there is a condition for selling if(IS_PATTERN_USAGE(0) && close>upper-m_limit_in*width && close<upper+m_limit_out*width) result=m_pattern_0; //--- if the model 1 is used and price is above the rollback zone, then there is a condition for selling if(IS_PATTERN_USAGE(1) && close<lower-m_limit_out*width) result=m_pattern_1; //--- return the result return(result); } //+------------------------------------------------------------------+
现在,我们将修改代码的某些部分。
为避免混淆,修改过的代码将以高亮显示:
//+------------------------------------------------------------------+
//| MySignal.mqh |
//| Copyright © 2013, Vladimir Karputov |
//| http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
修改过的代码,是指需要复制和粘贴到交易信号生成器的代码。我希望这种高亮显示将有助于你们更好地理解代码。
由于我们编写我们自己的交易信号生成器类,其名称应与基类的名称不同。因此,我们在整个代码中用 CMySignalEnvelopes 替换 CSignalEnvelopes。
图 12. 重命名类
要确保交易信号生成器类在 MQL5 向导中以其名称显示,在描述块中将类名称
//| Title=Signals of indicator 'Envelopes' |
改为
//| Title=Signals of indicator 'MySignalEnvelopes' |
将 MA 周期值
//| Parameter=PeriodMA,int,45,Period of averaging |
改为 13(这只是我的建议,您可以设置您喜欢的任何值)
//| Parameter=PeriodMA,int,13,Period of averaging |
此外,我们还修改 Deviation 参数
//| Parameter=Deviation,double,0.15,Deviation |
方法是设置一个较大的值
//| Parameter=Deviation,double,1.15,Deviation |
根据我们的实现逻辑,我们需要声明一个用于存储指向主信号的指针的内部变量。
由于这必须是一个内部变量(仅在交易信号生成器类范围内),必须将其添加至下述代码块:
protected: CiEnvelopes m_env; // object-indicator //--- adjusted parameters int m_ma_period; // the "period of averaging" parameter of the indicator int m_ma_shift; // the "time shift" parameter of the indicator ENUM_MA_METHOD m_ma_method; // the "method of averaging" parameter of the indicator ENUM_APPLIED_PRICE m_ma_applied; // the "object of averaging" parameter of the indicator double m_deviation; // the "deviation" parameter of the indicator //--- "weights" of market models (0-100) int m_pattern_0; // model 0 CExpertSignal *m_signal; // storing the pointer to the main signal
亦请注意,我从代码中删除了不必要的变量。
存储指向主信号的指针的方法将在另一个代码块中声明 - "method of setting the pointer to the main signal"(设置指向主信号的指针的方法)。在这里,我还删除了一些不相关的方法。
public: CMySignalEnvelopes(void); ~CMySignalEnvelopes(void); //--- methods of setting adjustable parameters void PeriodMA(int value) { m_ma_period=value; } void Shift(int value) { m_ma_shift=value; } void Method(ENUM_MA_METHOD value) { m_ma_method=value; } void Applied(ENUM_APPLIED_PRICE value) { m_ma_applied=value; } void Deviation(double value) { m_deviation=value; } //--- methods of adjusting "weights" of market models void Pattern_0(int value) { m_pattern_0=value; } //--- method of verification of settings virtual bool ValidationSettings(void); //--- method of creating the indicator and timeseries virtual bool InitIndicators(CIndicators *indicators); //--- methods of checking if the market models are formed virtual int LongCondition(void); virtual int ShortCondition(void); //--- method of setting the pointer to the main signal virtual bool InitSignal(CExpertSignal *signal=NULL);
现在,让我们在构造函数中指定一些修改过的参数和删除不再需要的变量:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CMySignalEnvelopes::CMySignalEnvelopes(void) : m_ma_period(13), m_ma_shift(0), m_ma_method(MODE_SMA), m_ma_applied(PRICE_CLOSE), m_deviation(1.15), m_pattern_0(50)
至此,我们可以继续根据我们的交易系统修改交易信号生成逻辑。
负责买入信号的代码块:
int CMySignalEnvelopes::LongCondition(void) { int result=0; int idx =StartIndex(); double close=Close(idx); double upper=Upper(idx); double lower=Lower(idx); double width=upper-lower; //--- if the model 0 is used and price is in the rollback zone, then there is a condition for buying if(IS_PATTERN_USAGE(0) && close<lower+m_limit_in*width && close>lower-m_limit_out*width) result=m_pattern_0; //--- if the model 1 is used and price is above the rollback zone, then there is a condition for buying if(IS_PATTERN_USAGE(1) && close>upper+m_limit_out*width) result=m_pattern_1; //--- return the result return(result); }
将如下所示(已进行必要更改):
int CMySignalEnvelopes::LongCondition(void) //---buy
{
int result=0;
int idx =StartIndex();
double open=Open(idx);
double close=Close(idx);
double prlevel;
if(IS_PATTERN_USAGE(0) && close<open)
{
prlevel=GetPriceLevelStopp(open,Open(0));
m_signal.PriceLevel(prlevel);
result=m_pattern_0;
}
//--- return the result
return(result);
}
负责卖出信号的代码块:
int CMySignalEnvelopes::ShortCondition(void) { int result =0; int idx =StartIndex(); double close=Close(idx); double upper=Upper(idx); double lower=Lower(idx); double width=upper-lower; //--- if the model 0 is used and price is in the rollback zone, then there is a condition for selling if(IS_PATTERN_USAGE(0) && close>upper-m_limit_in*width && close<upper+m_limit_out*width) result=m_pattern_0; //--- if the model 1 is used and price is above the rollback zone, then there is a condition for selling if(IS_PATTERN_USAGE(1) && close<lower-m_limit_out*width) result=m_pattern_1; //--- return the result return(result); }
将如下所示(已进行必要更改):
int CMySignalEnvelopes::ShortCondition(void) //---sell
{
int result =0;
int idx =StartIndex();
double open=Open(idx);
double close=Close(idx);
double prlevel;
if(IS_PATTERN_USAGE(0) && close>open)
{
prlevel=GetPriceLevelStopp(Open(0),open);
m_signal.PriceLevel(prlevel);
result=m_pattern_0;
}
//--- return the result
return(result);
}
如果一个特定信号要求的条件得到满足,我们调用 GetPriceLevelStopp 方法,该方法返回类似于 "20" 或 "15" 的数字 - 与当前价格的距离值。
接下来是调用 m_signal 对象的 PriceLevel 方法(设置确定挂单价格水平的距离)。需要提醒读者的是,m_signal 是存储指向主信号的 CExpertSignal 类对象。
GetPriceLevelStopp 方法的代码如下:
double CMySignalEnvelopes::GetPriceLevelStopp(double price_0,double min)
{
double level;
double temp;
temp-=(price_0-min)/PriceLevelUnit();
level=NormalizeDouble(temp,0);
return(level);
}
我们需要在类头文件中声明此方法:
protected: //--- method of initialization of the indicator bool InitMA(CIndicators *indicators); //--- methods of getting data double Upper(int ind) { return(m_env.Upper(ind)); } double Lower(int ind) { return(m_env.Lower(ind)); } double GetPriceLevelStopp(double price,double min); };
我们需要的另一个方法是将指向主信号的指针传递至内部变量的方法:
bool CMySignalEnvelopes::InitSignal(CExpertSignal *signal)
{
m_signal=signal;
return(true);
}
之后,我们应在 MQL5 向导中创建一个“EA 交易”,并将其包含在信号模块 "MySignalEnvelopes" 中。
我们还需要将 InitSignal 方法调用添加至使用 MQL5 向导生成的“EA 交易”的代码中:
//--- Set filter parameters filter0.PeriodMA(Signal_Envelopes_PeriodMA); filter0.Shift(Signal_Envelopes_Shift); filter0.Method(Signal_Envelopes_Method); filter0.Applied(Signal_Envelopes_Applied); filter0.Deviation(Signal_Envelopes_Deviation); filter0.Weight(Signal_Envelopes_Weight); filter0.InitSignal(signal); //...
为更好地可视化“EA 交易”的操作,我提供了一段简短的视频:
使用 MQL5 向导生成的“EA 交易”的代码以及信号模块的代码已附于本文。
下图所示即为“EA 交易”的测试结果。这是货币对为 EURUSD 和 USDJPY 的测试,参数如下:测试周期 2013.01.01 - 2013.09.01,时间表 - D1,止损水平 = 85,获利水平 = 195。
图 13. EURUSD 在 D1 上的测试
图 14. USDJPY 在 D1 上的测试
我们刚刚讨论了如何修改交易信号模块的代码,以实现允许以距当前价格的任意距离设置挂单的功能:它可以是上一个柱的收盘价或开盘价,或者是移动平均线的值。有很多的选择。重要的是,您可以为挂单设置任意的开盘价。
本文讨论了我们可以访问指向主信号的指针以及从而访问 CExpertSignal 类方法的方式。我相信本文对于使用挂单交易的交易人员而言会有所帮助。
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...
移动端课程