Linear Regression Slope 指标:公式、参数与读法
线性回归斜率在最近 N 个收盘价上拟合一条最小二乘直线,并绘制这条直线的斜率,单位为每根K线的价格变化。正值表示拟合线在窗口内上升;负值表示下降。
Senzoukria · 指标 · Updated September 2026
Linear Regression Slope — 动量, 显示在图表下方的独立面板中.
Linear Regression Slope 测量的是什么
回归以居中形式计算,时间轴从窗口中点而不是第一根K线开始度量。这个选择出于数值考虑,并非表面功夫:在五位数的期货收盘价上,教科书式的公式会让两个巨大且几乎相等的总和相减,损失大约一半的有效数字。默认窗口为 20 根K线,与本目录中布林带和 Z 分数使用的统计窗口相同。由于对任何两根及以上K线的窗口,时间离差平方和都严格为正,完全走平的窗口得到的斜率恰好为 0——这是一个真实的数值,而不是被掩盖的除以零。在窗口填满之前不绘制任何值。
公式:代码中的实现
这不是别处常见的定义,而是所发布代码实际计算的内容,记录在实现旁边:
Entier ≥ `min` depuis les params (garde-fou UI). */ function intParam( params: Record<string, unknown>, key: string, fallback: number, min = 1, ): number { return Math.max(min, Math.floor(numParam(params, key, fallback))); } function toPoints( bars: readonly FootprintBar[], values: ReadonlyArray<number | null>, ): SeriesPoint[] { return bars.map((b, i) => ({ time: b.bucketTsNs, value: values[i] ?? null })); } /** Rendements alignés sur les barres : ret[i] = close[i] − close[i−1] ; ret[0] = null (pas de barre précédente). Cf. en-tête pour le choix différence vs log-return. */ function closeReturns(bars: readonly FootprintBar[]): Array<number | null> { return bars.map((b, i) => (i === 0 ? null : b.close - bars[i - 1].close)); } /** Les N derniers rendements se terminant à la barre `i`. `null` tant que la fenêtre n'est pas pleine — il faut i ≥ N puisque ret[0] n'existe pas. */ function returnWindow( rets: ReadonlyArray<number | null>, i: number, n: number, ): number[] | null { if (i < n) return null; const win: number[] = []; for (let j = i - n + 1; j <= i; j++) { const r = rets[j]; if (r === null) return null; win.push(r); } return win; } /** Les N derniers closes se terminant à la barre `i` (i ≥ N−1). */ function closeWindow( bars: readonly FootprintBar[], i: number, n: number, ): number[] { const win: number[] = new Array<number>(n); for (let j = 0; j < n; j++) win[j] = bars[i - n + 1 + j].close; return win; } /** Moyenne et Σ(x − moyenne)² — TWO-PASS (cf. en-tête). Tableau vide → ss = 0 et moyenne 0 : les appelants ne passent jamais de fenêtre vide, la garde `ss > 0` en aval couvre le cas. */ function meanSS(xs: readonly number[]): { mean: number; ss: number } { if (xs.length === 0) return { mean: 0, ss: 0 }; let sum = 0; for (const x of xs) sum += x; const mean = sum / xs.length; let ss = 0; for (const x of xs) { const d = x - mean; ss += d * d; } return { mean, ss }; } /** Corrélation de Pearson TWO-PASS entre deux séries de même longueur : r = Σ(x−x̄)(y−ȳ) / ( √Σ(x−x̄)² · √Σ(y−ȳ)² ) Deux racines SÉPARÉES au dénominateur (jamais √(Sxx·Syy)) : le produit de deux sommes de carrés déborderait sur des volumes cumulés. Sxx ou Syy nul (une des deux séries plate) → null : la corrélation est indéfinie, pas « 0 ». Borné dur [−1, 1] — l'arrondi f64 peut dépasser de 1 ulp sur une corrélation parfaite. */ function pearson(xs: readonly number[], ys: readonly number[]): number | null { const a = meanSS(xs); const b = meanSS(ys); if (!(a.ss > 0) || !(b.ss > 0)) return null; let sxy = 0; for (let i = 0; i < xs.length; i++) { sxy += (xs[i] - a.mean) * (ys[i] - b.mean); } return clamp(sxy / (Math.sqrt(a.ss) * Math.sqrt(b.ss)), -1, 1); } function clamp(v: number, lo: number, hi: number): number { return Math.min(hi, Math.max(lo, v)); } type Fit = { /** Pente b de y = a + b·t, en unités de PRIX PAR BARRE. */ slope: number; /** Valeur du fit au point COURANT : a + b·(n−1). */ value: number; /** Coefficient de détermination ∈ [0,1] ; null si la fenêtre est plate. */ r2: number | null; }; /** Moindres carrés y = a + b·t sur t = 0 (la plus ANCIENNE) … n−1 (la courante), formulation CENTRÉE : t̄ = (n−1)/2 ; Sxx = Σ(t−t̄)² = n(n²−1)/12 ; Sxy = Σ(t−t̄)(y−ȳ) b = Sxy/Sxx ; a = ȳ − b·t̄ ; fit(n−1) = ȳ + b·(n−1)/2 R² = Sxy² / (Sxx·Syy) (Syy = Σ(y−ȳ)²) Centrée et non « normale » (Σt·y) : sur des closes de futures à 5 chiffres, Σt·y et Σt·Σy sont deux grands nombres presque égaux — leur différence perd la moitié des chiffres significatifs. Sxx > 0 dès n ≥ 2, donc la pente n'a JAMAIS de division par zéro (une fenêtre plate rend une pente 0 EXACTE, pas un 0/0). Syy = 0 → R² null (variance nulle, cf. en-tête). n < 2 → null (pas de droite à ajuster). / function regressFit(ys: readonly number[]): Fit | null { const n = ys.length; if (n < 2) return null; const tBar = (n - 1) / 2; const { mean: yBar, ss: syy } = meanSS(ys); let sxx = 0; let sxy = 0; for (let t = 0; t < n; t++) { const dt = t - tBar; sxx += dt * dt; sxy += dt * (ys[t] - yBar); } const slope = sxy / sxx; // sxx = n(n²−1)/12 > 0 dès n ≥ 2 return { slope, value: yBar + slope * (n - 1 - tBar), r2: syy > 0 ? clamp((sxy * sxy) / (sxx * syy), 0, 1) : null, }; } /** Moment centré d'ordre p, convention POPULATION (÷N). */ function centralMoment( xs: readonly number[], mean: number, p: number, ): number { let acc = 0; for (const x of xs) acc += (x - mean) ** p; return acc / xs.length; } /** Dimension fractale de HIGUCHI (1988) sur une fenêtre de n valeurs : pour k = 1…kmax et m = 1…k (sous-séries décimées de pas k, départ m) : L_m(k) = [ Σ_{i=1..⌊(n−m)/k⌋} |x(m+ik) − x(m+(i−1)k)| · (n−1) / (⌊(n−m)/k⌋ · k) ] / k L(k) = moyenne des L_m(k) sur m L(k) ∝ k^(−D) ⇒ D = − pente de ln L(k) vs ln k (moindres carrés). Higuchi PLUTÔT que Katz ou box-counting : c'est l'estimateur de référence sur une série temporelle échantillonnée régulièrement (les barres le sont), il ne dépend d'aucune échelle d'amplitude (Katz mélange une distance de prix et un nombre de pas — donc dépend de l'instrument) et il se calcule en O(n·kmax) sans structure auxiliaire. D ∈ [1, 2] en théorie (1 = courbe lisse, 2 = bruit qui remplit le plan) ; l'estimateur n'est PAS borné artificiellement — on rend ce que la pente dit. kmax est clampé à ⌊n/2⌋ (au-delà, les sous-séries n'ont plus qu'un segment et L(k) n'a plus de sens) et à 2 minimum (une pente exige 2 points). Un L(k) nul (fenêtre plate, ou zigzag exactement en phase avec le pas k) rend le log-log indéfini → null : on ne trace pas une pente amputée. / function higuchiFd(x: readonly number[], kmaxParam: number): number | null { const n = x.length; const kmax = Math.min(Math.max(2, Math.floor(kmaxParam)), Math.floor(n / 2)); if (n < 4 || kmax < 2) return null; const lnK: number[] = []; const lnL: number[] = []; for (let k = 1; k <= kmax; k++) { let acc = 0; let used = 0; for (let m = 1; m <= k; m++) { const count = Math.floor((n - m) / k); if (count < 1) continue; let sum = 0; for (let i = 1; i <= count; i++) { sum += Math.abs(x[m + i * k - 1] - x[m + (i - 1) * k - 1]); } acc += (sum * (n - 1)) / (count * k * k); used += 1; } if (used === 0) return null; const lk = acc / used; if (!(lk > 0)) return null; // ln indéfini → pas de valeur inventée lnK.push(Math.log(k)); lnL.push(Math.log(lk)); } if (lnK.length < 2) return null; const a = meanSS(lnK); if (!(a.ss > 0)) return null; const yBar = meanSS(lnL).mean; let sxy = 0; for (let i = 0; i < lnK.length; i++) sxy += (lnK[i] - a.mean) * (lnL[i] - yBar); return -(sxy / a.ss); } // ── Défs — régression linéaire (le trio partage fenêtre et formule) ───────── /** Pente de la régression linéaire des N derniers closes (moindres carrés, formulation centrée — cf. `regressFit`), en PRIX PAR BARRE : > 0 = la droite monte. Défaut N=20 (la fenêtre statistique standard du catalogue, celle des Bollinger et du z-score). Warm-up (< N barres) → null ; fenêtre plate → 0 EXACT (Sxx > 0, ce n'est pas un 0/0).
如何解读
- 符号告诉你拟合线在窗口内指向哪个方向;它不告诉你价格当前是否靠近这条线。
- 把数值大小与品种的最小变动价位(tick)比较:每根K线四分之一个 tick 的斜率,与每根K线两个 tick 的斜率,说的是完全不同的事。
- 斜率穿越零表示窗口内的最佳拟合线已经走平并反向,这发生在价格本身转向之后,因为较早的K线仍在影响拟合。
- 与同周期的 R 平方搭配使用:斜率给出方向和陡峭程度,R 平方说明一条直线是否真的能描述这个窗口。
- 在最小二乘斜率中,窗口中最早和最新的K线权重最大,所以两端的单个异常值对直线的影响大于中间的异常值。
参数与默认值
周期默认 20,可设 2 到 500。短窗口得到的斜率频繁变号、几根K线内就会反应;长窗口读数更稳定,其符号能扛住普通的回调,代价是对真正的转向反应更晚。
| 参数 | 类型 | 默认值 | 取值范围 |
|---|---|---|---|
| Period | number | 20 | 2 – 500 |
它无法显示的内容
斜率报告的是拟合的陡峭程度,从不报告拟合的质量:纯噪音的窗口也有斜率,而且可能很大。单位是每根K线的价格变化,所以读数在不同品种之间、不同K线周期之间,以及价格水平本身跳变的期货合约换月前后都无法比较。它只使用收盘价,因此波幅、跳空和K线内部结构都不可见。成交量和主动方向同样缺席——每根K线两手成交的陡峭斜率,与大量参与下的陡峭斜率得到同样的数字。
相关指标
其他语言版本
Frequently asked questions
- 线性回归斜率的数值到底代表什么?
- 它是最能拟合最近 N 个收盘价的那条直线上,每根K线的价格变化。斜率 0.5 表示拟合线每根K线上升半个价格单位;在 20 根K线的窗口中,首尾两点相隔十九根K线,沿直线约上升 9.5 个单位。单位是品种的价格单位,所以这个数字只有放在该品种的最小变动价位旁边才有意义。
- 线性回归斜率和移动平均斜率一样吗?
- 不一样。移动平均斜率比较平滑序列的两个相邻值,取决于平滑方式如何给旧数据加权。回归斜率一次性用一条直线拟合全部 N 个收盘价并报告其陡峭程度,所以窗口中的每根K线都直接参与拟合。
- 为什么价格走平的窗口斜率恰好为零?
- 最小二乘斜率的分母只取决于时间轴,对任何两根及以上K线的窗口都严格为正。每个收盘价都相同的窗口,分子为零,除以一个正数,结果就是精确的零,而不是无定义。