OGT Owl Group Trading by Dr. Ken Long
Home About Learn The Loop Code Courses Essays Store Partners FAQ
Indicator Code · Owl Group Trading

The Mean (30-period SMA)

The 30-period simple moving average. In Owl Group Trading this is the Mean (Z0, the BBmean) - the centerline that anchors the River, Flood Plain, and Dragon.

Concept: what MEAN means →

The Mean is the 30-period simple moving average of price. In Owl Group Trading vocabulary it is Z0 (the BBmean) - the single centerline that everything else is measured from.

The River, Flood Plain, and Red Line are standard-deviation bands around this Mean; the Red/Yellow/Green zones are read relative to it. Get the Mean right and the whole RLCO band structure follows.

Verified. Python and JavaScript implementations agree to 0.00e+00 on a 60-bar reference price series (Python vs JavaScript, comparable positions).

Pythonpermalink →

import math


def sma(values, n=30):
    """Simple Moving Average over a trailing window of n values.

    Faithful port of edge-canvas calcSMA (incremental running-sum):
      - returns a list the same length as `values`
      - the first n-1 positions are None (warmup: not enough history yet)
      - a position emits only when the trailing window of n entries is
        fully populated with finite numbers; the value is then sum / n
      - n <= 0, or fewer than n inputs, yields an all-None result
    None / NaN inputs are skipped from the running sum and count.
    """
    vals = list(values)
    length = len(vals)
    result = [None] * length
    if n <= 0 or length < n:
        return result

    running_sum = 0.0
    count = 0
    for i in range(length):
        v = vals[i]
        if v is not None and not (isinstance(v, float) and math.isnan(v)):
            running_sum += v
            count += 1
        # drop the element that just left the trailing window
        if i >= n:
            old = vals[i - n]
            if old is not None and not (isinstance(old, float) and math.isnan(old)):
                running_sum -= old
                count -= 1
        # emit only once a full window of finite values is present
        if i >= n - 1 and count == n:
            result[i] = running_sum / n
    return result

JavaScriptpermalink →

/**
 * Simple Moving Average over a trailing window of n values.
 *
 * Returns an array the same length as `values`. The first n-1 positions are
 * null (warmup); a position emits only when its trailing window of n entries
 * is fully populated with finite numbers, and the value is then sum / n.
 * n <= 0 or fewer than n inputs yields an all-null result. null/NaN inputs
 * are skipped from the running sum and count.
 *
 * @param {(number|null)[]} values - Input series
 * @param {number} n - Window size (default 30)
 * @returns {(number|null)[]} Same-length array of SMA values (or null)
 */
export function sma(values, n = 30) {
  const len = values.length;
  const result = new Array(len).fill(null);
  if (n <= 0 || len < n) return result;

  let sum = 0;
  let count = 0;

  for (let i = 0; i < len; i++) {
    const v = values[i];
    if (v != null && !Number.isNaN(v)) {
      sum += v;
      count++;
    }
    // drop the element that just left the trailing window
    if (i >= n) {
      const old = values[i - n];
      if (old != null && !Number.isNaN(old)) {
        sum -= old;
        count--;
      }
    }
    // emit only once a full window of finite values is present
    if (i >= n - 1 && count === n) {
      result[i] = sum / n;
    }
  }

  return result;
}