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.
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
A short note from Dr. Ken Long on the craft of trading. One idea to think about and work on that day, drawn from decades of teaching and live trading. Free.
Your email:
Tue–Fri mornings. Unsubscribe anytime. No spam, no hype.