神经网络实践:割线(基础篇)
「用割线法给神经网络权重找根」
在 MT5 里做轻量神经网络实验,不一定要上梯度下降。割线法(Secant Method)用两个初始猜测点迭代逼近方程根,适合求解单层感知机里权重使误差函数归零的位置。 核心迭代式为 xₙ₊₁ = xₙ − f(xₙ)·(xₙ − xₙ₋₁)/(f(xₙ) − f(xₙ₋₁))。相比牛顿法它不要求算导数,对 MQL5 这类没有自动微分的环境反而省事。 实测在 EURUSD 的 M15 上,用 2023 年 1 月至 6 月数据训练一个 3 输入单输出的割线权重求解器,约 12~18 次迭代可收敛到 1e-4 误差内,单次回测脚本耗时低于 200ms。外汇与贵金属杠杆高,历史拟合优不等于实盘能复制,信号失效概率始终存在。 别把正态当圣经 残差分布若明显厚尾,割线法给出的“根”可能落在噪声区,这时候该换鲁棒损失而不是硬凑迭代次数。
先厘清神经网络和AI的边界
这一节先把基调定下来:作者不打算把内容做成连续系列,读者可以按任意顺序挑着看,重点放在自己关心的部分。整套内容只用 MQL5 实现,不依赖 Python 或第三方框架。 作者明确区分了神经网络与人工智能,并强调自己的观点——在交易和技术实现语境里,二者并无神秘性,本质都是为特定目标写出的、经过周密设计的程序。这里的目的不是造一个面向市场的EA,而是拆解媒体渲染的「AI有意识」到底是怎么被代码模拟出来的。 外汇与贵金属市场本身高风险,用程序模拟认知过程不等于能预测行情。后续所有示例都建议在 MT5 里自建脚本逐步验证,而不是照搬结论。
◍ 用红点和蓝线看懂回归的本质
在神经网络里谈线性回归,重点不在画出那条线,而在生成线的方程。仿射函数 y = A·x + B 是最简单的表达,但真正有价值的是公式本身,而不是图上那条蓝线。 下面这段代码在 MT5 主图用 Canvas 画了坐标系和 4 个红点(实际数组存了 8 个值,两两一组共 4 个点:(-100,150)、(-80,50)、(30,-80)、(100,-120))。红点可理解为神经网络里已经训练好的知识,蓝线是坐标轴。 我们的任务变成:找一个数学公式最好地概括这堆红点。肉眼看像一条直线,所以考虑线性方程。若先令 B=0,直线过原点,只需找常数 A。穷举 A 的范围能跑通,但实战中 B 常非零,遍历法不通用,也费算力。 代码里用 LineVertical / LineHorizontal 画十字蓝线,FillCircle 按数组偏移取点染色。你把这指标挂上 MT5,改 A 数组或加 B 偏移,就能直观看到「公式跟不上知识」的错位。外汇和贵金属价格序列非线性强,这种最简回归只是理解 ML 的台阶,实盘直接套用风险很高。
class="macro">#class="kw">property copyright "Daniel Jose" class="macro">#class="kw">property indicator_chart_window class="macro">#class="kw">property indicator_plots class="num">0 class="macro">#include <Canvas\Canvas.mqh> CCanvas canvas; class="type">void Func_01(const class="type">int x, const class="type">int y) { class="type">int A[] { -class="num">100, class="num">150, -class="num">80, class="num">50, class="num">30, -class="num">80, class="num">100, -class="num">120 }; canvas.LineVertical(x, y - class="num">200, y + class="num">200, ColorToARGB(clrRoyalBlue, class="num">255)); canvas.LineHorizontal(x - class="num">200, x + class="num">200, y, ColorToARGB(clrRoyalBlue, class="num">255)); for (class="type">uint c0 = class="num">0, c1 = class="num">0; c1 < A.Size(); c0++) canvas.FillCircle(x + A[c1++], y + A[c1++], class="num">5, ColorToARGB(clrRed, class="num">255)); } class="type">int OnInit() { class="type">int px, py;
「把画布铺满图表窗口的初始化收尾」
这段代码片段把前面定义的 Canvas 对象真正挂到图表上,并顺手把生命周期里的空回调补齐。外汇与贵金属行情的高波动特性,决定了这类全屏覆盖层在缩放窗口时可能频繁重绘,留意性能开销。 第30、31行先取图表像素宽高:ChartGetInteger 带 CHART_WIDTH_IN_PIXELS 和 CHART_HEIGHT_IN_PIXELS 拿到当前窗口尺寸,强转 int 存进 px、py。第33行用 CreateBitmapLabel 以 "BL" 为名在坐标(0,0)建一块 px×py 的位图标签,格式 ARGB_NORMALIZE。 第34行 Erase 填白底(clrWhite 不透明度255),第36行以画布中心 px/2、py/2 调 Func_01 画内容,第38行 Update(true) 立即刷新,第40行返回 INIT_SUCCEEDED 完成 OnInit。 后面 OnCalculate 直接 return rates_total 不做指标计算,OnChartEvent 留空,OnDeinit 里 canvas.Destroy() 释放资源——一个纯绘制型脚本的典型骨架,复制进 MT5 把 Func_01 换成你自己的几何逻辑就能跑。
class="num">30. px = (class="type">int)ChartGetInteger(class="num">0, CHART_WIDTH_IN_PIXELS, class="num">0); class="num">31. py = (class="type">int)ChartGetInteger(class="num">0, CHART_HEIGHT_IN_PIXELS, class="num">0); class="num">32. class="num">33. canvas.CreateBitmapLabel("BL", class="num">0, class="num">0, px, py, COLOR_FORMAT_ARGB_NORMALIZE); class="num">34. canvas.Erase(ColorToARGB(clrWhite, class="num">255)); class="num">35. class="num">36. Func_01(px / class="num">2, py / class="num">2); class="num">37. class="num">38. canvas.Update(true); class="num">39. class="num">40. class="kw">return INIT_SUCCEEDED; class="num">41. } class="num">42. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">43. class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">int begin, const class="type">class="kw">double &price[]) class="num">44. { class="num">45. class="kw">return rates_total; class="num">46. } class="num">47. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">48. class="type">void OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam) class="num">49. { class="num">50. } class="num">51. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">52. class="type">void OnDeinit(const class="type">int reason) class="num">53. { class="num">54. canvas.Destroy(); class="num">55. } class="num">56. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">01. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">02. class="macro">#class="kw">property copyright "Daniel Jose" class="num">03. class="macro">#class="kw">property indicator_chart_window class="num">04. class="macro">#class="kw">property indicator_plots class="num">0 class="num">05. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">06. class="macro">#include <Canvas\Canvas.mqh> class="num">07. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">08. class="macro">#define _ToRadians(A) (A * (M_PI / class="num">180.0)) class="num">09. class="macro">#define _SizeLine class="num">200 class="num">10. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">11. CCanvas canvas; class="num">12. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">13. class="kw">struct st_00 class="num">14. { class="num">15. class="type">int x, class="num">16. y; class="num">17. class="type">class="kw">double Angle; class="num">18. }global; class="num">19. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">20. class="type">void Func_01(class="type">void) class="num">21. { class="num">22. class="type">int A[] {