
如何利用 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

