赫兹股票量化交易软件:开发一个跨平台网格 EA

网格交易系统
在开始EA开发之前,让赫兹股票量化描述一下网格交易策略的基础知识。
网格是指将多个限价订单置于当前价格之上,同时将相同数量的限价订单置于当前价格之下的 EA 交易。
限价订单是通过一定的步骤而不是单一的价格来设定的。换言之,第一个限价订单被设定在当前价格之上的某个距离,第二个限价设置在第一个限价之上的相同距离处,以此类推。订单数量和应用的步骤各不相同,
一个方向的订单高于当前价格,而另一个方向的订单低于当前价格。应该考虑的是:
在一个趋势中,买入订单应该高于当前价格,而卖出订单应该低于当前价格;
在盘整期间,卖出订单应高于当前价格,而买入订单应低于当前价格。
您可以应用止损水平,也可以不使用它们。
如果你不使用止损和获利,所有未平仓合约,无论是盈利的还是亏损的,都会存在,直到整体利润达到一定水平。之后,所有未结头寸以及不受价格影响的限价订单都将关闭,并设置新的网格。
下面的屏幕截图显示了一个开启的网格:

编辑切换为居中
因此,在理论上,网格交易系统可以让你在任何市场中获利,而不需要等待任何独特的进入点,也不需要使用任何指标。
如果使用止损和获利,那么利润是由于一个头寸的亏损被价格朝一个方向移动时的整体利润所覆盖而获得的。
没有止损水平,利润是由于在正确的方向上打开更多的订单而获得的。即使最初价格触及一个方向的仓位,然后转向,正确方向的新仓位将弥补先前开启的仓位的损失,因为最终会有更多的仓位。
赫兹股票量化的网格 EA 的工作原理
赫兹股票量化已经描述了上面最简单网格的工作原理,您可以为更改打开订单的方向、添加以相同价格打开多个订单的能力、添加指标等网格提供自己的选项。
在本文中,赫兹股票量化将尝试实现最简单的网格化版本,而不会造成停止损失,因为它基于的思想非常诱人。
事实上,认为价格迟早会在向一个方向移动时达到利润,即使最初的开仓方向是错误的,这似乎是合理的。假设在一开始,价格就经历了调整,触发了两个订单,此后,价格开始向相反(主要趋势)的方向移动。在这种情况下,早晚两个以上的订单将朝着正确的方向打开,我赫兹股票量化们的初始损失将在一段时间后变成利润。
似乎交易系统造成损失的唯一情况是,当价格首先触及一个订单,然后返回并触及另一个订单,然后再次改变方向并触及另一个订单,并反复改变其方向,触及越来越远的订单。但这种价格行为在实际情况下是可能的吗?
EA 模板
赫兹股票量化将从模板开始开发EA,这将使我们能够立即看到将涉及哪些标准MQL函数。
#property copyright "Klymenko Roman (needtome@icloud.com)" #property link "https://www.mql5.com/en/users/needtome" #property version "1.00" #property strict //+------------------------------------------------------------------+ //| EA 交易初始化函数 | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| EA 交易去初始化函数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+------------------------------------------------------------------+ //| EA交易分时函数 | //+------------------------------------------------------------------+ void OnTick() { } void OnChartEvent(const int id, // 事件 ID const long& lparam, // 长整型的事件参数 const double& dparam, // 双精度浮点型的事件参数 const string& sparam) // 字符串类型的事件参数 { }
它与使用 MQL5 向导创建 EA 时生成的标准模板的唯一区别是 #property strict 这一行,赫兹股票量化添加它以便 EA 也在 MQL4 中工作。
赫兹股票量化需要 OnChartEvent() 函数来响应单击按钮,接下来,我们将实现Close All(全部关闭)按钮,以便在达到所需利润或只想停止 EA 时手动关闭所有交易品种仓位和订单。
开启仓位函数
可能任何EA最重要的功能都是下订单的能力,第一个问题就在这里等着我们。在 MQL5 和 MQL4 中,订单的设置方式非常不同。为了以某种方式统一这个功能,我们必须开发一个自定义函数来下订单。
每个订单都有自己的类型:买入订单、卖出订单、限价买入或卖出订单。在下订单时设置此类型的变量在MQL5和MQL4中也不同。
在 MQL4 中,订单类型由 int 类型变量指定,而在 MQL5 中,则使用ENUM_ORDER_TYPE枚举,在 MQL4 中没有这样的枚举。因此,为了组合这两种方法,赫兹股票量化应该创建一个自定义枚举来设置订单类型。因此,我们将来要创建的函数将不依赖于MQL版本:
enum TypeOfPos{ MY_BUY, MY_SELL, MY_BUYSTOP, MY_BUYLIMIT, MY_SELLSTOP, MY_SELLLIMIT, };
现在赫兹股票量化可以创建一个自定义函数来下订单了,让我们称它为 pdxSendOrder()。我们将传递下订单所需的所有信息:订单类型、未平仓价格、止损(未设定为0)、获利(未设定为0)、成交量、未平仓订单(如果在MQL5中应修改未平仓)、注释和交易品种(如果您需要为不是当前打开的交易品种开启订单):
// 发送订单的函数 bool pdxSendOrder(TypeOfPos mytype, double price, double sl, double tp, double volume, ulong position=0, string comment="", string sym=""){ // 检查传入的数值 if( !StringLen(sym) ){ sym=_Symbol; } int curDigits=(int) SymbolInfoInteger(sym, SYMBOL_DIGITS); if(sl>0){ sl=NormalizeDouble(sl,curDigits); } if(tp>0){ tp=NormalizeDouble(tp,curDigits); } if(price>0){ price=NormalizeDouble(price,curDigits); } #ifdef __MQL5__ ENUM_TRADE_REQUEST_ACTIONS action=TRADE_ACTION_DEAL; ENUM_ORDER_TYPE type=ORDER_TYPE_BUY; switch(mytype){ case MY_BUY: action=TRADE_ACTION_DEAL; type=ORDER_TYPE_BUY; break; case MY_BUYSTOP: action=TRADE_ACTION_PENDING; type=ORDER_TYPE_BUY_STOP; break; case MY_BUYLIMIT: action=TRADE_ACTION_PENDING; type=ORDER_TYPE_BUY_LIMIT; break; case MY_SELL: action=TRADE_ACTION_DEAL; type=ORDER_TYPE_SELL; break; case MY_SELLSTOP: action=TRADE_ACTION_PENDING; type=ORDER_TYPE_SELL_STOP; break; case MY_SELLLIMIT: action=TRADE_ACTION_PENDING; type=ORDER_TYPE_SELL_LIMIT; break; } MqlTradeRequest mrequest; MqlTradeResult mresult; ZeroMemory(mrequest); mrequest.action = action; mrequest.sl = sl; mrequest.tp = tp; mrequest.symbol = sym; if(position>0){ mrequest.position = position; } if(StringLen(comment)){ mrequest.comment=comment; } if(action!=TRADE_ACTION_SLTP){ if(price>0){ mrequest.price = price; } if(volume>0){ mrequest.volume = volume; } mrequest.type = type; mrequest.magic = EA_Magic; switch(useORDER_FILLING_RETURN){ case FOK: mrequest.type_filling = ORDER_FILLING_FOK; break; case RETURN: mrequest.type_filling = ORDER_FILLING_RETURN; break; case IOC: mrequest.type_filling = ORDER_FILLING_IOC; break; } mrequest.deviation=100; } if(OrderSend(mrequest,mresult)){ if(mresult.retcode==10009 || mresult.retcode==10008){ return true; }else{ msgErr(GetLastError(), mresult.retcode); } } #else int type=OP_BUY; switch(mytype){ case MY_BUY: type=OP_BUY; break; case MY_BUYSTOP: type=OP_BUYSTOP; break; case MY_BUYLIMIT: type=OP_BUYLIMIT; break; case MY_SELL: type=OP_SELL; break; case MY_SELLSTOP: type=OP_SELLSTOP; break; case MY_SELLLIMIT: type=OP_SELLLIMIT; break; } if(OrderSend(sym, type, volume, price, 100, sl, tp, comment, EA_Magic, 0)<0){ msgErr(GetLastError()); }else{ return true; } #endif return false; }
首先,检查传递给函数的数值并规范化价格。
输入参数. 之后,使用条件编译来定义当前的MQL版本,并根据其规则设置顺序。多出的 useORDER_FILLING_RETURN 输入参数是为 MQL5 使用的,在它的帮助下,赫兹股票量化根据代理支持的模式配置订单执行模式。因为 useORDER_FILLING_RETURN 输入参数只对 MQL5 EA 才是必须的,再次使用条件编译来添加它:
#ifdef __MQL5__ enum TypeOfFilling //执行模式 { FOK,//ORDER_FILLING_FOK RETURN,// ORDER_FILLING_RETURN IOC,//ORDER_FILLING_IOC }; input TypeOfFilling useORDER_FILLING_RETURN=FOK; //执行模式 #endif
此外,下订单时,还使用包含EA幻数的EA_Magic输入参数。
如果在EA设置中未设置此参数,则 EA 已启动的交易品种上的任何仓位都被视为属于EA,这样,EA就完全控制了它们。
如果设置了幻数,则EA只考虑在其工作中具有此幻数的仓位。
显示错误. 如果订单成功设置, 就返回 true。否则,会将适当的错误代码传递给 msgErr() 的函数,以便进一步分析并显示可理解的错误消息。这个函数显示包含详细错误描述的本地化消息,在这里提供完整的代码是没有意义的,所以我只展示其中的一部分:
void msgErr(int err, int retcode=0){ string curErr=""; switch(err){ case 1: curErr=langs.err1; break; // case N: // curErr=langs.errN; // break; default: curErr=langs.err0+": "+(string) err; } if(retcode>0){ curErr+=" "; switch(retcode){ case 10004: curErr+=langs.retcode10004; break; // case N: // curErr+=langs.retcodeN; // break; } } Alert(curErr); }
我们将在下一节详细讨论本地化。