基于混沌理论的超买超卖分析·进阶篇
🌀

基于混沌理论的超买超卖分析·进阶篇

(2/3)· 当价格看似随机乱跳时,奇异吸引子与分形几何已悄悄标出回归路径

进阶 第 2/3 篇
很多人把横盘突破当作纯噪声,却没意识到那是奇异吸引子在重塑层级。用静态均线去套混沌市场,往往错过价格回归动态中心的窗口。先把吸引子当成无形磁极,再看分形怎么在不同周期重复自己。

◍ 给神经网络喂料前先调好口径

把历史报价直接塞进网络,和让俄语母语者去跟只懂英语的人谈合约一样低效。训练前必须把每个特征拉到同一尺度:线性映射把价格压进 [0,1] 区间,网络才跑得稳,也才适配 Sigmoid 类的激活路径。 隐藏层里我们弃用传统 Sigmoid,换成 tanh。它的输出域是 [-1,1],中心区斜率更陡,能同时吃下正负偏离信号,对秒级切换的混沌盘面更敏感。市场状态常在瞬息间翻转,这种陡坡让权重更新更快咬住突变。 前向与反向传播构成试错闭环:前向拿当前权重出预测,反向按误差回改权重,类似幼童学步,摔几次才稳。下面这段是 MT5 里可直抄的前向推断与配套函数,注意 NormalizePrice 依赖 PriceHistory 数组,实盘前先确保它装满了滑动窗口内的真实极值。 别把正态当圣经 标准化只是统一步频,不等于消除跳空。外汇与贵金属杠杆高、跳空频繁,[0,1] 映射遇到极端影线仍可能让隐藏层瞬时饱和,回测顺滑不等于实盘安全。 振荡器才是落点:当前价除以吸引子减 1 再乘 100,得出偏离百分比。读数 +30 代表价格相对模型‘自然水平’过热,倾向回拉;-30 则过冷,概率上反弹。代码里对吸引子为 0 做了除零保护,照搬时别手删那行。

MQL5 / C++
class="type">class="kw">double ForwardPass(class="type">class="kw">double &inputs[]) {
   class=class="str">"cmt">// Calculating outputs of the hidden layer
   for(class="type">int i = class="num">0; i < HiddenNeurons; i++) {
      class="type">class="kw">double sum = Network.hidden[i].bias;
      for(class="type">int j = class="num">0; j < InputNeurons; j++) {
         sum += inputs[j] * Network.hidden[i].weights[j];
      }
      Network.hidden[i].output = Sigmoid(sum);
   }
   
   class=class="str">"cmt">// Calculating neural network output
   class="type">class="kw">double sum = Network.output.bias;
   for(class="type">int i = class="num">0; i < HiddenNeurons; i++) {
      sum += Network.hidden[i].output * Network.output.weights[i];
   }
   Network.output.output = Sigmoid(sum);
   
   class="kw">return Network.output.output;
}
class="type">class="kw">double Tanh(class="type">class="kw">double x) {
   class="kw">return (MathExp(x) - MathExp(-x)) / (MathExp(x) + MathExp(-x));
}
class="type">class="kw">double NormalizePrice(class="type">class="kw">double price) {
   class="type">class="kw">double min = ArrayMin(PriceHistory);
   class="type">class="kw">double max = ArrayMax(PriceHistory);
   class="kw">return (price - min) / (max - min);
}
class=class="str">"cmt">// Calculate oscillator value as ratio of current price to attractor
if(AttractorBuffer[i] > class="num">0) {
   OscillatorBuffer[i] = (CurrentPriceBuffer[i] / AttractorBuffer[i] - class="num">1.0) * class="num">100.0;
} else {
   OscillatorBuffer[i] = class="num">0; class=class="str">"cmt">// Division-by-zero protection
}

「把旋钮拧到自己的行情里」

交易市场没有通用的指标模板,不同品种和周期自带情绪与波动节律,照搬别人的参数往往水土不服。把指标当成音频放大器,输入神经元数、隐藏层规模、学习率这些就是面板上的旋钮,想让信号更跟手就加 InputNeurons 或缩短 PredictionPeriod,噪声炸耳就把 Smoothing 打开。 PredictionPeriod 是往前看多久的“时间透镜”:在 M1 上填 5 代表预测其后 5 分钟,切到 D1 同样写 5 就是看后 5 个交易日。实操建议从 5 这样的小值起步,逐步往上推,观察曲线拐点何时开始滞后或过度拟合——这是用 MT5 跑一遍就能验证的。 李雅普诺夫指数给混沌加了把尺子。代码里用 0.0001 的微扰 epsilon 喂给两份近似输入,算两次前向预测的距离差再取对数平均,把“蝴蝶效应”量化成 -1.0 到 1.0 之间的数。该值偏正说明初始价微小变动会被放大,行情随机性更强,外汇与贵金属杠杆高,这种敏感期止损要更收紧。 分形噪声借曼德尔布罗的中点位移思路,给指标叠出自相似的市场肌理。下面这段参数声明与指数计算函数,直接贴进 MQ5 就能编译看效果。

MQL5 / C++
<span class="keyword">input</span> <span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;InputNeurons = <span class="number">class="num">10</span>;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// Number of input neurons(historical periods)</span>
<span class="keyword">input</span> <span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;HiddenNeurons = <span class="number">class="num">20</span>;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Number of neurons in the hidden layer</span>
<span class="keyword">input</span> <span class="keyword">class="type">class="kw">double</span> LearningRate = <span class="number">class="num">0.01</span>;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// Learning rate</span>
<span class="keyword">input</span> <span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;TrainBars = <span class="number">class="num">1000</span>;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Number of bars for training</span>
<span class="keyword">input</span> <span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;PredictionPeriod = <span class="number">class="num">5</span>;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Prediction period(in bars)</span>
<span class="keyword">input</span> <span class="keyword">class="type">bool</span>&nbsp;&nbsp; Smoothing = <span class="macro">false</span>;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// Apply smoothing to the oscillator</span>
<span class="keyword">input</span> <span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;SmoothingPeriod = <span class="number">class="num">3</span>;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// Smoothing period</span>
<span class="keyword">class="type">class="kw">double</span> CalculateLyapunovExponent(<span class="keyword">const</span> <span class="keyword">class="type">class="kw">double</span> &amp;close[], <span class="keyword">class="type">int</span> bars) {
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> epsilon = <span class="number">class="num">0.0001</span>; <span class="comment">class=class="str">"cmt">// Small perturbation 微小扰动量</span>
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> lyapunov = <span class="number">class="num">0.0</span>; <span class="comment">class=class="str">"cmt">// 李雅普诺夫指数累加器</span>
&nbsp;&nbsp; <span class="keyword">class="type">int</span> samples = <span class="functions">MathMin</span>(LyapunovPeriod, TrainBars/<span class="number">class="num">2</span>); <span class="comment">class=class="str">"cmt">// 取样数取周期与训练量一半的较小值</span>
&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i = <span class="number">class="num">0</span>; i &lt; samples; i++) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">int</span> startIdx = <span class="functions">MathRand</span>() % (TrainBars - InputNeurons - PredictionPeriod); <span class="comment">class=class="str">"cmt">// 随机起点避免偏采样</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> inputs1[]; <span class="comment">class=class="str">"cmt">// 原始输入序列</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ArrayResize</span>(inputs1, InputNeurons);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span>(<span class="keyword">class="type">int</span> j = <span class="number">class="num">0</span>; j &lt; InputNeurons; j++) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inputs1[j] = NormalizePrice(close[bars - TrainBars + startIdx + j]); <span class="comment">class=class="str">"cmt">// 归一化历史价填入</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> inputs2[]; <span class="comment">class=class="str">"cmt">// 扰动输入序列</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ArrayResize</span>(inputs2, InputNeurons);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ArrayCopy</span>(inputs2, inputs1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inputs2[<span class="functions">MathRand</span>() % InputNeurons] += epsilon; <span class="comment">class=class="str">"cmt">// 随机某一维加微扰</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> pred1 = ForwardPass(inputs1); <span class="comment">class=class="str">"cmt">// 原输入预测</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> pred2 = ForwardPass(inputs2); <span class="comment">class=class="str">"cmt">// 扰动输入预测</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> distance = <span class="functions">MathAbs</span>(pred2 - pred1); <span class="comment">class=class="str">"cmt">// 两预测间距</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>(distance &gt; <span class="number">class="num">0</span>) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lyapunov += <span class="functions">MathLog</span>(distance / epsilon); <span class="comment">class=class="str">"cmt">// 对数发散率累加</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp; }
&nbsp;&nbsp; lyapunov = lyapunov / samples; <span class="comment">class=class="str">"cmt">// 取平均</span>
&nbsp;&nbsp; lyapunov = <span class="functions">MathMax</span>(-<span class="number">class="num">1.0</span>, <span class="functions">MathMin</span>(<span class="number">class="num">1.0</span>, lyapunov)); <span class="comment">class=class="str">"cmt">// 截断至[-class="num">1,class="num">1]保稳定</span>
&nbsp;&nbsp; <span class="keyword">class="kw">return</span> lyapunov;
}
<span class="keyword">class="type">void</span> GenerateFractalNoise(<span class="keyword">class="type">int</span> size) {
&nbsp;&nbsp; <span class="functions">ArrayResize</span>(FractalNoiseBuffer, size);
&nbsp;&nbsp; FractalNoiseBuffer[<span class="number">class="num">0</span>] = <span class="number">class="num">0</span>; <span class="comment">class=class="str">"cmt">// 左端锚点</span>
&nbsp;&nbsp; FractalNoiseBuffer[size-<span class="number">class="num">1</span>] = <span class="number">class="num">0</span>; <span class="comment">class=class="str">"cmt">// 右端锚点</span>

递归位移后做一次归一化

上面那段收尾代码干了两件事:先对分形噪声缓冲做递归中点位移,再把整条序列压进 [-1, 1] 区间。 ArrayMin 与 ArrayMax 取缓冲极值后,循环里用 2.0*(x-min)/(max-min)-1.0 把每个点线性映射。这一步必须做,否则后续叠加到价格上时会因量纲不一而失真。 FractalDimension 参数值越高,生成的噪声曲线越崎岖混沌,对外汇或贵金属这类高杠杆品种,可能更贴近极端波动段的轨迹,但实盘验证仍属高风险行为,建议在 MT5 策略测试器里先跑裸噪声曲线。

MQL5 / C++
class=class="str">"cmt">// Recursive calculation of midpoints
  MidpointDisplacement(FractalNoiseBuffer, class="num">0, size-class="num">1, class="num">1.0, FractalDimension);
  
  class=class="str">"cmt">// Normalization
  class="type">class="kw">double min = ArrayMin(FractalNoiseBuffer, class="num">0, size);
  class="type">class="kw">double max = ArrayMax(FractalNoiseBuffer, class="num">0, size);
  
  for(class="type">int i = class="num">0; i < size; i++) {
      FractalNoiseBuffer[i] = class="num">2.0 * (FractalNoiseBuffer[i] - min) / (max - min) - class="num">1.0;
  }
}

◍ 把吸引子边界当成摆锤回中信号

神经吸引子震荡指标本质上是个受混沌约束的摆锤模型。价格偏离吸引子下边界太远(边界按李雅普诺夫指数动态调整),指标触底回升,可能给出多头入场点;升破上边界后回落,则可能是空头信号。李雅普诺夫指数越低,这类信号的可靠度越高,因为市场被更强地拉向均衡。 背离在这里比经典用法多一层含义:价格创新高/低而指标未跟随,不只是趋势减弱,更可能是吸引子结构本身在变——市场趋向的均衡点挪位了。若背离同时伴随李雅普诺夫指数由正转负,往往意味着高混沌期结束、新稳定趋势正在成形。 高混沌期(高李雅普诺夫指数)里,再漂亮的突破或背离都不可靠,实盘应减仓或离场观望。外汇与贵金属杠杆高、跳空频繁,这种时期止损可能被滑点击穿。 下方是输出层权重的动量优化片段,动量项让网络逃开局部最小值: // 带动量的输出层权重更新 for(int j = 0; j < HiddenNeurons; j++) { // 计算当前梯度步长:学习率×输出误差×隐层第j神经元输出 double delta = LearningRate * Network.output.error * Network.hidden[j].output; // 动量累加:旧动量乘衰减系数 + 新梯度步长乘(1-衰减) Network.output.momentum[j] = momentum * Network.output.momentum[j] + (1.0 - momentum) * delta; // 权重沿动量方向更新 Network.output.weights[j] += Network.output.momentum[j]; } 在 MT5 里把 momentum 从 0.0 调到 0.9 逐档回测,能直观看到陷入局部最小的概率变化。

MQL5 / C++
class=class="str">"cmt">// Updating the output layer weights with momentum
for(class="type">int j = class="num">0; j < HiddenNeurons; j++) {
   class="type">class="kw">double delta = LearningRate * Network.output.error * Network.hidden[j].output;
   Network.output.momentum[j] = momentum * Network.output.momentum[j] + (class="num">1.0 - momentum) * delta;
   Network.output.weights[j] += Network.output.momentum[j];
}
让小布替你跑这套
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到分形自相似与吸引子层级的实时标注,把重复劳动交给小布,你专注决策。

常见问题

普通支撑阻力是静态价格节点,奇异吸引子是系统趋近却永不重合的复杂结构,会随市场动态层级移动,不是固定线。
概率上有效,但周期越小噪声越多,建议用多周期对照确认形态复现,避免单周期误判。
遇到央行突袭式干预或流动性枯竭时,回归节奏会断裂,外汇贵金属本身高风险,这类极端初始条件会放大偏离。
小布盯盘在品种页直接叠出吸引子层级与分形重复区,省去手动切周期比对,你只需判断当前是否逼近动态中心。
初始条件微小四舍五入就可能让回测结果完全变样,实盘偏离概率偏高,参数需做敏感性压力测试。