简介
当我们找到一个稳定的EA策略时,我们通常将其加载到欧元兑美元图表上进行交易,不是吗?这种策略能比其他货币赚取更多利润吗?在其他货币中,这种策略在没有几何成倍成交量的情况下能表现得更好吗?
如果我们对1小时欧元兑美元的结果不满意怎么办?换成欧元兑日元的H4?
另外,即使我们有一个64位的操作系统,我们也不用担心测试速度,我们是否忘记了交易系统输入参数的可怕组合?在优化测试时执行所有枚举组合,并且在最终测试报告中必须忽略许多结果。
我已经解决了这些“小问题”,并在本文中与您分享了这些有效的解决方案。如果还有其他更好的解决方案,请告诉我。
优化时间框架
mql5提供一组时间帧:从m1、m2、m3、m4…H1,H2,…在月球地图上。总共有21个时间段。在优化测试的过程中,我们想知道我们的策略是最有效的时间框架——短时间周期如M1和M5,中时间周期如H2和H4,或长时间周期如D1和W1。
实际上,我们不需要这么多选择。在任何情况下,如果我们发现该策略在M5时间周期内被证明是有效的,那么下一步就是在M3或M6上对其进行优化,以测试该策略的性能。
如果我们使用枚举时间范围类型输入参数:
input ENUM_TIMEFRAMES marcoTF= PERIOD_M5;
然后优化器将提供21个参数。我们真的需要这么多参数吗?
我们真的不需要它。如何简化优化过程?首先,我们可以定义枚举值:
enum mis_MarcosTMP { _M1= PERIOD_M1, _M5= PERIOD_M5, _M15=PERIOD_M15, // _M20=PERIOD_M20, _M30=PERIOD_M30, _H1= PERIOD_H1, _H2= PERIOD_H2, _H4= PERIOD_H4, // _H8= PERIOD_H8, _D1= PERIOD_D1, _W1= PERIOD_W1, _MN1=PERIOD_MN1 };
我们可以添加或删除不必要的时间周期。在代码开头定义输入参数以优化测试:
input mis_MarcosTMP timeframe= _H1;
在library.mqh中定义新函数:
//----------------------------------------- DEFINE THE TIMEFRAME ---------------------------------------------------------- ENUM_TIMEFRAMES defMarcoTiempo(mi_MARCOTMP_CORTO marco) { ENUM_TIMEFRAMES resp= _Period; switch(marco) { case _M1: resp= PERIOD_M1; break; case _M5: resp= PERIOD_M5; break; case _M15: resp= PERIOD_M15; break; //case _M20: resp= PERIOD_M20; break; case _M30: resp= PERIOD_M30; break; case _H1: resp= PERIOD_H1; break; case _H2: resp= PERIOD_H2; break; case _H4: resp= PERIOD_H4; break; //case _H8: resp= PERIOD_H8; break; case _D1: resp= PERIOD_D1; break; case _W1: resp= PERIOD_W1; break; case _MN1: resp= PERIOD_MN1; } return(resp); }
声明一个新的全局变量:
ENUM_TIMEFRAMES marcoTmp= defMarcoTiempo(marcoTiempo); //时间周期被定义为全局变量
“marcotmp”是定义EA使用的图表周期的全局变量。在优化器参数列表中,可以定义“marcotiempo”变量的加载间隔。这允许我们只测试我们感兴趣的值,而不需要花费时间和资源分析M6或M12。这样,我们可以分析EA在不同时间段的性能。
当然,它可以通过这种方式实现。
ENUM_TIMEFRAMES marcoTmp= (ENUM_TIMEFRAMES)marcoTiempo;
显然,要实现上述代码,您必须是致力于简化代码的完美主义者,并且在程序开发方面具有丰富的经验。或者你正在使用vps并不断优化计算性能以节省资金。
优化货币对或一组货币对
MetaTrader 5 Strategy Tester中有一个优化模式,使EA能够在市场观察窗口中的所有货币对上运行。但是,如果所选货币对是另一个参数,则此函数不能用于优化测试。因此,如果选择15个货币对,测试人员将执行15个测试过程。我们如何才能为我们的EA找到最合适的货币对?如果这是一个多货币对EA,那么哪一组货币对和哪些参数组合可以获得最佳结果?在MQL5中,字符串变量不能用作优化变量。我们怎么能做到?
按以下方式将一种或一组货币定义为输入参数:
input int selecDePar= 0; string cadParesFX= selecPares(selecDePar);
“selecdepar”用作优化参数,将其类型转换为字符串变量。使用ea中的“cadparesfx”变量。货币对的名称(与此处的多货币策略无关)将与其他要优化的常规变量一起存储在该变量中。
//------------------------------------- SELECT THE SET OF PAIRS ------------------------------------- string selecPares(int combina= 0) { string resp="EURUSD"; switch(combina) { case 1: resp= "EURJPY"; break; case 2: resp= "USDJPY"; break; case 3: resp= "USDCHF"; break; case 4: resp= "GBPJPY"; break; case 5: resp= "GBPCHF"; break; case 6: resp= "GBPUSD"; break; case 7: resp= "USDCAD"; break; case 8: resp= "CADJPY"; break; case 9: resp= "XAUUSD"; break; case 10: resp= "EURJPY;USDJPY"; break; case 11: resp= "EURJPY;GBPJPY"; break; case 12: resp= "GBPCHF;GBPJPY"; break; case 13: resp= "EURJPY;GBPCHF"; break; case 14: resp= "USDJPY;GBPCHF"; break; case 15: resp= "EURUSD;EURJPY;GBPJPY"; break; case 16: resp= "EURUSD;EURJPY;GBPCHF"; break; case 17: resp= "EURUSD;EURJPY;USDJPY"; break; case 18: resp= "EURJPY;GBPCHF;USDJPY"; break; case 19: resp= "EURJPY;GBPUSD;GBPJPY"; break; case 20: resp= "EURJPY;GBPCHF;GBPJPY"; break; case 21: resp= "USDJPY;GBPCHF;GBPJPY"; break; case 22: resp= "EURUSD;USDJPY;GBPJPY"; break; case 23: resp= "EURUSD;EURJPY;USDJPY;GBPUSD;USDCHF;USDCAD"; break; case 24: resp= "EURUSD;EURJPY;USDJPY;GBPUSD;USDCHF;USDCAD;AUDUSD"; break; } return(resp); }
这取决于我们定义分析投资组合的货币和通知测试人员的间隔的目标。给策略测试人员一个指令,将“selecdepar”参数从15到22个步骤进行优化(参见下图)。如果我们想比较单一货币对的结果呢?在这种情况下,从0到9运行优化程序只需要一个步骤。
例如,在EA中,参数cadparesfx=“欧元兑美元;欧元兑日元;英镑兑瑞士法郎”。在OnInit()函数中调用“cargapares()”函数。它将动态数组array ares[]分配给变量cadparesfx中以分号分隔的字符串。必须将所有全局变量加载到动态数组中,以保留每个货币的值,包括控制货币对的新列形状。仅对于货币对,数组的长度等于1。
//-------------------------------- STRING CONVERSION FROM CURRENCY PAIRS INTO AN ARRAY ----------------------------------------------- int cargaPares(string cadPares, string &arrayPares[]) { //convierte "EURUSD;GBPUSD;USDJPY" a {"EURUSD", "GBPUSD", "USDJPY"}; devuelve el número de paresFX string caract= ""; int i= 0, k= 0, contPares= 1, longCad= StringLen(cadPares); if(cadPares=="") { ArrayResize(arrayPares, contPares); arrayPares[0]= _Symbol; } else { for (k= 0; k<longCad; k++) if (StringSubstr(cadPares, k, 1)==";") contPares++; ArrayResize(arrayPares, contPares); ZeroMemory(arrayPares); for(k=0; k<longCad; k++) { caract= StringSubstr(cadPares, k, 1); if (caract!=";") arrayPares[i]= arrayPares[i]+caract; else i++; } } return(contPares); }
在OnInit()中,此功能的实现如下:
string ar_ParesFX[]; //包含EA所运行的货币对名称的数组 int numSimbs= 1; //变量,EA运行货币对的编号 int OnInit() { //... numSimbs= cargaPares(cadParesFX, ar_ParesFX); //返回ar_ParesFX 数组,EA运行的货币对 //... }
如果numsmbs>;1,将调用onChartEvent()函数。这对于多货币系统很有用。否则,请使用ontick()函数:
void OnTick() { string simb=""; bool entrar= (nSimbs==1); if(entrar) { .../... simb= ar_ParesFX[0]; gestionOrdenes(simb); .../... } return; } //+------------------------------------------------------------------+ //| EVENT HANDLER | //+-----------------------------------------------------------------+ void OnChartEvent(const int idEvento, const long& lPeriodo, const double& dPrecio, const string &simbTick) { bool entrar= nSimbs>1 && (idEvento>=CHARTEVENT_CUSTOM); if(entrar) { .../... gestionOrdenes(simbTick); .../... } }
这意味着所有函数的参数必须至少包含货币到名称变量。例如,数字()功能必须替换为以下格式:
//--------------------------------- SYMBOLS OF A SYMBOL --------------------------------------- int digitosSimb(string simb= NULL) { int numDig= (int)SymbolInfoInteger(simb, SYMBOL_DIGITS); return(numDig); }
换句话说,我们必须忘记符号()或点()函数以及元目标4中的其他常规变量,比如ask和bid。
//----------------------------------- POINT VALUE in price (Point())--------------------------------- double valorPunto(string simb= NULL) { double resp= SymbolInfoDouble(simb, SYMBOL_POINT); return(resp); } //--------------------------- precio ASK-BID ----------------------------------------- double precioAskBid(string simb= NULL, bool ask= true) { ENUM_SYMBOL_INFO_DOUBLE precioSolic= ask? SYMBOL_ASK: SYMBOL_BID; double precio= SymbolInfoDouble(simb, precioSolic); return(precio); }
还必须更正控制新列到达的功能。如果欧元兑美元新列的开盘价出现了,而美元兑日元在接下来的2秒钟内可能没有新的报价,当下一个美元兑日元报价出现并通知EA这是新列的开盘时,欧元兑美元实际上在2秒钟前产生了一个新列。
//------------------------------------- NEW MULTI-CURRENCY CANDLESTICK ------------------------------------- bool nuevaVelaMD(string simb= NULL, int numSimbs= 1, ENUM_TIMEFRAMES marcoTmp= PERIOD_CURRENT) { static datetime arrayHoraNV[]; static bool primVez= true; datetime horaVela= iTime(simb, marcoTmp, 0); //当前蜡烛线的开始时间 bool esNueva= false; int codS= buscaCadArray(simb, nombreParesFX); if(primVez) { ArrayResize(arrayHoraNV, numSimbs); ArrayInitialize(arrayHoraNV, 0); primVez= false; } esNueva= codS>=0? arrayHoraNV[codS]!= horaVela: false; if(esNueva) arrayHoraNV[codS]= horaVela; return(esNueva); }
这种方法使我能够在优化期间得出结论:
- EA在欧元兑美元上运行良好。
- 欧元兑日元汇率的结果很糟糕。
- 它对美元日元很有效
- 在欧元兑美元、英镑兑瑞士法郎、欧元兑日元货币对中,结果非常好(正确)。
M5时间段与其它优化参数的组合可以取得较好的效果,但不能在h1或h2时间段。
有点困惑。我请求技术支持。我不知道为什么,但是策略测试仪中的优化测试的结果随所选货币的不同而不同。这就是为什么我总是使用这个货币对来测试策略开发中的测试结果,以确保它是优化器可以分析的货币对之一。
参数组合优化
有时,优化过程中一些看似不合逻辑的参数组合可以获得更好的结果。有时这种策略似乎不合理。例如,“maxspread”定义入口点差异。我们在许多货币对上优化它。这些经纪人的平均利差一般小于30,Xauusd小于400。如果货币对对等价差超过50,而xauusd小于200,那么这样的分析就没有意义了。将数据传输到优化器并设置“20时的最大排列间距从0到600”,但使用其他参数设置许多组合是毫无意义的。
按照上面描述的方式,我们在“selecpares()”函数中定义要优化的货币对。欧元兑美元相当于0,Xauusd相当于9。然后声明全局bool类型为“paramcorrect”的变量。
bool paramCorrect= (selecDePar<9 && maxSpread<50) || (selecDePar==9 && maxSpread>200);
仅当paramcorrect为true时在OnInit()中执行。
int OnInit() { ENUM_INIT_RETCODE resp= paramCorrect? INIT_SUCCEEDED: INIT_PARAMETERS_INCORRECT; if (paramCorrect) { //... nSimbs= cargaPares(cadParesFX, nombreParesFX); //返回包含EA所运行货币对的nombreParesFX数组 //... EA初始化函数 } return(resp); }
如果paramcorrect为false,则ea在oninit()函数中不执行任何操作,并向测试策略测试仪返回init_参数不正确,表明输入参数组合不正确。当策略测试仪从OnInit()接收到Init_参数_错误值时,此参数组合将不会传递给测试引擎以运行。优化测试结果中的这一行将为0,并以红色突出显示(参见下图)。
程序关闭的原因作为输入参数传递给OndeInit(),这有助于理解EA终止的原因。但那是另一个故事。
void OnDeinit(const int motivo) { if(paramCorrect) { //程序关闭函数 } infoDeInit(motivo); return; } //+-------------------------------------- INFORMATION ABOUT THE PROGRAM SHUTDOWN---------------------------- string infoDeInit(int codDeInit) { //告知程序关闭的原因 string texto= "program initialization...", text1= "CIERRE por: "; switch(codDeInit) { case REASON_PROGRAM: texto= text1+"The EA finished its work with the ExpertRemove() function"; break; //0 case REASON_ACCOUNT: texto= text1+"The account was changed"; break; //6 case REASON_CHARTCHANGE: texto= text1+"Symbol or timeframe change"; break; //3 case REASON_CHARTCLOSE: texto= text1+"The chart was closed"; break; //4 case REASON_PARAMETERS: texto= text1+"Input parameters changed by the user"; break; //5 case REASON_RECOMPILE: texto= text1+"The program was recompiled"; break; //2 case REASON_REMOVE: texto= text1+"The program was deleted from the chart"; break; //1 case REASON_TEMPLATE: texto= text1+"Another chart template was used"; break; //7 case REASON_CLOSE: texto= text1+"The terminal was closed"; break; //9 case REASON_INITFAILED: texto= text1+"The OnInit() handler returned non-zero value"; break; //8 default: texto= text1+"Other reason"; } Print(texto); return(texto); }
实际上,如果优化器接收到的参数组合在某个时间将参数“paramcorrect”设置为flase(例如,当欧元兑美元的差额设置为100点时),那么EA将不运行,此步骤的优化结果为0,这将大大减少您租用的不必要的计算机开销和计算代理资源。MQL5社区。
当然,上述所有方法都可以在ontesterinit()、ParameterTrange()和ParameterTrange()函数中实现,但上述方法更简单。此方法比使用ontesterinit()更可靠。
总结
我们讨论了如何优化etarader 5中的时间段,以及在etarader 5不支持字符串变量优化的情况下如何优化“货币”参数,使其独立于ea运行的货币对。我们还看到了如何减少优化步骤,删除不合逻辑的参数组合,维护您的计算机性能和节省资金。
上述想法并不新鲜。有一定编程经验的新手或程序员可以实现。这些思想来源于收集相关信息和调试代码的长期经验。简单但实用。如果MQL5社区想要盈利,你会问我为什么要分享这些想法。答案是克服程序员的孤独感。
谢谢你的关注。如果您已经阅读过本文,并且您是一名经验丰富的程序员,请毫不犹豫地更正它。
由MetaQuotes Software Corp.从西班牙语翻译成原来的
文章。https://www.mql5.com/es/articles/1052
MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!
免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经(www.myfxtop.cn)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。