如何利用 MQL5 创建简单的多币种智能交易系统(第 7 部分):依据动量振荡器指标的之字折线
概述
多货币智能系统是一款自动交易系统,可从单个交易品种图表中开立、平仓、和管理多个品种对的订单。本文专注于交易 30 对、并采用之字折线指标的智能系统,该指标利用动量振荡器过滤或过滤彼此的信号。
如前几篇文章所示,多货币交易可以借助 MQL5 交易终端和策略测试器的强立功能、和设施。为了满足交易者寻求高效和有效自动交易的需求,我们来依靠 MQL5 提供的可靠强力功能、和设施。我们的目标是创建一个简单的多币种智能系统,可运用各种思路和策略。本文将专注于使用之字折线指标,其依据动量振荡器进行过滤、或者过滤彼此的信号。
计划和功能
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 对。
为了确保本文中讨论的智能系统的顺利运行,我实现了一个自动处理品种名称带有前缀和/或后缀的函数。不过,重点要注意的是,该函数仅适工作于 MT5 中的外汇和金属品种对名称,不适用于特殊品种和指数。
1.1. 附加功能:
1.1.1. 单一货币智能系统
一些用户询问有关该多币种 EA 如何当作单一货币、或独立 EA。
为了解决这个问题,添加了一项功能,允许将该多币种 EA 当作单货币 EA。
在选择交易对模式(多种或单种)选项中,交易者可以在两种交易对条件之间进行选择:单一货币或多货币。
1. Single Pair (single-currency) — 单对(单币种)
2. Multi Pairs (multi-currency) — 多对(多币种)
设置智能系统输入参数,如下图所示
如果选择了'单对':
'SP' 或单对选项将智能系统限制为仅在其所放置的货币对上进行交易。举例,如果智能系统被放置在 EURUSD 对上,它只会在 EURUSD 对上进行交易。
如果选择了“多对”:
选项组“选择要交易的货币对”包括 10 对将要交易的货币选项。
其中一对称为“交易者愿望对”,它要求交易者在“智能系统输入”属性中手工输入要交易的货币对。不过,重点是要记住,输入的货币对名称必须已经在 30 对的列表当中。
“交易者的愿望对”选项可用于确保智能系统仅交易单一货币、或作为独立的 EA 工作。
智能系统将仅依据提供的期望货币对名称进行交易或工作。这确保了专注方式交易、或工作于单一货币对。
如果交易者仅输入 XAUUSD 对的名称,则智能系统仅在该货币对上进行交易。
智能系统将仅在 XAUUSD 对上进行交易,无论它在 30 个可用货币对中的位置如何。
必须如下图所示配置智能系统输入参数。
故此,在本文中,有两种方式来交易单一货币,或作为单独专用智能系统。
1. 坚持使用多货币对或 “MP” 选项,然后选择“交易者期望对”选项。不过,仅需输入一个货币对名称,例如 XAUUSD。
该选项将智能系统限制为仅据"交易者期望对“指定的货币对上进行交易。它不会交易任何其它货币对。2. 在选择”交易对模式“中,选择 “SP” 或单一对。
如果智能系统应用于 EURUSD 对,它将专门在 EURUSD 对上进行交易。
1.1.2. Trade on Specific Time — 在特定时间交易
在”在特定时间交易”组中,为交易者提供了想要在该时段进行交易的选项。
也许很多交易者都想按照时区进行交易,所以要交易的货币对可与对应交易时段相应,所以在这个智能系统,我们仍然使用交易时段(时区)的选项。
2. 信号指标。
2.1. 之字折线(ZigZag)指标。
之字折线指标是一种衡量价格走势的方法,且无不必要噪音。它的运算是判定价格摇摆(高点和低点)之间的距离。随后,该指标计算回调。如果回调超过某个预期额度,则认为价格走势已完成。
众所周知,之字折线是最古老的技术指标之一,并将其运作方式从股票市场搬至货币交易。它令交易者能够把市场结构可视化。
当尝试估测价格走势时,之字折线指标可帮助交易者自动过滤掉次要的价格走势。利用之字折线指标有助于更清晰地理解市场结构。
当价格走势条件受到随机价格波动的影响而波动时,之字折线指标可用来帮助识别价格趋势,和价格趋势中的变化。
技术分析师言道:
- 之字折线指标降低了随机价格波动的影响,用于识别价格趋势、及价格趋势的变化。
- 该指标降低了噪音级别,强调底层趋势的更高点和更低点。
- 之字折线指标在趋势强劲的市场中效果最佳。
之字折线指标限制。
与其它趋势跟踪指标类似,买入和卖出信号是通过查看过去的价格走势来判定的,也许无法准确预测未来的价格走势。举例,在生成之字折线指示线时,可能趋势已经走完大部分。
交易者应意识到,最近的之字折线也许不是永久的(译者按:即所谓的“未来函数”)。当价格改变方向时,指标开始绘制一条新指示线。
如果该指示线未达到指标的百分比设置,且证券价格逆转,则该指示将被删除,并替换为趋势原始方向的延长线。
之字折线参数设置:
之字折线指标有 3 个输入参数:
1. Depth - 深度默认值为 12。
Depth – 指它在图表柱线图序列中能看到多远。
为了获得定义的高点和低点,您需要确保有足够的 “深度”。
它是最小柱线数量,即区间内柱线的差价最大值或最小值唯一不二(例如:如果我们在蜡烛 x 处已有一个最大值,深度为 12,那么至少 x+12 根蜡烛以内不可绘制后续的最大值)。
2. Deviation - 默认值为 5。
Deviation - 是指将趋势线从正向变为负向所需的价格变化百分比。
3. Backstep - 默认值为 3。
Backstep - 摆动高点和低点之间的最小柱线数量
之字折线指标公式:
ZigZag (HL, %change=X, retrace=FALSE, LastExtreme=TRUE)
如果 %change>=X,绘制之字折线
其中:
HL = 价格序列或收盘价序列的(高点-低点)
%change = 最小价格变化百分比
Retrace = 它是前一次移动的回撤变化,或从波峰到波谷的绝对变化?
Last Extreme = 如果极值价格在多个周期内相同,则极值价格是第一个观察值还是最后一个观察值?
根据我的观察,对于之字折线指标,至少有 4 种信号算法可以使用。
在这个智能系统中,我创建了一个选项,以便交易者可以选择并尝试来自之字折线 指标的四种算法信号。
2.2. 动量振荡器(AO)。
动量震荡指标(AO)是交易者用来识别市场趋势和动量的动量指标。它由知名技术分析师比尔·威廉姆斯(Bill Williams)开发,因其简单性和可靠性而成为交易者的流行工具。
动量振荡器指标是一个市场动量指标,它将近期的市场走势与历史市场走势进行比较。
它在中心有一条零线,根据两条不同移动平均线的比较在其两侧绘制价格走势。
动量振荡器指标的计算方法是从金融产品中点 (H+L)/2 价格的 5-周期 SMA 中减去 34-周期简单移动平均线(SMA)。
中点价格被认为比开盘价或收盘价更准确地代表真实市场价格,因为它同时考虑了给定时期的最高价和最低价。
AO 在正值和负值之间振荡,正值表示看涨趋势,负值表示看跌趋势。
34-周期简单移动平均线(SMA)和 5-周期 SMA 的值在指标代码中永久设置,并且没有机会在指标输入参数属性中进行更改。
动量振荡器指标 = 5-周期 SMA(中位价,5-周期)– 34-周期 SMA(中位价,34-周期)
其中,中间价为:(时段周期的最高价 + 时段周期的最低价)/ 2。
对于 AO 指标,至少有 3 种信号算法可以使用。
因此,在这个智能系统中,我创建了一个选项,以便交易者可以选择并尝试来自该 AO 指标的三个算法信号。
下图所示,运用动量振荡器过滤、或彼此过滤信号的之字折线指标。
下面的信号插图是选项 2 之字折线信号,和选项 2 AO 信号
3. 交易和订单管理
该多货币智能系统提供若干种方式管理交易:
3.1. 止损单。
选项:使用订单止损(是)或(否)
- 如果选中“使用订单止损(否)”选项,则所有订单在开单时都不带止损。
如果“由反向信号平单”设置为“是”,则不带止损开单将是安全的。但是,如果“由反向信号平单”设置为“否”,则对净值存在非常高的风险。因此,我添加了一个函数来启用检查净值和余额之间的百分比。在这种情况下,如果余额的净值百分比小于(100% - 每笔交易的净值风险百分比),则智能系统将运行 CheckLoss() 函数(运行虚拟止损的函数),并在亏损大于设置的止损值情况下平单。
//-- if(use_sl==No && CheckVSLTP==Yes) { if(!mc.CheckEquityBalance()) if(mc.CloseAllLoss()) mc.Do_Alerts(symbol,"Close order due stop in loss to secure equity."); } //--
bool MCEA::CheckEquityBalance(void) { //--- bool isgood=false; if((mc_account.Equity()/mc_account.Balance()*100) > (100.00-Risk)) isgood=true; //-- return(isgood); //--- } //-end CheckEquityBalance() //---------//
bool MCEA::CheckLoss(const string symbol,ENUM_POSITION_TYPE intype,double slc=0.0) { //--- Pips(symbol); bool inloss=false; double lossval=slc==0.0 ? (SLval*0.5) : slc; double posloss = mc_symbol.NormalizePrice(slc*pip); int ttlorder=PositionsTotal(); // number of open positions //-- for(int x=0; x<arrsymbx; x++) { string symbol=DIRI[x]; //-- 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 price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double posloss = mc_symbol.NormalizePrice(lossval*pip); double pricegab = mc_symbol.NormalizePrice(fabs(price-pos_open)); //--- if(type==intype && pricegab>posloss) inloss=true; } } } //-- return(inloss); //---- } //-end CheckLoss() //---------//
bool MCEA::CloseAllLoss(void) { //---- ResetLastError(); //-- bool orclose=false; string isloss="due stop in loss."; //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- int ttlorder=PositionsTotal(); // number of open positions //-- for(int x=0; x<arrsymbx; x++) { string symbol=DIRI[x]; Pips(symbol); double posloss=mc_symbol.NormalizePrice(SLval*pip); 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 price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double posloss = mc_symbol.NormalizePrice(SLval*pip); double pricegab = mc_symbol.NormalizePrice(fabs(price-pos_open)); ulong position_ticket = PositionGetTicket(i); //--- if(type==POSITION_TYPE_BUY && pricegab>posloss) { RefreshTick(position_Symbol); orclose = mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close Buy %s %s %s",symbol,EnumToString(POSITION_TYPE_BUY),isloss); } if(type==POSITION_TYPE_SELL && pricegab>posloss) { RefreshTick(position_Symbol); orclose = mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close Sell %s %s %s",symbol,EnumToString(POSITION_TYPE_BUY),isloss); } } } } //-- return(orclose); //---- } //-end CloseAllLoss() //---------//
- 如果选项“使用订单止损(是)”:再次给出选项:“使用自动计算止损(是)或(否)”
- 如果选项是“自动计算止损(是)”,则将由智能系统自动执行止损计算。
- 如果选项“自动计算止损(否)”,则交易者必须以点数为单位输入止损值。
- 如果选项“使用订单止损(否)”:则智能系统将检查每笔开单,信号条件是否仍然良好,订单是否可维持盈利;或信号疲软,需要平单以保障盈利;或信号条件已逆转方向,订单必须以亏损条件了结。
注意:特别是对于“因信号疲软并保障盈利而了结交易”,会给出一个选项,是否激活它。
在 ExpertActionTrade() 函数的一部分:
//-- if(SaveOnRev==Yes) //-- Close Trade and Save profit due to weak signal (Yes) { mc.CheckOpenPMx(symbol); if(mc.profitb[x]>mc.minprofit && 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]>mc.minprofit && 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."); } } //--
因信号疲软并保障盈利而平仓的代码如下:
int MCEA::GetCloseInWeakSignal(const string symbol,int exis) // Signal Indicator Position Close in profit { //--- int ret=0; int rise=1, down=-1; //-- int AOdir=AOColorSignal(symbol); int ZZDir=ZigZagSignal(symbol); //-- if(exis==down && (AOdir==rise||ZZDir==rise)) ret=rise; if(exis==rise && (AOdir==down||ZZDir==down)) ret=down; //-- return(ret); //--- } //-end GetCloseInWeakSignal() //---------//
- 如果它没有被激活(否),即使信号已经疲软,订单仍将持有、或不会为了保障盈利而了结。
- 如果激活(Yes),则之字折线指标和 AO 指标的条件为:
对于多头订单平仓:
当 AO 指标的颜色从绿色变为红色(默认颜色),或当之字折线指标达到高点极值,且价格改变方向时,多头订单将被平仓。对于空头订单平仓:
当 AO 指标的颜色从红色变为绿色(默认颜色),或当之字折线指标达到低点极值位置,且价格改变方向时,空头订单将被平仓。设置止损单的代码如下:
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() //---------//
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是(否)”,则 EA 不会执行尾随止损和尾随止盈。
- 如果选项“使用尾随止损(是)”:交易者可以在 3 个选项之间进行选择:
1. 按价格尾随
尾随止损将由 EA 按价格走势和输入属性中的值来执行。
2. 按指标尾随
尾随止损将由 EA 按 VIDYA 指标执行。
根据我的研究和实验,与抛物线 SAR、或移动平均线指标的几种变体相比,VIDYA 指标略好,非常适合尾随止损。
与抛物线 SAR 指标相比,VIDYA 指标更接近价格走势,而与 AMA、DEMA 和 MA 指标相比,VIDYA 指标离价格走势更远。
故此,在本文中,我决定基于 VIDYA 指标打造尾随止损功能。
3. 按前一根柱线的最高价或最低价尾随止损
对于买入订单,尾随止损的位置将会放置在前一根柱线的最低价(LOW[1])。
对于卖出订单,尾随止损位置将放置在前一根柱线的最高价(HIGH[1])。
尾随止损价格函数:
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 byprice: { 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 byindi: { double VIDyAv[]; ArrayResize(VIDyAv,br,br); ArraySetAsSeries(VIDyAv,true); CopyBuffer(hVIDyAv[x],0,0,br,VIDyAv); RefreshPrice(xsymb,TFt,br); //-- if(ptype==POSITION_TYPE_BUY && (VIDyAv[0]<mc_symbol.NormalizePrice(mc_symbol.Bid()-TSval*pip))) pval=VIDyAv[0]; if(ptype==POSITION_TYPE_SELL && (VIDyAv[0]>mc_symbol.NormalizePrice(mc_symbol.Ask()+TSval*pip))) pval=VIDyAv[0]; break; } case byHiLo: { UpdatePrice(xsymb,TFt,2); //-- if(ptype==POSITION_TYPE_BUY && (HIGH[0]>HIGH[1])) pval=LOW[1]; if(ptype==POSITION_TYPE_SELL && (LOW[0]<LOW[1])) pval=HIGH[1]; break; } } //-- return(pval); //--- } //-end TSPrice() //---------//
bool MCEA::ModifyOrdersSL(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_tp = mc_position.TakeProfit(); 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-1.0)*pip)); double modbuysl=vtrsb; bool modbuy = (price>modminsl && modbuysl>modstart && (pos_stop==0.0||modbuysl>pos_stop)); //-- if(modbuy && netp>minprofit) { modist=mc_trade.PositionModify(symbol,modbuysl,pos_tp); } } 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_tp = mc_position.TakeProfit(); 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+1.0)*pip)); double modselsl=vtrss; bool modsel = (price<modminsl && modselsl<modstart && (pos_stop==0.0||modselsl<pos_stop)); //-- if(modsel && netp>minprofit) { modist=mc_trade.PositionModify(symbol,modselsl,pos_tp); } } } } //-- return(modist); //--- } //-end ModifyOrdersSL() //---------//
3.4. 尾随止盈
选项:“使用尾随止盈(是)或(否)”
- 如果选项“使用尾随止盈是(否)”,则 EA 不会执行尾随止盈。
- 如果选项“使用尾随止盈为(是)”,则以点数为单位输入尾随止盈值(默认值 25 点),智能交易系统将根据变量值 TPmin(最小尾随止盈值)执行尾随止盈。
bool MCEA::ModifyOrdersTP(const string symbx) { //--- ResetLastError(); MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- 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 pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_tp = mc_position.TakeProfit(); double modbuytp = pos_tp==0.0 ? mc_symbol.NormalizePrice(pos_open+TPmin*pip) : pos_tp; double modpostp = mc_symbol.NormalizePrice(price+TPmin*pip); bool modtpb = (price>pos_open && modbuytp-price<TPmin*pip && pos_tp<modpostp); //-- if(modtpb) { modist=mc_trade.PositionModify(symbol,pos_stop,modpostp); } } if(opstype==POSITION_TYPE_SELL) { RefreshTick(symbol); double price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_tp = mc_position.TakeProfit(); double modseltp = pos_tp==0.0 ? mc_symbol.NormalizePrice(pos_open-TPmin*pip) : pos_tp; double modpostp = mc_symbol.NormalizePrice(price-TPmin*pip); bool modtps = (price<pos_open && price-modseltp<TPmin*pip && pos_tp>modpostp); //-- if(modtps) { modist=mc_trade.PositionModify(symbol,pos_stop,modpostp); } } } } //-- return(modist); //--- } //-end ModifyOrdersTP() //---------//
3.5. 由反向信号平仓
选项:“由反向信号平仓(是)或(否)”
- 如果“由反向信号平仓(是)”:
故此,如果之前已开立了卖出订单,然后指标信号反转,那么卖出订单将被平仓,然后智能系统将开立一笔买入订单。反之亦然。
在 ExpertActionTrade() 函数的一部分:
if(Close_by_Opps==Yes && mc.xos[x]>0) mc.CloseSellPositions(symbol);bool MCEA::CloseSellPositions(const string symbol) { //--- ResetLastError(); bool sellclose=false; int total=PositionsTotal(); // number of open positions ENUM_POSITION_TYPE closetype = POSITION_TYPE_SELL; ENUM_ORDER_TYPE type_req = ORDER_TYPE_BUY; //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- int x=PairsIdxArray(symbol); //--- iterate over all open positions for(int i=total-1; i>=0; i--) { if(mc_position.SelectByIndex(i)) { //--- Parameters of the order string position_Symbol = PositionGetSymbol(i); ulong position_ticket = PositionGetTicket(i); ENUM_POSITION_TYPE type = mc_position.PositionType(); //--- if the MagicNumber matches if((position_Symbol==symbol) && (mc_position.Magic()==magicEA)) { //-- if(type==closetype) { RefreshTick(position_Symbol); sellclose=mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close Sell #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type)); } } } } //--- return(sellclose); //---- } //-end CloseSellPositions() //---------//
在 ExpertActionTrade() 函数的一部分:
if(Close_by_Opps==Yes && mc.xob[x]>0) mc.CloseBuyPositions(symbol);
bool MCEA::CloseBuyPositions(const string symbol) { //--- //-- ResetLastError(); bool buyclose=false; int total=PositionsTotal(); // number of open positions ENUM_POSITION_TYPE closetype = POSITION_TYPE_BUY; ENUM_ORDER_TYPE type_req = ORDER_TYPE_SELL; //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- int x=PairsIdxArray(symbol); //--- iterate over all open positions for(int i=total-1; i>=0; i--) { if(mc_position.SelectByIndex(i)) { //--- Parameters of the order string position_Symbol = PositionGetSymbol(i); ulong position_ticket = PositionGetTicket(i); ENUM_POSITION_TYPE type = mc_position.PositionType(); //--- if the MagicNumber matches if((position_Symbol==symbol) && (mc_position.Magic()==magicEA)) { //-- if(type==closetype) { RefreshTick(position_Symbol); buyclose=mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close Buy #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type)); } } } } //--- return(buyclose); //---- } //-end CloseBuyPositions() //---------//
如果”由反向信号平仓(否)“将出现问题:
1. 订单将是每对已交易总数的 2 倍。
2. 在一对中,会有处于亏损状态的订单,也有处于盈利状态的订单。
3. 由于亏损和盈利条件不平衡,净值将会消减。
为了克服这个问题,我创建了几个函数来检测亏损和盈利订单条件。
在 ExpertActionTrade() 函数的一部分:
//-- mc.CheckOpenPMx(symbol); if(Close_by_Opps==No && (mc.xob[x]+mc.xos[x]>1)) { if(mc.CheckProfitLoss(symbol)) mc.Do_Alerts(symbol,"Close order due stop in loss."); } //--
bool MCEA::CheckProfitLoss(const string symbol) { //---- ResetLastError(); //-- bool closeinloss=false; string isloss="due stop in loss."; //-- int xx=PairsIdxArray(symbol); //-- bool BuyProfitSellLoss=(xob[xx]>0 && CheckProfit(symbol,POSITION_TYPE_BUY)) && (xos[xx]>0 && CheckLoss(symbol,POSITION_TYPE_SELL,0.0)); bool SellProfitBuyLoss=(xos[xx]>0 && CheckProfit(symbol,POSITION_TYPE_SELL)) && (xob[xx]>0 && CheckLoss(symbol,POSITION_TYPE_BUY,0.0)); //-- if(BuyProfitSellLoss && !SellProfitBuyLoss) { if(CloseSellPositions(symbol)) { PrintFormat("Close Sell %s %s %s",symbol,EnumToString(POSITION_TYPE_BUY),isloss); closeinloss=true; } } if(SellProfitBuyLoss && !BuyProfitSellLoss) { if(CloseBuyPositions(symbol)) { PrintFormat("Close Buy %s %s %s",symbol,EnumToString(POSITION_TYPE_SELL),isloss); closeinloss=true; } } //-- return(closeinloss); //---- } //-end CheckProfitLoss() //---------//
CheckProfitLoss() 函数将调用另外 2 个函数,这些函数将按条件比较一对的订单:买入盈利和卖出亏损,或买入亏损和卖出盈利。
- 如果买入盈利且卖出亏损,则卖出订单将被平仓。
- 如果卖出亏损且卖出盈利,则买入订单将被平仓。
bool MCEA::CheckProfit(const string symbol,ENUM_POSITION_TYPE intype) { //--- Pips(symbol); double posprofit=mc_symbol.NormalizePrice((TPval*0.5)*pip); bool inprofit=false; //-- int ttlorder=PositionsTotal(); // number of open positions //-- for(int x=0; x<arrsymbx; x++) { string symbol=DIRI[x]; //-- 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 price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double posprofit = mc_symbol.NormalizePrice((TPval*0.5)*pip); double pricegab = mc_symbol.NormalizePrice(fabs(price-pos_open)); //--- if(type==intype && posprofit<pricegab) inprofit=true; } } } //-- return(inprofit); //---- } //-end CheckProfit() //---------//
bool MCEA::CheckLoss(const string symbol,ENUM_POSITION_TYPE intype,double slc=0.0) { //--- Pips(symbol); bool inloss=false; double lossval=slc==0.0 ? (SLval*0.5) : slc; double posloss = mc_symbol.NormalizePrice(slc*pip); int ttlorder=PositionsTotal(); // number of open positions //-- for(int x=0; x<arrsymbx; x++) { string symbol=DIRI[x]; //-- 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 price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double posloss = mc_symbol.NormalizePrice(lossval*pip); double pricegab = mc_symbol.NormalizePrice(fabs(price-pos_open)); //--- if(type==intype && pricegab>posloss) inloss=true; } } } //-- return(inloss); //---- } //-end CheckLoss() //---------//
4. 手工订单管理。
在这个多币种智能系统中,添加了几个手工点击按钮,为交易者提供高效和有效手段,监控智能系统的运作。
4.1. 为所有订单设置止损/止盈:
如果交易者输入了参数集合“使用订单止损(否)和/或使用订单止盈(否)”,但随后交易者想在所有订单上使用止损或止盈,那么只需单击“所有订单设置止损/止盈”按钮,所有订单将被修改,并应用止损和/或止盈。4.2. 所有订单平仓:
如果交易者希望所有订单平仓,只需单击“所有订单平仓”按钮即可把所有持单了结。
4.3. Close All Orders Profit — 所有盈利单平仓
如果交易者想要把所有已经盈利的订单了结,只需单击 “所有盈利订单平仓” 按钮即可把所有已经盈利的持单了结。
5. 管理订单和图表品种。
对于仅从一个品种图表交易 30 对的多货币智能系统,若是为所有品种提供一个按钮面板,如此交易者只需单击一下即可更改图表时间帧或品种,并可在智能系统开立订单或订单平仓时查看指标信号的准确性。
在 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 Autralia 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 }; //--
选择 10 个要交易对选项的枚举
//-- 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 枚举用于智能系统属性中的(是)或(否)选项。
//-- enum YN { No, Yes }; //--
资金管理手数的枚举
//-- enum mmt { FixedLot, // Fixed Lot Size DynamLot // Dynamic Lot Size }; //--
选择计算信号指标时间帧的枚举
//-- enum TFUSE { TFM5, // 00_PERIOD_M5 TFM15, // 01_PERIOD_M15 TFM30, // 02_PERIOD_M30 TFH1, // 03_PERIOD_H1 TFH2, // 04_PERIOD_H2 TFH3, // 05_PERIOD_H3 TFH4, // 06_PERIOD_H4 TFH6, // 07_PERIOD_H6 TFH8, // 08_PERIOD_H8 TFH12, // 09_PERIOD_H12 TFD1 // 10_PERIOD_D1 }; //--
在 TFUSE 枚举中,我把计算智能交易时间帧的限制在仅从 TF-M5 到 TF-D1。
在尾随止损计算中选择要用的类型枚举
//-- enum TrType { byprice, // Trailing Stop by Price byindi, // Trailing Stop by Indicator byHiLo // Trailing Stop in HIGH or LOW bar }; //--
枚举是为选择智能系统的交易类型,单一货币或多货币。
//-- enum MS { SP, // Single Pair MP // Multi Pairs }; //--
从之字折线指标中选择信号算法的枚举
//-- enum SignalZZ { SZZ1, // ZigZagSignal 1 SZZ2, // ZigZagSignal 2 SZZ3, // ZigZagSignal 3 SZZ4 // ZigZagSignal 4 }; //--
从 AO 指标中选择信号算法的枚举
//-- enum SignalAO { SAO1, // AOSignal 1 SAO2, // AOSignal 2 SAO3 // AOSignal 3 }; //--
智能系统输入属性
//--- input group "=== Global Strategy EA Parameter ==="; // Global Strategy EA Parameter input TFUSE tfinuse = TFH4; // Select Expert TimeFrame, default PERIOD_H4 //--- input group "=== ZigZag Indicator Input Properties ==="; // ZigZag Indicator Input Properties input int zzDepth = 12; // Input ZigZag Depth, default 12 input int zzDevia = 5; // Input ZigZag Deviation, default 5 input int zzBackS = 3; // Input ZigZag Back Step, default 3 input SignalZZ sigzz = SZZ2; // Select ZigZag Signal to Use input SignalAO sigao = SAO2; // Select AO Signal to Use //--- input group "=== Selected Pairs to Trade ==="; // Selected Pairs to trading input MS trademode = MP; // Select Trading Pairs Mode (Multi or Single) 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 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.0; // 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 = 60.0; // If Not Use Automatic TP - Input TP value in Pips input YN TrailingSL = Yes; // Use Trailing Stop Loss (Yes) or (No) input TrType trlby = byHiLo; // Select Trailing Stop Type input double TSval = 10.0; // If Use Trailing Stop by Price Input value in Pips input double TSmin = 5.0; // Minimum Pips to start Trailing Stop input YN TrailingTP = Yes; // Use Trailing Take Profit (Yes) or (No) input double TPmin = 25.0; // 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) input YN CheckVSLTP = Yes; // Check Virtual SL/TP & Close Loss Trade (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 = 20240218; // Expert ID (Magic Number) //---
注意:如果智能系统 ID(魔幻数字)的输入属性留空,EA 就能够管理手工开立的订单。
在全局策略 EA 参数输入属性组中,交易者被引导选择智能系统时间帧来计算指标信号,默认为 PERIOD_H4。
在智能系统的“选择交易对”属性组中,交易者需要从提供的 10 个选项中选择要交易的货币对;默认情况下,设置为所有外汇 30 对。
为了配置要交易的货币对,我们将调用 HandlingSymbolArrays() 函数。WithHandlingSymbolArrays() 函数,我们将处理所有将要交易的货币对。
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); areur=ArraySize(EURs); aretc=ArraySize(JPYs); 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<areur; 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 & Metal 30 Pairs { ArrayResize(DIRI,sall,sall); arrsymbx=sall; ArraySymbolResize(); ArrayCopy(DIRI,All30,0,0,WHOLE_ARRAY); pairs="Multi Currency "+string(sall)+" 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,areur,areur); 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) { //--- int sym_Lenpre=0; int sym_Lensuf=0; string sym_pre=""; string sym_suf=""; SymbolSelect(Symbol(),true); string insymbol=Symbol(); int inlen=StringLen(insymbol); int toseek=-1; string dep=""; string bel=""; string sym_use =""; int pairx=-1; string xcur[]={"EUR","GBP","AUD","NZD","USD","CAD","CHF"}; // 7 major currency int xcar=ArraySize(xcur); //-- for(int x=0; x<xcar; x++) { toseek=StringFind(insymbol,xcur[x],0); if(toseek>=0) { pairx=x; break; } } if(pairx>=0) { int awl=toseek-3 <0 ? 0 : toseek-3; int sd=StringFind(insymbol,"SD",0); if(toseek==0 && sd<4) { dep=StringSubstr(insymbol,toseek,3); bel=StringSubstr(insymbol,toseek+3,3); sym_use=dep+bel; } else if(toseek>0) { dep=StringSubstr(insymbol,toseek,3); bel=StringSubstr(insymbol,toseek+3,3); sym_use=dep+bel; } else { dep=StringSubstr(insymbol,awl,3); bel=StringSubstr(insymbol,awl+3,3); sym_use=dep+bel; } } //-- string sym_nmx=sym_use; int lensx=StringLen(sym_nmx); //-- if(inlen>lensx && lensx==6) { sym_Lenpre=StringFind(insymbol,sym_nmx,0); sym_Lensuf=inlen-lensx-sym_Lenpre; //-- if(sym_Lenpre>0) { sym_pre=StringSubstr(insymbol,0,sym_Lenpre); for(int i=0; i<xcar; i++) if(StringFind(sym_pre,xcur[i],0)>=0) sym_pre=""; } if(sym_Lensuf>0) { sym_suf=StringSubstr(insymbol,sym_Lenpre+lensx,sym_Lensuf); for(int i=0; i<xcar; i++) if(StringFind(sym_suf,xcur[i],0)>=0) sym_suf=""; } } //-- pre=sym_pre; suf=sym_suf; inpre=StringLen(pre); insuf=StringLen(suf); posCur1=inpre; posCur2=posCur1+3; //-- return; //--- } //-end SetSymbolNamePS() //---------//
注意:智能系统将验证每一对。如果交易者在输入货币对名称时出错(拼写错误),或者货币对验证失败,智能系统将收到警告,且智能系统将从图表中移除。
在智能系统输入属性组"按指定时间交易"中,交易者将在此处选择“按指定时间交易(是)或(否)"。
如果"按指定时间交易" 选项指定为“否,则智能系统会在 MT5 的 00:15 至 23:59 之间进行交易。
如果“是”,选择枚举选项:
- 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 计算。
2. 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 point; 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 GetIndiSignals(const string symbol); int ZigZagSignal(const string symbol); int AOSignal(const string symbol); int AOColorSignal(const string symbol); int PARSAR05(const string symbol); int PARSAR15(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: //--- //-- ZigZag_AO_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 indiname1; //-- //--- Indicators Handle int hZigZag[], hAO[]; int hVIDyAv[]; int hPar05[], hPar15[]; //--- int ALO, dgts, arrsar, arrsymbx; int sall, arusd, areur, aretc, arspc, arper; ulong slip; //-- double profitb[], profits[]; double minprofit; //-- 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, TFT15; //-- bool PanelExtra; //------------ MCEA(void); ~MCEA(void); //------------ //-- virtual void ZigZag_AO_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 CloseAllOrders(void); void CheckClose(const string symbx); void TodayOrders(void); void UpdatePrice(const string symbol,ENUM_TIMEFRAMES xtf); void UpdatePrice(const string symbol,ENUM_TIMEFRAMES xtf,int bars); void RefreshPrice(const string symbx,ENUM_TIMEFRAMES xtf,int bars); //-- bool CheckEquityBalance(void); 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 ModifyOrdersSL(const string symbx,int TS_type); bool ModifyOrdersTP(const string symbx); bool CloseAllProfit(void); bool CloseAllLoss(void); bool ManualCloseAllProfit(void); bool CheckProfitLoss(const string symbol); bool CloseBuyPositions(const string symbol); bool CloseSellPositions(const string symbol); bool CheckProfit(const string symbol,ENUM_POSITION_TYPE intype); bool CheckLoss(const string symbol,ENUM_POSITION_TYPE intype,double slc=0.0); //-- 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 //---------// MCEA mc; //---------//
多货币智能系统操作流程中第一个也是最重要的函数是从 OnInit() 调用的,其为 ZigZag_AO_MCEA_Config()。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(void) { //--- mc.ZigZag_AO_MCEA_Config(); //-- return(INIT_SUCCEEDED); //--- } //-end OnInit() //---------//
ZigZag_AO_MCEA_Config() 函数配置所有要用到的品种、所有时间帧、所有指标句柄、以及包含头文件,其内有智能系统工作流程所需的一些重要函数。
ZigZag_AO_MCEA_Config() 函数描述并实现如何处理时间帧,并为智能系统工作流程中所用的所有指标创建指标句柄。
//+------------------------------------------------------------------+ //| Expert Configuration | //+------------------------------------------------------------------+ void MCEA::ZigZag_AO_MCEA_Config(void) { //--- //-- HandlingSymbolArrays(); // With this function we will handle all pairs that will be traded //-- TFT05=PERIOD_M5; TFT15=PERIOD_M15; 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 //-- //-- Indicators handle for all symbol for(int x=0; x<arrsymbx; x++) { hZigZag[x] = iCustom(DIRI[x],TFt,indiname1,zzDepth,zzDevia,zzBackS); //-- Handle for the ZigZag indicator hAO[x] = iAO(DIRI[x],TFt); //-- Handle for the Awesome_Oscillator indicator hVIDyAv[x] = iVIDyA(DIRI[x],TFt,9,12,0,PRICE_WEIGHTED); //-- Handle for the VIDYA indicator for Trailing Stop hPar05[x] = iSAR(DIRI[x],TFT05,SARstep,SARmaxi); //-- Handle for the iSAR indicator for M5 Timeframe hPar15[x] = iSAR(DIRI[x],TFT15,SARstep,SARmaxi); //-- Handle for the iSAR indicator for M15 Timeframe //-- } //-- TesterHideIndicators(true); minprofit=NormalizeDouble(TSmin/100.0,2); //-- ALO=(int)mc_account.LimitOrders()>sall ? sall : (int)mc_account.LimitOrders(); if(Close_by_Opps==No) { if((int)mc_account.LimitOrders()>=(sall*2)) ALO=sall*2; else ALO=(int)(mc_account.LimitOrders()/2); } //-- LotPS=(double)ALO; //-- mc_trade.SetExpertMagicNumber(magicEA); mc_trade.SetDeviationInPoints(slip); mc_trade.SetMarginMode(); Set_Time_Zone(); //-- return; //--- } //-end ZigZag_AO_MCEA_Config() //---------//
3. 智能系统即刻报价函数和工作流程
在智能系统的即刻报价函数 OnTick() 中,我们将调用多币种智能系统的最重要函数之一,即 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++) { //-- switch(trademode) { case SP: { if(mc.DIRI[x]!=Symbol()) continue; symbol=Symbol(); mc.pairs=mc.pairs+" ("+symbol+")"; break; } case MP: { if(mc.DIRI[x]==Symbol()) symbol=Symbol(); else symbol=mc.DIRI[x]; break; } } //-- 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 mc.CloseAllProfit(); //-- if(mc.xos[x]>0 && use_sl==No && CheckVSLTP==Yes) { if(mc.CheckLoss(symbol,POSITION_TYPE_SELL,SLval)) if(mc.CloseSellPositions(symbol)) mc.Do_Alerts(symbol,"Check Profit Trade and Close order due stop in loss."); } } } 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 mc.CloseAllProfit(); //-- if(mc.xob[x]>0 && use_sl==No && CheckVSLTP==Yes) { if(mc.CheckLoss(symbol,POSITION_TYPE_BUY,SLval)) if(mc.CloseBuyPositions(symbol)) mc.Do_Alerts(symbol,"Check Profit Trade and Close order due stop in loss."); } } } } //-- 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]>mc.minprofit && 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]>mc.minprofit && 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(TrailingSL==Yes) mc.ModifyOrdersSL(symbol,trlby); //-- Use Trailing Stop Loss (Yes) if(TrailingTP==Yes) mc.ModifyOrdersTP(symbol); //-- Use Trailing Take Profit (Yes) } //-- mc.CheckOpenPMx(symbol); if(Close_by_Opps==No && (mc.xob[x]+mc.xos[x]>1)) { if(mc.CheckProfitLoss(symbol)) mc.Do_Alerts(symbol,"Close order due stop in loss."); } //-- if(use_sl==No && CheckVSLTP==Yes) { if(!mc.CheckEquityBalance()) if(mc.CloseAllLoss()) mc.Do_Alerts(symbol,"Close order due stop in loss to secure equity."); } //-- 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() //---------//
注意:"交易日开/关"条件将显示在图表上的交易信息当中。
由 TradingDay() 函数执行
string MCEA::TradingDay(void) { //--- int trdday=ThisTime(dow); switch(trdday) { case 0: daytrade="Sunday"; break; case 1: daytrade="Monday"; break; case 2: daytrade="Tuesday"; break; case 3: daytrade="Wednesday"; break; case 4: daytrade="Thursday"; break; case 5: daytrade="Friday"; break; case 6: daytrade="Saturday"; break; } return(daytrade); //--- } //-end TradingDay() //---------//
交易者可以在智能系统的“在指定时间交易”属性组中选择按时区进行交易。
注意:如上解释,在新西兰时段交易到美国纽约时段交易的情况下,由 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() //---------//
“在指定时段交易”的工作流程如下,首先 ExpertActionTrade() 函数调用 Trade_session() 函数,然后 Trade_session() 函数调用 Set_Time_Zone() 函数,最后 Set_Time_Zone() 函数调用 Time_Zone() 函数。
void MCEA::Set_Time_Zone(void) { //--- //-- Server Time==TimeCurrent() datetime TTS=TimeTradeServer(); datetime GMT=TimeGMT(); //-- MqlDateTime svrtm,gmttm; TimeToStruct(TTS,svrtm); TimeToStruct(GMT,gmttm); int svrhr=svrtm.hour; // Server time hour int gmthr=gmttm.hour; // GMT time hour int difhr=svrhr-gmthr; // Time difference Server time to GMT time //-- int NZSGMT=12; // New Zealand Session GMT/UTC+12 int AUSGMT=10; // Australia Sydney Session GMT/UTC+10 int TOKGMT=9; // Asia Tokyo Session GMT/UTC+9 int EURGMT=0; // Europe London Session GMT/UTC 0 int USNGMT=-5; // US New York Session GMT/UTC-5 //-- int NZSStm=8; // New Zealand Session time start: 08:00 Local Time int NZSCtm=17; // New Zealand Session time close: 17:00 Local Time int AUSStm=7; // Australia Sydney Session time start: 07:00 Local Time int AUSCtm=17; // Australia Sydney Session time close: 17:00 Local Time int TOKStm=9; // Asia Tokyo Session time start: 09:00 Local Time int TOKCtm=18; // Asia Tokyo Session time close: 18:00 Local Time int EURStm=9; // Europe London Session time start: 09:00 Local Time int EURCtm=19; // Europe London Session time close: 19:00 Local Time int USNStm=8; // US New York Session time start: 08:00 Local Time int USNCtm=17; // US New York Session time close: 17:00 Local Time //-- int nzo = (NZSStm+difhr-NZSGMT)<0 ? 24+(NZSStm+difhr-NZSGMT) : (NZSStm+difhr-NZSGMT); int nzc = (NZSCtm+difhr-NZSGMT)<0 ? 24+(NZSCtm+difhr-NZSGMT) : (NZSCtm+difhr-NZSGMT); //-- int auo = (AUSStm+difhr-AUSGMT)<0 ? 24+(AUSStm+difhr-AUSGMT) : (AUSStm+difhr-AUSGMT); int auc = (AUSCtm+difhr-AUSGMT)<0 ? 24+(AUSCtm+difhr-AUSGMT) : (AUSCtm+difhr-AUSGMT); //-- int tko = (TOKStm+difhr-TOKGMT)<0 ? 24+(TOKStm+difhr-TOKGMT) : (TOKStm+difhr-TOKGMT); int tkc = (TOKCtm+difhr-TOKGMT)<0 ? 24+(TOKCtm+difhr-TOKGMT) : (TOKCtm+difhr-TOKGMT); //-- int euo = (EURStm+difhr-EURGMT)<0 ? 24+(EURStm+difhr-EURGMT) : (EURStm+difhr-EURGMT); int euc = (EURCtm+difhr-EURGMT)<0 ? 24+(EURCtm+difhr-EURGMT) : (EURCtm+difhr-EURGMT); //-- int uso = (USNStm+difhr-USNGMT)<0 ? 24+(USNStm+difhr-USNGMT) : (USNStm+difhr-USNGMT); int usc = (USNCtm+difhr-USNGMT)<0 ? 24+(USNCtm+difhr-USNGMT) : (USNCtm+difhr-USNGMT); if(usc==0||usc==24) usc=23; //-- //---Trading on Custom Session int _days00=ThisTime(day); int _days10=ThisTime(day); if(stsescuh>clsescuh) _days10=ThisTime(day)+1; tmopcu=ReqDate(_days00,stsescuh,stsescum); tmclcu=ReqDate(_days10,clsescuh,clsescum); //-- //--Trading on New Zealand Session GMT/UTC+12 int _days01=ThisTime(hour)<nzc ? ThisTime(day)-1 : ThisTime(day); int _days11=ThisTime(hour)<nzc ? ThisTime(day) : ThisTime(day)+1; tmop01=ReqDate(_days01,nzo,0); // start: 08:00 Local Time == 20:00 GMT/UTC tmcl01=ReqDate(_days11,nzc-1,59); // close: 17:00 Local Time == 05:00 GMT/UTC //-- //--Trading on Australia Sydney Session GMT/UTC+10 int _days02=ThisTime(hour)<auc ? ThisTime(day)-1 : ThisTime(day); int _days12=ThisTime(hour)<auc ? ThisTime(day) : ThisTime(day)+1; tmop02=ReqDate(_days02,auo,0); // start: 07:00 Local Time == 21:00 GMT/UTC tmcl02=ReqDate(_days12,auc-1,59); // close: 17:00 Local Time == 07:00 GMT/UTC //-- //--Trading on Asia Tokyo Session GMT/UTC+9 int _days03=ThisTime(hour)<tkc ? ThisTime(day) : ThisTime(day)+1; int _days13=ThisTime(hour)<tkc ? ThisTime(day) : ThisTime(day)+1; tmop03=ReqDate(_days03,tko,0); // start: 09:00 Local Time == 00:00 GMT/UTC tmcl03=ReqDate(_days13,tkc-1,59); // close: 18:00 Local Time == 09:00 GMT/UTC //-- //--Trading on Europe London Session GMT/UTC 00:00 int _days04=ThisTime(hour)<euc ? ThisTime(day) : ThisTime(day)+1; int _days14=ThisTime(hour)<euc ? ThisTime(day) : ThisTime(day)+1; tmop04=ReqDate(_days04,euo,0); // start: 09:00 Local Time == 09:00 GMT/UTC tmcl04=ReqDate(_days14,euc-1,59); // close: 19:00 Local Time == 19:00 GMT/UTC //-- //--Trading on US New York Session GMT/UTC-5 int _days05=ThisTime(hour)<usc ? ThisTime(day) : ThisTime(day)+1; int _days15=ThisTime(hour)<=usc ? ThisTime(day) : ThisTime(day)+1; tmop05=ReqDate(_days05,uso,0); // start: 08:00 Local Time == 13:00 GMT/UTC tmcl05=ReqDate(_days15,usc,59); // close: 17:00 Local Time == 22:00 GMT/UTC //-- //--Not Use Trading Time Zone if(trd_time_zone==No) { tmopno=ReqDate(ThisTime(day),0,15); tmclno=ReqDate(ThisTime(day),23,59); } //-- Time_Zone(); //-- return; //--- } //-end Set_Time_Zone() //---------//
void MCEA::Time_Zone(void) { //--- //-- tz_ses=""; //-- switch(session) { case Cus_Session: { SesCuOp=StringToTime(tmopcu); SesCuCl=StringToTime(tmclcu); zntm=SesCuOp; znop=SesCuOp; zncl=SesCuCl; tz_ses="Custom_Session"; tz_opn=timehr(stsescuh,stsescum); tz_cls=timehr(clsescuh,clsescum); break; } case New_Zealand: { Ses01Op=StringToTime(tmop01); Ses01Cl=StringToTime(tmcl01); zntm=Ses01Op; znop=Ses01Op; zncl=Ses01Cl; tz_ses="New_Zealand/Oceania"; tz_opn=timehr(ReqTime(Ses01Op,hour),ReqTime(Ses01Op,min)); tz_cls=timehr(ReqTime(Ses01Cl,hour),ReqTime(Ses01Cl,min)); break; } case Australia: { Ses02Op=StringToTime(tmop02); Ses02Cl=StringToTime(tmcl02); zntm=Ses02Op; znop=Ses02Op; zncl=Ses02Cl; tz_ses="Australia Sydney"; tz_opn=timehr(ReqTime(Ses02Op,hour),ReqTime(Ses02Op,min)); tz_cls=timehr(ReqTime(Ses02Cl,hour),ReqTime(Ses02Cl,min)); break; } case Asia_Tokyo: { Ses03Op=StringToTime(tmop03); Ses03Cl=StringToTime(tmcl03); zntm=Ses03Op; znop=Ses03Op; zncl=Ses03Cl; tz_ses="Asia/Tokyo"; tz_opn=timehr(ReqTime(Ses03Op,hour),ReqTime(Ses03Op,min)); tz_cls=timehr(ReqTime(Ses03Cl,hour),ReqTime(Ses03Cl,min)); break; } case Europe_London: { Ses04Op=StringToTime(tmop04); Ses04Cl=StringToTime(tmcl04); zntm=Ses04Op; znop=Ses04Op; zncl=Ses04Cl; tz_ses="Europe/London"; tz_opn=timehr(ReqTime(Ses04Op,hour),ReqTime(Ses04Op,min)); tz_cls=timehr(ReqTime(Ses04Cl,hour),ReqTime(Ses04Cl,min)); break; } case US_New_York: { Ses05Op=StringToTime(tmop05); Ses05Cl=StringToTime(tmcl05); zntm=Ses05Op; znop=Ses05Op; zncl=Ses05Cl; tz_ses="US/New_York"; tz_opn=timehr(ReqTime(Ses05Op,hour),ReqTime(Ses05Op,min)); tz_cls=timehr(ReqTime(Ses05Cl,hour),ReqTime(Ses05Cl,min)); break; } } //-- if(trd_time_zone==No) { SesNoOp=StringToTime(tmopno); SesNoCl=StringToTime(tmclno); zntm=SesNoOp; znop=SesNoOp; zncl=SesNoCl; tz_ses="Not Use Time Zone"; tz_opn=timehr(ReqTime(SesNoOp,hour),ReqTime(SesNoOp,min)); tz_cls=timehr(ReqTime(SesNoCl,hour),ReqTime(SesNoCl,min)); } //-- return; //--- } //-end Time_Zone() //---------//
4. 如何获取开仓的交易信号?
为了获得开仓信号,ExpertActionTrade() 函数调用 GetOpenPosition() 函数。
int MCEA::GetOpenPosition(const string symbol) // Signal Open Position { //--- int ret=0; int rise=1, down=-1; //-- int ZZAOSignal=GetIndiSignals(symbol); int dirmove=DirectionMove(symbol,TFt); int psar15=PARSAR15(symbol); //-- if(ZZAOSignal==rise && dirmove==rise && psar15==rise) ret=rise; if(ZZAOSignal==down && dirmove==down && psar15==down) ret=down; //-- return(ret); //--- } //-end GetOpenPosition() //---------//
而 GetOpenPosition() 函数会调用 3 个函数:
- GetIndiSignals(symbol)
- DirectionMove(symbol,TFt)
- PARSAR15(symbol)
4.1. GetIndiSignals(symbol) 函数将调用 2 个函数:
- ZigZagSignal(symbol)
- AOSignal(symbol)
4.1.1. 之字折线信号
在 ZigZagSignal() 函数中,我们使用并调用 1 个函数,即 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() 函数获取所请求品种的名称,及其指标句柄。然后调用相应的指标句柄,从该时间帧获取之字折线指标的缓冲区值。
//-- Indicators handle for all symbol for(int x=0; x<arrsymbx; x++) { hZigZag[x] = iCustom(DIRI[x],TFt,indiname1,zzDepth,zzDevia,zzBackS); //-- Handle for the ZigZag indicator hAO[x] = iAO(DIRI[x],TFt); //-- Handle for the Awesome_Oscillator indicator hVIDyAv[x] = iVIDyA(DIRI[x],TFt,9,12,0,PRICE_WEIGHTED); //-- Handle for the VIDYA indicator for Trailing Stop hPar05[x] = iSAR(DIRI[x],TFT05,SARstep,SARmaxi); //-- Handle for the iSAR indicator for M5 Timeframe hPar15[x] = iSAR(DIRI[x],TFT15,SARstep,SARmaxi); //-- Handle for the iSAR indicator for M15 Timeframe //-- } //--
如此,为了获得之字折线指标的缓冲区值,我们将从之字折线指标句柄中复制每个缓冲区。
为了将之字折线缓冲区(缓冲区 0)从之字折线指标句柄复制到目标数组:
CopyBuffer(hZigZag[x],0,0,barcalc,ZZBuffer);
除此之外,它还将调用 UpdatePrice() 函数来获取最高价和最低价,其将用于获取之字折线缓冲区的最高和最低柱线位置。
void MCEA::UpdatePrice(const string symbol,ENUM_TIMEFRAMES xtf) { //--- //-- ArrayFree(OPEN); ArrayFree(HIGH); ArrayFree(LOW); ArrayFree(CLOSE); ArrayFree(TIME); //-- ArrayResize(OPEN,arper,arper); ArrayResize(HIGH,arper,arper); ArrayResize(LOW,arper,arper); ArrayResize(CLOSE,arper,arper); ArrayResize(TIME,arper,arper); //-- ArraySetAsSeries(OPEN,true); ArraySetAsSeries(HIGH,true); ArraySetAsSeries(LOW,true); ArraySetAsSeries(CLOSE,true); ArraySetAsSeries(TIME,true); //-- ArrayInitialize(OPEN,0.0); ArrayInitialize(HIGH,0.0); ArrayInitialize(LOW,0.0); ArrayInitialize(CLOSE,0.0); ArrayInitialize(TIME,0); //-- RefreshPrice(symbol,xtf,arper); //-- int co=CopyOpen(symbol,xtf,0,arper,OPEN); int ch=CopyHigh(symbol,xtf,0,arper,HIGH); int cl=CopyLow(symbol,xtf,0,arper,LOW); int cc=CopyClose(symbol,xtf,0,arper,CLOSE); int ct=CopyTime(symbol,xtf,0,arper,TIME); //-- return; //--- } //-end UpdatePrice() //---------//
为了获得之字折线最高和最低柱线位置,我们进行迭代,并与最高价和最低价进行比较。
//-- for(int i=barcalc-1; i>=0; i--) { if(ZZBuffer[i]==HIGH[i]) ZH=i; if(ZZBuffer[i]==LOW[i]) ZL=i; } //--
在获得之字折线高点(ZH)和之字折线低点(ZL)的柱线位置后,它只取决于在属性输入中选择的信号之字折线指标选项。
完整的 ZigZagSignal() 函数如下:
int MCEA::ZigZagSignal(const string symbol) // ZigZag Signal for Open Position { //--- int ret=0; int rise=1, down=-1; int ZH=-1, ZL=-1; int barcalc=100; bool ZZrise=false; bool ZZdown=false; //-- double ZZBuffer[]; ArrayResize(ZZBuffer,barcalc,barcalc); ArraySetAsSeries(ZZBuffer,true); //-- int x=PairsIdxArray(symbol); UpdatePrice(symbol,TFt); //-- CopyBuffer(hZigZag[x],0,0,barcalc,ZZBuffer); //-- for(int i=barcalc-1; i>=0; i--) { if(ZZBuffer[i]==HIGH[i]) ZH=i; if(ZZBuffer[i]==LOW[i]) ZL=i; } //-- switch(sigzz) { case SZZ1: { ZZrise=((ZH==0 && HIGH[0]>HIGH[1])||(ZL<ZH && ZL>1)); ZZdown=((ZL==0 && LOW[0]<LOW[1])||(ZH<ZL && ZH>1)); //-- break; } case SZZ2: { ZZrise=(ZL<ZH && ZL>1); ZZdown=(ZH<ZL && ZH>1); //-- break; } case SZZ3: { ZZrise=((ZH==0 && HIGH[0]>HIGH[1])||(ZL<ZH && ZL>0)); ZZdown=((ZL==0 && LOW[0]<LOW[1])||(ZH<ZL && ZH>0)); //-- break; } case SZZ4: { ZZrise=(ZL<ZH && ZL>0); ZZdown=(ZH<ZL && ZH>0); //-- break; } }; //-- if(ZZrise) ret=rise; if(ZZdown) ret=down; //-- return(ret); //--- } //-end ZigZagSignal() //---------//
4.1.2. AO 信号
就像在 ZigZagSignal() 函数中一样,在 AOSignal() 函数中,我们也必须调用 PairsIdxArray() 函数,以便从 AO 指标获取缓冲区值。
故此,若要获取 AO 指标的缓冲区值,我们将从 AO 指标句柄中复制每个缓冲区。
为了将 AO 缓冲区(缓冲区 0)从 AO 指标句柄复制到目标数组:
CopyBuffer(hAO[x],0,0,barcalc,AOValue);
然后它只取决于在属性输入中选择的信号 AO 指标选项。
除此之外,为了完成来自 AO 指标的信号,我们还使用 AO 指标的颜色来验证来自缓冲区值的信号。
为此目的,我们创建了一个名为 AOColorSignal() 的函数来获取 AO 指标的颜色值
在该函数中,我们将从 AO 指标复制缓冲区 1(指标颜色索引缓冲区)。
CopyBuffer(hAO[x],1,0,barcalc,AOColor);
int MCEA::AOColorSignal(const string symbol) { //--- int ret=0; int rise=1, down=-1; int barcalc=9; //-- double AOColor[]; ArrayResize(AOColor,barcalc,barcalc); ArraySetAsSeries(AOColor,true); //-- int x=PairsIdxArray(symbol); UpdatePrice(symbol,TFt,barcalc); //-- CopyBuffer(hAO[x],1,0,barcalc,AOColor); //-- bool AORise=((AOColor[1]==1.0 && AOColor[0]==0.0)||(AOColor[1]==0.0 && AOColor[0]==0.0)); bool AODown=((AOColor[1]==0.0 && AOColor[0]==1.0)||(AOColor[1]==1.0 && AOColor[0]==1.0)); //-- if(AORise) ret=rise; if(AODown) ret=down; //-- return(ret); //--- } //-end AOColorSignal() //---------//
完整的 AOSignal() 函数如下:
int MCEA::AOSignal(const string symbol) { //--- int ret=0; int rise=1, down=-1; int barcalc=9; bool AORValue=false; bool AODValue=false; //-- double AOValue[]; ArrayResize(AOValue,barcalc,barcalc); ArraySetAsSeries(AOValue,true); //-- int x=PairsIdxArray(symbol); UpdatePrice(symbol,TFt,barcalc); //-- CopyBuffer(hAO[x],0,0,barcalc,AOValue); //-- switch(sigao) { case SAO1: { AORValue=(AOValue[2]<=0.0 && AOValue[1]>0.0 && AOValue[0]>AOValue[1])||(AOValue[1]>AOValue[2] && AOValue[0]>AOValue[1]); AODValue=(AOValue[2]>=0.0 && AOValue[1]<0.0 && AOValue[0]<AOValue[1])||(AOValue[1]<AOValue[2] && AOValue[0]<AOValue[1]); //-- break; } case SAO2: { AORValue=(AOValue[1]<=0.0 && AOValue[0]>0.0)||(AOValue[0]>0.0 && AOValue[0]>AOValue[1]); AODValue=(AOValue[1]>=0.0 && AOValue[0]<0.0)||(AOValue[0]<0.0 && AOValue[0]<AOValue[1]); //-- break; } case SAO3: { AORValue=(AOValue[1]<=0.0 && AOValue[0]>0.0)||(AOValue[0]>AOValue[1]); AODValue=(AOValue[1]>=0.0 && AOValue[0]<0.0)||(AOValue[0]<AOValue[1]); //-- break; } }; //-- bool AORise=(AOColorSignal(symbol)==rise); bool AODown=(AOColorSignal(symbol)==down); //-- if(AORValue && AORise) ret=rise; if(AODValue && AODown) ret=down; //-- return(ret); //--- } //-end AOSignal() //---------//
GetIndiSignals() 函数将 ZigZagSignal() 函数和 AOSignal() 函数的返回值相加。
int MCEA::GetIndiSignals(const string symbol) // Get Signal for Open Position { //--- int ret=0; int rise=1, down=-1; int sigrise=2; int sigdown=-2; //-- int ZZSignal=ZigZagSignal(symbol); int AwSignal=AOSignal(symbol); //Print(symbol+" = ZZ="+string(ZZSignal)+" AO="+string(AwSignal)+" Signal="+string(ZZSignal+AwSignal)); //-- if(ZZSignal+AwSignal==sigrise) ret=rise; if(ZZSignal+AwSignal==sigdown) ret=down; //-- return(ret); //--- } //-end GetIndiSignals() //---------//
GetIndiSignals() 函数将 ZigZagSignal() 函数和 AOSignal() 函数的返回值相加。
- 若结果为 2,则为买入信号。
- 若结果为 -2,则为卖出信号。
4.2. DirectionMove 函数。
DirectionMove() 函数对于获取当前柱线的收盘价位置很实用,无论是高于开盘价(上涨)、亦或低于开盘价(下跌)。
int MCEA::DirectionMove(const string symbol,const ENUM_TIMEFRAMES stf) // Bar Price Direction { //--- int ret=0; int rise=1, down=-1; //-- Pips(symbol); double difud=mc_symbol.NormalizePrice(1.5*pip); UpdatePrice(symbol,stf,2); //-- if(CLOSE[0]>OPEN[0]+difud) ret=rise; if(CLOSE[0]<OPEN[0]-difud) ret=down; //-- return(ret); //--- } //-end DirectionMove() //---------//
4.3. PARSAR15() 函数。
PARSAR15() 函数可将之字折线指标和 AO 指标的移动与 M15 时间帧上的抛物线 SAR 指标(PSAR/iSAR) 对齐。
int MCEA::PARSAR15(const string symbol) // formula Parabolic SAR M15 { //--- int ret=0; int rise=1, down=-1; int br=2; //-- double PSAR[]; ArrayResize(PSAR,br,br); ArraySetAsSeries(PSAR,true); int xx=PairsIdxArray(symbol); CopyBuffer(hPar15[xx],0,0,br,PSAR); //-- UpdatePrice(symbol,TFT15,br); //-- if(PSAR[0]<LOW[0]) ret=rise; if(PSAR[0]>HIGH[0]) ret=down; //-- return(ret); //--- } //-end PARSAR15() //---------//
在执行了 3 个主要信号函数和若干个支持函数之后,GetOpenPosition() 函数将提供数值:
- 值为 0,信号未知。
- 值为 1,是开立多头订单的信号。
- 值为 -1,是开立空头订单的信号。
当 GetOpenPosition() 函数返回值 1 时,智能系统调用 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,则智能系统将调用 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 //---------//
5. 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() //---------//
在输入属性的“其它智能系统参数”组中,交易者有机会选择是否在图表上显示交易信息“是”、或“否”。
如果选项选择“是”,则交易信息将通过调用 TradeInfo() 函数显示在加载智能系统的图表上。
作为 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() //---------//
然后多货币智能系统 ZigZag_AO_MCEA 界面类似于下图。
如您所见,在智能交易系统名称 ZigZag_AO_MCEA 下有按钮 “M”、“C” 和 “R”。
单击 M 按钮时,将显示一个手工单击按钮面板,如下所示。
当显示手工点击按钮面板时,交易者可以手工管理订单:
5.1. Set SL/TP All Orders — 为所有订单设置止损/止盈
为所有订单设置止损/止盈解释如上,如果交易者输入参数"使用订单止损(否)和/或使用订单止盈(否)",但随后交易者想对所有订单使用止损或止盈,那么单击“为所有订单设置止损/止盈”按钮将修改所有订单,并应用止损和/或止盈。
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 //---------//
5.2. Close All Orders — 所有订单平仓
如果交易者希望所有订单平仓,只需单击“所有订单平仓”按钮即可把所有持单了结。
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() //---------//
5.3. Close All Profits — 所有盈利单平仓
如果交易者希望所有盈利单了结,只需单击“所有订单平仓”按钮即可把所有盈利单了结。
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 个品种名称或交易对的面板按钮,交易者可以点击其中一个对名称或瓶中名称。
单击其中一个货币对名称或品种名称,立即将图表品种切换到被点击的品种。
void CreateSymbolPanel() { //--- //-- ResetLastError(); DeletePanelButton(); int sydis=83; int tsatu=int(mc.sall/2); //-- CreateButtonTemplate(0,"Template",180,367,STYLE_SOLID,5,BORDER_RAISED,clrYellow,clrBurlyWood,clrWhite,CORNER_RIGHT_UPPER,187,45,true); CreateButtonTemplate(0,"TempCCS",167,25,STYLE_SOLID,5,BORDER_RAISED,clrYellow,clrBlue,clrWhite,CORNER_RIGHT_UPPER,181,50,true); CreateButtonClick(0,"X",14,14,"Arial Black",10,BORDER_FLAT,"X",clrWhite,clrWhite,clrRed,ANCHOR_CENTER,CORNER_RIGHT_UPPER,22,48,true,"Close Symbol Panel"); //-- string chsym="Change SYMBOL"; int cspos=int(181/2)+int(StringLen(chsym)/2); CreateButtontLable(0,"CCS","Bodoni MT Black",chsym,11,clrWhite,ANCHOR_CENTER,CORNER_RIGHT_UPPER,cspos,62,true,"Change Chart Symbol"); //-- for(int i=0; i<tsatu; i++) CreateButtonClick(0,mc.AS30[i],80,17,"Bodoni MT Black",8,BORDER_RAISED,mc.AS30[i],clrYellow,clrBlue,clrWhite,ANCHOR_CENTER,CORNER_RIGHT_UPPER,180,sydis+(i*22),true,"Change to "+mc.AS30[i]); //-- for(int i=tsatu; i<mc.sall; i++) CreateButtonClick(0,mc.AS30[i],80,17,"Bodoni MT Black",8,BORDER_RAISED,mc.AS30[i],clrYellow,clrBlue,clrWhite,ANCHOR_CENTER,CORNER_RIGHT_UPPER,94,sydis+((i-tsatu)*22),true,"Change to "+mc.AS30[i]); //-- ChartRedraw(0); //-- return; //--- } //-end CreateSymbolPanel() //---------//
在这种情况下,当单击其中一个品种名称时,OnChartEvent() 函数将调用 ChangeChartSymbol() 函数。
if(id==CHARTEVENT_OBJECT_CLICK) { int lensymbol=StringLen(Symbol()); int lensparam=StringLen(sparam); //--- 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 按钮将从图表中删除多货币智能交易系统 ZigZag_AO_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 策略测试器平台上,我们将测试 ZigZag_AO_MCEA 多货币智能交易系统。
在测试中,我们将 ZigZag_AO_MCEA 放在 XAUUSD 货币对和 H4 时间帧上,自定义时间段为 2023.10.01 至 2024.02.17。
结果如下图所示。
结束语
依据来自之字折线指标的信号创建多货币智能系统,这些信号运用动量振荡器过滤,或过滤彼此的信号,从而利用 MQL5 进行外汇交易,结局如下:
- 事实证明,在 MQL5 中创建多货币智能系统非常简单,与创建单一货币智能系统没有太大区别,其中多货币智能系统也可以当作单一货币智能系统。
- 创建多币种智能交易系统将消除打开太多品种图表进行交易的需要,从而提高交易者的效率和有效性。
- 与使用单一货币的智能交易系统相比,应用正确的交易策略将增加获利的可能性。这是因为一个货币对的损失能由其它货币对的盈利所弥补。
- 这个 ZigZag_AO_MCEA 多币种智能系统只是一个学习和实现思路的示例。其在策略测试器当中的测试结果仍然不佳。因此,通过在不同的时间帧、或不同的指标周期计算上进行实验和测试,并选择不同的信号,可以获得更好的策略和更有利可图的结果。
- 在我看来,这个依据 AO 指标的之字折线策略应该通过各种不同的实验进一步研究,从时间帧开始,之字折线指标参数输入的区分值,也许您还可以为之子折线指标和 AO 指标添加其它算法信号。
- 根据我在策略测试器上的实验结果,在 H1 以下的时间帧上,大量开仓,结果并不好;与之相比,在 H1 及以上的时间帧上,少量开仓,结果略好,但仍亏损。
感谢您的阅读。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/14329