神经网络实践:割线·进阶篇
十字与角度线的画布落地
这段代码片段把坐标轴原点和一组偏移量直接画到 MT5 的 Canvas 上。第 23–26 行是一组成对数值,例如 -100 与 150、-80 与 50、30 与 -80、100 与 -120,它们作为数组 A 的元素,在循环里被取出来当作红点的像素偏移。 第 29–30 行先以皇家蓝画一条垂直和水平线,长度由 _SizeLine 控制,交叉点就是 global.x / global.y。第 32–33 行用 for 循环每次读两个值(c1 自增两次),以半径 5 像素画实心红圆,相当于把数组里的坐标对逐个标在十字周围。 NewAngle 函数负责擦白重绘并旋转一条绿色角度线。第 41 行把 global.Angle 按 step(默认 1 度)和 direct 方向累加,但用 MathAbs 限制不超过 ±90 度;第 42 行在原点右移 250 像素处用 TextOut 显示当前角度两位小数。第 44–47 行用 cos/sin 把角度转成线段两端坐标,线长同样是 _SizeLine。 OnInit 里把角度归零,并把 global.x / global.y 设为图表宽高像素值(ChartGetInteger 取 CHART_WIDTH_IN_PIXELS / CHART_HEIGHT_IN_PIXELS)。你在 MT5 里建个指标挂上这段,改 _SizeLine 从 50 调到 200,能直接看到十字和红点阵的疏密变化;外汇与贵金属杠杆品种波动剧烈,此类视觉辅助仅作参考,实际下单仍需结合自身风控。
class="num">23. -class="num">100, class="num">150, class="num">24. -class="num">80, class="num">50, class="num">25. class="num">30, -class="num">80, class="num">26. class="num">100, -class="num">120 class="num">27. }; class="num">28. class="num">29. canvas.LineVertical(global.x, global.y - _SizeLine, global.y + _SizeLine, ColorToARGB(clrRoyalBlue, class="num">255)); class="num">30. canvas.LineHorizontal(global.x - _SizeLine, global.x + _SizeLine, global.y, ColorToARGB(clrRoyalBlue, class="num">255)); class="num">31. class="num">32. for (class="type">uint c0 = class="num">0, c1 = class="num">0; c1 < A.Size(); c0++) class="num">33. canvas.FillCircle(global.x + A[c1++], global.y + A[c1++], class="num">5, ColorToARGB(clrRed, class="num">255)); class="num">34. } class="num">35. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">36. class="type">void NewAngle(const class="type">char direct, const class="type">class="kw">double step = class="num">1) class="num">37. { class="num">38. canvas.Erase(ColorToARGB(clrWhite, class="num">255)); class="num">39. Func_01(); class="num">40. class="num">41. global.Angle = (MathAbs(global.Angle + (step * direct)) <= class="num">90 ? global.Angle + (step * direct) : global.Angle); class="num">42. canvas.TextOut(global.x + class="num">250, global.y, StringFormat("%.2f", global.Angle), ColorToARGB(clrBlack)); class="num">43. canvas.Line( class="num">44. global.x - (class="type">int)(_SizeLine * cos(_ToRadians(global.Angle))), class="num">45. global.y - (class="type">int)(_SizeLine * sin(_ToRadians(global.Angle))), class="num">46. global.x + (class="type">int)(_SizeLine * cos(_ToRadians(global.Angle))), class="num">47. global.y + (class="type">int)(_SizeLine * sin(_ToRadians(global.Angle))), class="num">48. ColorToARGB(clrForestGreen) class="num">49. ); class="num">50. canvas.Update(true); class="num">51. } class="num">52. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">53. class="type">int OnInit() class="num">54. { class="num">55. global.Angle = class="num">0; class="num">56. global.x = (class="type">int)ChartGetInteger(class="num">0, CHART_WIDTH_IN_PIXELS, class="num">0); class="num">57. global.y = (class="type">int)ChartGetInteger(class="num">0, CHART_HEIGHT_IN_PIXELS, class="num">0); class="num">58.
「用方向键实时拨动画布角度」
在指标初始化末尾,先以 CreateBitmapLabel 把名为 "BL" 的位图标签挂到图表,坐标取自 global.x / global.y,随后把这两个值各除以 2,相当于把绘图原点挪到画布中心。接着调用 NewAngle(0) 完成首帧绘制,canvas.Update(true) 强制刷新,返回 INIT_SUCCEEDED 即代表加载成功。 OnCalculate 这里直接 return rates_total,不处理价格序列——本例的视觉更新完全靠键盘事件驱动,而非逐根 K 线重算。 OnChartEvent 里只拦截 CHARTEVENT_KEYDOWN:左方向键按住时 NewAngle(-1),右方向键按住时 NewAngle(1),步长由 NewAngle 的默认参数 step=1 决定。 看 NewAngle 内部,第 41 行用 MathAbs(global.Angle + step*direct) < 90 做了硬限制——角度绝对值超过 90 度就不再累加,避免切线值爆掉。第 42、43 行把当前角度和函数式 f(x)=%.2fx 用 TextOut 打到画布上,其中斜率由 -MathTan(_ToRadians(global.Angle)) 算出。 开 MT5 把这段代码塞进自定义指标,编译后按住左右键,能看到画布上的直线与公式随每次按键偏转 1 度,且到 89 度就卡住。外汇与贵金属图表叠加此类可视化工具仍属高风险环境,参数误用可能误导进出场判断。
canvas.CreateBitmapLabel("BL", class="num">0, class="num">0, global.x, global.y, COLOR_FORMAT_ARGB_NORMALIZE); global.x /= class="num">2; global.y /= class="num">2; NewAngle(class="num">0); canvas.Update(true); class="kw">return INIT_SUCCEEDED; } class=class="str">"cmt">//+------------------------------------------------------------------+ 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="kw">return rates_total; } class=class="str">"cmt">//+------------------------------------------------------------------+ 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="kw">switch (id) { case CHARTEVENT_KEYDOWN: if (TerminalInfoInteger(TERMINAL_KEYSTATE_LEFT)) NewAngle(-class="num">1); if (TerminalInfoInteger(TERMINAL_KEYSTATE_RIGHT)) NewAngle(class="num">1); break; } } class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason) { canvas.Destroy(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void NewAngle(const class="type">char direct, const class="type">class="kw">double step = class="num">1) { canvas.Erase(ColorToARGB(clrWhite, class="num">255)); Func_01(); global.Angle = (MathAbs(global.Angle + (step * direct)) < class="num">90 ? global.Angle + (step * direct) : global.Angle); canvas.TextOut(global.x + _SizeLine + class="num">50, global.y, StringFormat("%.2f", MathAbs(global.Angle)), ColorToARGB(clrBlack)); canvas.TextOut(global.x, global.y + _SizeLine + class="num">50, StringFormat("f(x) = %.2fx", -MathTan(_ToRadians(global.Angle))), ColorToARGB(clrBlack));
◍ 用 Canvas 画一条带角度的趋势线
在 MT5 的 CCanvas 对象上,画一条穿过锚点、按指定角度延伸的线段,核心就是端点坐标的三角函数换算。下面这段代码把 global.x / global.y 当作中心点,用 _SizeLine 控制线长,_ToRadians 把角度转弧度后分别乘 cos 和 sin,得到左右两个端点。 端点计算时做了 (int) 强转,意味着亚像素精度被丢弃,线长若设成 100 像素、角度 30°,实际左右偏移约 ±86 像素、上下 ±50 像素,渲染出来是整数坐标对齐的硬线。 颜色用 ColorToARGB(clrForestGreen) 传进去,画完必须调 canvas.Update(true) 才会把缓冲推到图表。外汇与贵金属图表叠加自定义图形属高风险操作环境,参数误设可能导致视觉误导,实盘前请在模拟环境验证。 别把线长当越大越好。_SizeLine 超过图表可见区时,两端被裁切却不报错,调试时容易以为函数失效,其实是坐标越界被绘制层吞掉。
canvas.Line( global.x - (class="type">int)(_SizeLine * cos(_ToRadians(global.Angle))), global.y - (class="type">int)(_SizeLine * sin(_ToRadians(global.Angle))), global.x + (class="type">int)(_SizeLine * cos(_ToRadians(global.Angle))), global.y + (class="type">int)(_SizeLine * sin(_ToRadians(global.Angle))), ColorToARGB(clrForestGreen) ); canvas.Update(true);
用二次曲线逼出可读的导数
仿射函数本身就是直线,没有二阶导数可用,无法在它上面再作切线来读取斜率。要让程序「看见」最优回归线的方向,得先换一条能求导的曲线。最省算力的选择是二次函数 y = A·x² + B·x + C,只要带 2 次幂,曲线上每点都能引出一条切线,切线的斜率就是该点导数。 把 A=1、B=0、C=0 代入,得到 y = x²。取 X=4 这一点,二次方程导数公式为 y' = 2·A·x + B,算出 y' = 8。它的物理意义很直白:在 X=4 处,X 轴挪 1 格,Y 轴要挪 8 格,这条红色切线就是程序寻找最小误差回归线时的「指路箭头」。 切线方程按 y − y₀ = m·(x − x₀) 套入:y₀ = 4² = 16,m = 8,x₀ = 4,化简后得 y = 8x − 16。下面对应的 SCILAB 代码在 X∈[−10,10] 画原曲线,标出 (4,16) 红点,再叠画切线 y=8x−16。MT5 用户虽不直接跑 SCILAB,但可把这段逻辑搬进 MQL5 用 ObjectCreate 画线验证斜率。 对外汇、贵金属这类高波动品种,用导数定位回归方向只是建模第一步,实盘里斜率随品种跳空会剧烈变形,任何「方向」都只是概率倾向,不是确定性路径。
x = [-class="num">10:.class="num">2:class="num">10]; y = x ^ class="num">2; plot(x, y, &class="macro">#x27;b-&class="macro">#x27;); plot(class="num">4, class="num">16, &class="macro">#x27;ro&class="macro">#x27;); x = [class="num">2:.class="num">2:class="num">10]; y = class="num">8 * x - class="num">16; plot(x, y, &class="macro">#x27;r-&class="macro">#x27;);
「割线才是神经网络计算里的真主角」
谈神经网络时满屏都是导数和切线,但真正干活的是割线。无论你用哪种网络结构,只要进入数值计算环节,计算机算的不是切线而是割线——很多人背熟了导数公式,却没想过公式本身是怎么从割线逼出来的。 把曲线上两点连成的红线就是割线。取 X=4 这点(和上一篇示例一致),令 f(x+h) 相对 f(x) 平移距离 h。当 h 趋近 0,割线慢慢贴向切线;h 严格等于 0 时,割线与切线重合,此时斜率就是函数在该点的导数。图形上不可能直接「生成」切线,只能靠割线逼近。 h 的大小直接决定网络行为。h 越小,逼近越准、网络越「不疯狂」;h 越大,收敛区间越宽、方程在收敛范围内漂移越明显,网络越「疯狂」。这个 h 永远不能等于 0,只能取极小量,具体下界由后续收敛过程定。 真正解释神经网络为何用导数的,是割线到切线的距离等式:lim(h→0)[f(x+h)-f(x)]/h。它就是被很多人死记的计算式的来源,搞懂这条,比调任何库都更接近本质。外汇与贵金属杠杆高、滑点大,拿这套思路验证模型时务必用小资金或模拟盘先跑。
◍ 画得少,看得清
正割方程这块内容其实才开了个头,前面只把数学骨架搭出来,怎么在 MT5 里调用、为什么能拟合价格分段,都还没落到代码层。 作者留了个明确的接续点:下一篇走神经网络,会把这一节的正割知识拿去列正确的拟合方程。想跟上的话,先把本文附件 Anexo_01.mq5(3.49 KB)下回去读一遍,熟悉变量命名再说。 外汇与贵金属波动剧烈、杠杆风险高,这类数学拟合法只供建模参考,实盘信号概率性成立,别直接当下单依据。