能否在同一EA中同时使用OnInit()与OnCalculate()?附RSI显示问题
MetaTrader EA 问题解答
一、能否在同一个EA中同时使用void OnInit()和int OnCalculate()?
完全可以,这两个都是MT4/MT5 EA的标准回调函数,各司其职:
OnInit():EA加载时仅执行一次,适合做初始化操作(比如创建交易对象、生成指标句柄)OnCalculate():每根K线更新时触发,用于处理行情数据、计算指标逻辑
你代码报错的根源不是同时使用这两个函数,而是OnCalculate()里的两处错误:
- 指标句柄重复创建:每次
OnCalculate()都调用iRSI()生成新句柄,会导致句柄泄漏,最终触发系统错误。应该把句柄创建移到OnInit()中,全局保存复用。 - 数组未初始化:调用
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
相关产品推荐
相关产品推荐

