MT5 1847 版本修复了Bars 函数 挂起的错误。此外,还添加了标准 iBarShift 函数。
因此,为了提高这些函数的性能(10 倍左右),现在最好使用此代码变体:
int fBars(string symbol_name,ENUM_TIMEFRAMES timeframe,datetime start_time,datetime stop_time) { static string LastSymb=NULL; static ENUM_TIMEFRAMES LastTimeFrame=0; static datetime LastTime=0; static datetime LastTime0=0; static int PerSec=0; static int PreBars=0; static datetime LastBAR=0; static datetime LastTimeCur=0; static bool flag=true; static int max_bars=TerminalInfoInteger(TERMINAL_MAXBARS); datetime TimeCur; if(timeframe==0) timeframe=_Period; const bool changeTF=LastTimeFrame!=timeframe; const bool changeSymb=LastSymb!=symbol_name; const bool change=changeTF || changeSymb || flag; LastTimeFrame=timeframe; LastSymb=symbol_name; if(changeTF) PerSec=::PeriodSeconds(timeframe); if(PerSec==0) { flag=true; return(0);} if(stop_time<start_time) { TimeCur=stop_time; stop_time=start_time; start_time=TimeCur; } if(changeSymb) { if(!SymbolInfoInteger(symbol_name,SYMBOL_SELECT)) { SymbolSelect(symbol_name,true); ChartRedraw(); } } TimeCur=TimeCurrent(); if(timeframe==PERIOD_W1) TimeCur-=(TimeCur+345600)%PerSec; // 1970年1月1日--星期四。减去4天 if(timeframe<PERIOD_W1) TimeCur-=TimeCur%PerSec; if(start_time>TimeCur) { flag=true; return(0);} if(timeframe==PERIOD_MN1) { MqlDateTime dt; TimeToStruct(TimeCur,dt); TimeCur=dt.year*12+dt.mon; } if(changeTF || changeSymb || TimeCur!=LastTimeCur) LastBAR=(datetime)SeriesInfoInteger(symbol_name,timeframe,SERIES_LASTBAR_DATE); LastTimeCur=TimeCur; if(start_time>LastBAR) { flag=true; return(0);} datetime tS,tF=0; if(timeframe==PERIOD_W1) tS=start_time-(start_time+345599)%PerSec-1; else if(timeframe<PERIOD_MN1) tS=start_time-(start_time-1)%PerSec-1; else // PERIOD_MN1 { MqlDateTime dt; TimeToStruct(start_time-1,dt); tS=dt.year*12+dt.mon; } if(stop_time<=LastBAR) { if(timeframe<PERIOD_W1) tF=stop_time-(stop_time)%PerSec; else if(timeframe==PERIOD_W1) tF=stop_time-(stop_time+345600)%PerSec; else // PERIOD_MN1 { MqlDateTime dt0; TimeToStruct(stop_time-1,dt0); tF=dt0.year*12+dt0.mon; } } if(change || tS!=LastTime || tF!=LastTime0) { PreBars=Bars(symbol_name,timeframe,start_time,stop_time); LastTime=tS; LastTime0=tF; } flag=false; return(PreBars); } int fBarShift(string symb,ENUM_TIMEFRAMES TimeFrame,datetime time,bool exact=false) { int Res=fBars(symb,TimeFrame,time+1,UINT_MAX); if(exact) if((TimeFrame!=PERIOD_MN1 || time>TimeCurrent()) && Res==fBars(symb,TimeFrame,time-PeriodSeconds(TimeFrame)+1,UINT_MAX)) return(-1); return(Res); }
- 2016.09.01
- www.mql5.com
因此,为了提高这些函数的运行速度(大约 10 倍),现在最好使用这种代码变体:
你对速度的要求是不是有点太草率了? 特别是考虑到你的代码中存在大量的各种检查。 我懒得去研究它的本质,但我很难相信这样一团糟的代码能快速运行。
你是不是对速度反应过激了? 尤其是考虑到你的代码中存在大量的各种检查。 我懒得深入探讨其本质,但我很难相信这样一团糟的代码能快速运行。
你最好懒得写这条信息。))
为了测试速度,我特意在这些函数中附加了一个指标。
Bars 函数以微秒为单位执行,而检查、算术运算(甚至计算二进制数的平方根)则小于纳秒:
void OnStart() { ulong t; double sum=0; t=GetMicrosecondCount(); for (double i=1; i<2;i+=0.000001 ) sum+=sqrt(i); t=GetMicrosecondCount()-t; Print("1,000,000根的总和 = " + DoubleToString(sum,18)+ "为" + IntegerToString((int)t) + "微秒"。); } 2018.06.14 19:23:31.188 SpeedSQRT (EURUSD,M4) Сумма 1 000 000 корней = 1218952.6235881459433586 за 1990 микросекунд 2018.06.14 19:26:30.814 SpeedSQRT (EURUSD,M4) Сумма 1 000 000 корней = 1218952.6235881459433586 за 1946 микросекунд 2018.06.14 19:26:34.188 SpeedSQRT (EURUSD,M4) Сумма 1 000 000 корней = 1218952.6235881459433586 за 1946 микросекунд 2018.06.14 19:26:36.344 SpeedSQRT (EURUSD,M4) Сумма 1 000 000 корней = 1218952.6235881459433586 за 1973 микросекунд
从这个速度测试中我们可以看到,计算二进制数根的和的 100 万次循环是在 2000 微秒内完成的。
因此,一个循环是在 2 纳秒内完成的。一个周期是 1 次检查、2 次二进制数求和、1 次二进制数平方根计算。
你最好懒得写这篇文章。))
我特别为这些功能附加了一个速度测试指标。
Bars 函数的执行速度为微秒级,而检查、算术运算(甚至计算二进制数的平方根)的速度都小于纳秒级:
从这个速度测试中我们可以看到,计算二进制数根的和的 100 万个周期的执行时间为 2000 微秒。
因此,一个周期的执行时间为 2 纳秒。一个周期是 1 次检查、2 次二进制数求和、1 次二进制数平方根计算。
我还没有安装新版本,所以无法重现。据我所知,您的函数是针对一种特殊情况进行优化的,这在您的指标中有所体现。 符号不会改变,时间框架也不会改变,等等。 我想这就是为什么 Bars 函数在这里被调用一次的原因。 符号和时间框架都被愚蠢地设置为常量。 这就是为什么很有可能编译器优化了您的整个函数,扔掉了所有不必要的东西。
简而言之,测试是不正确的。 应该有一个符号数组和一个时段数组,你应该在所有这些数组上运行你的函数。 然后我们才能讨论一些问题。
我还没有安装新版本,所以无法重现。我知道你的函数是针对一种特殊情况进行优化的,这在你的指标中有所体现。 符号不会改变,时间框架也不会改变,等等。 因此,我认为 Bars 函数只被调用一次,而符号和时间框架都被愚蠢地设置为常量。 因此,很有可能编译器优化了你的整个函数,扔掉了所有不必要的东西。
简而言之,测试的执行方式是错误的。 应该有一个字符数组和一个句点数组。 你应该把你的函数全部运行一遍。 然后我们就可以讨论一些问题了。
这些都不是特例:"符号不变,时间范围也不变"!
当然,您是对的。如果每次调用 Bars 时都伴随着 TF 或符号的变化,那么标准指标的运行速度会更快。虽然在这种情况下,你只需要用一个大小与 TF 数量相同的静态变量 数组来代替静态变量。但我认为这是一种特殊情况。但如果有必要的话,这种特殊情况下的解决方案也有其存在的价值。也许应该针对这种情况修改代码。А ...这可能就是你要说的。
关于常量--谢谢你的注意。我更正是因为原始函数中的参数不是常量。在速度方面没有任何变化。
您是否有很多这样的指标或 Expert Advisors,它们会不断改变 TF 和符号?
是的,非常多。 我专注于投资组合分析和投资组合交易。 不过,当涉及到大型计算时,iBarShift 就不合适了,因为我们使用的是数组。 事实上,iBarShift 和其他类似函数都是为罕见调用而设计的。我们只需在 CopyTime 中获取数组,然后快速找到所有内容。 这就是为什么测试示例在实际应用中用处不大的原因。 当然,除非编码者是个傻瓜)而且我认为不值得为了傻瓜而费心)。
我认为将函数封装到一个类中更合乎逻辑。 这样您就不必在每次调用时都检查符号/周期。 您将为每个时间序列拥有一个单独的对象。
用于 MT5 的快速 iBarShift 和 Bars:
完整且快速的函数,似于 MQL4 中的 Bars 和 iBarShift。
作者: Nikolai Semko