本文讨论了多种方式来增加交易机器人的操作稳定性, 譬如消减可能的重复触发 (抖动): 既可以分别使用入场和离场算法,也可将它们连结。
若是当前蜡烛条幅度过高, 并且在交易机器人里没有提供预防抖动措施, 则虚假触发问题在暴跌暴涨行情中尤为突出。它会导致在当前蜡烛条上连续重复开单、平仓。
依据行情的特殊算法以及交易机器人的开发者所设置的参数, 其结算后果是变化的。在所有情况下, 交易者的点差开销会随着抖动期间触发数量而成比例增加。
在本文中, 我不会涉及金融工具 (技术和基本面特征) 分析的话题, 这能够影响智能交易程序操作的稳定性, 并有助于避免散射 (这是一个单独的话题 — 我是脉冲均衡理论及其应用系统的作者)。在此, 我们重点关注那些软件手段, 而非直接依赖金融市场分析的方法。
所以, 让我们来着手解决问题。作为一个示例, 我将使用来自 МetaТrader 4 客户端标准集里提供的 "MACD 样本" EA。
如图例所示 EURUSD 价格在当年的十月份第二天飙升 (М15 时间帧, "MACD 样本" EA 省缺设置), 它可直观解释散射问题:
屏幕截图清楚显示在单根蜡烛条里有 8 个连串的触发 (买进入场)。它们之中只有 1 个是正确的 (按照正常的行情逻辑条件), 其余 7 个是散射。
在这种特殊情况下虚假触发背后的原因是:
我们已经同意, 过滤行情波动的事项不是考虑的目地 (因为每位交易者有自己的入场和离场算法), 所以为了解决问题, 我们考虑以下更普遍的因素:
最简单同时也是最可靠地固定入场点的方法是通过时间因素, 其原因有以下几点:
这种方式, 主要因素是入场算法的触发时刻, 更加具体的是, 开仓所需的订单触发时刻 (OrderSend), 因为这两个时刻也许不相符, 如果在算法里有一些特别的开单延迟。
因此, 我们要记住开仓的时刻 (当前时间)。但如何在入场算法里使用这个参数, 以便在指定的蜡烛条上禁止随后的重复入场?我们无法预先知道这一时刻 (其绝对值), 所以我们不能在入场算法里预先输入它。算法应考虑 (包括) 一些通常的条件来解决在蜡烛条上的首次入场, 且无需计算触发即可禁止在蜡烛条上的后续入场 (我们之前拒绝的带计数器的选项)。
此解决方案是相当简单的。首先, 我将会编写一些带注释的代码, 然后将会澄清更多细节。这是一段辅助代码 (以黄色加亮), 需要放置于交易 EA 的算法里 (参看 MACD_Sample_plus1.mq4):
//+------------------------------------------------------------------+ //| MACD Sample.mq4 | //| 版权所有 2005-2014, MetaQuotes 软件公司| //| https://www.mql4.com | //+------------------------------------------------------------------+ #property copyright "2005-2014, MetaQuotes 软件公司" #property link "https://www.mql4.com" input double TakeProfit =50; input double Lots =0.1; input double TrailingStop =30; input double MACDOpenLevel =3; input double MACDCloseLevel=2; input int MATrendPeriod =26; //--- 输入新变量 (此时间帧内一根柱线的秒数, 对于 М15 等于 60 с х 15 = 900 с) datetime Time_open=900; //--- 输入新变量 (柱线开盘时间, 首次入场) datetime Time_bar = 0; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnTick(void) { double MacdCurrent,MacdPrevious; double SignalCurrent,SignalPrevious; double MaCurrent,MaPrevious; int cnt,ticket,total; //--- // 初始数据检查 // 它对于确保程序能在正常图表上工作十分重要 // 而且用户在设置外部变量时不可出错 // (Lots, StopLoss, TakeProfit, // TrailingStop), 在我们的例子中, 我们检查止盈 // 在图表上是否小于 100 根柱线 //--- if(Bars<100) { Print("柱线数小于 100"); return; } if(TakeProfit<10) { Print("TakeProfit 小于 10"); return; } //--- 为了简化代码, 并加速存取, 数据被放到内部变量 MacdCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,0); MacdPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1); SignalCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,0); SignalPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,1); MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0); MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1); total=OrdersTotal(); if(total<1) { //--- 未识别出已开订单 if(AccountFreeMargin()<(1000*Lots)) { Print("我们没有资金。Free Margin = ",AccountFreeMargin()); return; } //--- 检查多头仓位 (买入) 的可行性 //--- 输入新字符串 (若新柱线开盘, 删除重复入场禁止标志) if( (TimeCurrent() - Time_bar) > 900 ) Time_open = 900; if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && MaCurrent>MaPrevious && (TimeCurrent()-Time[0])<Time_open) //输入新字符串至入场算法 (仅执行一次, 此蜡烛条上的条件以后不能完成) { ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0,Ask+TakeProfit*Point,"macd sample",16384,0,Green); if(ticket>0) { if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) { Print("买入订单已开 : ",OrderOpenPrice()); Time_open = TimeCurrent()-Time[0]; //输入新字符串 (保存入场时的柱线开盘时间到离场时刻的间隔) Time_bar = Time[0]; //输入新字符串 (记住柱线开盘时间已有首次入场) } } else Print("买入开单出错 : ",GetLastError()); return; }
阅读更多:
替代绝对时间 (入场时可), 我们使用相对时间 —自当前蜡烛条开盘时刻至入场时刻的时间缺口。此数值与预先设定的进行比较, 较大时间值 (整根蜡烛条的长度), 允许触发首次入场。在开仓时刻, 我们修改 (降低) Time_open 变量的数值, 写入自蜡烛条开盘到实际收盘时刻之间的缺口值。并且由于在随后的任意时刻, 数值 (TimeCurrent() - Time[0]) 将会超出我们写入的入场点数值, 则 (TimeCurrent() - Time[0]) < Time_open 条件仍将是不可能的, 即通过阻塞此蜡烛条上随后的入场来达成。
这样, 无需任何入场数量计数器, 以及分析价格变动的幅度, 我们就解决了虚假触发的问题。
以下是 EA 的初始入场算法经过简单改进后的结果 ("MACD Sample_plus1"):
我们看到, 在一根蜡烛条上只有一次入场, 不存在任何虚假触发, 且散射完全消除。省缺设置全部保存, 所以很显然, 这个问题在不改变 EA 设置的协助下得以解决。
现在入场的散射问题得以解决, 我们将改进入场算法以便排除快速平仓时可能的散射, 在这种特殊情况下增加盈利 (脉冲很不错, 快速离场, 早发)。
由于最初的问题涉及如何消除交易机器人的散射可能性, 而非增加盈利, 那么在此话题里我将不会考虑分析动态金融工具的相关问题, 并通过固定选择的参数限制我自己, 这种动态不予考虑。
之前, 我们已经使用了一个安全性参数和时间因素, 我们将用它再次严格规范依照时间平仓的时刻, 具体而言, 紧随蜡烛条开盘的关键点 (入场之后)。在离场算法中的这一时刻, 我们将显示为:
if(!OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES)) continue; if(OrderType()<=OP_SELL && // 检查已开仓位 OrderSymbol()==Symbol()) // 检查品种 { //--- 已开多头仓位 if(OrderType()==OP_BUY) { //--- 应平仓否? if(/* MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && // 删除 MACD 离场触发代码, 不去干扰平仓的新条件 (看之后) MacdCurrent>(MACDCloseLevel*Point) && */ Bid > OrderOpenPrice() && // enter new string - optional (price in a positive area in regards to the entry level) TimeCurrent() == Time[0] ) // 输入新字符串 (立场算法的简单实现: 离场限制在当前蜡烛条的开盘时刻) { //--- 平仓退出 if(!OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet)) Print("平仓错误 ",GetLastError()); return; }
如此小的修改可令入场算法起作用 (已开仓位, 无条件平仓), 持仓直到 TimeCurrent() == Time[0] 时刻并在新蜡烛条开始的脉冲到达时并行平仓。最终, 不仅散射得到保护, 我们还赢得了良好的收益 (参看图片 "MACD Sample_plus2"):
为此目的, 我们不得不从离场算法里去除由 MACD 触发, 否则离场的必要条件将不能发生。
因此, 看来该散射问题可以分别在入场和离场的算法里解决。现在, 我们来讨论如何通过连接这些开仓和平仓算法来解决问题。
连接意味着整个过程的初步建模: 开仓 - 管理 - 平仓。这也反映在入场和离场算法里如何选择指标和函数。
例如, 如果您在离场算法里使用 TimeCurrent() = Time[0] 条件, 且离场点的设置限制在当前蜡烛条的开始点, 则入场算法应在之前的完整柱线上测试, 所以离场条件可满足。因此, 为了在 TimeCurrent() = Time[0] 条件且无其它附加条件下平仓, 完整的比较算法 (离场) 有必要在之前 (完成) 的柱线上执行。在指标的设置里应有一个抵消等于 1, 参与数值比较。在这种情况下, 数值比较将会是正确的, 并且当前蜡烛条的开始将是离场算法逻辑的终结。
这样, 离场与入场的连接算法也与时间因素链接。
通过在入场算法里使用时间因素, 智能交易程序的虚假触发问题得以有效解决。此外, 通过固定离场点 (例如, 依照时间), EA 操作的稳定性也得以达成, 并通过触发和抵消的主要逻辑的初步建模连接入场和离场算法 (指标的一根柱线或是函数将被计算)。
以下是 EA 代码: 初始那个 (MACD_Sample.mq4), 含有入场改进 (MACD_Sample_plus1.mq4), 含有离场改进 (MACD_Sample_plus2.mq4)。只有买入通道有所提高, 而卖出通道依旧没有改变, 这是为了刻意比较初始和改进算法。
而且, 当然, 所有介绍的 EA 均用于演示目的, 而非针对金融市场的实盘交易。
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...
移动端课程