RSI 指标:公式、参数与读法
RSI(相对强弱指数)在回看窗口内比较上涨收盘的平均幅度与下跌收盘的平均幅度,并以 0 到 100 的刻度给出两者的力量对比。Senzoukria 在K线收盘价上采用 Wilder 原始平滑方法计算,默认周期为 14。
Senzoukria · 指标 · Updated September 2026
RSI — 动量, 显示在图表下方的独立面板中.
RSI 测量的是什么
这个数值反映的是平滑窗口内平均涨幅与平均跌幅之间的比例,而不是价格走过的距离。序列以前十四个收盘价变化的简单平均作为起点,因此在这十四个变化被计入之前不会绘制任何数值;此后每根K线都按 Wilder 递推更新两个平均值,更早的每一次变化都以递减权重保留在记忆中。三种边界情况有明确定义:窗口内只有上涨时返回 100,只有下跌时返回 0;窗口完全走平、两个平均值均为零时,不返回任何数值,而不是返回 100,因为没有波动的市场不包含方向信息。计算连续进行,不会在交易时段切换时重置。
公式:代码中的实现
这不是别处常见的定义,而是所发布代码实际计算的内容,记录在实现旁边:
Période entière ≥ `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 })); } /** SMA N sur série à trous : null dès qu'UNE valeur de la fenêtre est null — moyenner autour d'un trou mentirait sur la période effective. */ function smaNullable( values: ReadonlyArray<number | null>, period: number, ): Array<number | null> { const n = Math.max(1, Math.floor(period)); const out: Array<number | null> = new Array<number | null>(values.length).fill(null); for (let i = n - 1; i < values.length; i++) { let sum = 0; let ok = true; for (let j = i - n + 1; j <= i; j++) { const v = values[j]; if (v === null) { ok = false; break; } sum += v; } if (ok) out[i] = sum / n; } return out; } /** EMA N sur une série dont les valeurs définies forment un SUFFIXE contigu (sortie d'un lissage précédent) : l'EMA tourne sur le suffixe, le préfixe reste null. Série toute-null (ou trou interne — impossible dans nos usages) → tout-null, on ne devine pas. */ function suffixEma( values: ReadonlyArray<number | null>, period: number, ): Array<number | null> { const out: Array<number | null> = new Array<number | null>(values.length).fill(null); const start = values.findIndex((v) => v !== null); if (start < 0) return out; const defined: number[] = []; for (let i = start; i < values.length; i++) { const v = values[i]; if (v === null) return out; // pas un suffixe contigu → dégradation propre defined.push(v); } const ema = emaSeries(defined, period); for (let j = 0; j < ema.length; j++) out[start + j] = ema[j]; return out; } /** Égalités RSI (documentées) : marché PLAT (les deux moyennes nulles) → null — pas d'information directionnelle (déviation assumée du Pine `down==0 → 100`, qui afficherait 100 sur du plat) ; que des gains → 100 ; que des pertes → 0. */ function rsiValue(avgGain: number, avgLoss: number): number | null { if (avgLoss === 0 && avgGain === 0) return null; if (avgLoss === 0) return 100; if (avgGain === 0) return 0; return 100 - 100 / (1 + avgGain / avgLoss); } /** RSI de Wilder sur une série de valeurs (closes ici, série RSI pour le StochRSI). Graine = moyenne SIMPLE des N premières variations, puis lissage Wilder ; premier point à l'indice N (N variations consommées). */ function wilderRsiSeries( values: readonly number[], period: number, ): Array<number | null> { const n = Math.max(1, Math.floor(period)); const out: Array<number | null> = new Array<number | null>(values.length).fill(null); if (values.length < n + 1) return out; let avgGain = 0; let avgLoss = 0; for (let i = 1; i <= n; i++) { const ch = values[i] - values[i - 1]; if (ch > 0) avgGain += ch; else avgLoss -= ch; } avgGain /= n; avgLoss /= n; out[n] = rsiValue(avgGain, avgLoss); for (let i = n + 1; i < values.length; i++) { const ch = values[i] - values[i - 1]; avgGain = (avgGain * (n - 1) + Math.max(0, ch)) / n; avgLoss = (avgLoss * (n - 1) + Math.max(0, -ch)) / n; out[i] = rsiValue(avgGain, avgLoss); } return out; } /** %K brut : 100·(close − LL_N)/(HH_N − LL_N) sur high/low des barres. Fenêtre PLATE (HH == LL) → null : 0/0 = pas d'information (même règle que delta-percent). */ function rawStochK( bars: readonly FootprintBar[], period: number, ): Array<number | null> { const n = Math.max(1, Math.floor(period)); const out: Array<number | null> = new Array<number | null>(bars.length).fill(null); for (let i = n - 1; i < bars.length; i++) { let hh = Number.NEGATIVE_INFINITY; let ll = Number.POSITIVE_INFINITY; for (let j = i - n + 1; j <= i; j++) { if (bars[j].high > hh) hh = bars[j].high; if (bars[j].low < ll) ll = bars[j].low; } const range = hh - ll; out[i] = range > 0 ? (100 * (bars[i].close - ll)) / range : null; } return out; } function typicalPrice(b: FootprintBar): number { return (b.high + b.low + b.close) / 3; } /** True range de la barre i — TR[0] = high − low (pas de close précédent), même définition que `atrSeries` (averages.ts). */ function trueRangeAt(bars: readonly FootprintBar[], i: number): number { const b = bars[i]; if (i === 0) return b.high - b.low; const pc = bars[i - 1].close; return Math.max(b.high - b.low, Math.abs(b.high - pc), Math.abs(b.low - pc)); } // ── Defs ──────────────────────────────────────────────────────────────────── /** RSI (Wilder, 1978) — lissage WILDER : gain = max(0, close − closeₚ) ; loss = max(0, closeₚ − close) graine = moyenne simple des N premiers gains/losses (à l'indice N) ; puis avg = (avg·(N−1) + x)/N ; RSI = 100 − 100/(1 + avgGain/avgLoss) Égalités : cf. `rsiValue` (plat → null, tout-gain → 100, tout-perte → 0). CONTINU — pas de reset session. Défaut N=14 (Wilder).
如何解读
- 读数高于 70 表示平滑窗口内以上涨收盘为主。在持续的趋势中,RSI 可以长时间停留在这一区域,因此应把它看作对已发生情况的描述,而不是反转信号。
- 读数低于 30 描述的是对称的情形:窗口内以下跌收盘为主。
- 50 线把平均涨幅大于平均跌幅的窗口与相反的窗口分开。穿越 50 意味着这一对比发生了变化,但单凭它并不能确立趋势。
- 背离——价格创出更高的高点而 RSI 的高点更低——说明第二段行情是由较小的上涨收盘构成的。下结论之前,请对照这两段行情的足迹图。
- 线条中断表示窗口走平、数值未定义。缺口不等于零值。
参数与默认值
周期默认为 14,即 Wilder 本人的选择,可设为 2 到 200。周期越短,对每个新收盘价的反应越强,触及 30 和 70 区域的次数也多得多;周期越长,曲线越平缓,极值越少见。颜色设置只改变线条在面板中的绘制方式。
| 参数 | 类型 | 默认值 | 取值范围 |
|---|---|---|---|
| Period | number | 14 | 2 – 200 |
它无法显示的内容
RSI 只读取收盘价。一段行情是买方主动吃掉卖价挂单推动的,还是卖方撤单退让造成的,这一区别只存在于足迹图K线的 Bid × Ask 列中,这条线的任何读数都无法还原它。由于序列跨交易时段连续计算,隔夜跳空会作为一次很大的收盘价变化进入计算,并在随后许多根K线中持续影响平均值。Wilder 平滑本身记忆很长,因此单根异常大的K线在形成之后很久仍会体现在数值中。走平的区间会在线条中产生真实的缺口,而不是给出默认读数。
相关指标
- Stochastic — 动量
- Stoch RSI — 动量
- CCI — 动量
- ROC — 动量
- Williams %R — 动量
- Momentum — 动量
其他语言版本
Frequently asked questions
- RSI 为 70 是什么意思?
- RSI 为 70 表示在回看窗口内,平均上涨收盘幅度约为平均下跌收盘幅度的 2.33 倍。它描述的是一段以上涨收盘为主的时期,并不衡量价格还能走多远;在趋势市场中,这个数值可以连续许多根K线保持在 70 以上。
- 这里的 RSI 和其他看盘平台上的一样吗?
- 平滑方式是标准的 Wilder 递推,并以前 N 个变化的简单平均作为起点,与常见实现一致。有一种情况是刻意不同的:当平均涨幅和平均跌幅都为零、即市场完全走平时,Senzoukria 不返回数值,而部分源自 Pine 的版本在这种情况下返回 100。
- RSI 会在每个交易时段开始时重置吗?
- 不会。这里的 RSI 序列是连续的:平滑平均值从一个交易时段延续到下一个。因此新时段的第一个变化就是上一收盘价与新收盘价之间的缺口,对于分时段交易的期货合约,这个缺口可能很大。