
如何利用 MQL5 创建简单的多币种智能交易系统(第 5 部分):凯尔特纳(Keltner)通道上的布林带 — 指标信号
概述
本文中多币种 EA 的定义是智能交易系统或交易机器人,它可以从一个品种图表中交易(开单、平单、和管理订单,例如:尾随止损和止盈)超过 1 个以上的品种(对),在本文中,EA 将交易 30 个对。
在本文中,我们将用到来自两个指标的信号,在本例中为凯尔特纳通道上的布林带®。
在老平台(MetaTrader 4)上,如这般使用信号已知是调用 iBandsOnArray 函数。
在 MQL4 参考中对 iBandsOnArray 函数的解释指出: 注意:与 iBands(...) 不同,iBandsOnArray() 函数获取数据时不按品种名称、时间帧、应用价格。价格数据必须预先准备好......
在 MQL5 论坛的讨论中,我曾读到甚至有交易者或用户说 iBandOnArray() 在 MQL5 中不存在。
确实,iBandOnArray() 不在 MQL5 函数列表中,但通过使用 iBands() 指标句柄,我们实际上可以在 MQL5 中轻松创建 iBandOnArray()。事实上,我的观点是,在 MQL5 中使用指标句柄比在 MetaTrader 4 中使用 iBandsOnArray() 函数更容易、更方便。
正如之前的文章所证明的,我们都知道,在交易终端和策略测试器上,多币种交易都可由 MQL5 提供的强大能力和设施来支持。
因此,我们的目标是满足交易者寻找高效且有效交易机器人的基本需求,故依靠高度可靠的 MQL5 提供的优势、能力和设施,我们可以创建一个简单的多币种智能交易系统,在本文中,它用到 2 个指标信号来开单:凯尔特纳通道上的布林带®, 其中布林带指标取用的价格数据来自凯尔特纳通道指标。同时,对于尾随停止,我们仍将采用抛物线 SAR(iSAR)指标。
计划和功能
1. 交易货币对。
此多币种智能交易系统旨在按如下方式交易交易品种或货币对:
EURUSD,GBPUSD,AUDUSD,NZDUSD,USDCAD,USDCHF,USDJPY,EURGBP,EURAUD, EURNZD, EURCAD, EURCHF, EURJPY, GBPAUD, GBPNZD, GBPCAD,GBPCHF,GBPJPY,AUDNZD,AUDCAD,AUDCHF,AUDJPY,NZDCAD,NZDCHF,NZDJPY, CADCHF, CADJPY, CHFJPY = 28 对
加上 2 种贵金属对:XAUUSD(黄金)和 XAGUSD(白银)
总共是 30 对。
如前文,在本文中,我们致力于简化它,即在输入属性里为品种(对)名称加上特殊的前缀和后缀。然后,由一个简单的函数,我们将处理与 30 个已注册品种名称结合的前缀和/或后缀,如此这般在 MetaTrader5 上 EA 应对经纪商提供的含有此类特殊品种名称,一切也都会顺利运行。
检测含有前缀和后缀的品种名称的函数的弱点在于,该函数仅适用于 MT5 中的外汇和贵金属品种名称,但并不适用于特殊品种和指数。除此之外,这种方法的另一个弱点是,如果交易者在货币对的前缀和/或后缀的名称中有拼写错误(输入必须区分大小写)。因此,对于那些使用本文中 EA 的交易者,我们希望在输入货币对名称前缀和/或后缀时,确认其精确性和准确性。
如前文,在该 EA 中,我们还为此时将要交易的货币对添加了 10 个选项。将要交易的 10 个选项对之一是“Trader's Desired Pairs(交易者的期望对)”,其中要交易的对必须由交易者在智能系统输入属性中手工输入。但您必须始终记住,您输入的货币对的名称必须已经在 30 对列表之中。
在该版本的 EA 中,我们还添加了交易时段(时区)选项,如此即可把要交易的货币与交易时段的时间相对应。
2. 信号指标。
在该版本的 EA 中,我们将用到两个指标的信号,在本例中为凯尔特纳通道上的布林带®。作为布林带®指标的价格数据,我们将从凯尔特纳通道指标获取。
2.1. 凯尔特纳(Keltner)通道指标。
凯尔特纳通道由切斯特·凯尔特纳(Chester Keltner)于 1960 年代首次引入。原始公式使用简单移动平均线(SMA),和最高/最低价格范围来计算波段。在 1980 年代,引入了一个使用平均真实范围(ATR)的新公式。ATR 方法目前最常用的。
本文中 EA 采用凯尔特纳通道指标,我特意采用当今流行的方法创建,即周期 20 的指数移动平均线(EMA),其边带上限和下限则采用周期 20 的 ATR 指标。
凯尔特纳通道指标输入属性如下:
2.2. 布林带®指标。
布林带®由约翰·布林格(John Bollinger)在 1980 年代创造,并迅速成为技术分析领域使用最广泛的指标之一。布林带®由三条波带组成 — 上边带、中轨和下边带 — 这些是为了突出市场的短期价格极端。上边带是超买条件的标志,而下边是超卖条件的标志。大多数金融分析师会用到布林带®,并将它与其它指标相结合,从而更好地分析市场状态全貌。
在本文的 EA 中,我们将采用周期为 38 的布林带®指标,该指标所用的价格数据取自凯尔特纳通道指标。
布林带®指标输入属性如下:
图例 1 和图例 2 中可见凯尔特纳通道指标作为布林带®指标的价格数据源,示意买入或卖出信号。
图例 1. 买入信号
图例 2. 卖出信号
在上面的示意图中,只有当凯尔特纳通道的中线高于布林带®上线、或低于布林带®下线时,才会发出信号。但对于本文中的 EA,指标信号实际上是凯尔特纳通道指标的中线和布林带®指标的上、中、下线之间的交叉。
- 对于买入信号:
- 第一个信号:当凯尔特纳通道指标的中线向上穿过布林带®指标的下线时;或
- 第二个信号:当凯尔特纳通道指标的中线向上穿过布林带®指标的中线时;或
- 第三个信号:当凯尔特纳通道指标的中线向上穿过布林带®指标的上线时。
- 对于卖出信号:
- 第一个信号:当凯尔特纳通道指标的中线向下穿过布林带®指标的上线时;或
- 第二个信号:当凯尔特纳通道指标的中线向下穿过布林带®指标的中线时;或
- 第三个信号:当凯尔特纳通道指标的中线向下穿过布林带®指标的下线时。
3. 交易和订单管理
该多币种 EA 为您提供了多种管理交易的选项:
3.1. 止损单
选项:使用订单止损(是)或(否)
- 如果选中“使用订单止损(否)”选项,则所有订单在开单时都不带止损。
- 如果选项“使用订单止损(是)”:再次给出选项:使用自动计算止损(是)或(否)
- 如果选项“自动计算止损(是)”,则将由智能系统自动执行止损计算。
- 如果选项“自动计算止损(否)”,则交易者必须以点数为单位输入止损值。
- 如果选项“使用订单止损(否)”:则智能系统将检查每笔开立的订单,信号条件是否仍然良好,订单是否可维持盈利;或信号疲软,需要平单以保障盈利;或信号条件已逆转方向,订单必须已亏损条件了结。
注意:特别是对于因信号疲软而了结交易并保障盈利,会给出一个选项,是否激活它。
如果它没有被激活(否),即使信号已经疲软,订单仍将持有、或不会了结以保障盈利。
如果激活(是),则凯尔特纳通道和布林带®指标的条件为:
- 多头平单:当凯尔特纳通道指标的下线向下穿过布林带®的上线时,多头订单将被平仓。
- 空头平单:当凯尔特纳通道指标的上线穿过布林带®的下线时,空头订单将被平仓。
设置止损单的代码如下:
double MCEA::OrderSLSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice) { //--- slv=0.0; int x=PairsIdxArray(xsymb); Pips(xsymb); RefreshTick(xsymb); //-- switch(type) { case (ORDER_TYPE_BUY): { if(use_sl==Yes && autosl==Yes) slv=mc_symbol.NormalizePrice(atprice-38*pip); else if(use_sl==Yes && autosl==No) slv=mc_symbol.NormalizePrice(atprice-SLval*pip); else slv=0.0; //-- break; } case (ORDER_TYPE_SELL): { if(use_sl==Yes && autosl==Yes) slv=mc_symbol.NormalizePrice(atprice+38*pip); else if(use_sl==Yes && autosl==No) slv=mc_symbol.NormalizePrice(atprice+SLval*pip); else slv=0.0; } } //--- return(slv); //--- } //-end OrderSLSet() //---------//
因信号疲软而平仓并保障盈利的代码如下:
int MCEA::GetCloseInWeakSignal(const string symbol,int exis) // Signal Indicator Position Close in profit { //--- int ret=0; int rise=1, down=-1; //-- int br=3; Pips(symbol); double difud=mc_symbol.NormalizePrice(1.5*pip); //-- double KCub[], KClb[]; double BBub[], BBlb[]; //-- ArrayResize(KCub,br,br); ArrayResize(KClb,br,br); ArrayResize(BBub,br,br); ArrayResize(BBlb,br,br); ArraySetAsSeries(KCub,true); ArraySetAsSeries(KClb,true); ArraySetAsSeries(BBub,true); ArraySetAsSeries(BBlb,true); //-- int xx=PairsIdxArray(symbol); //-- CopyBuffer(hKC[xx],1,0,br,KCub); CopyBuffer(hKC[xx],2,0,br,KClb); CopyBuffer(hBB[xx],1,0,br,BBub); CopyBuffer(hBB[xx],2,0,br,BBlb); //-- int dirmove=DirectionMove(symbol,TFt); bool closebuy=(KClb[1]>=BBub[1] && KClb[0]<BBub[0]-difud); bool closesel=(KCub[1]<=BBlb[1] && KCub[0]>BBlb[0]+difud); //-- if(exis==down && closesel && dirmove==rise) ret=rise; if(exis==rise && closebuy && dirmove==down) ret=down; //-- return(ret); //--- } //-end GetCloseInWeakSignal() //---------//
3.2. 止盈订单
选项:“使用订单止盈(是)或(否)”
- 如果选中“使用订单止盈(否)”选项,则在开单时,所有订单都不带止盈。
- 如果选项是“使用订单止盈“(是):再次给出选项:使用自动计算订单止盈(是)或(否)
- 如果选项“自动计算订单止盈(是)”,则由智能系统自动执行止盈计算。
- 如果选项“自动计算订单止盈(否)“,则交易者必须以点数输入订单止盈值。
设置止盈订单的代码如下:
double MCEA::OrderTPSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice) { //--- tpv=0.0; int x=PairsIdxArray(xsymb); Pips(xsymb); RefreshTick(xsymb); //-- switch(type) { case (ORDER_TYPE_BUY): { if(use_tp==Yes && autotp==Yes) tpv=mc_symbol.NormalizePrice(atprice+50*pip); else if(use_tp==Yes && autotp==No) tpv=mc_symbol.NormalizePrice(atprice+TPval*pip); else tpv=0.0; //-- break; } case (ORDER_TYPE_SELL): { if(use_tp==Yes && autotp==Yes) tpv=mc_symbol.NormalizePrice(atprice-50*pip); else if(use_tp==Yes && autotp==No) tpv=mc_symbol.NormalizePrice(atprice-TPval*pip); else tpv=0.0; } } //--- return(tpv); //--- } //-end OrderTPSet() //---------//
3.3. 尾随止损和尾随止盈
选项:“使用尾随止损/止盈(是)或(否)”
- 如果”使用尾随止损/止盈(否)“选项,则智能系统不会执行尾随止损和尾随止盈。
- 如果选项是使用尾随 SL/TP(是):再次给出选项:使用自动尾随(是)或(否)
- 如果选项“使用自动追踪(是)”,则尾随止损将由智能系统采用相同时间帧的抛物线 SAR(iSAR)值计算,同时基于变量值 TPmin(最小尾随盈利值)执行尾随止盈。
- 如果选项”使用自动尾随(否)“,则智能系统将采用输入属性中的值执行尾随止损。
注意:智能系统将同时执行尾随止盈和尾随止损。
尾随止损价格函数:
double MCEA::TSPrice(const string xsymb,ENUM_POSITION_TYPE ptype,int TS_type) { //--- int br=2; double pval=0.0; int x=PairsIdxArray(xsymb); Pips(xsymb); //-- switch(TS_type) { case 0: { RefreshTick(xsymb); if(ptype==POSITION_TYPE_BUY) pval=mc_symbol.NormalizePrice(mc_symbol.Bid()-TSval*pip); if(ptype==POSITION_TYPE_SELL) pval=mc_symbol.NormalizePrice(mc_symbol.Ask()+TSval*pip); break; } case 1: { double PSAR[]; ArrayResize(PSAR,br,br); ArraySetAsSeries(PSAR,true); CopyBuffer(hParIU[x],0,0,br,PSAR); RefreshPrice(xsymb,TFt,br); //-- if(ptype==POSITION_TYPE_BUY && (PSAR[0]<iLow(xsymb,TFt,0))) pval=PSAR[0]; if(ptype==POSITION_TYPE_SELL && (PSAR[0]>iHigh(xsymb,TFt,0))) pval=PSAR[0]; break; } } //-- return(pval); //--- } //-end TSPrice() //---------//
修改 SL/TP 函数:
bool MCEA::ModifySLTP(const string symbx,int TS_type) { //--- ResetLastError(); MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- int TRSP=TS_type; bool modist=false; int x=PairsIdxArray(symbx); Pips(symbx); //-- int total=PositionsTotal(); //-- for(int i=total-1; i>=0; i--) { string symbol=PositionGetSymbol(i); if(symbol==symbx && mc_position.Magic()==magicEA) { ENUM_POSITION_TYPE opstype = mc_position.PositionType(); if(opstype==POSITION_TYPE_BUY) { RefreshTick(symbol); double price = mc_position.PriceCurrent(); double vtrsb = mc_symbol.NormalizePrice(TSPrice(symbx,opstype,TRSP)); double pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_profit = mc_position.Profit(); double pos_swap = mc_position.Swap(); double pos_comm = mc_position.Commission(); double netp=pos_profit+pos_swap+pos_comm; double modstart=mc_symbol.NormalizePrice(pos_open+TSmin*pip); double modminsl=mc_symbol.NormalizePrice(vtrsb+TSmin*pip); double modbuysl=vtrsb; double modbuytp=mc_symbol.NormalizePrice(price+TPmin*pip); bool modbuy = (price>modminsl && modbuysl>modstart && (pos_stop==0.0||modbuysl>pos_stop)); //-- if(modbuy && netp>0.05) { modist=mc_trade.PositionModify(symbol,modbuysl,modbuytp); } } if(opstype==POSITION_TYPE_SELL) { RefreshTick(symbol); double price = mc_position.PriceCurrent(); double vtrss = mc_symbol.NormalizePrice(TSPrice(symbx,opstype,TRSP)); double pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_profit = mc_position.Profit(); double pos_swap = mc_position.Swap(); double pos_comm = mc_position.Commission(); double netp=pos_profit+pos_swap+pos_comm; double modstart=mc_symbol.NormalizePrice(pos_open-TSmin*pip); double modminsl=mc_symbol.NormalizePrice(vtrss-TSmin*pip); double modselsl=vtrss; double modseltp=mc_symbol.NormalizePrice(price-TPmin*pip); bool modsel = (price<modminsl && modselsl<modstart && (pos_stop==0.0||modselsl<pos_stop)); //-- if(modsel && netp>0.05) { modist=mc_trade.PositionModify(symbol,modselsl,modseltp); } } } } //-- return(modist); //--- } //-end ModifySLTP() //---------//
4. 手工订单管理。
为了提高该多币种智能交易系统的效率,将添加若干个手工单击按钮
4.1. Set SL / TP All Orders — 为所有订单设置止损/止盈
当交易者在输入参数设置”使用订单止损“(否)和/或”使用订单止盈“(否)时
但交易者打算对所有订单使用止损或止盈时,只需单击按钮
“为所有订单设置止损/止盈”,所有订单将被修改,并会应用止损和/或止盈。
4.2. “所有订单平仓”,如果交易者想要了结所有订单,那么只需单击“所有订单平仓”按钮,所有持仓将被平仓。
4.3. “所有盈利订单平仓”,如果交易者想要了结所有已盈利的订单,那么只需单击“所有盈利订单平仓”按钮,所有已盈利的持仓将被平仓。
5. 管理订单和图表品种。
对于仅从一个品种图表交易 30 对的多币种 EA,为所有品种提供一个按钮面板会非常有效和高效,以便交易者只需单击一下,即可更改图表或品种,从而在智能开单或平单时查看指标信号的准确性。
在 MQL5 程序中实现计划
1. 程序头文件和输入属性
包含 MQL5 头文件
//+------------------------------------------------------------------+ //| Include | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> #include <Trade\PositionInfo.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\AccountInfo.mqh> //-- CTrade mc_trade; CSymbolInfo mc_symbol; CPositionInfo mc_position; CAccountInfo mc_account; //---
所用时区的枚举
//-- enum tm_zone { Cus_Session, // Trading on Custom Session New_Zealand, // Trading on New Zealand Session Australia, // Trading on Australia Sydney Session Asia_Tokyo, // Trading on Asia Tokyo Session Europe_London, // Trading on Europe London Session US_New_York // Trading on US New York Session }; //--
选择时间钟点的枚举
//-- enum swhour { hr_00=0, // 00:00 hr_01=1, // 01:00 hr_02=2, // 02:00 hr_03=3, // 03:00 hr_04=4, // 04:00 hr_05=5, // 05:00 hr_06=6, // 06:00 hr_07=7, // 07:00 hr_08=8, // 08:00 hr_09=9, // 09:00 hr_10=10, // 10:00 hr_11=11, // 11:00 hr_12=12, // 12:00 hr_13=13, // 13:00 hr_14=14, // 14:00 hr_15=15, // 15:00 hr_16=16, // 16:00 hr_17=17, // 17:00 hr_18=18, // 18:00 hr_19=19, // 19:00 hr_20=20, // 20:00 hr_21=21, // 21:00 hr_22=22, // 22:00 hr_23=23 // 23:00 }; //--
选择时间分钟的枚举
//-- enum inmnt { mn_00=0, // Minute 0 mn_05=5, // Minute 5 mn_10=10, // Minute 10 mn_15=15, // Minute 15 mn_20=20, // Minute 20 mn_25=25, // Minute 25 mn_30=30, // Minute 30 mn_35=35, // Minute 35 mn_40=40, // Minute 40 mn_45=45, // Minute 45 mn_50=50, // Minute 50 mn_55=55 // Minute 55 }; //--
选择可交易选项对的枚举
//-- enum PairsTrade { All30, // All Forex 30 Pairs TrdWi, // Trader Wishes Pairs Usds, // Forex USD Pairs Eurs, // Forex EUR Pairs Gbps, // Forex GBP Pairs Auds, // Forex AUD Pairs Nzds, // Forex NZD Pairs Cads, // Forex CDD Pairs Chfs, // Forex CHF Pairs Jpys // Forex JPY Pairs }; //--
枚举 YN 是智能系统输入属性中的选项(Yes)或(No)
资金管理手数的枚举
//-- enum YN { No, Yes }; //--
选择计算信号指标时间帧的枚举
//-- enum TFUSE { TFM5, // PERIOD_M5 TFM15, // PERIOD_M15 TFM30, // PERIOD_M30 TFH1, // PERIOD_H1 TFH2, // PERIOD_H2 TFH3, // PERIOD_H3 TFH4, // PERIOD_H4 TFH6, // PERIOD_H6 TFH8, // PERIOD_H8 TFH12, // PERIOD_H12 TFD1 // PERIOD_D1 }; //--
注意:对于 TFUSE 枚举,我们限制智能系统仅从 TF-M5 到 TF-D1 的时间帧计算
智能系统输入属性
//--- input group "=== Global Strategy EA Parameter ==="; // Global Strategy EA Parameter input TFUSE tfinuse = TFH1; // Select Expert TimeFrame, default PERIOD_H1 input int KCPeriod = 20; // Input Keltner Channel Period, default 20 input ENUM_MA_METHOD KCMethod = MODE_EMA; // Select Keltner Channel MA Method, default EMA input ENUM_APPLIED_PRICE KCMAAP = PRICE_TYPICAL; // Select Keltner Channel MA Applied Price, default Price Typical input int KCATRPer = 20; // Input Keltner Channel ATR Period, default 20 input double KCATRBandsMulti = 1.0; // Input Keltner Channel ATR bands multiplier input int BBPeriod = 38; // Input Bollinger Bands® Indicator period, default 38 input double BBDevi = 1.0; // Input Bollinger Bands® Indicator Deviations, default 1.00 //--- input group "=== Select Pairs to Trade ==="; // Selected Pairs to trading input PairsTrade usepairs = All30; // Select Pairs to Use input string traderwishes = "eg. eurusd,usdchf"; // If Use Trader Wishes Pairs, input pair name here, separate by comma input string sym_prefix = ""; // Input the symbol prefix in case sensitive (if any) input string sym_suffix = ""; // Input the symbol suffix in case sensitive (if any) //-- input group "=== Money Management Lot Size Parameter ==="; // Money Management Lot Size Parameter input mmt mmlot = DynamLot; // Money Management Type input double Risk = 10.0; // Percent Equity Risk per Trade (Min=1.0% / Max=10.0%) input double Lots = 0.01; // Input Manual Lot Size FixedLot //--Trade on Specific Time input group "=== Trade on Specific Time ==="; // Trade on Specific Time input YN trd_time_zone = Yes; // Select If You Like to Trade on Specific Time Zone input tm_zone session = Cus_Session; // Select Trading Time Zone input swhour stsescuh = hr_00; // Time Hour to Start Trading Custom Session (0-23) input inmnt stsescum = mn_15; // Time Minute to Start Trading Custom Session (0-55) input swhour clsescuh = hr_23; // Time Hour to Stop Trading Custom Session (0-23) input inmnt clsescum = mn_55; // Time Minute to Stop Trading Custom Session (0-55) //--Day Trading On/Off input group "=== Day Trading On/Off ==="; // Day Trading On/Off input YN ttd0 = No; // Select Trading on Sunday (Yes) or (No) input YN ttd1 = Yes; // Select Trading on Monday (Yes) or (No) input YN ttd2 = Yes; // Select Trading on Tuesday (Yes) or (No) input YN ttd3 = Yes; // Select Trading on Wednesday (Yes) or (No) input YN ttd4 = Yes; // Select Trading on Thursday (Yes) or (No) input YN ttd5 = Yes; // Select Trading on Friday (Yes) or (No) input YN ttd6 = No; // Select Trading on Saturday (Yes) or (No) //--Trade & Order management Parameter input group "=== Trade & Order management Parameter ==="; // Trade & Order management Parameter input YN use_sl = No; // Use Order Stop Loss (Yes) or (No) input YN autosl = Yes; // Use Automatic Calculation Stop Loss (Yes) or (No) input double SLval = 30; // If Not Use Automatic SL - Input SL value in Pips input YN use_tp = Yes; // Use Order Take Profit (Yes) or (No) input YN autotp = Yes; // Use Automatic Calculation Take Profit (Yes) or (No) input double TPval = 100; // If Not Use Automatic TP - Input TP value in Pips input YN TrailingSLTP = Yes; // Use Trailing SL/TP (Yes) or (No) input YN autotrl = Yes; // Use Automatic Trailing (Yes) or (No) input double TSval = 5; // If Not Use Automatic Trailing Input Trailing value in Pips input double TSmin = 5; // Minimum Pips to start Trailing Stop input double TPmin = 25; // Input Trailing Profit Value in Pips input YN Close_by_Opps = Yes; // Close Trade By Opposite Signal (Yes) or (No) input YN SaveOnRev = Yes; // Close Trade and Save profit due to weak signal (Yes) or (No) //--Others Expert Advisor Parameter input group "=== Others Expert Advisor Parameter ==="; // Others EA Parameter input YN alerts = Yes; // Display Alerts / Messages (Yes) or (No) input YN UseEmailAlert = No; // Email Alert (Yes) or (No) input YN UseSendnotify = No; // Send Notification (Yes) or (No) input YN trade_info_display = Yes; // Select Display Trading Info on Chart (Yes) or (No) input ulong magicEA = 20231204; // Expert ID (Magic Number) //---
注意:如果智能系统 ID(魔幻数字)的输入属性留空,EA 就能够管理手工开立的订单。
在智能系统的 “全局策略 EA 参数” 输入属性组中,要求交易者选择“智能系统时间帧”,这是为了计算指标信号,并输入“凯尔特纳通道”和“布林带®”指标的参数。
在本文中创建 EA 时,EA 中凯尔特纳通道指标的输入属性将与指标完全相同。
//-- input int period_kc = 20; // Input Keltner Channel Period input ENUM_MA_METHOD ma_method = MODE_EMA; // Select MA Type of smoothing input ENUM_APPLIED_PRICE ma_price = PRICE_TYPICAL; // Select MA Applied Price input int atr_period = 20; // Input ATR Period (typically over 10 or 20) input double band_multi = 1.00; // Input the Band Multiplier ATR Desired //--
在智能系统输入属性组“选择要交易的货币对”中,交易者必须从已提供的 10 个选项中选择要交易的货币对,默认情况下是所有的 30 个外汇对。
为了配置要交易的货币对,我们将调用 HandlingSymbolArrays() 函数。调用 HandlingSymbolArrays() 函数,我们将处理所有可交易的货币对。
void MCEA::HandlingSymbolArrays(void) { //--- string All30[]={"EURUSD","GBPUSD","AUDUSD","NZDUSD","USDCAD","USDCHF","USDJPY","EURGBP", "EURAUD","EURNZD","EURCAD","EURCHF","EURJPY","GBPAUD","GBPNZD","GBPCAD", "GBPCHF","GBPJPY","AUDNZD","AUDCAD","AUDCHF","AUDJPY","NZDCAD","NZDCHF", "NZDJPY","CADCHF","CADJPY","CHFJPY","XAUUSD","XAGUSD"}; // 30 pairs string USDs[]={"USDCAD","USDCHF","USDJPY","AUDUSD","EURUSD","GBPUSD","NZDUSD","XAUUSD","XAGUSD"}; // USD pairs string EURs[]={"EURAUD","EURCAD","EURCHF","EURGBP","EURJPY","EURNZD","EURUSD"}; // EUR pairs string GBPs[]={"GBPAUD","GBPCAD","GBPCHF","EURGBP","GBPJPY","GBPNZD","GBPUSD"}; // GBP pairs string AUDs[]={"AUDCAD","AUDCHF","EURAUD","GBPAUD","AUDJPY","AUDNZD","AUDUSD"}; // AUD pairs string NZDs[]={"AUDNZD","NZDCAD","NZDCHF","EURNZD","GBPNZD","NZDJPY","NZDUSD"}; // NZD pairs string CADs[]={"AUDCAD","CADCHF","EURCAD","GBPCAD","CADJPY","NZDCAD","USDCAD"}; // CAD pairs string CHFs[]={"AUDCHF","CADCHF","EURCHF","GBPCHF","NZDCHF","CHFJPY","USDCHF"}; // CHF pairs string JPYs[]={"AUDJPY","CADJPY","CHFJPY","EURJPY","GBPJPY","NZDJPY","USDJPY"}; // JPY pairs //-- sall=ArraySize(All30); arusd=ArraySize(USDs); aretc=ArraySize(EURs); ArrayResize(VSym,sall,sall); ArrayCopy(VSym,All30,0,0,WHOLE_ARRAY); //-- if(usepairs==TrdWi && StringFind(traderwishes,"eg.",0)<0) { string to_split=traderwishes; // A string to split into substrings pairs name string sep=","; // A separator as a character ushort u_sep; // The code of the separator character //--- Get the separator code u_sep=StringGetCharacter(sep,0); //--- Split the string to substrings int p=StringSplit(to_split,u_sep,SPC); if(p>0) { for(int i=0; i<p; i++) StringToUpper(SPC[i]); //-- for(int i=0; i<p; i++) { if(ValidatePairs(SPC[i])<0) ArrayRemove(SPC,i,1); } } arspc=ArraySize(SPC); } //-- SetSymbolNamePS(); // With this function we will detect whether the Symbol Name has a prefix and/or suffix //-- if(inpre>0 || insuf>0) { if(usepairs==TrdWi && arspc>0) { for(int t=0; t<arspc; t++) { SPC[t]=pre+SPC[t]+suf; } } //-- for(int t=0; t<sall; t++) { All30[t]=pre+All30[t]+suf; } for(int t=0; t<arusd; t++) { USDs[t]=pre+USDs[t]+suf; } for(int t=0; t<aretc; t++) { EURs[t]=pre+EURs[t]+suf; } for(int t=0; t<aretc; t++) { GBPs[t]=pre+GBPs[t]+suf; } for(int t=0; t<aretc; t++) { AUDs[t]=pre+AUDs[t]+suf; } for(int t=0; t<aretc; t++) { NZDs[t]=pre+NZDs[t]+suf; } for(int t=0; t<aretc; t++) { CADs[t]=pre+CADs[t]+suf; } for(int t=0; t<aretc; t++) { CHFs[t]=pre+CHFs[t]+suf; } for(int t=0; t<aretc; t++) { JPYs[t]=pre+JPYs[t]+suf; } } //-- ArrayCopy(VSym,All30,0,0,WHOLE_ARRAY); ArrayResize(AS30,sall,sall); ArrayCopy(AS30,All30,0,0,WHOLE_ARRAY); for(int x=0; x<sall; x++) {SymbolSelect(AS30[x],true);} if(ValidatePairs(Symbol())>=0) symbfix=true; if(!symbfix) { Alert("Expert Advisors will not trade on pairs "+Symbol()); Alert("-- "+expname+" -- ",Symbol()," -- Expert Advisor will be Remove from the chart."); ExpertRemove(); } //-- switch(usepairs) { case 0: // All Forex 30 Pairs { ArrayResize(DIRI,sall,sall); arrsymbx=sall; ArraySymbolResize(); ArrayCopy(DIRI,All30,0,0,WHOLE_ARRAY); pairs="Multi Currency 30 Pairs"; //-- break; } case 1: // Trader wishes pairs { ArrayResize(DIRI,arspc,arspc); arrsymbx=arspc; ArraySymbolResize(); ArrayCopy(DIRI,SPC,0,0,WHOLE_ARRAY); pairs="("+string(arspc)+") Trader Wishes Pairs"; //-- break; } case 2: // USD pairs { ArrayResize(DIRI,arusd,arusd); arrsymbx=arusd; ArraySymbolResize(); ArrayCopy(DIRI,USDs,0,0,WHOLE_ARRAY); pairs="("+string(arusd)+") Multi Currency USD Pairs"; //-- break; } case 3: // EUR pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,EURs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex EUR Pairs"; //-- break; } case 4: // GBP pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,GBPs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex GBP Pairs"; //-- break; } case 5: // AUD pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,AUDs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex AUD Pairs"; //-- break; } case 6: // NZD pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,NZDs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex NZD Pairs"; //-- break; } case 7: // CAD pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,CADs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex CAD Pairs"; //-- break; } case 8: // CHF pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,CHFs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex CHF Pairs"; //-- break; } case 9: // JPY pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,JPYs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex JPY Pairs"; //-- break; } } //-- return; //--- } //-end HandlingSymbolArrays() //---------//
在 HandlingSymbolArrays() 函数中,我们将调用 SetSymbolNamePS() 函数。调用 SetSymbolNamePS(),我们就能够处理含有前缀和/或后缀的品种名称。
void MCEA::SetSymbolNamePS(void) { //--- symbfix=false; int ptriml; int ptrimr; string insymbol=Symbol(); int sym_Lenpre=StringLen(prefix); int sym_Lensuf=StringLen(suffix); if(sym_Lenpre>0) { ptriml=StringTrimLeft(suffix); ptriml=StringTrimRight(suffix); } if(sym_Lensuf>0) { ptrimr=StringTrimLeft(suffix); ptrimr=StringTrimRight(suffix); } string sym_pre=prefix; string sym_suf=suffix; //-- pre=sym_pre; suf=sym_suf; inpre=StringLen(pre); insuf=StringLen(suf); posCur1=inpre; posCur2=posCur1+3; //-- return; //--- } //-end SetSymbolNamePS() //---------//
注意:智能系统会验证货币对。如果交易者在输入货币对名称或前缀和/或后缀时出现错误(拼写错误),或者如果货币对验证失败,智能系统将收到警告,并从图表中删除 EA。
在智能系统输入属性组“特定时间交易”中,交易者将在此处选择“特定时区交易(是)或(否)”,如果是,则选择枚举选项:
- Trading on Custom Session(在自定义时段交易)
- Trading on New Zealand Session(在新西兰时段交易)
- Trading on Australia Sydney Session(在澳大利亚悉尼时段交易)
- Trading on Asia Tokyo Session(在亚洲东京时段交易)
- Trading on Europe London Session(在欧洲伦敦时段交易)
- Trading on America New York Session(在美国纽约时段交易)
自定义时段交易:在该时段中,交易者需要设置开始交易时间的小时和分钟,以及结束交易的小时和分钟。这意味着 EA 只会在指定开始到结时间段内执行活动。
在新西兰至美国纽约的交易时段中,从交易开始到交易结束的时间由 EA 计算。
EA 的工作类。
为了判定 EA 工作流程中的构造和配置,我们创建一个类来声明该多币种 EA 所需的所有变量、对象和函数。
//+------------------------------------------------------------------+ //| Class for working Expert Advisor | //+------------------------------------------------------------------+ class MCEA { //--- private: //---- int x_year; // Year int x_mon; // Month int x_day; // Day of the month int x_hour; // Hour in a day int x_min; // Minutes int x_sec; // Seconds //-- int oBm, oSm, ldig; //--- Variables used in prefix and suffix symbols int posCur1, posCur2; int inpre, insuf; bool symbfix; string pre,suf; string prefix,suffix; //--- Variables are used in Trading Time Zone int ishour, onhour; int tftrlst, tfcinws; datetime rem, znop, zncl, zntm; datetime SesCuOp, SesCuCl, Ses01Op, Ses01Cl, Ses02Op, Ses02Cl, Ses03Op, Ses03Cl, Ses04Op, Ses04Cl, Ses05Op, Ses05Cl, SesNoOp, SesNoCl; //-- string tz_ses, tz_opn, tz_cls; //-- string tmopcu, tmclcu, tmop01, tmcl01, tmop02, tmcl02, tmop03, tmcl03, tmop04, tmcl04, tmop05, tmcl05, tmopno, tmclno; //---------------------- //-- double LotPS; double slv, tpv, pip, xpip; double SARstep, SARmaxi; double floatprofit, fixclprofit; //-- string pairs, hariini, daytrade, trade_mode; //-- double OPEN[], HIGH[], LOW[], CLOSE[]; datetime TIME[]; datetime closetime; //-- //------------ //------------ void SetSymbolNamePS(void); void HandlingSymbolArrays(void); void Set_Time_Zone(void); void Time_Zone(void); bool Trade_session(void); string PosTimeZone(void); int ThisTime(const int reqmode); int ReqTime(datetime reqtime,const int reqmode); //-- int DirectionMove(const string symbol,const ENUM_TIMEFRAMES stf); int BBOnKeltnerChannel(const string symbol); int PARSAR05(const string symbol); int LotDig(const string symbol); //-- double MLots(const string symbx); double NonZeroDiv(double val1,double val2); double OrderSLSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice); double OrderTPSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice); double SetOrderSL(const string xsymb,ENUM_POSITION_TYPE type,double atprice); double SetOrderTP(const string xsymb,ENUM_POSITION_TYPE type,double atprice); double TSPrice(const string xsymb,ENUM_POSITION_TYPE ptype,int TS_type); //-- string ReqDate(int d,int h,int m); string TF2Str(ENUM_TIMEFRAMES period); string timehr(int hr,int mn); string TradingDay(void); string AccountMode(); string GetCommentForOrder(void) { return(expname); } //------------ public: //--- //-- BBOnKeltnerChannel_MCEA Config -- string DIRI[], AS30[], VSym[]; string SPC[]; string USD[]; string EUR[]; string GBP[]; string AUD[]; string NZD[]; string CAD[]; string CHF[]; string JPY[]; //-- string expname; string indiname; //-- int hKC[]; int hBB[]; int hParIU[], hPar05[]; int ALO, dgts, arrsar, arrsymbx; int sall, arusd, aretc, arspc, arper; ulong slip; //-- double profitb[], profits[]; //-- int Buy, Sell; int ccur, psec, xtto, checktml; int OpOr[],xob[],xos[]; //-- int year, // Year mon, // Month day, // Day hour, // Hour min, // Minutes sec, // Seconds dow, // Day of week (0-Sunday, 1-Monday, ... ,6-Saturday) doy; // Day number of the year (January 1st is assigned the number value of zero) //-- ENUM_TIMEFRAMES TFt, TFT05; //-- bool PanelExtra; //------------ MCEA(void); ~MCEA(void); //------------ //-- virtual void BBOnKeltnerChannel_MCEA_Config(void); virtual void ExpertActionTrade(void); //-- void ArraySymbolResize(void); void CurrentSymbolSet(const string symbol); void Pips(const string symbol); void TradeInfo(void); void Do_Alerts(const string symbx,string msgText); void CheckOpenPMx(const string symbx); void SetSLTPOrders(void); void CloseBuyPositions(const string symbol); void CloseSellPositions(const string symbol); void CloseAllOrders(void); void CheckClose(const string symbx); void TodayOrders(void); void UpdatePrice(const string symbol,ENUM_TIMEFRAMES xtf); void RefreshPrice(const string symbx,ENUM_TIMEFRAMES xtf,int bars); //-- bool RefreshTick(const string symbx); bool TradingToday(void); bool OpenBuy(const string symbol); bool OpenSell(const string symbol); bool ModifyOrderSLTP(double mStop,double ordtp); bool ModifySLTP(const string symbx,int TS_type); bool CloseAllProfit(void); bool ManualCloseAllProfit(void); //-- int PairsIdxArray(const string symbol); int ValidatePairs(const string symbol); int GetOpenPosition(const string symbol); int GetCloseInWeakSignal(const string symbol,int exis); //-- string getUninitReasonText(int reasonCode); //-- //------------ //--- }; //-end class MCEA //---------//
在多币种 EA 工作流程中,由 OnInit() 调用的最先、也是最重要的函数是 BBOnKeltnerChannel_MCEA_Config()。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(void) { //--- mc.BBOnKeltnerChannel_MCEA_Config(); //-- return(INIT_SUCCEEDED); //--- } //-end OnInit() //---------//
BBOnKeltnerChannel_MCEA_Config() 函数配置要交易的所有品种、要用到的所有指标句柄、以及包含的头文件里涉及 EA 工作流程的一些重要函数。
在函数代码行 468 至 484,解释了如何处理时间帧,并为将要用到的所有指标创建句柄。
//+------------------------------------------------------------------+ //| Expert Configuration | //+------------------------------------------------------------------+ void MCEA::BBOnKeltnerChannel_MCEA_Config(void) { //--- //-- HandlingSymbolArrays(); // With this function we will handle all pairs that will be traded //-- TFT05=PERIOD_M5; ENUM_TIMEFRAMES TFs[]={PERIOD_M5,PERIOD_M15,PERIOD_M30,PERIOD_H1,PERIOD_H2,PERIOD_H3,PERIOD_H4,PERIOD_H6,PERIOD_H8,PERIOD_H12,PERIOD_D1}; int arTFs=ArraySize(TFs); //-- for(int x=0; x<arTFs; x++) if(tfinuse==x) TFt=TFs[x]; // TF for calculation signal //-- //-- Keltner Channel and Bollinger Bands® Indicators handle for all symbol for(int x=0; x<arrsymbx; x++) { hKC[x]=iCustom(DIRI[x],TFt,indiname,KCPeriod,KCMethod,KCMAAP,KCATRPer,KCATRBandsMulti); //-- Handle for the Keltner Channel indicator hParIU[x]=iSAR(DIRI[x],TFt,SARstep,SARmaxi); //-- Handle for the iSAR indicator according to the selected Timeframe hPar05[x]=iSAR(DIRI[x],TFT05,SARstep,SARmaxi); //-- Handle for the iSAR indicator for M5 Timeframe //-- } //-- for(int x=0; x<arrsymbx; x++) hBB[x]=iBands(DIRI[x],TFt,BBPeriod,0,BBDevi,hKC[x]); //-- Handle for the iBands On Keltner Channel Indicator handle //-- ALO=(int)mc_account.LimitOrders()>sall ? sall : (int)mc_account.LimitOrders(); //-- LotPS=(double)ALO; //-- mc_trade.SetExpertMagicNumber(magicEA); mc_trade.SetDeviationInPoints(slip); mc_trade.SetMarginMode(); Set_Time_Zone(); //-- return; //--- } //-end BBOnKeltnerChannel_MCEA_Config() //---------//
2. 智能系统即刻报价函数
在智能系统的即刻报价函数 OnTick() 中,我们将调用多币种 EA 的最重要函数之一,即 ExpertActionTrade() 函数。
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(void) { //--- mc.ExpertActionTrade(); //-- return; //--- } //-end OnTick() //---------//
这是该函数中 EA 工作流程的顺序。
ExpertActionTrade() 函数将执行所有活动,并开始接管自动交易,从开单、平单、尾随止损或止盈、及其它附加活动。
void MCEA::ExpertActionTrade(void) { //--- //--Check Trading Terminal ResetLastError(); //-- if(!MQLInfoInteger(MQL_TRADE_ALLOWED) && mc.checktml==0) //-- Check whether MT5 Algorithmic trading is Allow or Prohibit { mc.Do_Alerts(Symbol(),"Trading Expert at "+Symbol()+" are NOT Allowed by Setting."); mc.checktml=1; //-- Variable checktml is given a value of 1, so that the alert is only done once. return; } //-- if(!DisplayManualButton("M","C","R")) DisplayManualButton(); //-- Show the expert manual button panel //-- if(trade_info_display==Yes) mc.TradeInfo(); //-- Displayed Trading Info on Chart //--- //-- int mcsec=mc.ThisTime(mc.sec); //-- if(fmod((double)mcsec,5.0)==0) mc.ccur=mcsec; //-- if(mc.ccur!=mc.psec) { string symbol; //-- Here we start with the rotation of the name of all symbol or pairs to be traded for(int x=0; x<mc.arrsymbx && !IsStopped(); x++) { //-- if(mc.DIRI[x]==Symbol()) symbol=Symbol(); else symbol=mc.DIRI[x]; //-- mc.CurrentSymbolSet(symbol); //-- if(mc.TradingToday() && mc.Trade_session()) { //-- mc.OpOr[x]=mc.GetOpenPosition(symbol); //-- Get trading signals to open positions //-- //-- and store in the variable OpOr[x] if(mc.OpOr[x]==mc.Buy) //-- If variable OpOr[x] get result of GetOpenPosition(symbol) as "Buy" (value=1) { //-- mc.CheckOpenPMx(symbol); //-- if(Close_by_Opps==Yes && mc.xos[x]>0) mc.CloseSellPositions(symbol); //-- if(mc.xob[x]==0 && mc.xtto<mc.ALO) mc.OpenBuy(symbol); else if(mc.xtto>=mc.ALO) { //-- mc.Do_Alerts(symbol,"Maximum amount of open positions and active pending orders has reached"+ "\n the limit = "+string(mc.ALO)+" Orders "); //-- mc.CheckOpenPMx(symbol); //-- if(mc.xos[x]>0 && mc.profits[x]<-1.02 && mc.xob[x]==0) {mc.CloseSellPositions(symbol); mc.OpenBuy(symbol);} else if(SaveOnRev==Yes) mc.CloseAllProfit(); } } if(mc.OpOr[x]==mc.Sell) //-- If variable OpOr[x] get result of GetOpenPosition(symbol) as "Sell" (value=-1) { //-- mc.CheckOpenPMx(symbol); //-- if(Close_by_Opps==Yes && mc.xob[x]>0) mc.CloseBuyPositions(symbol); //-- if(mc.xos[x]==0 && mc.xtto<mc.ALO) mc.OpenSell(symbol); else if(mc.xtto>=mc.ALO) { //-- mc.Do_Alerts(symbol,"Maximum amount of open positions and active pending orders has reached"+ "\n the limit = "+string(mc.ALO)+" Orders "); //-- mc.CheckOpenPMx(symbol); //-- if(mc.xob[x]>0 && mc.profitb[x]<-1.02 && mc.xos[x]==0) {mc.CloseBuyPositions(symbol); mc.OpenSell(symbol);} else if(SaveOnRev==Yes) mc.CloseAllProfit(); } } } //-- mc.CheckOpenPMx(symbol); //-- if(mc.xtto>0) { //-- if(SaveOnRev==Yes) //-- Close Trade and Save profit due to weak signal (Yes) { mc.CheckOpenPMx(symbol); if(mc.profitb[x]>0.02 && mc.xob[x]>0 && mc.GetCloseInWeakSignal(symbol,mc.Buy)==mc.Sell) { mc.CloseBuyPositions(symbol); mc.Do_Alerts(symbol,"Close BUY order "+symbol+" to save profit due to weak signal."); } if(mc.profits[x]>0.02 && mc.xos[x]>0 && mc.GetCloseInWeakSignal(symbol,mc.Sell)==mc.Buy) { mc.CloseSellPositions(symbol); mc.Do_Alerts(symbol,"Close SELL order "+symbol+" to save profit due to weak signal."); } } //-- if(TrailingSLTP==Yes) //-- Use Trailing SL/TP (Yes) { if(autotrl==Yes) mc.ModifySLTP(symbol,1); //-- If Use Automatic Trailing (Yes) if(autotrl==No) mc.ModifySLTP(symbol,0); //-- Use Automatic Trailing (No) } } //-- mc.CheckClose(symbol); } //-- mc.psec=mc.ccur; } //-- return; //--- } //-end ExpertActionTrade() //---------//
定期交易开/关属性组为交易者提供了在周日至周六的指定日子进行交易的选项。以这种方式,交易者可以选择(是)或(否)选项,来激活或取消智能系统在指定日子的交易。
//--Day Trading On/Off input group "=== Day Trading On/Off ==="; // Day Trading On/Off input YN ttd0 = No; // Select Trading on Sunday (Yes) or (No) input YN ttd1 = Yes; // Select Trading on Monday (Yes) or (No) input YN ttd2 = Yes; // Select Trading on Tuesday (Yes) or (No) input YN ttd3 = Yes; // Select Trading on Wednesday (Yes) or (No) input YN ttd4 = Yes; // Select Trading on Thursday (Yes) or (No) input YN ttd5 = Yes; // Select Trading on Friday (Yes) or (No) input YN ttd6 = No; // Select Trading on Saturday (Yes) or (No)
定期交易开/关的执行如下:
bool MCEA::TradingToday(void) { //--- bool tradetoday=false; int trdday=ThisTime(dow); hariini="No"; //-- int ttd[]; ArrayResize(ttd,7); ttd[0]=ttd0; ttd[1]=ttd1; ttd[2]=ttd2; ttd[3]=ttd3; ttd[4]=ttd4; ttd[5]=ttd5; ttd[6]=ttd6; //-- if(ttd[trdday]==Yes) {tradetoday=true; hariini="Yes";} //-- return(tradetoday); //--- } //-end TradingToday() //---------//
注意:定期交易开/关条件将显示在图表上的交易信息中。
在 EA 的“特定时间交易”属性组中,交易者可以选择按时区进行交易。
input group "=== Trade on Specific Time ==="; // Trade on Specific Time input YN trd_time_zone = Yes; // Select If You Like to Trade on Specific Time Zone input tm_zone session = Cus_Session; // Select Trading Time Zone input swhour stsescuh = hr_00; // Time Hour to Start Trading Custom Session (0-23) input inmnt stsescum = mn_15; // Time Minute to Start Trading Custom Session (0-55) input swhour clsescuh = hr_23; // Time Hour to Stop Trading Custom Session (0-23) input inmnt clsescum = mn_55; // Time Minute to Stop Trading Custom Session (0-55)
注:如上所述,在新西兰至美国纽约时段交易的情况下,从交易开始到交易结束的时间由 EA 计算。因此,在智能系统输入属性中,交易者只需为自定义时段设置交易开始时间的小时和分钟,以及交易结束时间的小时和分钟。
特别是对于时区交易,在 ExpertActionTrade() 函数中加入调用布尔 Trade_session() 函数。
如果 Trade_session() 为 true,则 EA 工作流程将继续进行,直到完成;但如果为 false,则 EA 将执行任务“如果为(是),则因信号疲软而平仓,并保障盈利” 和 “如果为(是),则尾随止损”。
bool MCEA::Trade_session(void) { //--- bool trd_ses=false; ishour=ThisTime(hour); if(ishour!=onhour) Set_Time_Zone(); datetime tcurr=TimeCurrent(); // Server Time //-- switch(session) { case Cus_Session: { if(tcurr>=SesCuOp && tcurr<=SesCuCl) trd_ses=true; break; } case New_Zealand: { if(tcurr>=Ses01Op && tcurr<=Ses01Cl) trd_ses=true; break; } case Australia: { if(tcurr>=Ses02Op && tcurr<=Ses02Cl) trd_ses=true; break; } case Asia_Tokyo: { if(tcurr>=Ses03Op && tcurr<=Ses03Cl) trd_ses=true; break; } case Europe_London: { if(tcurr>=Ses04Op && tcurr<=Ses04Cl) trd_ses=true; break; } case US_New_York: { if(tcurr>=Ses05Op && tcurr<=Ses05Cl) trd_ses=true; break; } } //-- if(trd_time_zone==No) { if(tcurr>=SesNoOp && tcurr<=SesNoCl) trd_ses=true; } //-- onhour=ishour; //-- return(trd_ses); //--- } //-end Trade_session() //---------//
3. 如何获取开仓的交易信号?
为了获取信号,ExpertActionTrade() 函数调用 GetOpenPosition() 函数。
int MCEA::GetOpenPosition(const string symbol) // Signal Open Position { //--- int ret=0; int rise=1, down=-1; //-- int BBOnKC=BBOnKeltnerChannel(symbol); //-- if(BBOnKC==rise) ret=rise; if(BBOnKC==down) ret=down; //-- return(ret); //--- } //-end GetOpenPosition() //---------//
GetOpenPosition() 函数调用计算信号的 BBOnKeltnerChannel() 函数。
int MCEA::BBOnKeltnerChannel(const string symbol) // Fungction of Bollinger Bands® On Keltner Channel Indicator { //--- int ret=0; int rise=1, down=-1; int br=3; Pips(symbol); double difud=mc_symbol.NormalizePrice(1.5*pip); //-- double KCmb[], // Destination array for Keltner Channel Middle Line buffer KCub[], // Destination array for Keltner Channel Upper Band buffer KClb[]; // Destination array for Keltner Channel Lower Band buffer double BBmb[], // Destination array for Bollinger Bands® BASE_LINE buffer BBub[], // Destination array for Bollinger Bands® UPPER_BAND buffer BBlb[]; // Destination array for Bollinger Bands® LOWER_BAND buffer //-- ArrayResize(KCmb,br,br); ArrayResize(KCub,br,br); ArrayResize(KClb,br,br); ArrayResize(BBmb,br,br); ArrayResize(BBub,br,br); ArrayResize(BBlb,br,br); ArraySetAsSeries(KCmb,true); ArraySetAsSeries(KCub,true); ArraySetAsSeries(KClb,true); ArraySetAsSeries(BBmb,true); ArraySetAsSeries(BBub,true); ArraySetAsSeries(BBlb,true); //-- int xx=PairsIdxArray(symbol); //-- CopyBuffer(hKC[xx],0,0,br,KCmb); CopyBuffer(hKC[xx],1,0,br,KCub); CopyBuffer(hKC[xx],2,0,br,KClb); CopyBuffer(hBB[xx],0,0,br,BBmb); CopyBuffer(hBB[xx],1,0,br,BBub); CopyBuffer(hBB[xx],2,0,br,BBlb); //-- bool BBKCrise1=(KCmb[1]<=BBlb[1] && KCmb[0]>BBlb[0]+difud); bool BBKCrise2=(KCmb[1]<=BBmb[1] && KCmb[0]>BBmb[0]+difud); bool BBKCrise3=(KCmb[1]<=BBub[1] && KCmb[0]>BBub[0]+difud); //-- bool BBKCdown1=(KCmb[1]>=BBub[1] && KCmb[0]<BBub[0]-difud); bool BBKCdown2=(KCmb[1]>=BBmb[1] && KCmb[0]<BBmb[0]-difud); bool BBKCdown3=(KCmb[1]>=BBlb[1] && KCmb[0]<BBlb[0]-difud); //-- if(BBKCrise1 || BBKCrise2 || BBKCrise3) ret=rise; if(BBKCdown1 || BBKCdown2 || BBKCdown3) ret=down; //-- return(ret); //--- } //-end BBOnKeltnerChannel() //---------//
正如您所见,在 BBOnKeltnerChannel() 函数中,我们会调用另一个函数,即 PairsIdxArray() 函数。
int xx=PairsIdxArray(symbol);
int MCEA::PairsIdxArray(const string symbol) { //--- int pidx=-1; //-- for(int x=0; x<arrsymbx; x++) { if(DIRI[x]==symbol) { pidx=x; break; } } //-- return(pidx); //--- } //-end PairsIdxArray() //---------//
PairsIdxArray() 函数获取所请求品种的名称,及其指标句柄。然后调用相应的指标句柄,并获取该时间帧中的凯尔特纳通道和布林带®信号的缓冲区数值。
接下来,我们必须知道凯尔特纳通道指标的缓冲区编号。在凯尔特纳通道指标中,我们知道在 OnInit() 函数中,缓冲区编号如下: 0 - 中线,1 - 上轨线,2 - 下轨线
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //-- assignment of array to indicator buffer SetIndexBuffer(0,KC_Middle,INDICATOR_DATA); SetIndexBuffer(1,KC_UpperB,INDICATOR_DATA); SetIndexBuffer(2,KC_LowerB,INDICATOR_DATA); SetIndexBuffer(3,KC_ATRtemp,INDICATOR_CALCULATIONS); //-- handleMA=iMA(Symbol(),Period(),period_kc,0,MODE_EMA,ma_price); //-- if(handleMA==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the Moving Average indicator for the symbol %s/%s, error code %d", Symbol(), EnumToString(Period()), GetLastError()); //--- the indicator is stopped early return(INIT_FAILED); } //-- handleATR=iATR(Symbol(),Period(),atr_period); //-- if(handleATR==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the ATR indicator for the symbol %s/%s, error code %d", Symbol(), EnumToString(Period()), GetLastError()); //--- the indicator is stopped early return(INIT_FAILED); } //-- short_name=StringFormat("Keltner Channel(%d, %s, %.2f)",period_kc,EnumToString(ma_method),band_multi); IndicatorSetString(INDICATOR_SHORTNAME,short_name); IndicatorSetInteger(INDICATOR_DIGITS,Digits()); //-- return(INIT_SUCCEEDED); //--- } //---------//
在这种情况下,我们还需要知道布林带®指标的缓冲区编号。
同时,正如 iBands® 函数中所解释的那样:
“注意缓冲区编号如下:0 - BASE_LINE、1 - UPPER_BAND、2 - LOWER_BAND”
applied_price
[in] 应用的价格。可以是 ENUM_APPLIED_PRICE 枚举当中的任何价格常量,也可以是另一个指标的句柄。
在本文中,EA 将使用凯尔特纳通道指标的句柄作为布林带®的应用价格。
故此,为了获取凯尔特纳通道和布林带®指标每行的缓冲区值,我们将从指标句柄中复制每个缓冲区。
为了将中轨线缓冲区(缓冲区 0)从凯尔特纳通道指标句柄复制到目标数组:
CopyBuffer(hKC[xx],0,0,br,KCmb);
为了将上轨缓冲区(缓冲区 1)从凯尔特纳通道指标句柄复制到目标数组:
CopyBuffer(hKC[xx],1,0,br,KCub);
为了将下轨缓冲区(缓冲区 2)从凯尔特纳通道指标句柄复制到目标数组:
CopyBuffer(hKC[xx],2,0,br,KClb);
为了将 BASE_LINE 缓冲区(缓冲区 0)从布林带®指标句柄复制到目标数组:
CopyBuffer(hBB[xx],0,0,br,BBmb);
为了将 UPPER_BAND 缓冲区(缓冲区 1)从布林带®指标句柄复制到目标数组:
CopyBuffer(hBB[xx],1,0,br,BBub);
为了将 LOWER_BAND 缓冲区(缓冲区 2)从布林带®指标句柄复制到目标数组:
CopyBuffer(hBB[xx],2,0,br,BBlb);
接下来,GetOpenPosition() 函数返回以下结果:
- 值为 0,信号未知。
- 值为 1,是开立多头订单的信号。
- 值为 -1,是开立空头订单的信号。
当 GetOpenPosition() 函数返回值 1 时,EA 将调用 OpenBuy() 函数。
bool MCEA::OpenBuy(const string symbol) { //--- ResetLastError(); //-- bool buyopen = false; string ldComm = GetCommentForOrder()+"_Buy"; double ldLot = MLots(symbol); ENUM_ORDER_TYPE type_req = ORDER_TYPE_BUY; //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- structure is set to zero ZeroMemory(req); ZeroMemory(res); ZeroMemory(check); //-- CurrentSymbolSet(symbol); double SL=OrderSLSet(symbol,type_req,mc_symbol.Bid()); double TP=OrderTPSet(symbol,type_req,mc_symbol.Ask()); //-- if(RefreshTick(symbol)) buyopen=mc_trade.Buy(ldLot,symbol,mc_symbol.Ask(),SL,TP,ldComm); //-- int error=GetLastError(); if(buyopen||error==0) { string bsopen="Open BUY Order for "+symbol+" ~ Ticket= ["+(string)mc_trade.ResultOrder()+"] successfully..!"; Do_Alerts(symbol,bsopen); } else { mc_trade.CheckResult(check); Do_Alerts(Symbol(),"Open BUY order for "+symbol+" FAILED!!. Return code= "+ (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]"); return(false); } //-- return(buyopen); //-- //--- } //-end OpenBuy //---------//
同理,如果 GetOpenPosition() 函数返回值为 -1,则 EA 将调用 OpenSell() 函数。
bool MCEA::OpenSell(const string symbol) { //--- ResetLastError(); //-- bool selopen = false; string sdComm = GetCommentForOrder()+"_Sell"; double sdLot = MLots(symbol); ENUM_ORDER_TYPE type_req = ORDER_TYPE_SELL; //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- structure is set to zero ZeroMemory(req); ZeroMemory(res); ZeroMemory(check); //-- CurrentSymbolSet(symbol); double SL=OrderSLSet(symbol,type_req,mc_symbol.Ask()); double TP=OrderTPSet(symbol,type_req,mc_symbol.Bid()); //-- if(RefreshTick(symbol)) selopen=mc_trade.Sell(sdLot,symbol,mc_symbol.Bid(),SL,TP,sdComm); //-- int error=GetLastError(); if(selopen||error==0) { string bsopen="Open SELL Order for "+symbol+" ~ Ticket= ["+(string)mc_trade.ResultOrder()+"] successfully..!"; Do_Alerts(symbol,bsopen); } else { mc_trade.CheckResult(check); Do_Alerts(Symbol(),"Open SELL order for "+symbol+" FAILED!!. Return code= "+ (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]"); return(false); } //-- return(selopen); //-- //--- } //-end OpenSell //---------//
4. ChartEvent 函数
为了支持多币种智能交易系统的有效性和效率,有必要为管理订单、更改图表或品种,创建一个或多个手工动按钮。
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- //--- handling CHARTEVENT_CLICK event ("Clicking the chart") ResetLastError(); //-- ENUM_TIMEFRAMES CCS=mc.TFt; //-- if(id==CHARTEVENT_OBJECT_CLICK) { int lensymbol=StringLen(Symbol()); int lensparam=StringLen(sparam); //-- //--- if "Set SL All Orders" button is click if(sparam=="Set SL/TP All Orders") { mc.SetSLTPOrders(); Alert("-- "+mc.expname+" -- ",Symbol()," -- Set SL/TP All Orders"); //--- unpress the button ObjectSetInteger(0,"Set SL/TP All Orders",OBJPROP_STATE,false); ObjectSetInteger(0,"Set SL/TP All Orders",OBJPROP_ZORDER,0); CreateManualPanel(); } //--- if "Close All Order" button is click if(sparam=="Close All Order") { mc.CloseAllOrders(); Alert("-- "+mc.expname+" -- ",Symbol()," -- Close All Orders"); //--- unpress the button ObjectSetInteger(0,"Close All Order",OBJPROP_STATE,false); ObjectSetInteger(0,"Close All Order",OBJPROP_ZORDER,0); CreateManualPanel(); } //--- if "Close All Profit" button is click if(sparam=="Close All Profit") { mc.ManualCloseAllProfit(); Alert("-- "+mc.expname+" -- ",Symbol()," -- Close All Profit"); //--- unpress the button ObjectSetInteger(0,"Close All Profit",OBJPROP_STATE,false); ObjectSetInteger(0,"Close All Profit",OBJPROP_ZORDER,0); CreateManualPanel(); } //--- if "X" button is click if(sparam=="X") { ObjectsDeleteAll(0,0,OBJ_BUTTON); ObjectsDeleteAll(0,0,OBJ_LABEL); ObjectsDeleteAll(0,0,OBJ_RECTANGLE_LABEL); //--- unpress the button ObjectSetInteger(0,"X",OBJPROP_STATE,false); ObjectSetInteger(0,"X",OBJPROP_ZORDER,0); //-- DeleteButtonX(); mc.PanelExtra=false; DisplayManualButton(); } //--- if "M" button is click if(sparam=="M") { //--- unpress the button ObjectSetInteger(0,"M",OBJPROP_STATE,false); ObjectSetInteger(0,"M",OBJPROP_ZORDER,0); mc.PanelExtra=true; CreateManualPanel(); } //--- if "C" button is click if(sparam=="C") { //--- unpress the button ObjectSetInteger(0,"C",OBJPROP_STATE,false); ObjectSetInteger(0,"C",OBJPROP_ZORDER,0); mc.PanelExtra=true; CreateSymbolPanel(); } //--- if "R" button is click if(sparam=="R") { Alert("-- "+mc.expname+" -- ",Symbol()," -- Expert Advisor will be Remove from the chart."); ExpertRemove(); //--- unpress the button ObjectSetInteger(0,"R",OBJPROP_STATE,false); ObjectSetInteger(0,"R",OBJPROP_ZORDER,0); if(!ChartSetSymbolPeriod(0,Symbol(),Period())) ChartSetSymbolPeriod(0,Symbol(),Period()); DeletePanelButton(); ChartRedraw(0); } //--- if Symbol button is click if(lensparam==lensymbol) { int sx=mc.ValidatePairs(sparam); ChangeChartSymbol(mc.AS30[sx],CCS); mc.PanelExtra=false; } //-- } //-- return; //--- } //-end OnChartEvent() //---------//
在输入属性的“其它 EA 参数”组中,交易者可以选择是在图表上显示交易信息(是)或(否)。
如果选择该选项(是),则交易信息将通过调用 TradeInfo() 函数显示在加载 EA 的图表上。
作为 TradeInfo() 函数的一部分,我们还添加了一个函数来根据交易时区条件描述时间。
string MCEA::PosTimeZone(void) { //--- string tzpos=""; //-- if(ReqTime(zntm,day)>ThisTime(day)) { tzpos=tz_opn+ " Next day to " +tz_cls + " Next day"; } else if(TimeCurrent()<znop) { if(ThisTime(day)==ReqTime(znop,day) && ThisTime(day)==ReqTime(zncl,day)) tzpos=tz_opn+" to " +tz_cls+ " Today"; //else if(ThisTime(day)==ReqTime(znop,day) && ThisTime(day)<ReqTime(zncl,day)) tzpos=tz_opn+ " Today to " +tz_cls+ " Next day"; } else if(TimeCurrent()>=znop && TimeCurrent()<zncl) { if(ThisTime(day)<ReqTime(zncl,day)) tzpos=tz_opn+ " Today to " +tz_cls+ " Next day"; else if(ThisTime(day)==ReqTime(zncl,day)) tzpos=tz_opn+" to " +tz_cls+ " Today"; } else if(ThisTime(day)==ReqTime(znop,day) && ThisTime(day)<ReqTime(zncl,day)) { tzpos=tz_opn+" Today to " +tz_cls+ " Next day"; } //-- return(tzpos); //---- } //-end PosTimeZone() //---------//
void MCEA::TradeInfo(void) // function: write comments on the chart { //---- Pips(Symbol()); double spread=SymbolInfoInteger(Symbol(),SYMBOL_SPREAD)/xpip; rem=zntm-TimeCurrent(); string postime=PosTimeZone(); string eawait=" - Waiting for active time..!"; //-- string comm=""; TodayOrders(); //-- comm="\n :: Server Date Time : "+string(ThisTime(year))+"."+string(ThisTime(mon))+"."+string(ThisTime(day))+ " "+TimeToString(TimeCurrent(),TIME_SECONDS)+ "\n ------------------------------------------------------------"+ "\n :: Broker : "+ TerminalInfoString(TERMINAL_COMPANY)+ "\n :: Expert Name : "+ expname+ "\n :: Acc. Name : "+ mc_account.Name()+ "\n :: Acc. Number : "+ (string)mc_account.Login()+ "\n :: Acc. TradeMode : "+ AccountMode()+ "\n :: Acc. Leverage : 1 : "+ (string)mc_account.Leverage()+ "\n :: Acc. Equity : "+ DoubleToString(mc_account.Equity(),2)+ "\n :: Margin Mode : "+ (string)mc_account.MarginModeDescription()+ "\n :: Magic Number : "+ string(magicEA)+ "\n :: Trade on TF : "+ EnumToString(TFt)+ "\n :: Today Trading : "+ TradingDay()+" : "+hariini+ "\n :: Trading Session : "+ tz_ses+ "\n :: Trading Time : "+ postime; if(TimeCurrent()<zntm) { comm=comm+ "\n :: Time Remaining : "+(string)ReqTime(rem,hour)+":"+(string)ReqTime(rem,min)+":"+(string)ReqTime(rem,sec) + eawait; } comm=comm+ "\n ------------------------------------------------------------"+ "\n :: Trading Pairs : "+pairs+ "\n :: BUY Market : "+string(oBm)+ "\n :: SELL Market : "+string(oSm)+ "\n :: Total Order : "+string(oBm+oSm)+ "\n :: Order Profit : "+DoubleToString(floatprofit,2)+ "\n :: Fixed Profit : "+DoubleToString(fixclprofit,2)+ "\n :: Float Money : "+DoubleToString(floatprofit,2)+ "\n :: Nett Profit : "+DoubleToString(floatprofit+fixclprofit,2); //-- Comment(comm); ChartRedraw(0); return; //---- } //-end TradeInfo() //---------//
多币种智能交易系统 BBOnKeltnerChannel_MCEA 的界面如下图所示。
如您所见 BBOnKeltnerChannel_MCEA 名称之下有按钮 “M”、“C” 和 “R”
点击 “M” 键后,会显示手工点击按钮面板,如下图所示。
当显示手工点击按钮面板时,交易者可以手工管理订单:
1. 为所有订单设置止损/止盈,如上所述,如果交易者输入参数“使用订单止损(否)和/或使用订单止盈(否)”,但随后交易者打算为所有订单加上止损或止盈,则单击“为所有订单设置止损/止盈”按钮,修改所有订单,并应用止损和/或止盈。
void MCEA::SetSLTPOrders(void) { //--- ResetLastError(); MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- double modbuysl=0; double modselsl=0; double modbuytp=0; double modseltp=0; string position_symbol; int totalorder=PositionsTotal(); //-- for(int i=totalorder-1; i>=0; i--) { string symbol=PositionGetSymbol(i); position_symbol=symbol; if(mc_position.Magic()==magicEA) { ENUM_POSITION_TYPE opstype = mc_position.PositionType(); if(opstype==POSITION_TYPE_BUY) { Pips(symbol); RefreshTick(symbol); double price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_take = mc_position.TakeProfit(); modbuysl=SetOrderSL(symbol,opstype,pos_open); if(price<modbuysl) modbuysl=mc_symbol.NormalizePrice(price-slip*pip); modbuytp=SetOrderTP(symbol,opstype,pos_open); if(price>modbuytp) modbuytp=mc_symbol.NormalizePrice(price+slip*pip); //-- if(pos_stop==0.0 || pos_take==0.0) { if(!mc_trade.PositionModify(position_symbol,modbuysl,modbuytp)) { mc_trade.CheckResult(check); Do_Alerts(symbol,"Set SL and TP for "+EnumToString(opstype)+" on "+symbol+" FAILED!!. Return code= "+ (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]"); } } } if(opstype==POSITION_TYPE_SELL) { Pips(symbol); RefreshTick(symbol); double price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_take = mc_position.TakeProfit(); modselsl=SetOrderSL(symbol,opstype,pos_open); if(price>modselsl) modselsl=mc_symbol.NormalizePrice(price+slip*pip); modseltp=SetOrderTP(symbol,opstype,pos_open); if(price<modseltp) modseltp=mc_symbol.NormalizePrice(price-slip*pip); //-- if(pos_stop==0.0 || pos_take==0.0) { if(!mc_trade.PositionModify(position_symbol,modselsl,modseltp)) { mc_trade.CheckResult(check); Do_Alerts(symbol,"Set SL and TP for "+EnumToString(opstype)+" on "+symbol+" FAILED!!. Return code= "+ (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]"); } } } } } //-- return; //--- } //-end SetSLTPOrders //---------//
2. 所有订单平仓,如果交易者想要所有订单平仓,那么只需单击“所有订单平仓”按钮,所有持仓都将被平仓。
void MCEA::CloseAllOrders(void) //-- function: close all order { //---- ResetLastError(); //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- int total=PositionsTotal(); // number of open positions //--- iterate over all open positions for(int i=total-1; i>=0; i--) { //--- if the MagicNumber matches if(mc_position.Magic()==magicEA) { //-- string position_Symbol = PositionGetSymbol(i); // symbol of the position ulong position_ticket = PositionGetTicket(i); // ticket of the the opposite position ENUM_POSITION_TYPE type = mc_position.PositionType(); RefreshTick(position_Symbol); bool closepos = mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type)); //--- } } //--- return; //---- } //-end CloseAllOrders() //---------//
3. 所有盈利订单平仓,如果交易者想要所有已盈利订单了结,只需单击“所有盈利订单平仓”按钮,即可把所有已盈利订单了结。
bool MCEA::ManualCloseAllProfit(void) { //---- ResetLastError(); //-- bool orclose=false; //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- int ttlorder=PositionsTotal(); // number of open positions //-- for(int x=0; x<arrsymbx; x++) { string symbol=DIRI[x]; orclose=false; //-- for(int i=ttlorder-1; i>=0; i--) { string position_Symbol = PositionGetSymbol(i); ENUM_POSITION_TYPE type = mc_position.PositionType(); if((position_Symbol==symbol) && (mc_position.Magic()==magicEA)) { double pos_profit = mc_position.Profit(); double pos_swap = mc_position.Swap(); double pos_comm = mc_position.Commission(); double cur_profit = NormalizeDouble(pos_profit+pos_swap+pos_comm,2); ulong position_ticket = PositionGetTicket(i); //--- if(type==POSITION_TYPE_BUY && cur_profit>0.02) { RefreshTick(position_Symbol); orclose = mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type)); } if(type==POSITION_TYPE_SELL && cur_profit>0.02) { RefreshTick(position_Symbol); orclose = mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type)); } } } } //-- return(orclose); //---- } //-end ManualCloseAllProfit() //---------//
当点击 “C” 按钮时,会显示一个包含 30 个品种名称或交易对的面板按钮,交易者可以点击其中一个品种名称、或交易品种名称。单击其中一个货币对名称、或交易品种,会立即将图表品种替换为单击其名称的品种。
在这种情况下,当单击其中一个品种名称时,OnChartEvent() 函数将调用 ChangeChartSymbol() 函数。
//--- if Symbol button is click if(lensparam==lensymbol) { int sx=mc.ValidatePairs(sparam); ChangeChartSymbol(mc.AS30[sx],CCS); mc.PanelExtra=false; } //--
void ChangeChartSymbol(string c_symbol,ENUM_TIMEFRAMES cstf) { //--- //--- unpress the button ObjectSetInteger(0,c_symbol,OBJPROP_STATE,false); ObjectSetInteger(0,c_symbol,OBJPROP_ZORDER,0); ObjectsDeleteAll(0,0,OBJ_BUTTON); ObjectsDeleteAll(0,0,OBJ_LABEL); ObjectsDeleteAll(0,0,OBJ_RECTANGLE_LABEL); //-- ChartSetSymbolPeriod(0,c_symbol,cstf); //-- ChartRedraw(0); //-- return; //--- } //-end ChangeChartSymbol() //---------//
单击 “R” 按钮将从图表中删除多币种智能交易系统 BBOnKeltnerChannel_MCEA,如此交易者不必手工切断智能系统。
if(id==CHARTEVENT_OBJECT_CLICK) { //-- //--- if "R" button is click if(sparam=="R") { Alert("-- "+mc.expname+" -- ",Symbol()," -- Expert Advisor will be Remove from the chart."); ExpertRemove(); //--- unpress the button ObjectSetInteger(0,"R",OBJPROP_STATE,false); ObjectSetInteger(0,"R",OBJPROP_ZORDER,0); if(!ChartSetSymbolPeriod(0,Symbol(),Period())) ChartSetSymbolPeriod(0,Symbol(),Period()); DeletePanelButton(); ChartRedraw(0); } //--- }
策略测试器
如您所知,MetaTrader 5终端策略测试器支持并允许我们测试策略,交易多个品种、或测试所有可用品种和所有可用时间帧的自动交易。因此,在 MetaTrader 5 策略测试器平台上,我们将测试 BBOnKeltnerChannel_MCEA 作为多币种智能交易系统。
在本次测试中,我们将 BBOnKeltnerChannel_MCEA 放置在 XAGUSD 货币对的 H1 时间帧内,自定义时间段为 2023.09.04 至 2023.12.02。
该测试是使用两个不同的输入属性进行的,特别是在交易和订单管理参数组当中。
1. 默认输入属性。
2. 自定义输入属性。
结束语
利用 MQL5 创建多币种智能交易系统,并采用基于凯尔特纳通道指标数据的布林带®指标的信号,得到的结论如下:
- 事实证明,以 MQL5 创建多币种智能交易系统非常简单,与创建单币种智能交易系统没有太大区别。
- 与旧平台(MetaTrader 4)相比,利用 MQL5 中的指标句柄获取指标值和信号更容易、更方便。
- 创建多币种智能交易系统将消除打开许多品种图表进行交易的需要,从而提高交易者的效率和有效性。
- 与使用单一货币的智能交易系统相比,应用正确的交易策略将增加获利的可能性。这是因为一个货币对的损失将被其它货币对的盈利所弥补。
- 这个 BBOnKeltnerChannel_MCEA 多币种智能交易系统只是一个学习和思路验证的示例。其在策略测试器当中的测试结果仍然不佳。因此,实验并测试按不同时间帧、或不同指标周期计算,或许可以获得更好、更多盈利的结果。
- BBOnKeltnerChannel_MCEA 策略测试器的测试结果表明,自定义输入参数属性的结果优于默认输入参数属性。
我们希望本文和 MQL5 多币种智能交易系统计划对交易者学习和开拓思路有所帮助。
感谢您的阅读。
注意:如果您有基于内置 MQL5 标准指标信号创建简单多币种智能交易系统的思路,请在评论中提出建议。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/13861
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.


你好,罗伯托,我已将 EA 所需的两个指标加载到图表上,但 EA 仍提示无法打开这两个指标。
见附件
谢谢
版主自动翻译
从您提供的图片中可以看出,您使用的凯尔特纳通道指标文件名是错误的。
请查看指标文件名的区别。
您使用的指标与专家顾问要求的指标不同。
从您提供的图片中可以看出,您使用的 Keltner Channel 指标文件名是错误的。
请查看指标文件名的区别。
您使用的指标与专家顾问要求的指标不同。
您好,Roberto
已修改指标名称,EA 运行正常。
但在一个符号上打开了许多订单
你好,罗伯托
,已修改指标名称,EA 运行正常。
但在一个符号上打开许多订单
也许您不了解或不知道互联网上有许多 Keltner Channel 指标的版本。
您是看了我在文章中的解释,还是只是下载了 EA 程序。
在第 2 部分.信号指标》中,我已经解释过了:
"本文中用于智能交易系统的凯尔特纳通道指标,是我使用当今流行的一种方法专门创建的,即指数移动平均线(EMA)周期 20,并使用 ATR 指标周期 20 的上下限带"。
因此,不要希望重命名指标后 EA 就能正常工作,因为不同的指标会有不同的输入参数和计算方法。
如果你不使用我专门为 Bollinger Bands® On Keltner Channel EA 创建的 Keltner Channel 指标,你就不要指望能够使用 Bollinger Bands® On Keltner Channel EA。
你好,罗伯托、
你做得很好!我希望它能在我的电脑上运行。
我确实收到了以下信息:Keltner Channel.ex5 打开错误和其他错误。
因此,测试仪上没有任何结果。
感谢您的帮助。
阿兰
你好,罗伯托、
你做得很好!我希望它能在我的电脑上运行。
我确实收到了以下信息:Keltner Channel.ex5 打开错误以及其他错误。
因此,测试仪上没有任何结果。
感谢您的帮助。
阿兰
您好、
我的智能交易系统名称是 BBOnKeltnerChannel_MCEA,而不是 Multi-currency BB-Keltner(在您发送给我的电子邮件图片中,EA 的名称是 Multi-currency BB-Keltner)
,我专门为 BBOnKeltnerChannel_MCEA 创建的指标名称是 Keltner Channel.mq5(查看并下载本文附件)。
网上有许多 Keltner Channel 指标的版本。
请密切注意指标和专家中的文件名和属性输入,因为这是区分大小写的。
如果输入属性不一致,程序将无法运行。
如果您修改了 EA 程序并出现错误,请不要询问我。
。
MQL5 中的所有程序都经过负责版主的验证,因此如果程序未通过验证,则不可能由 MQL5 发布。