基于RSI穿越50绘制箭头的MQL4指标无显示求助
问题排查与代码修复
你的代码逻辑能正确触发打印语句,但箭头不显示,核心问题在于缓冲区序列对齐错误、RSI句柄重复创建以及缓冲区未初始化这几个关键点。以下是具体问题分析和修复后的代码:
核心问题分析
RSI句柄在OnCalculate中重复创建
每次调用OnCalculate时都用iRSI创建新句柄,会导致资源泄漏,且新创建的句柄可能未完成数据加载,影响CopyBuffer的正确性。箭头缓冲区未设置为序列模式
你将RSIArray和PriceInfo设为序列数组(最新 bar 索引为0),但箭头缓冲区upArrow、downArrow未做同样设置,导致赋值的箭头位置与实际 bar 索引错位,箭头被绘制到了不可见的历史 bar 上。缓冲区未初始化空值
未满足条件的 bar 未将缓冲区值设为EMPTY_VALUE(即你设置的0.0),可能残留旧值或导致绘图逻辑异常。未初始化的
draw变量draw变量未赋值就用于PLOT_DRAW_BEGIN,虽默认值为0不影响,但显式初始化更规范。
修复后的完整代码
#property indicator_chart_window #property indicator_buffers 2 #property indicator_width1 2 #property indicator_width2 2 #property indicator_color1 clrRed #property indicator_color2 clrGreen double upArrow[], downArrow[]; int rsiHandle; // 全局存储RSI句柄 int OnInit(){ // 初始化RSI句柄 rsiHandle = iRSI(Symbol(), Period(), 14, PRICE_CLOSE); if(rsiHandle == INVALID_HANDLE){ Print("创建RSI句柄失败"); return INIT_FAILED; } IndicatorSetString(INDICATOR_SHORTNAME,"Arrow"); // 配置买箭头缓冲区 PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_ARROW); PlotIndexSetInteger(0, PLOT_ARROW, 233); PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0); PlotIndexSetString(0, PLOT_LABEL, "Buy"); PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 0); // 显式设置从第0根bar开始绘制 SetIndexBuffer(0, upArrow); ArraySetAsSeries(upArrow, true); // 设置为序列模式 // 配置卖箭头缓冲区 PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_ARROW); PlotIndexSetInteger(1, PLOT_ARROW, 234); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0); PlotIndexSetString(1, PLOT_LABEL, "Sell"); PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, 0); SetIndexBuffer(1, downArrow); ArraySetAsSeries(downArrow, true); // 设置为序列模式 return(INIT_SUCCEEDED); } void OnDeinit(const int reason){ // 释放RSI句柄,避免资源泄漏 if(rsiHandle != INVALID_HANDLE){ IndicatorRelease(rsiHandle); } } 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 &spread[]) { int limit = rates_total - prev_calculated; if(limit <= 0) return rates_total; double RSIArray[]; ArraySetAsSeries(RSIArray, true); // 复制RSI数据并检查结果 int copiedRSI = CopyBuffer(rsiHandle, 0, 0, limit, RSIArray); if(copiedRSI != limit){ Print("复制RSI数据失败,仅复制", copiedRSI, "条"); return rates_total; } // 初始化缓冲区为空值,确保未满足条件的bar不显示箭头 for(int i = 0; i < limit; i++){ upArrow[i] = 0.0; downArrow[i] = 0.0; } // 遍历检测RSI交叉信号 for(int i = limit - 1; i >= 1; i--){ double rsi_current = RSIArray[i]; double rsi_prev = RSIArray[i+1]; // RSI上穿50 if(rsi_prev < 50 && rsi_current >= 50){ upArrow[i] = low[i] - 10 * Point(); // 绘制在当前bar的下方 Print("RSI上穿50,bar索引:", i, ",收盘价:", close[i]); } // RSI下穿50 else if(rsi_prev > 50 && rsi_current <= 50){ downArrow[i] = high[i] + 10 * Point(); // 绘制在当前bar的上方 Print("RSI下穿50,bar索引:", i, ",收盘价:", close[i]); } } return(rates_total); }
关键修复说明
- RSI句柄管理:在
OnInit中创建一次RSI句柄,OnDeinit中释放,避免资源泄漏和数据加载异常。 - 缓冲区序列对齐:将
upArrow和downArrow设为序列模式,确保赋值的索引与实际bar位置对应。 - 缓冲区初始化:每次计算前将新bar的缓冲区值设为空值,避免残留旧数据。
- 交叉信号逻辑优化:调整循环范围,确保
i+1不越界,同时将箭头绘制在信号触发的当前bar上(若需保留原逻辑绘制在前一根bar,可改回low[i+1])。
内容的提问来源于stack exchange,提问作者Raphael Tunji
相关产品推荐
相关产品推荐

