简介
亲爱的读者,您好!本文中,我们会试着为您解释并向您呈现可以如何轻松快速地掌握创建“EA 交易”、使用指标等等原则的要领。本文面向初学者,所以不会包含任何难懂或晦涩的示例。因此,对于那些已经清楚如何编写“EA 交易”程序的人来说,本文可能没有那么多的启示感悟,信息量也不是很充分。
“EA 交易”及其结构
“EA 交易”是一种以 MQL 语言缩写、指定交易执行或搁置情况的程序。
基本上,“EA 交易”的结构可以由超大数量的块构成,但为了方便理解,我想展示的是一个利用 MetaEditor 默认生成的非常简单的示例。
整体的“EA 交易”可以从视觉上划分为 4 个部分,每个部分都负责待执行的某特定部分的工作。
图 1. 主要的“EA 交易”块
- 参数块中包含允许终端以恰当方式处理“EA 交易”的信息。最常见的参数为“EA 交易”版本、生产厂家名称以及一个简要描述。
- OnInit() 块会在“EA 交易”被载入终端时获得控制权。其中可能包含与此“EA 交易”初始化相关的各种数据 – 声明变量与数组、获取指标句柄等。也就是说,此块并不包含任何与交易直接关联的函数。
- OnDeinit() 块充当的是 OnInit() 块的反方向。会在“EA 交易”完成其操作(“EA 交易”/终端关闭或某“EA 交易”未能成功初始化)时被调用。此块的主要功能之一,即是重新分配“EA 交易”占用且不再需要的内存空间。换而言之,它描述出了删除变量、数组和指标句柄等等的过程。
- OnTick() 块每当由服务器接收与交易品种(货币对)相关的新信息时,就会被调用。它会指明交易执行的情况以及交易本身的函数。
图 2. MetaEditor 中默认生成的新文档示例
我们利用上例对其进行解释。我们已有一段“空” EA 的代码,一种需要随后填写的“EA 交易”模板。
这里我们可能看到的内容如下:
- 前五行(第 1 行到第 5 行)为注释,其中包含“EA 交易”的名称(文件名)、生产厂家的名称及其网站。您可以于此写下您想要的任何内容。此文本不会于任何地方显示,甚至可以跳过。其所包含的信息只针对开发人员;
- 接下来的三行(第 6 至 8 行)为参数块。启动终端中的“EA 交易”时,就会看到此信息;
- 然后是 OnInit() 函数(第 12 至 19 行)。此为 OnInit() 块。此函数不会获取任何参数,但会返回(也可能不返回)初始化代码;
- 接下来是 OnDeinit(const int reason) 函数(第 22 至 26 行)。此为 OnDeinit() 块。它拥有一个指定“EA 交易”关闭原因的参数。
如果“EA 交易”未能成功初始化,则此函数会接收一个相关代码作为参数; - 最后一个函数为 OnTick() (第 30 至 34 行)。此为之前谈到过的 OnTick() 块。此块可以说是“EA 交易”的“大脑”,因为其中包含了负责交易的所有函数。
我前面说过,这种结构可以复杂得多,而且可能由大量的块构成,而不像这个易于掌握的示例这么简单。如果您觉得这个已经满足不了您的需求,则可以添加自己的块。
指标及其处理方式
指标是在 MQL 中缩写的小型程序,会在价格图表或其下方的一个独立窗口中显示,允许我们对市场执行技术分析。
所有的指标都可以划分为两种类型:顺势指标与摆荡指标。
顺势指标一般均于价格图表中绘制,用于识别趋势方向;而摆荡指标则通常见于价格图表下方,用于识别进场点。
大多数指标都至少有一个缓冲区(指标缓冲区),其中包含某给定时间点其读数数据。与“EA 交易”相似,指标拥有其借以计算的交易品种和时间表。
可将指标缓冲区视为一个队列,该队列的最后一个元素是一个运行值。
图 3. 移动平均线指标示例
指标缓冲区是一种数组,其中的第一个元素(索引为 0)携带最右侧烛形的相关数据,接下来的元素(索引为 1)则携带右数第二个烛形的相关数据,如此等等。而这种元素布置则被称为时间序列。
看一看下方示例:
假设我们的货币对为欧元兑美元,时间表为 1 小时。
首先,我们需要向“EA 交易”添加指标并获取其句柄。
句柄是一种指向指标的独特指针,可以让我们处理程序中任何地方的指标。
int iMA_handle; iMA_handle=iMA("EURUSD",PERIOD_H1,10,0,MODE_SMA,PRICE_CLOSE);
我们仔细观察一下。
第一行描述的是一个将用于存储指标句柄的变量。第二行会调用指标(此处为移动平均线指标),指定其参数,并将句柄保存到变量中以备将来使用。
图 4. “移动平均线”指标参数的工具提示示例
下方我们可以看到从左到右排列的各个参数:
- 交易品种名称(工具提示中以粗体字符显示)是一种文本参数,货币对(交易品种);
- 时间表;
- 指标时期(此处为平均时期);
- 图表平移向前/向后 N 个柱。正数表示图表向前平移 N 个柱,而负数则表示图表向后平移 N 个柱;
- 平均方法;
- 适用价格或某不同指标的句柄。
有一系列独特的变量以及其针对每一种指标的类型。如果您碰到一个未知指标,其相关信息通常都可以在内置的上下文“帮助”中找到。比如说,一旦您键入 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[] 数组中。这些数据已经可以使用了。
订单、交易与持仓
我们从订单开始。
- 订单是被交易服务器接受的交易请求。如果请求无效,则会被拒绝。
为避免填写交易请求中遇到的困难,稍后我会向您展示如何利用标准库让这一切更简单地实现。
订单分两种类型:市价单(供即时执行)与挂单。
市价单是指按当前市价卖出或买入特定数量指定金融工具的指令。
挂单则指根据特定情况执行交易的指令。挂单有一个特定的到期时间,届时即被删除。
- 交易是指订单(执行某交易的指令)执行的结果。每次交易均基于一个特定的单一订单,而单一订单则可能导致多次交易。比如说,一个欲买入 10 手的订单,可通过一系列连续性交易的部分执行而实现。交易始终保存于交易历史中且不能修改。此终端会于 “History” (历史)选项卡中显示交易。
- 持仓是指活动订单的成果。每个交易品种只能建一个长仓或短仓。
为了更清楚地表达,我们来看一个例子:我们建一个 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 个参数,其中只有一个为必填(订单数量 – 此为第一参数)。
现在我就分别指定:
- lot 是待下订单的数量;
- symbol_name 是该订单所应用的交易品种(货币对)(如未指定,则采用“EA 交易”的当前交易品种);
- price 为开盘价(因为此函数用于打开某活动的订单,所以其价格可能未被指定,这种情况下,则会自动从价格图表直接获取);
- sl 是此订单的平仓价格 – 假如价格对我们不利(如果策略并没有使用止损的意思,则可忽略);
- tp 是此订单的平仓价格 – 假如价格向我们期望的方向发展,即,获利(如果策略并没有使用获利的意思,则可忽略);
- comment 即订单注释,比如说,指明下此订单的原因。
平仓的方法有许多种:
- 完全平仓
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 交易”进行测试:
- 交易品种 – 欧元兑美元;
- 时间表 – H1;
- 交易模式“仅开盘价”。
因为我们从第一柱(零柱为当前、活动柱)开始使用指标值与收盘价,所以此图表不会重绘。也就是说,我们可以使用“仅开盘价”交易模式。它不会影响到测试质量,却会令运行加快。
而这里则是采用历史数据的快速测试结果。
图 6. 我们的“EA 交易”测试结果
赔损当然不能被忽略。但是,本文主旨并不是要编写一个拥有超大利润潜力和极低赔损的“超级‘EA 交易’”,而是要说明一个掌握基础知识的人可以如何轻松地制作一个“EA 交易”。
我们已经完成了由不到一百行代码所构成的“EA 交易”。
总结
本文讲到了编制“EA 交易”程序时要考虑到的主要原则。通过学习,我们已经知道了如何使用 MetaEditor 5 中的内置上下文“帮助”来获取各个函数的相关信息,了解了订单和持仓的总体概念,而且掌握了标准库的使用。
本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/496
MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!
免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。