使用若干中间指标缓存创建多货币指标(基础篇)
📘

使用若干中间指标缓存创建多货币指标(基础篇)

第 1/3 篇

多货币指标的中间缓存思路

在 MT5 里做多货币指标,直接在每个 OnCalculate 里反复跨品种抓价,会拖慢主图刷新,尤其在同时盯 10 个以上外汇对时。作者 Alexey Klenov 在 2013-12-06 发布的方案是:先开一组中间指标缓存,把各货币对的计算结果暂存,再让主指标统一读取。 这套做法的核心是用 iCustom 或 iClose 把外部品种数据拉进附属缓冲区,主指标只处理已缓存的值,避免重复调用终端接口。实测在 8 货币对篮子指标上,相比实时逐根抓取,CPU 占用倾向更低、掉帧概率更小。 外汇与贵金属市场高杠杆、高波动,跨品种指标只是辅助视角,任何信号都只是概率倾向,开 MT5 自建一个双货币缓存指标验证延迟差异最直观。

◍ 群集指标的起点与多币种重构

最早接触群集指标,是读《建立 FOREX 群集指标的理论基础》那篇时,当时就想着能不能把多市场分析做成可落地的东西。我先写了代号为 MultiCurrencyIndex 的自用指标,用货币指数去算 RSI、MACD、CCI 这些经典量。 转到 MT5 后,我用 MQL5 重做了这套逻辑,把 CCI 换成随机动量指标(Stochastic Oscillator)——个人觉得它更靠前反应。美元指数按 Neutron 给的公式算双精度值:USD/YYY 取所有直接报价(如 USD/CHF),XXX/USD 取所有反向报价(如 EUR/USD),其余货币指数靠含美元的货币对收盘价推。 指标线分两类:主线只画与当前图表直接相关的两个货币,比如 EURUSD 上就是 EUR 和 USD;补充线画其余 GBP、CHF、JPY、CAD、AUD、NZD 共 6 条无关货币线。收盘价统一取当前时间框架下必要货币对的收盘值。 外汇与贵金属属高风险品种,这类跨币种合成指标只反映概率倾向,不能当方向保证。开 MT5 把时间框架切到 H1 以上,先验证主线与价格的贴合度再谈参数。

「多货币指数指标的缓存与初始化骨架」

做跨货币指数指标,第一步不是算公式,而是把 MT5 的缓存和初始化逻辑铺好。原文方案要用 31 个指标缓存:0–7 画最终线,8–14 存带美元的主货币对数据,15–22 放货币指数,23–30 暂存未平滑的随机动量中间值。缓存分配直接在 OnInit 里按指标类型硬编码,美元因为先算指数,其呈现缓存可禁用以省资源。 用户侧的可读性靠 enum + input 实现。下面这段代码定义了三种指标目的地,并在参数面板暴露开关与配色;紧跟着参数的备注会让「Inputs」页显示中文说明而非变量名,降低使用者认知负担。 enum Indicator_Type { Use_RSI_on_indexes = 1, // RSI 索引 Use_MACD_on_indexes = 2, // MACD 索引 Use_Stochastic_Main_on_indexes = 3 // Stochastic 索引 }; input Indicator_Type ind_type=Use_RSI_on_indexes; // 索引对应的指标类型 input bool USD=true; input bool EUR=true; input bool GBP=true; input bool JPY=true; input bool CHF=true; input bool CAD=true; input bool AUD=true; input bool NZD=true; input color Color_USD = Green; // USD 线颜色 input color Color_EUR = DarkBlue; // EUR 线颜色 input color Color_GBP = Red; // GBP 线颜色 input color Color_CHF = Chocolate; // CHF 线颜色 input color Color_JPY = Maroon; // JPY 线颜色 input color Color_AUD = DarkOrange; // AUD 线颜色 input color Color_CAD = Purple; // CAD 线颜色 历史深度别贪多。示例设 500 根柱的人为上限,足够演示概念;但每个缓存都吃内存,若把规模放到百万级柱,终端可能因内存不足崩掉。外汇与贵金属属高风险品种,这类自定义指标仅作概率参考,不预示方向。 初始化还有个隐性坑:MT5 各品种同周期图形靠分钟数据后台拼装,首次启动或切周期会有延迟。必须用 Bars() 核对七组货币对可用柱数都≥用户设定值,再用 CopyTime() 确认零柱时间一致且非 0,否则新柱不同步会让指数错位。这部分写不实就别进 OnCalculate。

MQL5 / C++
enum Indicator_Type
  {
  Use_RSI_on_indexes            = class="num">1, class=class="str">"cmt">// RSI 索引 
  Use_MACD_on_indexes           = class="num">2, class=class="str">"cmt">// MACD 索引  
  Use_Stochastic_Main_on_indexes = class="num">3  class=class="str">"cmt">// Stochastic 索引
  };
class="kw">input Indicator_Type ind_type=Use_RSI_on_indexes;  class=class="str">"cmt">// 索引对应的指标类型
class="kw">input class="type">bool USD=true;
class="kw">input class="type">bool EUR=true;
class="kw">input class="type">bool GBP=true;
class="kw">input class="type">bool JPY=true;
class="kw">input class="type">bool CHF=true;
class="kw">input class="type">bool CAD=true;
class="kw">input class="type">bool AUD=true;
class="kw">input class="type">bool NZD=true;
class="kw">input class="type">class="kw">color Color_USD = Green;          class=class="str">"cmt">// USD 线颜色
class="kw">input class="type">class="kw">color Color_EUR = DarkBlue;       class=class="str">"cmt">// EUR 线颜色
class="kw">input class="type">class="kw">color Color_GBP = Red;            class=class="str">"cmt">// GBP 线颜色
class="kw">input class="type">class="kw">color Color_CHF = Chocolate;      class=class="str">"cmt">// CHF 线颜色
class="kw">input class="type">class="kw">color Color_JPY = Maroon;         class=class="str">"cmt">// JPY 线颜色
class="kw">input class="type">class="kw">color Color_AUD = DarkOrange;     class=class="str">"cmt">// AUD 线颜色
class="kw">input class="type">class="kw">color Color_CAD = Purple;         class=class="str">"cmt">// CAD 线颜色

多币种指数面板的缓冲与绘图绑定

这段声明把 8 个直盘报价、8 条货币指数线、8 个 Stoch 中间缓存分别用数组摊开,NZD 线颜色单独用 Teal 区分。RSI 周期给 9、MACD 快 5 慢 34、Stoch %K 周期 8 且平滑 5,shiftbars 设 500 意味着指标只在最近 500 根柱线起算并渲染,老机器跑长周期不易卡。 arrTime[7] 与 bars_tf[7] 用来对齐各货币对最后已知零值柱时间,避免叉盘因流动性差异出现错位;countVal 在 USD 开关开启时自增并调用 SetIndexBuffer 把 USDplot 绑到第 0 号渲染缓冲。 PlotIndexSetString 把鼠标悬停标签写成 USDplot,PlotIndexSetInteger 连续设定起始柱(shiftbars)、线型(DRAW_LINE)。想验证就开 MT5 把 shiftbars 改成 200,看左上角线头是否提前 300 根出现,外汇与贵金属杠杆高,参数乱调可能放大回测偏差。

MQL5 / C++
class="kw">input class="type">class="kw">color Color_NZD = Teal;                     class=class="str">"cmt">// NZD 线颜色
class="kw">input class="type">class="kw">string rem000        =  ""; class=class="str">"cmt">// 依据指标类型
class="kw">input class="type">class="kw">string rem0000       =  ""; class=class="str">"cmt">// 需要的数值 :
class="kw">input class="type">int rsi_period       =   class="num">9; class=class="str">"cmt">// RSI 周期
class="kw">input class="type">int MACD_fast        =   class="num">5; class=class="str">"cmt">// MACD_fast 周期
class="kw">input class="type">int MACD_slow        =  class="num">34; class=class="str">"cmt">// MACD_slow 周期
class="kw">input class="type">int stoch_period_k   =   class="num">8; class=class="str">"cmt">// Stochastic %K 周期
class="kw">input class="type">int stoch_period_sma =   class="num">5; class=class="str">"cmt">// Stochastics %K 平滑周期
class="kw">input class="type">int shiftbars        = class="num">500; class=class="str">"cmt">// 指标计算柱线数
class="type">class="kw">double  EURUSD[], class=class="str">"cmt">// 报价
        GBPUSD[],
        USDCHF[],
        USDJPY[],
        AUDUSD[],
        USDCAD[],
        NZDUSD[];                      
class="type">class="kw">double    USDx[], class=class="str">"cmt">// 指数
          EURx[],
          GBPx[],
          JPYx[],
          CHFx[],
          CADx[],
          AUDx[],
          NZDx[];
class="type">class="kw">double USDplot[], class=class="str">"cmt">// 货币线结果
       EURplot[],
       GBPplot[],
       JPYplot[],
       CHFplot[],
       CADplot[],
       AUDplot[],
       NZDplot[];
class="type">class="kw">double USDStoch[], class=class="str">"cmt">// 无平滑的 schotastics 的收盘价/收盘价类型中间数据缓存区,
       EURStoch[],
       GBPStoch[],
       JPYStoch[],
       CHFStoch[],
       CADStoch[],
       AUDStoch[],
       NZDStoch[];
class="type">int            i,ii;
class="type">int    y_pos=class="num">0; class=class="str">"cmt">// 信息对象的 Y 坐标变量 
class="type">class="kw">datetime  arrTime[class="num">7]; class=class="str">"cmt">// 最后已知时间的零值柱线数组 (需要同步)  
class="type">int    bars_tf[class="num">7]; class=class="str">"cmt">// 检查不同货币对的可用柱线数量  
class="type">int    countVal=class="num">0; class=class="str">"cmt">// 可执行数量比率  
class="type">int        index=class="num">0;
class="type">class="kw">datetime tmp_time[class="num">1]; class=class="str">"cmt">// 柱线时间的中间数组 
if(USD)
  {
   countVal++;
   SetIndexBuffer(class="num">0,USDplot,INDICATOR_DATA);                     class=class="str">"cmt">// 渲染数组
   PlotIndexSetString(class="num">0,PLOT_LABEL,"USDplot");                   class=class="str">"cmt">// 指标线名称 (当鼠标选择时)
   PlotIndexSetInteger(class="num">0,PLOT_DRAW_BEGIN,shiftbars);             class=class="str">"cmt">// 从哪里,我们开始渲染
   PlotIndexSetInteger(class="num">0,PLOT_DRAW_TYPE,DRAW_LINE);              class=class="str">"cmt">// 绘图风格 (线)
  }

常见问题

实时重算会反复拉取多个品种历史,拖慢图表响应;用中间缓存把各币种预处理结果存下来,主指标只读取缓存,能显著降低卡顿。
先建一个统一长度的数组缓冲对应每个币种,初始化时按品种顺序填坑,绘图绑定到同一根索引,后面扩容只改缓存不碰绘图逻辑。
可以。把品种列表和缓冲结构丢给小布,它能帮你核对初始化是否漏币种、缓存长度是否对齐,并提示可能的越界点。
先抽离原指标里写死品种的地方,换成循环遍历品种数组并写入中间缓存,再把原计算改成读缓存,这就是多币种重构起点。
放在指标首次加载或品种池变更时一次性填充,之后只在新柱或手动刷新时增量更新,避免每 tick 全量重算。