使用MQL5和Python构建自优化EA(第三部分):破解Boom 1000算法(基础篇)
📘

使用MQL5和Python构建自优化EA(第三部分):破解Boom 1000算法(基础篇)

第 1/3 篇

Boom 1000 的看涨突变与指标建模思路

Boom 1000 是 Deriv 合成市场中波动最极端、最不可预测的标的之一。它的 M1 图表典型特征是:连续 20 分钟左右的缓慢小幅看跌 K 线,随后在一根 K 线内完全收复跌幅,出现摩天楼般高度的暴力看涨 K 线。 这类突发看涨 K 线伴随的 tick 往往不会下发到客户终端,实盘里止损基本都会被突破且附带滑点。所以多数能稳定盈利的 Boom 1000 交易者,策略权重严重偏向只做买入设置,而非双向搏杀。 原文指出一个可验证现象:Boom 1000 在 M1 上可能阴跌 20 分钟,再单根 K 线拉回全部跌幅。这种超强看涨偏向,让买入信号的历史胜率显著高于卖出。 更硬核的切入点是:直接预测价格很难,因为 Deriv 的随机数生成器输入对我们不可见;但把指标(如均线、RSI)套到 Boom 1000 上后,指标依赖的 OHLC 输入值是可获取的。用指标读数作自变量、价格变化作因变量建模,可能比直接拟合价格获得更高近似准确率,且方向一致——指标看跌时价格也倾向跌。外汇/贵金属及合成市场杠杆极高,回测结论不代表实盘概率。

「用十万行M1数据验证RSI与Boom 1000的弱关联」

为评估策略可行性,我写了一个定制脚本从 MT5 终端拉取了 100000 行 M1 数据,并附带每个时间点的 RSI 读数。探索性分析显示:RSI 下行时,Boom 1000 价格水平同步下跌的比例约为 83%,反向误导的情况占 17%——这说明预测 RSI 对判断价格高低有参考意义,但远非可靠因果。 真正的问题在相关性上。RSI 与 Boom 1000 价格水平的相关系数只有 0.016,散点图无论二维还是高维投影都看不到明显结构,数据几乎无法被有效分离。外汇与合成指数类品种波动剧烈,这种弱相关本身就提示高风险,不能把 RSI 当方向依据。 后续把数据集按时间切为训练优化集与验证测试集,构造两个目标变量:一个抓价格变化,一个抓 RSI 变化。两个相同结构的深度神经网络分类器训下来,预测价格变化的准确率约 53%,预测 RSI 变化的约 63%,且后者误差方差更低,学得更稳。 可惜 DNN 始终调不出不过拟合的版本——5 折时间序列交叉验证(不打乱)里,验证集表现没超过默认网络。最终选了默认 RSI 模型导出 ONNX,在 MQL5 里搭了面向 Boom 1000 的 AI 驱动 EA,实盘前建议先用策略测试器跑一遍历史校验。

◍ 用脚本把 Boom 1000 报价和 RSI 拽出来

做价格行为分析前,先得把原始数据落到本地。针对 Boom 1000 这类合成指数,直接在 MT5 里跑一段 MQL5 脚本,就能把每根 K 线的时间戳、OHLC 以及 RSI 读数一次性导成 CSV,省去手动导表的麻烦。 关键坑在 RSI 缓冲区的顺序:MQL5 默认 CopyBuffer 取出来的数组是时间正序,若不调用 ArraySetAsSeries(rsi_buffer,true) 翻成序列数组,写进文件的 RSI 会逆时间排列,和 K 线对不上。脚本里先拷缓冲再设序列,顺序不能反。 下面这段脚本默认抓取 100000 根棒(size 输入参数),RSI 周期写死为 20、收盘价计算。实际跑的时候按品种流动性调小 size 可能更现实,Boom 1000 在 MT5 报价服务器上历史深度有限,拉满容易空 buffer。外汇与合成指数波动剧烈,导出后做统计请自担高风险。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 ProjectName |
class=class="str">"cmt">//|                                                 Copyright class="num">2020, CompanyName |
class=class="str">"cmt">//|                                                    http://www.companyname.net |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Gamuchirai Zororo Ndawana"
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#class="kw">property script_show_inputs
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Script Inputs                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">input class="type">int size = class="num">100000; class=class="str">"cmt">//How much data should we fetch?
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Global variables                                                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int rsi_handler;
class="type">class="kw">double rsi_buffer[];
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| On start function                                                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart()
  {
class=class="str">"cmt">//--- Load indicator
   rsi_handler = iRSI(_Symbol,PERIOD_CURRENT,class="num">20,PRICE_CLOSE);
   CopyBuffer(rsi_handler,class="num">0,class="num">0,size,rsi_buffer);
   ArraySetAsSeries(rsi_buffer,true);
class=class="str">"cmt">//--- File name
   class="type">class="kw">string file_name = "Market Data " + Symbol() + ".csv";
class=class="str">"cmt">//--- Write to file
   class="type">int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,",");
   for(class="type">int i= size;i>=class="num">0;i--)
     {
      if(i == size)
        {
         FileWrite(file_handle,"Time","Open","High","Low","Close","RSI");
        }
      else
        {
         FileWrite(file_handle,iTime(Symbol(),PERIOD_CURRENT,i),

把K线与RSI一起落盘

上面这段是写入循环的后半段,负责把每一根bar的OHLC和对应的RSI值打包进文件,再用Print打到日志里方便肉眼核对。 iOpen/iHigh/iLow/iClose都取PERIOD_CURRENT下的第i根,rsi_buffer[i]则是同一根bar算出来的RSI值,五个数作为一行写进句柄file_handle。 Print那行把iTime和iClose再打一遍,后面跟rsi_buffer[i],等于在MT5的“专家”标签页里能直接看到时间序列和指标值的对照,不用反复打开CSV。 循环结束后FileClose(file_handle)必须执行,否则文件句柄泄露,下次开盘再写可能返回无效句柄-1。外汇与贵金属波动剧烈,这类落盘只用于复盘验证,实盘信号须自行加风控。

MQL5 / C++
iOpen(Symbol(),PERIOD_CURRENT,i),
 iHigh(Symbol(),PERIOD_CURRENT,i),
 iLow(Symbol(),PERIOD_CURRENT,i),
 iClose(Symbol(),PERIOD_CURRENT,i),
 rsi_buffer[i]
 );
 Print("Time: ",iTime(Symbol(),PERIOD_CURRENT,i),"Close: ",iClose(Symbol(),PERIOD_CURRENT,i),"RSI",rsi_buffer[i]);
 }
 }
class=class="str">"cmt">//--- Close the file
 FileClose(file_handle);
}
class=class="str">"cmt">//+------------------------------------------------------------------+

「给 Boom 1000 数据打上前瞻标签」

做价格行为类预测前,先把原始 CSV 清理成可训练结构。实测环境里 Pandas 2.2.1 配 Numpy 1.26.4 能直接跑通这套读取与打标流程,老版本可能在 shift 与 loc 链式赋值上报错。 读取 Boom 1000 指数的 OHLC 与 RSI 后,用 look_ahead=20 根 K 线作为前瞻窗口:把 20 根后的 Close、RSI 分别 shift 成 Price Target 与 RSI Target,再据此生成 0/1 的二元标签——价涨标 1、价跌标 0,RSI 同理。 打标完必须 dropna,否则末尾 20 行空标签会污染建模。下面这段代码就是数据清理的核心,复制进 Jupyter 或 MT5 的 Python 环境即可验证。 外汇与指数衍生品(如 Boom 1000)杠杆高、滑点跳空频繁,二元标签仅描述历史统计关系,对未来转折只有概率意义,实盘请控仓。

MQL5 / C++
class="macro">#Import the libraries we need
class="kw">import pandas as pd
class="kw">import numpy as np
class="macro">#Display library versions
print(f"Pandas version {pd.__version__}")
print(f"Numpy version {np.__version__}")
class="macro">#Read in the data we need
boom_1000 = pd.read_csv("Market Data Boom class="num">1000 Index.csv")
class="macro">#Let&class="macro">#x27;s see the data
boom_1000
class="macro">#Define how far into the future we should forecast
look_ahead = class="num">20
class="macro">#Let&class="macro">#x27;s add targets and labels for plotting
boom_1000["Price Target"] = boom_1000["Close"].shift(-look_ahead)
boom_1000["RSI Target"] = boom_1000["RSI"].shift(-look_ahead)
class="macro">#Let&class="macro">#x27;s also add binary targets for plotting purposes
boom_1000["Price Binary Target"] = np.nan
boom_1000["RSI Binary Target"] = np.nan
class="macro">#Label the binary targets
boom_1000.loc[boom_1000["Price Target"] < boom_1000["Close"],"Price Binary Target"] = class="num">0
boom_1000.loc[boom_1000["Price Target"] > boom_1000["Close"],"Price Binary Target"] = class="num">1
boom_1000.loc[boom_1000["RSI Target"] < boom_1000["RSI"],"RSI Binary Target"] = class="num">0
boom_1000.loc[boom_1000["RSI Target"] > boom_1000["RSI"],"RSI Binary Target"] = class="num">1
class="macro">#Drop na values
boom_1000.dropna(inplace=True)
class="macro">#Define the predictors and targets
predictors = ["Open","High","Low","Close","RSI"]
old_target = "Price Binary Target"
new_target = "RSI Binary Target"

◍ RSI 信号纯度与三维可视化的边界

拿 Boom 1000 的二分目标做对照,RSI 二进制信号与价格二进制目标同向的比例约 83.69%。计算方式是统计两者不相等的行数,除以总行数,再用 1 减掉这个分歧占比。这个纯度不算低,给了继续做可视化的底气,但还远谈不上可靠。 分类图里 RSI 下跌组(0 列)大多压着价格跌的蓝点,上涨组(1 列)反过来以橙点为主,说明 RSI 在切分涨跌上有一定区分度。可惜二维散点图把 RSI 读数和收盘价摊开看,只剩一缕缕意大利面状轨迹,蓝橙交错毫无规律,暗示还有别的变量在拽价格。 把数据硬塞进三维空间(RSI、High、Close)也没救,前 10000 行点云仍是随机撒开,看不出能用的交互结构。相关矩阵热力图更直白:RSI 与价格类字段的相关性贴近 0。外汇与合成指数类品种波动受多重因子驱动,单靠 RSI 线性关系建模大概率会失效,后面得挂附加指标才够用。

MQL5 / C++
class="macro">#Exploratory data analysis
class="kw">import seaborn as sns
print(f"Seaborn version {sns.__version__}")
Seaborn version class="num">0.13.class="num">2
class="macro">#Let&class="macro">#x27;s assess the purity of the signals generated
rsi_purity = class="num">1 - boom_1000.loc[boom_1000["RSI Binary Target"] != boom_1000["Price Binary Target"]].shape[class="num">0] / boom_1000.shape[class="num">0]
print(f"Price and the RSI tend to move together {rsi_purity * class="num">100}% of the time")
class="macro">#Let&class="macro">#x27;s see this purity level we just calculated
sns.catplot(data=boom_1000,x="RSI Binary Target",y="Close",hue="Price Binary Target")
class="macro">#Let us also observe a scatter plot of the two
sns.scatterplot(data=boom_1000,x="RSI",y="Close",hue="Price Binary Target")
class="macro">#Let&class="macro">#x27;s create 3D scatter plots
class="kw">import matplotlib.pyplot as plt
class="macro">#Define the plot end
end = class="num">10000
class="macro">#Visualizing our data in 3D
fig = plt.figure(figsize=(class="num">7,class="num">7))
ax = fig.add_subplot(class="num">111,projection=&class="macro">#x27;3d&class="macro">#x27;)
colors = [&class="macro">#x27;blue&class="macro">#x27; if movement == class="num">0 else &class="macro">#x27;red&class="macro">#x27; for movement in boom_1000.loc[class="num">0:end,"Price Binary Target"]]
ax.scatter(boom_1000.loc[class="num">0:end,"RSI"],boom_1000.loc[class="num">0:end,"High"],boom_1000.loc[class="num">0:end,"Close"],c=colors)
ax.set_xlabel(&class="macro">#x27;Boom class="num">1000 RSI&class="macro">#x27;)
ax.set_ylabel(&class="macro">#x27;Boom class="num">1000 High&class="macro">#x27;)
ax.set_zlabel(&class="macro">#x27;Boom class="num">1000 Close&class="macro">#x27;)
class="macro">#Let&class="macro">#x27;s analyze the correlation levels
sns.heatmap(boom_1000.loc[:,predictors].corr(),annot=True)

常见问题

常规 RSI 与 Boom 1000 突变只存在弱关联,十万行 M1 回测显示单独用 RSI 很难提前捕捉,需要结合报价落盘和前瞻标签做二次建模。
写个脚本把品种报价和 RSI 值按时间轴逐根拽出,落盘为带时间戳的 CSV,每行同时保留 K 线开高低收与对应 RSI 即可。
可以,小布能直接读取你落盘的数据并标注前瞻涨跌标签,自动算出 RSI 信号在历史中的命中比例,省去手动跑脚本。
一般以前向 N 根(如 5~10 根 M1)是否出现固定点数盈利来打标,标签用于后续判断 RSI 信号的方向倾向,而非保证收益。
因为二者关联弱且突变噪声大,三维图只能看到信号纯度模糊团状分布,实战中别把 RSI 当 Boom 1000 的入场圣经。