无法通过MQL5指标返回值:EA调用获取结果始终为0问题排查
解决MQL5指标被EA调用时返回值始终为0的问题
我帮你排查了代码里的几个关键问题,这就是为什么EA拿不到正确值的原因:
核心错误点及修正方案
1. EA根本没读取指标数据
你的EA代码里CopyBuffer被注释掉了!这就相当于你打开了冰箱门但没伸手拿东西,remaining_time数组一直是空的,自然输出0。必须启用CopyBuffer来从指标缓冲区拉取数据,还要加错误检查确保读取成功。
2. 指标缓冲区没设置序列模式
指标里的remainingtime缓冲区没有开启序列模式(ArraySetAsSeries),但EA里把remaining_time设成了序列模式,两边模式不匹配,导致EA读错了位置。指标缓冲区必须和K线时间序列保持一致,才能拿到最新的数值。
3. 剩余时间计算逻辑有漏洞
原来的计算int m=int(time[0]+PeriodSeconds()-TimeCurrent());在K线刚切换时会得到负数,导致s变成负数值,虽然Print可能暂时显示正常,但会给EA传递异常数据。得先判断剩余时间是否为正,负数就设为0。
4. EA缺少必要的错误检查
EA里没有检查指标是否成功加载,也没判断CopyBuffer是否执行成功,出问题了根本不知道哪里错。
修正后的完整代码
修正后的指标代码
#property indicator_chart_window #property strict #property indicator_buffers 1 //---- input parameters input color Clock_Color = clrWhite; input ENUM_BASE_CORNER Corner = CORNER_LEFT_LOWER; string objname="Spread&Bar"; double s1[]; double remainingtime[]; //+------------------------------------------------------------------+ //| expert initialization function | //+------------------------------------------------------------------+ int OnInit() { SetIndexBuffer(0,remainingtime,INDICATOR_CALCULATIONS); // 新增:将缓冲区设置为序列模式,和K线时间保持一致 ArraySetAsSeries(remainingtime, true); ObjectCreate(0, objname, OBJ_LABEL,0, 0, 0); ObjectSetInteger(0, objname, OBJPROP_CORNER, Corner); ObjectSetInteger(0, objname, OBJPROP_XDISTANCE, 10); ObjectSetInteger(0, objname, OBJPROP_YDISTANCE, 2); ENUM_ANCHOR_POINT Anchor = ANCHOR_LEFT_UPPER; switch (Corner) { case CORNER_LEFT_UPPER: Anchor=ANCHOR_LEFT_UPPER; break; case CORNER_RIGHT_UPPER: Anchor=ANCHOR_RIGHT_UPPER; break; case CORNER_LEFT_LOWER: Anchor=ANCHOR_LEFT_LOWER; break; case CORNER_RIGHT_LOWER: Anchor=ANCHOR_RIGHT_LOWER; break; } ObjectSetInteger(0, objname, OBJPROP_ANCHOR, Anchor); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectDelete(0, objname); } //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& open[], const double& high[], const double& low[], const double& close[], const long& tick_volume[], const long& volume[], const int& spreads[]) { ArraySetAsSeries(time, true); // 修正剩余时间计算逻辑,确保非负 datetime next_bar_time = time[0] + PeriodSeconds(); long remaining_seconds = next_bar_time - TimeCurrent(); if(remaining_seconds < 0) remaining_seconds = 0; int m = (int)(remaining_seconds / 60); int s = (int)(remaining_seconds % 60); long spread=SymbolInfoInteger(Symbol(), SYMBOL_SPREAD); string _sp="",_m="",_s=""; if(spread<10) _sp=".."; else if(spread<100) _sp="."; if(m<10) _m="0"; if(s<10) _s="0"; ObjectSetString(0, objname, OBJPROP_TEXT, "Spread: " +IntegerToString(spread)+_sp+" Next Bar in "+_m+IntegerToString(m)+":"+_s+IntegerToString(s)); remainingtime[0] = remaining_seconds; // 这里如果需要返回秒数部分,改成s即可 Print(remainingtime[0]); ObjectSetInteger(0, objname, OBJPROP_FONTSIZE, 10); ObjectSetInteger(0, objname, OBJPROP_COLOR, Clock_Color); ObjectSetString(0, objname, OBJPROP_FONT, "Courier"); return(rates_total); }
修正后的EA代码
#property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int candletime; double remaining_time[]; int OnInit() { //--- 加载指标,注意文件名要和编译后的ex5文件名一致 candletime = iCustom(_Symbol,_Period,"candle_time_end_and_spread"); if(candletime == INVALID_HANDLE) { Print("Failed to load indicator! Error code: ", GetLastError()); return(INIT_FAILED); } ArraySetAsSeries(remaining_time,true); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- 释放指标句柄,避免内存泄漏 if(candletime != INVALID_HANDLE) { IndicatorRelease(candletime); candletime = INVALID_HANDLE; } } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- 从指标的第0号缓冲区读取最新1个数据 if(CopyBuffer(candletime, 0, 0, 1, remaining_time) != 1) { Print("CopyBuffer failed! Error code: ", GetLastError()); return; } //--- 打印获取到的剩余时间 Print("Remaining time to next bar: ", remaining_time[0], " seconds"); ArrayPrint(remaining_time); }
额外说明
- 确保指标编译后的文件名和EA里
iCustom调用的名称完全一致(比如如果指标编译后是candle_time_end_and_spread.ex5,EA里写"candle_time_end_and_spread"即可,不用加后缀)。 - 如果你需要EA获取的是原来的秒数部分(不是总剩余秒数),把指标里
remainingtime[0] = remaining_seconds;改成remainingtime[0] = s;就行。
内容的提问来源于stack exchange,提问作者Jaffer Wilson




