一个为莫斯科交易所期货开发的点差策略实例·综合运用
(3/3)· 用 Si 与 RTS 的负相关把跨品种价差做成可回测的自动化信号,收尾篇补齐策略环境与风控提示
用点差占价百分比触发跨品种信号
点差策略的收尾一种玩法,是把点差绝对值换成「占主交易品种最新价的百分比」来判定。当 spread_percent = Spread_curr / last * 100 越过 spread_delta 阈值,就视为价差异常扩张,给出跨品种配对信号。 OnTick 里先等指标 BarsCalculated 与当前图表 Bars 对齐,避免跨品种数据不同步;取未完成柱的点差缓冲后,用 SymbolInfoTick 拿 symbol1 的 last 价算百分比。绝对值判断用 MathAbs,所以正负价差扩张都算触发。 触发后若无持仓,DoTrades 按策略方向同时进两个品种;已有持仓则 ReverseTrades 反转。另一层退出逻辑是固定 profit_percent:当账户浮盈占余额比 ≥ 2% 时,双向平仓落袋。 spread_delta 的优化区间设在 0.1%–1%。回测中「买一卖二」规则明显优于「卖一买二」,但这只是该样本周期内的倾向,外汇与贵金属自带高杠杆高风险,实盘前务必在 MT5 用历史数据重跑参数。
class="type">void OnTick() { if(isNewBar()) PrintFormat("新柱 %s 在 %s 开启", _Symbol, TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS)); class=class="str">"cmt">//--- 等待指标数据的刷新,因为它工作于两个交易品种上 if(BarsCalculated(ind_spreadLR)==Bars(_Symbol, _Period)) { class=class="str">"cmt">//--- 取得当前柱(今天)的点差数值 class="type">class="kw">double SpreadValues[]; class="type">int copied=CopyBuffer(ind_spreadLR, class="num">0, class="num">0, class="num">1, SpreadValues); class="type">class="kw">double Spread_curr=SpreadValues[class="num">0]; class=class="str">"cmt">// 当前未完成的柱的点差 if(copied!=-class="num">1) { class="type">MqlTick tick; SymbolInfoTick(symbol1, tick); class="type">class="kw">double last=tick.last; class="type">class="kw">double spread_percent=Spread_curr/last*class="num">100; class=class="str">"cmt">//--- 如果点差 % 达到了 spread_delta 的数值 if(MathAbs(spread_percent)>=spread_delta) { PrintFormat("点差达到了 %.1f%% (%G) %s", spread_percent, TimeToString(TimeCurrent(), TIME_SECONDS), Spread_curr); class=class="str">"cmt">//--- 如果没有持仓,就在两个交易品种中进入市场 if(PositionsTotal()==class="num">0) DoTrades(Spread_curr, strategy, symbol1, class="num">1, symbol2, class="num">1); class=class="str">"cmt">//--- 有持仓,就反转交易 else ReverseTrades(Spread_curr, positionstype, symbol1, symbol2); } class=class="str">"cmt">//--- 点差在可接受的范围之内,检查浮赢 - 是时候平仓吗? else { class="type">class="kw">double profit=AccountInfoDouble(ACCOUNT_PROFIT); class="type">class="kw">double balance=AccountInfoDouble(ACCOUNT_BALANCE); if(profit/balance*class="num">100 >= profit_percent) { class=class="str">"cmt">//--- 达到了要求的浮盈,就获利平仓 trade.PositionClose(symbol1); trade.PositionClose(symbol2); positionstype=class="num">0; } } } } }
「把 MT5 测试器当研究探针用」
前面那 4 个点差策略的回测和优化结果,只是在有限时间段里跑出来的,带随机性,不能直接当开仓依据。写它们的目的只有一个:让你看清在 MetaTrader 5 里验证和调试策略有多顺手。 MT5 测试器给 EA 开发者塞了不少硬货:自动拉取 EA 用到的所有品种分时历史;可视化调试里把指标、成交流、历史成交和专家日志叠在一起看;可视化模式下指标自动加载;用真实历史重演真实环境测策略;按自定义目标函数做多线程参数优化;调动数千个测试代理加速;按自定义规则把优化结果画出来看;支持多资产毫秒级同步策略回测;还能在测试里直接打断点、单步跑、查变量。 实务上,测试器最该被当成找方向的探针,而不是印钞机。先用单参数优化快速拿到倾向性结论,再往里加规则或改旧规则跑全量 EA 优化。算得慢就去接 MQL5 云网络,那是平台专门设计的分布式算力,能明显压低等待时间。外汇和贵金属杠杆高、滑点跳空频繁,任何测试结论都只是概率倾向,上真仓前务必自己开 MT5 复跑一遍。
◍ 画得少,看得清
点差策略回测里用的 Delta[i]=Close[i]-Close[i-1] 而非绝对收盘价,本质是剥离价格水平、只抓相对变动;本文四个 EA 都按每品种 1 手固定量跑,RTS-12.16 期货点值挂钩美元兑卢布汇率的 0.2 倍,所以 2016-10-25 那张优化截图的结果高度依赖当时美俄汇率,换时段重跑大概率漂移。 代码是在最理想条件写的:不处理发单失败、断线、手续费与滑点,也没管某个品种整根柱无报价时指标死等同步的坑。期货流动性常在合约过期后抬升,这部分同样没覆盖。 附带的 CalcShowRegression_script 用 CGraphic 加 Alglib 画点差散点趋势线,TwoSymbolsSpread_Ind 画双品种点差柱,四个 EA 分别吃回归系数符号翻转、点差符号翻转(新柱/当柱稳 N tick/首刻触百分比)进场——直接丢进 MT5 可视化测试,比盯着静态图更能看清回归线怎么被重算。 外汇与贵金属自带高杠杆高风险,这类价差模型在实盘前务必自己跑一遍带点差和滑点的压力测试,别把实验室数字当依据。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 双样本 F 检验| class=class="str">"cmt">//| 该测试检验了关于给定 | 的离散度的三个假设。 class=class="str">"cmt">//| 样本。| class=class="str">"cmt">//| 输入参数:| class=class="str">"cmt">//| X - 样本 class="num">1。索引从 class="num">0 到 N-class="num">1 的数组。 class=class="str">"cmt">//| N - 样本数量。| class=class="str">"cmt">//| Y - 样本 class="num">2.索引从 class="num">0 到 M-class="num">1 的数组。 class=class="str">"cmt">//| M - 样本数量。| class=class="str">"cmt">//| 输出参数:| class=class="str">"cmt">//| BothTails - 双尾检验的 p 值。 class=class="str">"cmt">//|如果 BothTails 小于给定值 class=class="str">"cmt">///显著性水平,零假设为 class=class="str">"cmt">//| 已拒绝。| class=class="str">"cmt">//| LeftTail - 左尾检验的 p 值。 class=class="str">"cmt">//|如果 LeftTail 小于给定值 class=class="str">"cmt">//| 在显著性水平上,零假设为 class=class="str">"cmt">//| 已拒绝。| class=class="str">"cmt">//| RightTail - 右尾检验的 p 值。 class=class="str">"cmt">//|如果右尾小于给定值 class=class="str">"cmt">///显著性水平,零假设为 class=class="str">"cmt">//| 已拒绝。| class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">static class="type">void CAlglib::FTest(const class="type">class="kw">double &x[],const class="type">int n,const class="type">class="kw">double &y[], const class="type">int m,class="type">class="kw">double &bothTails,class="type">class="kw">double &leftTail, class="type">class="kw">double &rightTail) class="type">class="kw">double m_x[]; ... CMatrixDouble xy(size,class="num">2); xy[class="num">0].Set(class="num">0, m_x[class="num">0]);