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.
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;
}
Improve Your Craft Every Morning
Daily commentary from Dr. Ken Long — what he's seeing in markets, how he's framing trades, and what's worth practicing today. Free.
Your email:
Tue–Fri mornings. Unsubscribe anytime. No spam, no hype.