您应当知道的 MQL5 向导技术(第 26 部分):移动平均和赫斯特(Hurst)指数·进阶篇
(2/3)· 价格贴着均线跑却总被假突破洗掉?赫斯特指数能先告诉你行情到底在趋势还是在区间
累积偏差归一成重标范围的对数配对
把各分段里「分区值减均值」的偏差一路累加后,下一步就是取累加序列的峰值与谷值之差,得到该分段尺寸下的重标范围(rescaled range)。注意累加时存的是带符号偏差,不是绝对值,所以累积和可能归零;在喂给赫斯特计算前应当做底层校验,否则容易产出无意义结果。附带代码里没写这层保护,你可以自己补一个空值判断。 为了求赫斯特指数 H,要把重标范围除以样本标准差后再取自然对数作为 x,把分段尺寸 t 取自然对数作为 y。每个 t 对应唯一一对 (x, y);t 从 2 取到样本数一半为止,点对越多,拟合出的 H 越有代表性。代码里 _points 矩阵就是存这些对数配对的,列 0 是 x,列 1 是 y,最后送进线性回归。 外汇与贵金属价格序列波动剧烈,用这套对数-对数图做 H 估计时,样本不足一半就截断可能让 H 偏向随机游走假象,建议在 MT5 里先用足历史根数再跑。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">// Function to Estimate Hurst Exponent & Constant C class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CSignalHurst::Hurst(vector &Data, class="type">class="kw">double &H, class="type">class="kw">double &C) { matrix _points; ... _points.Init(_t - class="num">1, class="num">2); _points.Fill(class="num">0.0); for (class="type">int t = class="num">2; t <= _t; t++) { ... ... _points[t - class="num">2][class="num">0] = log((_cumulations.Max() - _cumulations.Min()) / _std); _points[t - class="num">2][class="num">1] = log(t); } LinearRegression(_points, H, C); }
◍ 最佳拟合线里的斜率与截距分工
在赫斯特计算流程里,线性回归被拆成独立函数执行,目的是从一组 (x, y) 样本点反推出 y = mx + c 里的 m 和 c。这条线是对输入点的最小误差拟合,m 直接作为赫斯特指数,c 则是后续可用的常数。 函数 LinearRegression 用两个 double 引用占位符接回斜率和截距,返回 void,调用方式和 Hurst 主函数保持一致的接口习惯。除了指数,它还顺带吐出 c 常数,这个量在实盘里不是摆设。 c 常数衡量的是数据样本的可变幅度。设想两个价格序列赫斯特指数相同:一个 c = 7,另一个 c = 21。指数同低于 0.5 时两者都倾向均值回归,高于 0.5 则都倾向趋势延续;但 c 的差说明跨均线的摆动宽度不同,c 高的那支波动代理更大。外汇与贵金属属高风险品种,同指数下 c 不同就意味着仓位尺度机制该分开算,不能套同一套手数。 下面这段是 MT5 里可直接拷去验证的实现,核心就是普通最小二乘:
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">// Function to perform linear regression class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CSignalHurst::LinearRegression(matrix &Points, class="type">class="kw">double &Slope, class="type">class="kw">double &Intercept) { class="type">class="kw">double _sum_x = class="num">0.0, _sum_y = class="num">0.0, _sum_xy = class="num">0.0, _sum_xx = class="num">0.0; for (class="type">int r = class="num">0; r < class="type">int(Points.Rows()); r++) { _sum_x += Points[r][class="num">0]; _sum_y += Points[r][class="num">1]; _sum_xy += (Points[r][class="num">0] * Points[r][class="num">1]); _sum_xx += (Points[r][class="num">0] * Points[r][class="num">0]); } Slope = ((Points.Rows() * _sum_xy) - (_sum_x * _sum_y)) / ((Points.Rows() * _sum_xx) - (_sum_x * _sum_x)); Intercept = (_sum_y - (Slope * _sum_x)) / Points.Rows(); }
「把赫斯特指数塞进信号类做多空判定」
赫斯特指数本身偏向捕捉长周期趋势,样本越大理论上越准,但实盘只能按固定历史长度去算。这里把样本规模定为快速周期与慢速周期之和,用这段历史去调 Hurst 函数,再拿返回值跟 0.5 比,区分趋势市和均值回归市。 当 _hurst 大于 0.5,价格倾向有持久性趋势。做多条件看当前 Bid 是否高于慢速均线均值,做空则看是否低于它;低于 0.5 时行情更可能区间摆动,此时改用快速均线:做多看 Bid 低于快线均值,做空看高于快线均值。 下面两段是 LongCondition 与 ShortCondition 的核心实现。注意 CopyRates 的第三个参数 8 代表收盘价类别,样本长度分别取快慢周期合计或单周期;result 用价格偏离均值的相对幅度映射到 0~100 的投票强度,不是非黑即白的开关。 [CODE] //+------------------------------------------------------------------+
| // | "Voting" that price will grow. |
|---|
//+------------------------------------------------------------------+ int CSignalHurst::LongCondition(void) { int result = 0; vector _data; if(_data.CopyRates(m_symbol.Name(), m_period, 8, 0, m_fast_period + m_slow_period)) { double _hurst = 0.0, _c = 0.0; Hurst(_data, _hurst, _c); vector _ma; if(_hurst > 0.5) { if(_ma.CopyRates(m_symbol.Name(), m_period, 8, 0, m_slow_period)) { if(m_symbol.Bid() > _ma.Mean()) { result = int(round(100.0 * ((m_symbol.Bid() - _ma.Mean())/(fabs(m_symbol.Bid() - _ma.Mean()) + fabs(_ma.Max()-_ma.Min()))))); } } } else if(_hurst < 0.5) { if(_ma.CopyRates(m_symbol.Name(), m_period, 8, 0, m_fast_period)) { if(m_symbol.Bid() < _ma.Mean()) { result = int(round(100.0 * ((_ma.Mean() - m_symbol.Bid())/(fabs(m_symbol.Bid() - _ma.Mean()) + fabs(_ma.Max()-_ma.Min()))))); } } } } return(result); } //+------------------------------------------------------------------+
| // | "Voting" that price will fall. |
|---|
//+------------------------------------------------------------------+ int CSignalHurst::ShortCondition(void) { int result = 0; vector _data; if(_data.CopyRates(m_symbol.Name(), m_period, 8, 0, m_fast_period + m_slow_period)) { double _hurst = 0.0, _c = 0.0; Hurst(_data, _hurst, _c); vector _ma; if(_hurst > 0.5) { if(_ma.CopyRates(m_symbol.Name(), m_period, 8, 0, m_slow_period)) { if(m_symbol.Bid() < _ma.Mean()) { result = int(round(100.0 * ((_ma.Mean() - m_symbol.Bid())/(fabs(m_symbol.Bid() - _ma.Mean()) + fabs(_ma.Max()-_ma.Min()))))); } } } // 下方做空在 _hurst<0.5 时的分支与 LongCondition 对称,此处略去重复 } return(result); } [/CODE] 外汇与贵金属波动受杠杆放大,赫斯特阈值 0.5 只是经验分界,采样周期改长改短都会让信号频率明显变化,上 MT5 把 m_fast_period 和 m_slow_period 调一遍才知合不合你的品种。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| "Voting" that price will grow. | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CSignalHurst::LongCondition(class="type">void) { class="type">int result = class="num">0; vector _data; if(_data.CopyRates(m_symbol.Name(), m_period, class="num">8, class="num">0, m_fast_period + m_slow_period)) { class="type">class="kw">double _hurst = class="num">0.0, _c = class="num">0.0; Hurst(_data, _hurst, _c); vector _ma; if(_hurst > class="num">0.5) { if(_ma.CopyRates(m_symbol.Name(), m_period, class="num">8, class="num">0, m_slow_period)) { if(m_symbol.Bid() > _ma.Mean()) { result = class="type">int(round(class="num">100.0 * ((m_symbol.Bid() - _ma.Mean())/(fabs(m_symbol.Bid() - _ma.Mean()) + fabs(_ma.Max()-_ma.Min()))))); } } } else if(_hurst < class="num">0.5) { if(_ma.CopyRates(m_symbol.Name(), m_period, class="num">8, class="num">0, m_fast_period)) { if(m_symbol.Bid() < _ma.Mean()) { result = class="type">int(round(class="num">100.0 * ((_ma.Mean() - m_symbol.Bid())/(fabs(m_symbol.Bid() - _ma.Mean()) + fabs(_ma.Max()-_ma.Min()))))); } } } } class="kw">return(result); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| "Voting" that price will fall. | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CSignalHurst::ShortCondition(class="type">void) { class="type">int result = class="num">0; vector _data; if(_data.CopyRates(m_symbol.Name(), m_period, class="num">8, class="num">0, m_fast_period + m_slow_period)) { class="type">class="kw">double _hurst = class="num">0.0, _c = class="num">0.0; Hurst(_data, _hurst, _c); vector _ma; if(_hurst > class="num">0.5) { if(_ma.CopyRates(m_symbol.Name(), m_period, class="num">8, class="num">0, m_slow_period)) { if(m_symbol.Bid() < _ma.Mean()) { result = class="type">int(round(class="num">100.0 * ((_ma.Mean() - m_symbol.Bid())/(fabs(m_symbol.Bid() - _ma.Mean()) + fabs(_ma.Max()-_ma.Min())))));
反持久性下的偏空信号量化
当赫斯特指数跌破 0.5,意味着价格序列呈现反持久性,即近期下跌倾向延续的概率偏高,外汇与贵金属这类高杠杆品种此时逆势抄底风险显著放大。 代码里先以 CopyRates 拉取最近 8 根 K 线、周期为快速均线参数,若当前 Bid 仍高于快速均线均值,就按偏离度占通道跨度(最大值减最小值)的比例折算成 0~100 的整数信号。 该比例用 fabs 取绝对值做分母平滑,避免极值扭曲;若 Bid 低于均值则 result 保持默认 0,不强行给反向分值。 实盘可在 MT5 里把 m_fast_period 调成 13 或 21 对比信号密度,观察 EURUSD 在 15 分钟图反持久段是否频繁触发。
}
}
}
else if(_hurst < class="num">0.5)
{ if(_ma.CopyRates(m_symbol.Name(), m_period, class="num">8, class="num">0, m_fast_period))
{ if(m_symbol.Bid() > _ma.Mean())
{ result = class="type">int(round(class="num">100.0 * ((m_symbol.Bid() - _ma.Mean())/(fabs(m_symbol.Bid() - _ma.Mean()) + fabs(_ma.Max()-_ma.Min())))));
}
}
}
}
class="kw">return(result);
}◍ GBPCHF 的 H4 回测样本观察
用 GBPCHF 的 2023 年 H4 数据跑了一轮策略测试,整年下来成交笔数很少。这种低频次在价格行为系统里往往不是坏事,说明入场过滤偏严、只在高概率结构触发,属于有辨别力的表现。 但单年样本远不足以定性,外汇与贵金属本身高杠杆、跳空频繁,2023 年一种节奏不等于 2024 年仍成立。要下结论,至少把测试窗口拉长到 3~5 年,并叠加前向漫游(walk-forward)验证参数稳定性。 开 MT5 切到 GBPCHF H4,把同一 EA 的测试区间从 2023 拉到 2019–2024,看成交分布和权益曲线是否仍平滑,比盯单年报告更有意义。