依据价格相关性的统计数据过滤信号(基础篇)
「用相关性统计给信号做减法」
在 MT5 里跑趋势或反转策略,最怕信号扎堆却同向失效。一个实用做法是先统计候选品种间的价格相关性,把高度同向波动的标的视作冗余,只保留相关性低的一组来触发下单,相当于用统计关系给信号池做过滤。 原文作者 Михаил Тарачков 在 2014-03-07 发布的这一思路,截至统计时获得 2504 次查看与 16 条讨论,说明老交易员对「信号去重」有稳定需求。 具体落地时,可每日用 iPearson 或自定义相关系数函数计算近 20 根 H1 收盘的 pairwise 相关,超过 0.8 的配对只留一个进场,其余屏蔽。外汇与贵金属杠杆高、跳空频繁,这种过滤只降低拥挤度,不消除方向误判风险,相关结论均属概率倾向。
◍ 用模拟EA拆穿随机游走假设
很多人把市场想成掷硬币:扔100次大约50次正面,下一次正反概率仍是各半,前后互不影响。照这个逻辑,新K线出现时价格上扬和下降机会相等,历史柱根本不干扰当前柱,系统只要把数学期望调正就能躺赢——这想法太浪漫,也离谱。 我们直接在MT5里用MQL5向导建个EA模板,不真发单(点差和库存费会污染结果),只记录和模拟在1根、2根、3根柱收盘后买入的情形。测试区间取EUR/USD从2000年1月1日到2010年12月31日,跨度十一年。 跑完看收盘上涨占比:全样本里上涨比例明显偏离50%;若前一天收盘跌,次日上涨概率抬升;连跌两天、三天后,下一根反弹收阳的比例继续走高。价格有‘把损失赢回来’的倾向,前柱对当前柱影响相当显著。外汇和贵金属杠杆高,这种统计偏向只说明概率倾斜,不等于无风险套利。 下面这段是模拟用的核心代码,重点看OnDeinit里怎么算上涨百分比、OnTick里怎么抓时间结构:
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| explorer.mq5 | class=class="str">"cmt">//| Copyright class="num">2011, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2011, MetaQuotes Software Corp." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class=class="str">"cmt">//---Variables--- class="type">class="kw">double profit_percent,open_cur,close_cur; class="type">class="kw">double profit_trades=class="num">0,loss_trades=class="num">0,day_cur,hour_cur,min_cur,count; class="type">class="kw">double open[],close[]; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason) { class=class="str">"cmt">/* Calculate percent of closures with increase from the total number */ profit_percent=NormalizeDouble(profit_trades*class="num">100/(profit_trades+loss_trades),class="num">2); Print("Percent of closures with increase ",profit_percent,"%"); class=class="str">"cmt">// Enter data to the Journal } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert tick function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//---find out the time--- class="type">MqlDateTime time; class=class="str">"cmt">// Create a structure to store time TimeToStruct(TimeCurrent(),time); class=class="str">"cmt">// Structuring the data day_cur=time.day_of_week; class=class="str">"cmt">// Receive the value of the current day hour_cur=time.hour; class=class="str">"cmt">// Receive the current hour
用前一根阴线触发下一根涨跌统计
这段逻辑只盯一种情形:当上一根柱子收阴(close[1] < open[1])且尚未记录时,把当前根的开盘价 open_cur 存下来,count 置 1。注释里把连续三根阴线的条件划掉了,说明实盘只取单根信号,避免过度过滤。 等当前根走完(open_cur 不等于新的 open[0]),就把刚成型那根的收盘价 close[1] 赋给 close_cur,count 归零。此时若 close_cur >= open_cur,profit_trades 加 1,否则 loss_trades 加 1。 CopyOpen/CopyClose 都取 4 根并 ArraySetAsSeries 置为时间序列,索引 0 是最新根。你在 MT5 里把这段塞进 OnTick,跑 EURUSD 的 M1,肉眼能看到 profit_trades 和 loss_trades 随每根阴线后的第一根结果累加,外汇高杠杆下样本偏差会很大,仅作概率观察。
min_cur=time.min; class=class="str">"cmt">// Receive the current minute class=class="str">"cmt">//---Find out the prices--- CopyOpen(NULL,class="num">0,class="num">0,class="num">4,open);ArraySetAsSeries(open,true); CopyClose(NULL,class="num">0,class="num">0,class="num">4,close);ArraySetAsSeries(close,true); if(close[class="num">1]<open[class="num">1]class=class="str">"cmt">/*&&close[class="num">2]<open[class="num">2]&&close[class="num">3]<open[class="num">3]*/ && count==class="num">0) class=class="str">"cmt">// If it closed with a loss { open_cur=open[class="num">0]; class=class="str">"cmt">// Remember open price of the current bar count=class="num">1; } if(open_cur!=open[class="num">0] && count==class="num">1) class=class="str">"cmt">// The current bar has closed { close_cur=close[class="num">1]; class=class="str">"cmt">// Remember the close price of the formed bar count=class="num">0; if(close_cur>=open_cur)profit_trades+=class="num">1; class=class="str">"cmt">// If the close price is higher than open, else loss_trades+=class="num">1; class=class="str">"cmt">// +class="num">1 to closures with profit, otherwise +class="num">1 to closures with loss } }
「用交易日过滤把EA调稳」
把价格行为里的星期规律当成过滤器接进现有系统,往往比另起炉灶更划算。这里用 TDW 思路做验证:先假设一周内某些天更适合做多、某些天更适合做空,再用历史回测挑出最佳买卖日组合。 底层信号很简单——双 MA 交叉叠 MACD 直方图:快线自下而上穿慢线且 MACD 柱低于零轴做多,反之上穿且柱高于零轴做空,跟踪止损挂一个点,固定 0.1 手。原版 EA 把类封装在独立头文件里,用 MqlDateTime 抓服务器时间的星期索引(1=周一…5=周五)来比对设定的交易日期。 回测区间 EURUSD H1、2010 全年,未加过滤器时净值主要在前半段爬升;加上「周五买、周一卖」的日期过滤后,整段区间都更平稳。净盈利从约 3800 美元降到约 2800 美元(少 26%),但交易次数由 218 次砍到 62 次(近 3.5 倍),点差成本省下约 312 美元,盈利单比例升到 57%,单笔平均盈利提高到 113 美元。 外汇与贵金属属高风险品种,历史星期效应随时可能失效,上 MT5 跑一遍自己品种的 H1 回测再决定是否实盘跟随。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| moving.mqh | class=class="str">"cmt">//| Copyright class="num">2011, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2011, MetaQuotes Software Corp." class="macro">#class="kw">property link "[MQL5官方文档] class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Класс my_expert | class=class="str">"cmt">//+------------------------------------------------------------------+ class my_expert { class=class="str">"cmt">// 定义一个EA类 class=class="str">"cmt">// 私有成员 class="kw">private: class="type">int ma_red_per,ma_yel_per; class=class="str">"cmt">// 两条均线的周期参数 class="type">int ma_red_han,ma_yel_han,macd_han; class=class="str">"cmt">// 指标句柄 class="type">class="kw">double sl,ts; class=class="str">"cmt">// 止损与跟踪止损价位 class="type">class="kw">double lots; class=class="str">"cmt">// 固定手数 class="type">class="kw">double MA_RED[],MA_YEL[],MACD[]; class=class="str">"cmt">// 存放指标值的数组 class="type">MqlTradeRequest request; class=class="str">"cmt">// 交易请求结构体 class="type">MqlTradeResult result; class=class="str">"cmt">// 服务器返回结果结构体