You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否在同一EA中同时使用OnInit()与OnCalculate()?附RSI显示问题

MetaTrader EA 问题解答

一、能否在同一个EA中同时使用void OnInit()和int OnCalculate()?

完全可以,这两个都是MT4/MT5 EA的标准回调函数,各司其职:

  • OnInit():EA加载时仅执行一次,适合做初始化操作(比如创建交易对象、生成指标句柄)
  • OnCalculate():每根K线更新时触发,用于处理行情数据、计算指标逻辑

你代码报错的根源不是同时使用这两个函数,而是OnCalculate()里的两处错误:

  1. 指标句柄重复创建:每次OnCalculate()都调用iRSI()生成新句柄,会导致句柄泄漏,最终触发系统错误。应该把句柄创建移到OnInit()中,全局保存复用。
  2. 数组未初始化:调用CopyBuffer()前未调整db_ARRAY_RSI的数组大小,可能导致内存访问异常。

修正后的可运行代码示例:

#include <Trade\Trade.mqh>
CTrade md;
int i_HANDLE; // 全局存储RSI指标句柄
double db_ARRAY_RSI[];                             

void OnInit()
{          
   md.BuyLimit(0.01, 3370, _Symbol, 0, 0, ORDER_TIME_DAY, 0, NULL);
   // 在OnInit中仅创建一次RSI句柄
   int i_MA_PERIOD = 14;
   i_HANDLE = iRSI(Symbol(), PERIOD_CURRENT, i_MA_PERIOD, PRICE_CLOSE);
   if(i_HANDLE == INVALID_HANDLE){
      Print("RSI指标句柄创建失败");
      ExpertRemove();
   }
}

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 i_START_POS = 0;                               
   int i_COUNT = 1;                                  
   // 提前调整数组大小以匹配需要复制的数据量
   ArrayResize(db_ARRAY_RSI, i_COUNT);
   // 复制RSI数据到数组
   if(CopyBuffer(i_HANDLE, 0, i_START_POS, i_COUNT, db_ARRAY_RSI) != i_COUNT){
      Print("RSI数据复制失败");
      return(rates_total);
   }
   Comment("db_ARRAY_RSI[0]: ", NormalizeDouble(db_ARRAY_RSI[0], 2), "abc"); 
   return(rates_total); 
}

void OnDeinit(const int reason){
   // EA卸载时释放指标句柄,避免资源泄漏
   if(i_HANDLE != INVALID_HANDLE){
      IndicatorRelease(i_HANDLE);
   }
}

二、如何在图表上显示RSI?

有两种实用方式:

1. 直接加载系统内置RSI指标

用IndicatorCreate()函数在EA初始化时,把MT自带的RSI指标加载到当前图表,代码示例:

void OnInit()
{
   // 加载RSI指标到图表子窗口(第二个参数0为主窗口,>0为子窗口)
   long indicator_handle = IndicatorCreate(_Symbol, PERIOD_CURRENT, INDICATOR_RSI,
                                           1, // 子窗口索引
                                           14, // RSI周期
                                           PRICE_CLOSE); // 计算用价格类型
   if(indicator_handle == INVALID_HANDLE){
      Print("RSI指标加载失败");
   }
}

2. 自定义绘制RSI数值标签

通过ObjectCreate()创建文本标签,手动显示最新RSI数值,适合需要自定义样式的场景:

void OnCalculate(...)
{
   double rsi_value = db_ARRAY_RSI[0]; // 假设已获取最新RSI值
   
   // 创建并配置文本标签
   if(ObjectFind(0, "RSI_Label") == -1){
      ObjectCreate(0, "RSI_Label", OBJ_LABEL, 0, 0, 0);
      ObjectSetString(0, "RSI_Label", OBJPROP_FONT, "Arial");
      ObjectSetInteger(0, "RSI_Label", OBJPROP_FONTSIZE, 12);
      ObjectSetInteger(0, "RSI_Label", OBJPROP_COLOR, clrRed);
      ObjectSetInteger(0, "RSI_Label", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
      ObjectSetDouble(0, "RSI_Label", OBJPROP_XDISTANCE, 10);
      ObjectSetDouble(0, "RSI_Label", OBJPROP_YDISTANCE, 10);
   }
   // 更新标签文本
   ObjectSetString(0, "RSI_Label", OBJPROP_TEXT, "当前RSI: " + DoubleToString(rsi_value, 2));
}

如果需要绘制完整的RSI曲线,可通过创建折线对象逐个连接K线对应的RSI值,但实现较繁琐,优先推荐直接加载内置指标的方式。

内容的提问来源于stack exchange,提问作者jjo mmax

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 21:33:34