神经网络实践:割线·进阶篇
📘

神经网络实践:割线·进阶篇

第 2/2 篇

十字与角度线的画布落地

这段代码片段把坐标轴原点和一组偏移量直接画到 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,能直接看到十字和红点阵的疏密变化;外汇与贵金属杠杆品种波动剧烈,此类视觉辅助仅作参考,实际下单仍需结合自身风控。

MQL5 / C++
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 度就卡住。外汇与贵金属图表叠加此类可视化工具仍属高风险环境,参数误用可能误导进出场判断。

MQL5 / C++
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 超过图表可见区时,两端被裁切却不报错,调试时容易以为函数失效,其实是坐标越界被绘制层吞掉。

MQL5 / C++
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 画线验证斜率。 对外汇、贵金属这类高波动品种,用导数定位回归方向只是建模第一步,实盘里斜率随品种跳空会剧烈变形,任何「方向」都只是概率倾向,不是确定性路径。

MQL5 / C++
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)下回去读一遍,熟悉变量命名再说。 外汇与贵金属波动剧烈、杠杆风险高,这类数学拟合法只供建模参考,实盘信号概率性成立,别直接当下单依据。

常见问题

可以用 Canvas 在自定义指标里按数学公式逐像素绘制,用方向键实时调整角度参数,比手动拖拽更准。
取相邻两段价格连成割线,其斜率近似局部平均变化率;多画几条不同区间的割线,能看出动量是在加快还是放缓。
可以,小布盯盘的 AIGC 分析能直接对打开的品种页生成角度线与割线诊断,你只看结论做决策。
把拟合区间收窄到近期 20~30 根 K 线,看残差是否平稳;导数陡变处大概率对应速率切换,需结合量能验证。
是的,把角度步进从 5° 降到 1° 并限制重绘频率,卡顿会明显减轻,且足够精细。