MQL5 细则手册:在 MQL5 中开发多交易品种波动指标·进阶篇
(2/3)·跨品种 ATR 同步卡在柱起点?六个交易品种一键诊断的 MQL5 进阶写法
◍ 指标子窗口与画布的初始变量怎么定
在 MT5 写自定义指标时,先把子窗口和画布相关的全局变量一次性声明好,后面绘图逻辑才不会到处找坐标。下面这段声明来自一个 ATR 可视化指标的骨架,可以直接抄进你的工程改前缀。
class="type">class="kw">string empty_symbol="EMPTY"; class=class="str">"cmt">//--- Indicator subwindow properties class="type">int subwindow_number=WRONG_VALUE; class=class="str">"cmt">// Subwindow number class="type">int chart_width=class="num">0; class=class="str">"cmt">// Chart width class="type">int subwindow_height=class="num">0; class=class="str">"cmt">// Subwindow height class="type">int last_chart_width=class="num">0; class=class="str">"cmt">// Last saved chart width class="type">int last_subwindow_height=class="num">0; class=class="str">"cmt">// Last saved subwindow height class="type">int subwindow_center_x=class="num">0; class=class="str">"cmt">// Horizontal center of the subwindow class="type">int subwindow_center_y=class="num">0; class=class="str">"cmt">// Vertical center of the subwindow class="type">class="kw">string subwindow_shortname="MS_ATR"; class=class="str">"cmt">// Short name of the indicator class="type">class="kw">string prefix=subwindow_shortname+"_"; class=class="str">"cmt">// Prefix for objects class=class="str">"cmt">//--- Canvas properties class="type">class="kw">string canvas_name=prefix+"canvas"; class=class="str">"cmt">// Canvas name class="type">class="kw">color canvas_background=clrBlack; class=class="str">"cmt">// Canvas background class="type">class="kw">color class="type">uchar canvas_opacity=class="num">190; class=class="str">"cmt">// Opacity class="type">int font_size=class="num">16; class=class="str">"cmt">// Font size class="type">class="kw">string font_name="Calibri"; class=class="str">"cmt">// Font ENUM_COLOR_FORMAT clr_format=COLOR_FORMAT_ARGB_RAW; class=class="str">"cmt">// Color components should be correctly set by the user class=class="str">"cmt">//--- Canvas messages
class="type">class="kw">string empty_symbol="EMPTY"; class=class="str">"cmt">//--- Indicator subwindow properties class="type">int subwindow_number=WRONG_VALUE; class=class="str">"cmt">// Subwindow number class="type">int chart_width=class="num">0; class=class="str">"cmt">// Chart width class="type">int subwindow_height=class="num">0; class=class="str">"cmt">// Subwindow height class="type">int last_chart_width=class="num">0; class=class="str">"cmt">// Last saved chart width class="type">int last_subwindow_height=class="num">0; class=class="str">"cmt">// Last saved subwindow height class="type">int subwindow_center_x=class="num">0; class=class="str">"cmt">// Horizontal center of the subwindow class="type">int subwindow_center_y=class="num">0; class=class="str">"cmt">// Vertical center of the subwindow class="type">class="kw">string subwindow_shortname="MS_ATR"; class=class="str">"cmt">// Short name of the indicator class="type">class="kw">string prefix=subwindow_shortname+"_"; class=class="str">"cmt">// Prefix for objects class=class="str">"cmt">//--- Canvas properties class="type">class="kw">string canvas_name=prefix+"canvas"; class=class="str">"cmt">// Canvas name class="type">class="kw">color canvas_background=clrBlack; class=class="str">"cmt">// Canvas background class="type">class="kw">color class="type">uchar canvas_opacity=class="num">190; class=class="str">"cmt">// Opacity class="type">int font_size=class="num">16; class=class="str">"cmt">// Font size class="type">class="kw">string font_name="Calibri"; class=class="str">"cmt">// Font ENUM_COLOR_FORMAT clr_format=COLOR_FORMAT_ARGB_RAW; class=class="str">"cmt">// Color components should be correctly set by the user class=class="str">"cmt">//--- Canvas messages
初始化阶段的状态字符串与守卫逻辑
在 MT5 自定义指标里,把用户可能看到的等待/报错提示先固化成字符串变量,是避免运行时拼字符串卡顿的常见做法。下面这组声明把句柄无效、数据准备中、未同步等状态都预存下来,其中 msg_load_data、msg_sync_update、msg_last 初始留空,意味着后续由同步流程动态填充。
class="type">class="kw">string msg_invalid_handle ="Invalid indicator handle! Please wait..."; class="type">class="kw">string msg_prepare_data ="Preparing data! Please wait..."; class="type">class="kw">string msg_not_synchronized ="Unsynchronized data! Please wait..."; class="type">class="kw">string msg_load_data =""; class="type">class="kw">string msg_sync_update =""; class="type">class="kw">string msg_last =""; class=class="str">"cmt">//--- Maximum number of bars specified in the terminal settings class="type">int terminal_max_bars=class="num">0;
class="type">bool CheckInputParameters() { if(IndicatorPeriod>class="num">500) { Comment("Decrease the indicator period! Indicator Period: ",IndicatorPeriod,"; Limit: class="num">500;"); printf("Decrease the indicator period! Indicator Period: %d; Limit: %d;",IndicatorPeriod,class="num">500); class="kw">return(class="kw">false); } class="kw">return(true); }
class="type">class="kw">string msg_invalid_handle ="Invalid indicator handle! Please wait..."; class="type">class="kw">string msg_prepare_data ="Preparing data! Please wait..."; class="type">class="kw">string msg_not_synchronized ="Unsynchronized data! Please wait..."; class="type">class="kw">string msg_load_data =""; class="type">class="kw">string msg_sync_update =""; class="type">class="kw">string msg_last =""; class=class="str">"cmt">//--- Maximum number of bars specified in the terminal settings class="type">int terminal_max_bars=class="num">0; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- Check class="kw">input parameters for correctness if(!CheckInputParameters()) class="kw">return(INIT_PARAMETERS_INCORRECT); class=class="str">"cmt">//--- Set the timer at class="num">1-second intervals EventSetTimer(class="num">1); class=class="str">"cmt">//--- Set the font to be displayed on the canvas canvas.FontSet(font_name,font_size,FW_NORMAL); class=class="str">"cmt">//--- Initialization of arrays InitArrays(); class=class="str">"cmt">//--- Initialize the array of symbols InitSymbolNames(); class=class="str">"cmt">//--- Initialize the array of levels InitLevels(); class=class="str">"cmt">//--- Get indicator handles GetIndicatorHandles(); class=class="str">"cmt">//--- Set indicator properties SetIndicatorProperties(); class=class="str">"cmt">//--- Get the number of bars specified in the terminal settings terminal_max_bars=TerminalInfoInteger(TERMINAL_MAXBARS); class=class="str">"cmt">//--- Clear the comment Comment(""); class=class="str">"cmt">//--- Refresh the chart ChartRedraw(); class=class="str">"cmt">//--- Initialization completed successfully class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Checking class="kw">input parameters for correctness | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CheckInputParameters() { if(IndicatorPeriod>class="num">500) { Comment("Decrease the indicator period! Indicator Period: ",IndicatorPeriod,"; Limit: class="num">500;"); printf("Decrease the indicator period! Indicator Period: %d; Limit: %d;",IndicatorPeriod,class="num">500); class="kw">return(class="kw">false); } class=class="str">"cmt">//--- class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| First initialization of arrays | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void InitArrays() { ArrayInitialize(limit_time,NULL); ArrayInitialize(series_first_date,NULL); ArrayInitialize(series_first_date_last,NULL);
「多品种指标句柄的初始化套路」
多符号监控面板在 MT5 里第一道关,是把句柄和缓冲数组先置脏。ArrayInitialize(symbol_handles,INVALID_HANDLE) 把 6 个品种的指标句柄全填成 INVALID_HANDLE,随后 for 循环里对 atr_buffers[s].data 逐个填 EMPTY_VALUE,避免首根 K 线前读到野指针。 品种名与阈值都走独立初始化函数。InitSymbolNames 把 _Symbol 及 Symbol02~Symbol06 共 6 个字符串塞进 symbol_names 数组,InitLevels 则把 Level01~Level06 这 6 个外部参数写进 indicator_levels,数量硬编码为 6,改品种数得同步动这三处。 AddSymbolToMarketWatch 是真正落盘的逻辑:先 SymbolsTotal(false) 拿服务端全部品种数,再 for 遍历 SymbolName(i,false) 比对,命中后 SymbolSelect(name,true) 把品种加进市场报价窗,返回名;未命中返回 empty_symbol。这样即便某个品种券商没提供,面板也不会崩,只是留空位。 别把 6 这个数写死在脑里 若你接的手稿 SYMBOLS_COUNT 不是 6,InitSymbolNames 和 InitLevels 的数组下标必须跟着改,否则编译期越界或运行期漏初始化都可能发生。开 MT5 把 Symbol02~06 换成你盯的 XAUUSD、EURUSD 之类,跑一遍看市场报价窗是否多出对应行。
ArrayInitialize(symbol_handles,INVALID_HANDLE); class=class="str">"cmt">//--- for(class="type">int s=class="num">0; s<SYMBOLS_COUNT; s++) ArrayInitialize(atr_buffers[s].data,EMPTY_VALUE); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Initializing array of symbols | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void InitSymbolNames() { symbol_names[class="num">0]=AddSymbolToMarketWatch(_Symbol); symbol_names[class="num">1]=AddSymbolToMarketWatch(Symbol02); symbol_names[class="num">2]=AddSymbolToMarketWatch(Symbol03); symbol_names[class="num">3]=AddSymbolToMarketWatch(Symbol04); symbol_names[class="num">4]=AddSymbolToMarketWatch(Symbol05); symbol_names[class="num">5]=AddSymbolToMarketWatch(Symbol06); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Initializing array of levels | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void InitLevels() { indicator_levels[class="num">0]=Level01; indicator_levels[class="num">1]=Level02; indicator_levels[class="num">2]=Level03; indicator_levels[class="num">3]=Level04; indicator_levels[class="num">4]=Level05; indicator_levels[class="num">5]=Level06; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Adding the specified symbol to the Market Watch window | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">string AddSymbolToMarketWatch(class="type">class="kw">string symbol) { class="type">int total=class="num">0; class=class="str">"cmt">// Number of symbols class="type">class="kw">string name=""; class=class="str">"cmt">// Symbol name class=class="str">"cmt">//--- If an empty class="type">class="kw">string is passed, class="kw">return the empty class="type">class="kw">string if(symbol=="") class="kw">return(empty_symbol); class=class="str">"cmt">//--- Total symbols on the server total=SymbolsTotal(class="kw">false); class=class="str">"cmt">//--- Iterate over the entire list of symbols for(class="type">int i=class="num">0;i<total;i++) { class=class="str">"cmt">//--- Symbol name on the server name=SymbolName(i,class="kw">false); class=class="str">"cmt">//--- If this symbol is available, if(name==symbol) { class=class="str">"cmt">//--- add it to the Market Watch window and SymbolSelect(name,true); class=class="str">"cmt">//--- class="kw">return its name class="kw">return(name); } } class=class="str">"cmt">//--- If this symbol is not available, class="kw">return the class="type">class="kw">string representing the lack of the symbol class="kw">return(empty_symbol); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Getting indicator handles | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool GetIndicatorHandles() { class=class="str">"cmt">//--- An indication of all handles being valid class="type">bool valid_handles=true; class=class="str">"cmt">//--- Iterate over all symbols in a loop and ...
◍ 多品种 ATR 句柄获取与绘图属性初始化
在 MT5 里做跨品种监控,第一步是把每个交易品种的 ATR 指标句柄拿到手。下面这段循环会遍历 symbol_names 数组,跳过空品种名,只在对应句柄还是 INVALID_HANDLE 时才调用 iATR 重新获取;若某个品种连续拿不到句柄,就把 valid_handles 置为 false,并返回让上层在画布上提示「句柄无效」。 for(int s=0; s<SYMBOLS_COUNT; s++) { //--- 若品种名非空(可用) if(symbol_names[s]!=empty_symbol) { //--- 且当前品种句柄无效时 if(symbol_handles[s]==INVALID_HANDLE) { //--- 获取该品种 ATR 句柄,周期为当前图表周期 Period(),周期为 IndicatorPeriod symbol_handles[s]=iATR(symbol_names[s],Period(),IndicatorPeriod); //--- 若仍拿不到,下次再试,并标记整体句柄无效 if(symbol_handles[s]==INVALID_HANDLE) valid_handles=false; } } } //--- 若有任一品种句柄获取失败,打印消息并画布提示 if(!valid_handles) { msg_last=msg_invalid_handle; ShowCanvasMessage(msg_invalid_handle); } //--- 返回句柄是否有效 return(valid_handles); } 拿到句柄后,SetIndicatorProperties 负责把绘图层收拾干净。它先用 IndicatorSetString 设短名、IndicatorSetInteger 对齐 _Digits 小数位;随后对 0 到 SYMBOLS_COUNT-1 的每个索引,分别绑定 atr_buffers[s].data 为数据缓冲、设标签为「ATR (序号, 品种名)」、画线类型 DRAW_LINE、线宽固定 1、颜色取 line_colors[s]、空值用 EMPTY_VALUE 占位。 void SetIndicatorProperties() { //--- 设短名 IndicatorSetString(INDICATOR_SHORTNAME,subwindow_shortname); //--- 设小数位 IndicatorSetInteger(INDICATOR_DIGITS,_Digits); //--- 绑定各品种数据缓冲 for(int s=0; s<SYMBOLS_COUNT; s++) SetIndexBuffer(s,atr_buffers[s].data,INDICATOR_DATA); //--- 设当前品种标签 for(int s=0; s<SYMBOLS_COUNT; s++) PlotIndexSetString(s,PLOT_LABEL,"ATR ("+IntegerToString(s)+", "+symbol_names[s]+")"); //--- 画线类型 for(int s=0; s<SYMBOLS_COUNT; s++) PlotIndexSetInteger(s,PLOT_DRAW_TYPE,DRAW_LINE); //--- 线宽 1 for(int s=0; s<SYMBOLS_COUNT; s++) PlotIndexSetInteger(s,PLOT_LINE_WIDTH,1); //--- 线色 for(int s=0; s<SYMBOLS_COUNT; s++) PlotIndexSetInteger(s,PLOT_LINE_COLOR,line_colors[s]); //--- 空值不画 for(int s=0; s<SYMBOLS_COUNT; s++) PlotIndexSetDouble(s,PLOT_EMPTY_VALUE,EMPTY_VALUE); } 实盘里若 SYMBOLS_COUNT 设到 10 以上,初始化时反复 iATR 失败往往是因为品种不在市场报价窗口;开 MT5 前先确认「市场观察」已显示这些品种,否则 valid_handles 会卡在 false,子窗口什么都不画。外汇与贵金属波动剧烈,多品种 ATR 仅作波动参考,实际仓位仍须自担高风险。
for(class="type">int s=class="num">0; s<SYMBOLS_COUNT; s++) { class=class="str">"cmt">//--- If the symbol is available if(symbol_names[s]!=empty_symbol) { class=class="str">"cmt">// And if the handle of the current symbol is invalid if(symbol_handles[s]==INVALID_HANDLE) { class=class="str">"cmt">//--- Get it symbol_handles[s]=iATR(symbol_names[s],Period(),IndicatorPeriod); class=class="str">"cmt">//--- If the handle could not be obtained, try again next time if(symbol_handles[s]==INVALID_HANDLE) valid_handles=class="kw">false; } } } class=class="str">"cmt">//--- Print the relevant message if the handle for one of the symbols could not be obtained if(!valid_handles) { msg_last=msg_invalid_handle; ShowCanvasMessage(msg_invalid_handle); } class=class="str">"cmt">//--- class="kw">return(valid_handles); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Setting indicator properties | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void SetIndicatorProperties() { class=class="str">"cmt">//--- Set the class="type">class="kw">short name IndicatorSetString(INDICATOR_SHORTNAME,subwindow_shortname); class=class="str">"cmt">//--- Set the number of decimal places IndicatorSetInteger(INDICATOR_DIGITS,_Digits); class=class="str">"cmt">//--- Define buffers for drawing for(class="type">int s=class="num">0; s<SYMBOLS_COUNT; s++) SetIndexBuffer(s,atr_buffers[s].data,INDICATOR_DATA); class=class="str">"cmt">//--- Set labels for the current symbol for(class="type">int s=class="num">0; s<SYMBOLS_COUNT; s++) PlotIndexSetString(s,PLOT_LABEL,"ATR("+IntegerToString(s)+", "+symbol_names[s]+")"); class=class="str">"cmt">//--- Set the plotting type: lines for(class="type">int s=class="num">0; s<SYMBOLS_COUNT; s++) PlotIndexSetInteger(s,PLOT_DRAW_TYPE,DRAW_LINE); class=class="str">"cmt">//--- Set the line width for(class="type">int s=class="num">0; s<SYMBOLS_COUNT; s++) PlotIndexSetInteger(s,PLOT_LINE_WIDTH,class="num">1); class=class="str">"cmt">//--- Set the line class="type">class="kw">color for(class="type">int s=class="num">0; s<SYMBOLS_COUNT; s++) PlotIndexSetInteger(s,PLOT_LINE_COLOR,line_colors[s]); class=class="str">"cmt">//--- Empty value for plotting where nothing will be drawn for(class="type">int s=class="num">0; s<SYMBOLS_COUNT; s++) PlotIndexSetDouble(s,PLOT_EMPTY_VALUE,EMPTY_VALUE); }
把多品种行情灌进自定义指标缓冲区
做跨品种 ATR 监控时,第一步是把主图 OnCalculate 传进来的行情数组原样接住。下面这段把 rates_total、prev_calculated 以及 time/open/high/low/close 等九类数组用 ArrayCopy 拷到全局容器,后续子函数才能按索引取用,不必反复调 CopyRates。 OC_rates_total=rates_total 记下总柱数,OC_prev_calculated 保留已计算偏移,其余八个 ArrayCopy 逐一对齐。注意参数里的 spread[] 也是可拷贝项,在贵金属点差跳变时它能单独留痕。 真正跑多品种前还要给临时数组定尺寸。ResizeCalculatedArrays 按 SYMBOLS_COUNT 循环,对每个符号的 time 与 atr 值数组做 ArrayResize 到 OC_rates_total,否则跨符号写入会越界报错。 每次重算前必须清场。ZeroCalculatedArrays 把临时 time 置 NULL、atr 值置 EMPTY_VALUE;ZeroIndicatorBuffers 只清 atr_buffers 的 data。外汇与贵金属波动剧烈,若不清 EMPTY_VALUE,上一根残留 ATR 可能让信号出现伪连续,实盘验证时务必确认这一步被执行。
class="kw">const class="type">int prev_calculated, class="kw">const class="type">class="kw">datetime &time[], class="kw">const class="type">class="kw">double &open[], class="kw">const class="type">class="kw">double &high[], class="kw">const class="type">class="kw">double &low[], class="kw">const class="type">class="kw">double &close[], class="kw">const class="type">long &tick_volume[], class="kw">const class="type">long &volume[], class="kw">const class="type">int &spread[]) { OC_rates_total=rates_total; OC_prev_calculated=prev_calculated; ArrayCopy(OC_time,time); ArrayCopy(OC_open,open); ArrayCopy(OC_high,high); ArrayCopy(OC_low,low); ArrayCopy(OC_close,close); ArrayCopy(OC_tick_volume,tick_volume); ArrayCopy(OC_volume,volume); ArrayCopy(OC_spread,spread); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Resizing the size of arrays to the size of the main array | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void ResizeCalculatedArrays() { for(class="type">int s=class="num">0; s<SYMBOLS_COUNT; s++) { ArrayResize(tmp_symbol_time[s].time,OC_rates_total); ArrayResize(tmp_atr_values[s].value,OC_rates_total); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Zeroing out arrays for data preparation | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void ZeroCalculatedArrays() { for(class="type">int s=class="num">0; s<SYMBOLS_COUNT; s++) { ArrayInitialize(tmp_symbol_time[s].time,NULL); ArrayInitialize(tmp_atr_values[s].value,EMPTY_VALUE); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Zeroing out indicator buffers | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void ZeroIndicatorBuffers() { for(class="type">int s=class="num">0; s<SYMBOLS_COUNT; s++) ArrayInitialize(atr_buffers[s].data,EMPTY_VALUE); }
「OnCalculate 里的首次与增量计算分支」
自定义指标跑在 MT5 上,核心入口是 OnCalculate。它每次被终端调用都会带进 rates_total(当前 K 线总数)和 prev_calculated(上一次已处理柱数),这两个值决定了你是全量重算还是只补最后几根。 下面这段是典型骨架:先定义 limit 控制起点,再把入参整体拷进类内数组,然后按 prev_calculated==0 走首算分支——清零缓冲、记下 OC_prev_calculated=rates_total;否则 limit=prev_calculated-1,只从倒数第二根往后推。
class="type">int OnCalculate(class="kw">const class="type">int rates_total, class="kw">const class="type">int prev_calculated, class="kw">const class="type">class="kw">datetime &time[], class="kw">const class="type">class="kw">double &open[], class="kw">const class="type">class="kw">double &high[], class="kw">const class="type">class="kw">double &low[], class="kw">const class="type">class="kw">double &close[], class="kw">const class="type">long &tick_volume[], class="kw">const class="type">long &volume[], class="kw">const class="type">int &spread[]) { class=class="str">"cmt">//--- 定义计算起点 class="type">int limit=class="num">0; class=class="str">"cmt">//--- 拷贝 OnCalculate 入参到内部数组 CopyDataOnCalculate(rates_total,prev_calculated, time,open,high,low,close, tick_volume,volume,spread); class=class="str">"cmt">//--- 调整预备数组尺寸 ResizeCalculatedArrays(); class=class="str">"cmt">//--- 首算或补历史时 if(prev_calculated==class="num">0) { ZeroCalculatedArrays(); ZeroIndicatorBuffers(); OC_prev_calculated=rates_total; } class=class="str">"cmt">//--- 仅重算末值时 else limit=prev_calculated-class="num">1; class=class="str">"cmt">//--- 准备绘图数据(略) class=class="str">"cmt">//--- 填充绘图数组(略) class="kw">return(rates_total); }
class="type">int OnCalculate(class="kw">const class="type">int rates_total, class="kw">const class="type">int prev_calculated, class="kw">const class="type">class="kw">datetime &time[], class="kw">const class="type">class="kw">double &open[], class="kw">const class="type">class="kw">double &high[], class="kw">const class="type">class="kw">double &low[], class="kw">const class="type">class="kw">double &close[], class="kw">const class="type">long &tick_volume[], class="kw">const class="type">long &volume[], class="kw">const class="type">int &spread[]) { class="type">int limit=class="num">0; CopyDataOnCalculate(rates_total,prev_calculated, time,open,high,low,close, tick_volume,volume,spread); ResizeCalculatedArrays(); if(prev_calculated==class="num">0) { ZeroCalculatedArrays(); ZeroIndicatorBuffers(); OC_prev_calculated=rates_total; } else limit=prev_calculated-class="num">1; class="kw">return(rates_total); }