重构MQL5中的经典策略(第三部分):富时100指数预测(基础篇)
AI策略海洋里先解决筛选成本
AI在当代交易者策略里的落地场景几乎看不到天花板,但一个人的盯盘时间和回测精力都有硬上限。 现实是:没有哪个交易者能逐条手工验证海量AI策略再决定仓位分配,光是扫一遍信号源就可能耗掉一整天。 这篇系列用MT5实盘环境把各类基于AI的策略拆开重做,帮你按自己的风险偏好和品种习惯做减法,而不是堆更多看不懂的模型。 外汇与贵金属本身杠杆高、跳空频繁,接任何AI策略前先在MT5策略测试器跑历史数据,确认最大回撤和样本外表现再上真金。
「从富时100权重结构里抠交易线索」
伦敦证券交易所成立于1801年,是欧洲最大证交所,官网披露当前所有上市公司总市值约4.4万亿英镑。这个体量决定了它的指数波动不是单一标的能撬动的。 FTSE 100 跟踪伦交所市值前100的公司,也就是俗称的蓝筹股群体。这类标的长期业绩记录被市场视作相对安全,但安全不等于无波动,指数层面仍有可捕捉的均值偏移。 一个可行的切入点是:用指数当前收盘价,叠加上榜的10只大盘股表现,反推指数后续收盘的偏移方向。逻辑上,若这10只权重股同步走强,指数收高概率倾向抬升;若分化严重,则预测置信度下降。 外汇与贵金属交易者借道这类跨市场映射时,须明确这是高风险尝试,伦交所数据与MT5外汇盘之间有时差与流动性断层,任何推断都只是概率而非确定性。
◍ 用线性回归给指数做跨周期外推
我们这套 AI 驱动的 EA 完全用 MQL5 写成,核心是一个多元线性回归模型,总共吃 12 个输入,去预测富时 100 指数往后 20 根 K 线的收盘价。模型在时间框架上不挑,切换周期无需反复改结构,预测步长也能动态调。 输入在做预测前统一走了 Z 标准化:拿每列的均值和标准差把每一个特征归一化缩放,目标变量就是 20 步后的收盘价本身。这一处理让不同量纲的因子能直接进同一套权重运算。 树模型在这里天然吃亏——它靠把数据切成组、预测时返回最匹配组的均值,本质不会外推。五个时刻的输入只要够相似,树模型可能给出完全一样的收盘价。线性回归则能沿权重方向推出样本外数值,对证券未来收盘价给出有梯度差异的估计,这种能力在价格行为突破临界时尤其关键。 外汇与贵金属市场高波动、高杠杆,此类外推仅提供概率性参考,实盘前务必在 MT5 用历史数据回测验证。
从脚本起步搭线性回归骨架
先把交易想法落成一个 MQL5 脚本,比直接写 EA 更利于看清各模块怎么咬合。第一步是抓数据并做规范化,输入塞进一个矩阵,目标值单独放另一个矩阵,这样后面算参数时不会搅在一起。 模型参数用多元线性回归的闭式解直接算,不用迭代。系数矩阵里第一项其实是偏置:当所有输入为 0 时目标的平均值;在 FTSE 100 这个场景里,若成分股价格全为 0 英镑,指数未来均值也数学上趋近 0,实战里没什么直观含义。第二个系数代表其他股价不变时,某只股票每变动一单位带来的指数边际变化,比如阿迪尔(ADM.LSE)价格涨一个单位,模型给出的指数未来收盘价倾向轻微下移。 预测本身不复杂:把每只股票现价比上各自系数相乘,全部乘积相加即得。脚本里先定全局变量,fetch 取 2000 根 K 线、look_ahead 设 20 根作为前瞻,list_of_companies 存了 10 只 LSE 股票加 UK100 本身,input_matrix 是 12 行 fetch 列的零矩阵——多出来的那行留给截距向量。 跑通这套脚本后,你就知道数据怎么流、系数怎么出,下一步包成 EA 不过是加信号与下单逻辑。外汇与贵金属以外指数也属高波动标的,回测结论仅代表历史样本,实盘概率会漂移。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| UK100.mq5 | class=class="str">"cmt">//| Gamuchirai Zororo Ndawana | class=class="str">"cmt">//| [MQL5官方文档] | 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=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Script program start function | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//class="num">1) ADM.LSE - Admiral class=class="str">"cmt">//class="num">2) AAL.LSE - Anglo American class=class="str">"cmt">//class="num">3) ANTO.LSE - Antofagasta class=class="str">"cmt">//class="num">4) AHT.LSE - Ashtead class=class="str">"cmt">//class="num">5) AZN.LSE - AstraZeneca class=class="str">"cmt">//class="num">6) ABF.LSE - Associated British Foods class=class="str">"cmt">//class="num">7) AV.LSE - Aviva class=class="str">"cmt">//class="num">8) BARC.LSE - Barclays class=class="str">"cmt">//class="num">9) BP.LSE - BP class=class="str">"cmt">//class="num">10) BKG.LSE - Berkeley Group class=class="str">"cmt">//class="num">11) UK100 - FTSE class="num">100 Index class=class="str">"cmt">//+-------------------------------------------------------------------+ class=class="str">"cmt">//| Global variables | class=class="str">"cmt">//+-------------------------------------------------------------------+ class="type">int fetch = class="num">2000; class="type">int look_ahead = class="num">20; class="type">class="kw">double mean_values[class="num">11],std_values[class="num">11]; class="type">class="kw">string list_of_companies[class="num">11] = {"ADM.LSE","AAL.LSE","ANTO.LSE","AHT.LSE","AZN.LSE","ABF.LSE","AV.LSE","BARC.LSE","BP.LSE","BKG.LSE","UK100"}; vector intercept = vector::Ones(fetch); matrix target = matrix::Zeros(class="num">1,fetch); matrix coefficients; matrix input_matrix = matrix::Zeros(class="num">12,fetch); class="type">void OnStart() { class=class="str">"cmt">//--- Fetch the target target.CopyRates("UK100",PERIOD_CURRENT,COPY_RATES_CLOSE,class="num">1,fetch); class=class="str">"cmt">//--- Fill in the class="kw">input matrix for(class="type">int i = class="num">0; i < class="num">11; i++) { class=class="str">"cmt">//--- Add the symbol to market watch SymbolSelect(list_of_companies[i],true); class=class="str">"cmt">//--- Fetch historical data vector temp = vector::Zeros(fetch);
「把多品种收盘序列灌进回归矩阵」
上面这段把前面准备好的品种列表逐个拉取收盘价,做标准化后塞进同一张输入矩阵。CopyRates 取的是 PERIOD_CURRENT 下 1+look_ahead 根 K 线的收盘价,look_ahead 若设为 5,实际就抓 6 个样本点,样本极少,外汇与贵金属品种上跑这套极易过拟合,高风险。 均值和标准差先按各自序列算,再用 (x-mean)/std 把量纲拉开,避免某个指数波动绝对值大就 dominate 了回归。Row(temp,i) 把第 i 个品种的标准化行写进矩阵第 i 行,循环结束后再用 Row(intercept,11) 补一行截距项,矩阵变成 12 行。 矩阵拼好后 Print 出来肉眼核对,再用 target.MatMul(input_matrix.PInv()) 求系数——PInv 是伪逆,普通最小二乘闭式解。最后把系数转置打印,UK100 那行就是各品种对它的线性权重。打开 MT5 接上前面几节的数据,直接复制这段就能看到你自选列表的回归系数。
temp.CopyRates(list_of_companies[i],PERIOD_CURRENT,COPY_RATES_CLOSE,class="num">1+look_ahead,fetch); class=class="str">"cmt">//--- Store the mean value and standard deviation, also scale the data mean_values[i] = temp.Mean(); std_values[i] = temp.Std(); temp = ((temp - mean_values[i]) / std_values[i]); class=class="str">"cmt">//--- Add the data to the matrix input_matrix.Row(temp,i); } class=class="str">"cmt">//--- Add the intercept input_matrix.Row(intercept,class="num">11); class=class="str">"cmt">//--- Show the class="kw">input data Print("Input data:"); Print(input_matrix); class=class="str">"cmt">//--- Calculating coefficient values coefficients = target.MatMul(input_matrix.PInv()); class=class="str">"cmt">//--- Display the coefficient values Print("UK100 Coefficients:"); Print(coefficients.Transpose());