亲爱的读者,您好!本文中,我们会试着为您解释并向您呈现可以如何轻松快速地掌握创建“EA 交易”、使用指标等等原则的要领。本文面向初学者,所以不会包含任何难懂或晦涩的示例。因此,对于那些已经清楚如何编写“EA 交易”程序的人来说,本文可能没有那么多的启示感悟,信息量也不是很充分。
“EA 交易”是一种以 MQL 语言缩写、指定交易执行或搁置情况的程序。
基本上,“EA 交易”的结构可以由超大数量的块构成,但为了方便理解,我想展示的是一个利用 MetaEditor 默认生成的非常简单的示例。
整体的“EA 交易”可以从视觉上划分为 4 个部分,每个部分都负责待执行的某特定部分的工作。
图 1. 主要的“EA 交易”块
图 2. MetaEditor 中默认生成的新文档示例
我们利用上例对其进行解释。我们已有一段“空” EA 的代码,一种需要随后填写的“EA 交易”模板。
这里我们可能看到的内容如下:
我前面说过,这种结构可以复杂得多,而且可能由大量的块构成,而不像这个易于掌握的示例这么简单。如果您觉得这个已经满足不了您的需求,则可以添加自己的块。
指标是在 MQL 中缩写的小型程序,会在价格图表或其下方的一个独立窗口中显示,允许我们对市场执行技术分析。
所有的指标都可以划分为两种类型:顺势指标与摆荡指标。
顺势指标一般均于价格图表中绘制,用于识别趋势方向;而摆荡指标则通常见于价格图表下方,用于识别进场点。
大多数指标都至少有一个缓冲区(指标缓冲区),其中包含某给定时间点其读数数据。与“EA 交易”相似,指标拥有其借以计算的交易品种和时间表。
可将指标缓冲区视为一个队列,该队列的最后一个元素是一个运行值。
图 3. 移动平均线指标示例
指标缓冲区是一种数组,其中的第一个元素(索引为 0)携带最右侧烛形的相关数据,接下来的元素(索引为 1)则携带右数第二个烛形的相关数据,如此等等。而这种元素布置则被称为时间序列。
看一看下方示例:
假设我们的货币对为欧元兑美元,时间表为 1 小时。
首先,我们需要向“EA 交易”添加指标并获取其句柄。
句柄是一种指向指标的独特指针,可以让我们处理程序中任何地方的指标。
int iMA_handle; iMA_handle=iMA("EURUSD",PERIOD_H1,10,0,MODE_SMA,PRICE_CLOSE);我们仔细观察一下。
图 4. “移动平均线”指标参数的工具提示示例
下方我们可以看到从左到右排列的各个参数:
有一系列独特的变量以及其针对每一种指标的类型。如果您碰到一个未知指标,其相关信息通常都可以在内置的上下文“帮助”中找到。比如说,一旦您键入 iMA 并按下 F1之后,就会打开一个“帮助”窗口,提供该特定指标的相关信息,以及其所有属性的详尽描述。
图 5. 通过按下 F1 调用该指标描述“帮助”窗口示例
代码编写完成并于终端中启动“EA 交易”后,我们就会发现(一旦价格图表的右上角出现“EA 交易”)图表中的指标不见了。这并非错误 - 完全在意料当中。想令其出现,我们需要再添加一行:
ChartIndicatorAdd(ChartID(),0,iMA_handle);
现在,我们看看结果如何。鼠标指针悬停于 ChartIndicatorAdd 命令上方,按 F1 阅读该命令用途相关的“帮助”信息。它表示此命令:
将一个带有指定句柄的指标添加到某个指定的图表窗口中。
等于零的第二个参数为子窗口编号。子窗口通常包含摆荡指标,位于价格图表下方。记住没有?这种东西可能会有很多。欲于子窗口中显示指数,您只需要按照比现有编号大 1 (即现有最后一个编号的下一个数字)的标准指定子窗口编号。
代码行已如下变更:
ChartIndicatorAdd(ChartID(),1,iMA_handle);
我们的指标会于价格图表下方的子窗口中显示。
现在,到了尝试从指标获取一些数据的时候了。为此目的,我们声明一个动态数组,为方便起见按时间序列排列数组索引,并将指标值复制到该数组当中。
double iMA_buf[]; ArraySetAsSeries(iMA_buf,true); CopyBuffer(iMA_handle,0,0,3,iMA_buf);
上述示例表明,我们已经将双型动态数组 iMA_buf[] 声明为“移动平均线”指标(基于价格及价格片段)。
下一行则按照较小索引号元素存储较旧值、而较大索引号元素则存储较新值的原则设置数组的索引。如此使用是为了方便以避免混淆,因为所有指标中的指标缓冲区都按时间序列索引。
最后一行用于将指标值复制到 iMA_buf[] 数组中。这些数据已经可以使用了。
我们从订单开始。
市价单是指按当前市价卖出或买入特定数量指定金融工具的指令。
挂单则指根据特定情况执行交易的指令。挂单有一个特定的到期时间,届时即被删除。
为了更清楚地表达,我们来看一个例子:我们建一个 1 手的长仓,也就是说,我们(例如)以当前市价下一个手数为 1 的订单。如请求有效,则会被发往服务器进行处理。只要处理完成,此终端的 "Trade" (交易)选项卡中就会出现带订单参数的持仓。假设我们之后决定建另一个同为 1 手的长仓。待此订单处理完成后,我们并不会在 "Trade" 选项卡中看到两个订单,而是一个 2 手的持仓。即,持仓是一系列订单的执行成果。
现在我们继续练习。如欲制作一个请求,需要填充下述结构字段:
struct MqlTradeRequest { ENUM_TRADE_REQUEST_ACTIONS action; // 操作类型 ulong magic; // EA交易的ID(幻数) ulong order; // 订单号 string symbol; // 交易工具 double volume; // 请求的交易手数 double price; // 价格 double stoplimit; // 订单的StopLimit水平 double sl; // 订单的止损水平 double tp; // 订单的获利水平 ulong deviation; // 请求价格的最大允许点差 ENUM_ORDER_TYPE type; // 订单类型 ENUM_ORDER_TYPE_FILLING type_filling; // 订单的执行类型 ENUM_ORDER_TYPE_TIME type_time; // 订单的持续类型 datetime expiration; // 订单过期时间(订单的ORDER_TIME_SPECIFIED类型) string comment; // 订单的备注 };
因为订单多种多样,每种订单类型又都有其各自的强制参数集。我可不会长篇大论地讲解这些字段。网站上已就此提供了大量的相关信息。如果某特定订单类型的强制参数连一个都未指定(或未正确指定),则请求会失败。
上述结构在此处的布局,只是为了方便论证填写时产生的困难。
“止损”与“获利”都是作为“后备措施”下达的特殊订单。即,如有错误或是“EA 交易”建了一个呈现损失的仓位,则“止损”订单可将损失限定于某个特定的预定义价位处。
“获利”与其类似,只是这次是限制利润。不再为平仓担心可能会变得很有必要。一旦达到某特定的价位,它将会平仓。也就是说,如果市场对我们不利或是我们想要获利,这些订单就是我们的“保险方案”。
这种类型的订单不能自行独立下达 - 只能修改现有的持仓。
好,我们终于走到了标准库。此库随本终端一同提供,并由此得名 - 标准库。它由方便“EA 交易”编程及部分承担复杂过程(比如交易请求生成)的各种函数构成。
交易库(亦见交易类)位于下述路径:Include\Trade\ 且可利用 #include 指令添加。
例如:
#include <Trade\Trade.mqh> #include <Trade\PositionInfo.mqh>
上述类会被视作基础类,因为只利用这两个类(库),即可实现大多数“EA 交易”的编程。我称其为库:
#include <Trade\OrderInfo.mqh>
它包含操作订单的相关函数,比如说,我们的策略要求采用挂单。CTrade m_Trade; m_Trade.Sell(lot,symbol_name,price,sl,tp,comment);
这里共有 6 个参数,其中只有一个为必填(订单数量 - 此为第一参数)。
现在我就分别指定:
平仓的方法有许多种:
CPositionInfo m_Position; m_Position.Select(symbol_name); m_Trade.PositionClose(symbol_name);
CTrade m_Trade; m_Trade.Buy(lot,symbol_name,price,sl,tp,comment);
现在,到了把新习得的知识串成一个“EA 交易”的时候了。
//+------------------------------------------------------------------+ //| fast-start-example.mq5 | //| Copyright 2012, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2012, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| EA初始化函数 | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> //包含执行交易的库 #include <Trade\PositionInfo.mqh> //包含获取持仓信息的库 int iMA_handle; //存储指标句柄的变量 double iMA_buf[]; //存储指标值的动态数组 double Close_buf[]; //存储每个柱形收盘价格的动态数组 string my_symbol; //存储交易品种的变量 ENUM_TIMEFRAMES my_timeframe; //存储时间框架的变量 CTrade m_Trade; //执行交易的结构体 CPositionInfo m_Position; //获取持仓信息的结构体 //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int OnInit() { my_symbol=Symbol(); //保存当前图表的交易品种,用于此EA对其进一步的操作。 my_timeframe=PERIOD_CURRENT; //保存图表的当前时间框架,用于此EA对其的进一步操作。 iMA_handle=iMA(my_symbol,my_timeframe,40,0,MODE_SMA,PRICE_CLOSE); //应用此指标并获取指标句柄 if(iMA_handle==INVALID_HANDLE) //检查指标句柄是否可用 { Print("Failed to get the indicator handle"); //如果句柄没有获取到,打印相关报错信息到日志文件中 return(-1); //完成报错处理 } ChartIndicatorAdd(ChartID(),0,iMA_handle); //将指标添加到价格图表中 ArraySetAsSeries(iMA_buf,true); //将iMA_buf数组的索引设置为时间序列 ArraySetAsSeries(Close_buf,true); //将Close_buf数组的索引设置为时间序列 return(0); //返回0,初始化结束 } //+------------------------------------------------------------------+ //| EA去初始化函数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { IndicatorRelease(iMA_handle); //删除指标句柄并释放分配给它的存储空间 ArrayFree(iMA_buf); //释放动态数组iMA_buf的数据 ArrayFree(Close_buf); //释放动态数组Close_buf的数据 } //+------------------------------------------------------------------+ //| EA的tick函数 | //+------------------------------------------------------------------+ void OnTick() { int err1=0; //用于存储指标缓存处理结果的变量 int err2=0; //用于存储价格图表处理结果的变量 err1=CopyBuffer(iMA_handle,0,1,2,iMA_buf); //将数据从指标数组中拷贝到动态数组iMA_buf中,用于进一步处理 err2=CopyClose(my_symbol,my_timeframe,1,2,Close_buf); //将价格图表数据拷贝到动态数Close_buf中,用于进一步处理 if(err1<0 || err2<0) //如果出错 { Print("Failed to copy data from the indicator buffer or price chart buffer"); //打印相关错误信息到日志文件 return; //并退出函数 } if(iMA_buf[1]>Close_buf[1] && iMA_buf[0]<Close_buf[0]) //如果指标值大于收盘价并且开始变小 { if(m_Position.Select(my_symbol)) //如果该交易品种的持仓已经存在 { if(m_Position.PositionType()==POSITION_TYPE_SELL) m_Trade.PositionClose(my_symbol); //并且这是一个卖出持仓,那么平仓 if(m_Position.PositionType()==POSITION_TYPE_BUY) return; //或者,如果是一个买入持仓,那么退出 } m_Trade.Buy(0.1,my_symbol); //如果到这里,说明没有持仓;那么我们开仓 } if(iMA_buf[1]<Close_buf[1] && iMA_buf[0]>Close_buf[0]) //如果指标值小于收盘价并且在变大 { if(m_Position.Select(my_symbol)) //如果该交易品种的持仓已经存在 { if(m_Position.PositionType()==POSITION_TYPE_BUY) m_Trade.PositionClose(my_symbol); //并且这是一个买入持仓,那么平仓 if(m_Position.PositionType()==POSITION_TYPE_SELL) return; //或者,如果这是一个卖出持仓,那么退出 } m_Trade.Sell(0.1,my_symbol); //如果我们到这里,说明没有持仓;那么我们开仓 } } //+------------------------------------------------------------------+
我们如下利用相关参数对“EA 交易”进行测试:
因为我们从第一柱(零柱为当前、活动柱)开始使用指标值与收盘价,所以此图表不会重绘。也就是说,我们可以使用“仅开盘价”交易模式。它不会影响到测试质量,却会令运行加快。
而这里则是采用历史数据的快速测试结果。
图 6. 我们的“EA 交易”测试结果
赔损当然不能被忽略。但是,本文主旨并不是要编写一个拥有超大利润潜力和极低赔损的“超级‘EA 交易’”,而是要说明一个掌握基础知识的人可以如何轻松地制作一个“EA 交易”。
我们已经完成了由不到一百行代码所构成的“EA 交易”。
本文讲到了编制“EA 交易”程序时要考虑到的主要原则。通过学习,我们已经知道了如何使用 MetaEditor 5 中的内置上下文“帮助”来获取各个函数的相关信息,了解了订单和持仓的总体概念,而且掌握了标准库的使用。
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...