Skip to content

trading.indicators.calculations.bollinger_bands

trading.indicators.calculations.bollinger_bands

BollingerBandSnapshot

Bases: NamedTuple

A single snapshot of Bollinger Bands.

Attributes:

Name Type Description
upper float

The upper band for the current price.

middle float

The middle band (pure SMA) for the current price.

lower float

The lower band for the current price.

stdev float

The population standard deviation over the lookback window.

BollingerBands

BollingerBands(period: days = 20, k: float = 2.0)

Bases: Indicator[BollingerBandSnapshot | None]

Rolling Bollinger Bands with variance updates.

Wraps an SMA and tracks a running sum-of-squares so that standard deviation is recomputed in constant time on every tick. Returns None until the lookback window is full.

Parameters:

Name Type Description Default
period days

Lookback window in bars (default 20).

20
k float

Band-width multiplier in standard deviations (default 2.0).

2.0
Source code in src\contango\trading\indicators\calculations\bollinger_bands.py
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
def __init__(self, period: days = 20, k: float = 2.0) -> None:
    """
    Args:
        period: Lookback window in bars (default 20).
        k: Band-width multiplier in standard deviations (default 2.0).
    """
    self._period = period
    self._k = k
    self._sma = SMA(period)

    self._sum: float = 0.0
    self._sum_sq: float = 0.0
    self._count: int = 0

    self._window: list[float] = [0.0] * period
    self._head: int = 0

update

update(price: USD) -> BollingerBandSnapshot | None

Takes in a new price and return the current bands, or None while the window is still filling.

Parameters:

Name Type Description Default
price USD

The price for the current bar.

required

Returns:

Type Description
BollingerBandSnapshot | None

A BollingerBandSnapshot(upper, middle, lower), or None until

BollingerBandSnapshot | None

period prices have been seen.

Source code in src\contango\trading\indicators\calculations\bollinger_bands.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def update(self, price: USD) -> BollingerBandSnapshot | None:
    """
    Takes in a new price and return the current bands, or None while the
    window is still filling.

    Args:
        price: The price for the current bar.

    Returns:
        A BollingerBandSnapshot(upper, middle, lower), or None until
        `period` prices have been seen.
    """
    middle = self._sma.update(price)

    if self._count == self._period:
        oldest = self._window[self._head]
        self._sum -= oldest
        self._sum_sq -= oldest * oldest

    self._window[self._head] = price
    self._head = (self._head + 1) % self._period
    self._count = min(self._count + 1, self._period)

    self._sum += price
    self._sum_sq += price * price

    if middle is None:
        return None

    n = self._period
    variance = (self._sum_sq - self._sum * self._sum / n) / n
    stdev = variance ** 0.5
    band = self._k * stdev

    return BollingerBandSnapshot(
        upper=middle + band,
        middle=middle,
        lower=middle - band,
        stdev=stdev,
    )