Standard Error Bands (SEB)

Haiyue
15min

I. What is the Standard Error Bands Indicator

Standard Error Bands (SEB) is a channel indicator proposed by American technical analyst Jon Andersen. It is built upon linear regression and standard error, with the core idea of using a linear regression line as the “best-fit trend” for prices, then offsetting it above and below by a certain multiple of the standard error to form a statistically meaningful confidence interval.

Standard Error Bands belongs to the Volatility / Bands overlay indicator category and consists of three lines:

  • Middle Line: The endpoint value of the nn-period linear regression line (also known as the Linear Regression Curve)
  • Upper Band: Middle Line + Multiplier x Standard Error
  • Lower Band: Middle Line - Multiplier x Standard Error

The default parameters are period n=20n = 20 and multiplier m=2m = 2.

Note

The key difference between Standard Error Bands and Bollinger Bands lies in:

  • Bollinger Bands use SMA (a horizontal line) + standard deviation, measuring the dispersion of prices around the mean
  • Standard Error Bands use a linear regression line (a sloped line) + standard error, measuring the statistical significance of price deviations from a linear trend

If Bollinger Bands answer the question “how far has the price deviated from the mean,” then Standard Error Bands answer the question “how far has the price deviated from the trend line, and is this deviation statistically significant.”


II. Mathematical Principles and Calculation

2.1 Linear Regression Basics

Given the most recent nn closing prices Ctn+1,Ctn+2,,CtC_{t-n+1}, C_{t-n+2}, \ldots, C_t, let the independent variable x=1,2,,nx = 1, 2, \ldots, n and the dependent variable y=Ctn+1,,Cty = C_{t-n+1}, \ldots, C_t.

The linear regression model is:

y^i=a+bxi\hat{y}_i = a + b \cdot x_i

Slope bb:

b=nxiyixiyinxi2(xi)2b = \frac{n \sum x_i y_i - \sum x_i \sum y_i}{n \sum x_i^2 - (\sum x_i)^2}

Intercept aa:

a=yˉbxˉa = \bar{y} - b \cdot \bar{x}

where xˉ=n+12\bar{x} = \frac{n+1}{2} and yˉ=1nyi\bar{y} = \frac{1}{n}\sum y_i.

2.2 Linear Regression Curve (Middle Line)

The Linear Regression Curve fits a linear regression to the most recent nn data points at each time point tt, then takes the last fitted value on the regression line (i.e., the predicted value at x=nx = n):

Middlet=at+btn\text{Middle}_t = a_t + b_t \cdot n

This curve can be thought of as a “trend-following moving average” — it considers not only the mean but also the direction (slope) of price changes.

2.3 Standard Error

The standard error (also called the standard error of the regression) measures the dispersion of actual data points around the regression line:

SEt=1n2i=1n(yiy^i)2\text{SE}_t = \sqrt{\frac{1}{n-2} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2}
Note

2.4 Upper and Lower Bands

Upper Band:

Uppert=Middlet+m×SEt\text{Upper}_t = \text{Middle}_t + m \times \text{SE}_t

Lower Band:

Lowert=Middletm×SEt\text{Lower}_t = \text{Middle}_t - m \times \text{SE}_t

where mm is the standard error multiplier, default 2.

2.5 Statistical Significance

Under the normal distribution assumption:

  • m=1m = 1: Approximately 68% of price points fall within the channel
  • m=2m = 2: Approximately 95% of price points fall within the channel
  • m=3m = 3: Approximately 99.7% of price points fall within the channel

When the price breaks beyond the 2x standard error band, it means this deviation is “statistically significant” at the 95% confidence level — i.e., it is unlikely to be caused by random fluctuations alone.

2.6 Calculation Steps

  1. Take the most recent nn closing prices
  2. Perform linear regression on these nn data points to obtain slope bb and intercept aa
  3. Take the last fitted value on the regression line (x=nx = n) as the middle line
  4. Calculate the residuals (actual values - fitted values) and compute the standard error
  5. Upper Band = Middle Line + m×m \times Standard Error
  6. Lower Band = Middle Line - m×m \times Standard Error

III. Python Implementation

import numpy as np
import pandas as pd

def standard_error_bands(close: pd.Series, period: int = 20,
                         multiplier: float = 2.0) -> pd.DataFrame:
    """
    Calculate Standard Error Bands (SEB).

    Parameters:
        close      : closing price series
        period     : linear regression period, default 20
        multiplier : standard error multiplier, default 2.0

    Returns:
        DataFrame with columns: middle, upper, lower, std_error, slope
    """
    n = period
    middle = pd.Series(np.nan, index=close.index)
    std_err = pd.Series(np.nan, index=close.index)
    slope = pd.Series(np.nan, index=close.index)

    # Pre-compute x-related constants
    x = np.arange(1, n + 1, dtype=float)
    x_mean = x.mean()
    x_var = ((x - x_mean) ** 2).sum()

    for i in range(n - 1, len(close)):
        y = close.iloc[i - n + 1: i + 1].values.astype(float)
        y_mean = y.mean()

        # Linear regression coefficients
        b = np.sum((x - x_mean) * (y - y_mean)) / x_var
        a = y_mean - b * x_mean

        # Last point on the regression line (x = n)
        lr_value = a + b * n
        middle.iloc[i] = lr_value
        slope.iloc[i] = b

        # Residuals and standard error
        y_hat = a + b * x
        residuals = y - y_hat
        se = np.sqrt(np.sum(residuals ** 2) / (n - 2))
        std_err.iloc[i] = se

    upper = middle + multiplier * std_err
    lower = middle - multiplier * std_err

    return pd.DataFrame({
        'middle': middle,
        'upper': upper,
        'lower': lower,
        'std_error': std_err,
        'slope': slope
    })


def standard_error_bands_vectorized(close: pd.Series, period: int = 20,
                                     multiplier: float = 2.0) -> pd.DataFrame:
    """
    Vectorized implementation of Standard Error Bands (better performance).
    Uses numpy rolling window techniques to avoid Python loops.
    """
    n = period
    x = np.arange(1, n + 1, dtype=float)
    x_mean = x.mean()
    x_var = ((x - x_mean) ** 2).sum()

    # Use rolling apply
    def calc_lr_and_se(window):
        y = window.values
        y_mean = y.mean()
        b = np.sum((x - x_mean) * (y - y_mean)) / x_var
        a = y_mean - b * x_mean
        lr_value = a + b * n
        y_hat = a + b * x
        se = np.sqrt(np.sum((y - y_hat) ** 2) / (n - 2))
        return lr_value, se, b

    results = close.rolling(window=n).apply(
        lambda w: calc_lr_and_se(w)[0], raw=False
    )
    middle = results

    se_series = close.rolling(window=n).apply(
        lambda w: calc_lr_and_se(w)[1], raw=False
    )

    slope_series = close.rolling(window=n).apply(
        lambda w: calc_lr_and_se(w)[2], raw=False
    )

    upper = middle + multiplier * se_series
    lower = middle - multiplier * se_series

    return pd.DataFrame({
        'middle': middle,
        'upper': upper,
        'lower': lower,
        'std_error': se_series,
        'slope': slope_series
    })


# ============ Usage Example ============
if __name__ == '__main__':
    np.random.seed(42)
    n_days = 100

    # Generate simulated OHLCV data (with a slight uptrend)
    trend = np.linspace(0, 10, n_days)
    noise = np.cumsum(np.random.randn(n_days) * 0.3)
    base_price = 100 + trend + noise

    df = pd.DataFrame({
        'open':   base_price + np.random.randn(n_days) * 0.3,
        'high':   base_price + np.abs(np.random.randn(n_days) * 0.8),
        'low':    base_price - np.abs(np.random.randn(n_days) * 0.8),
        'close':  base_price,
        'volume': np.random.randint(1000, 10000, n_days)
    })

    # Calculate Standard Error Bands (loop version)
    seb = standard_error_bands(df['close'], period=20, multiplier=2.0)
    result = pd.concat([df[['close']], seb], axis=1)

    print("=== Standard Error Bands Results (last 10 rows) ===")
    print(result.tail(10).to_string())

    # Deviation analysis
    result['deviation'] = (result['close'] - result['middle']) / result['std_error']
    result['in_band'] = (result['close'] >= result['lower']) & \
                        (result['close'] <= result['upper'])

    valid = result.dropna()
    in_band_pct = valid['in_band'].sum() / len(valid) * 100
    print(f"\n=== Statistical Analysis ===")
    print(f"Percentage of price within bands: {in_band_pct:.1f}% (theoretical ~95%)")
    print(f"Average standard error: {valid['std_error'].mean():.4f}")
    print(f"Average slope: {valid['slope'].mean():.4f}")

    # Signal generation
    result['signal'] = np.where(
        result['close'] > result['upper'], 'Significantly High',
        np.where(result['close'] < result['lower'], 'Significantly Low', 'Normal Range')
    )
    print("\n=== Deviation Signal Statistics ===")
    print(result['signal'].value_counts())

IV. Problems the Indicator Solves

4.1 Trend Direction and Strength Assessment

The middle line (Linear Regression Curve) of Standard Error Bands not only shows the central position of prices but also directly reflects the trend direction and speed through its slope:

  • Positive slope: Uptrend
  • Negative slope: Downtrend
  • Slope near zero: Sideways
  • Greater absolute slope value: Stronger trend
Tip

4.2 Statistically Significant Deviation Detection

Price breaking through the upper or lower bands of the Standard Error Bands carries clear statistical meaning:

  • Breaking the 2x standard error band: At the 95% confidence level, the deviation is statistically significant
  • This means the price movement is unlikely to be simply random fluctuation around the trend line — it is more likely that the trend is accelerating or reversing

4.3 Mean Reversion Trading

In the context of Standard Error Bands, the “mean” is not a simple average but the linear regression line:

  1. Price rises above the upper band -> May be excessively deviating from the trend — wait for reversion to the middle line
  2. Price falls below the lower band -> May be excessively deviating from the trend — wait for reversion to the middle line
  3. Price returns from the channel edge to the middle line -> Reversion complete

4.4 Channel Contraction / Expansion

  • Standard error decreases (channel narrows) -> Price closely follows the linear trend; the trend is clear
  • Standard error increases (channel widens) -> Price fluctuation around the trend line increases; trend reliability decreases
Warning

V. Advantages, Disadvantages, and Use Cases

Advantages

AdvantageDescription
Solid statistical foundationBased on linear regression and standard error, with clear statistical meaning
Trend-adaptiveThe middle line is a regression line rather than a horizontal average, better fitting the trend
Clear confidence interval2x standard error corresponds to approximately 95% confidence interval
Built-in trend directionThe slope directly reflects the trend direction and strength
Meaningful channel widthReflects the degree of data deviation from the trend

Disadvantages

DisadvantageDescription
Computationally intensiveRequires linear regression at each point — far more computation than SMA
Linear assumptionAssumes price follows a linear trend during the lookback period; performs poorly in non-linear movements
Multiple sensitivity parametersBoth period and multiplier require tuning
Poor at trend reversalsAt trend inflection points, the regression line direction lags behind the actual reversal
Less popular than Bollinger BandsMost trading platforms may not directly offer this indicator

Use Cases

  • Best suited for: Market phases with clear linear trends; quantitative research and statistical arbitrage
  • Moderately suited for: Medium to long-term trend following; confirming statistical significance of price deviations
  • Not suited for: Markets with frequent V-shaped reversals; short-period high-frequency trading

Comparison with Similar Indicators

IndicatorMiddle LineChannel Width BasisTrend Adaptiveness
Standard Error BandsLinear Regression LineStandard ErrorStrongest (includes slope)
Bollinger BandsSMAStandard DeviationWeak (horizontal average)
Keltner ChannelsEMAATRModerate
EnvelopesSMAFixed PercentageWeak

tip Parameter tuning tips:

  • A period of 20 is suitable for most daily chart analysis
  • A shorter period (e.g., 10) makes the regression line closer to the current trend but less stable
  • A longer period (e.g., 50) makes the regression line smoother but slower to respond to trend changes
  • A multiplier of 2 provides approximately 95% confidence interval and is suitable for most situations; for stricter filtering, use 2.5 or 3
  • Using in conjunction with the R-squared (coefficient of determination) indicator yields better results: when R2R^2 is high, the linear trend is clear, and the Standard Error Bands are more meaningful