The River, Flood Plain & Red Line
Owl Group Trading's 30-period Bollinger zones around the Mean: River (+/-1 SD), Flood Plain (+/-2 SD), Red Line (+/-3 SD).
Concept: what RIVER-FLOOD-PLAIN means →
Bollinger bands matter in Owl Group Trading as named zones around the Mean (Z0 = the 30-period SMA). The River is +/-1 SD (top = Z1), the Flood Plain is +/-2 SD (Z2), and the Red Line is +/-3 SD (Z3).
Price drifting through these zones is how a trader reads stretch and reversion: inside the River is normal, into the Flood Plain is stretched, and at the Red Line is statistically extreme.
2.84e-14 on a 60-bar reference price series (Python vs JavaScript across the River/Flood Plain/Red Line bands (+/-1, +/-2, +/-3 SD over 30 periods) - float64 round-off only).Pythonpermalink →
import numpy as np
def _sma(values, n):
v = np.asarray(values, float)
out = np.full(len(v), np.nan)
for i in range(n - 1, len(v)):
out[i] = v[i - n + 1:i + 1].mean()
return out
def _rolling_std(values, n): # population std (ddof=0)
v = np.asarray(values, float)
out = np.full(len(v), np.nan)
for i in range(n - 1, len(v)):
out[i] = v[i - n + 1:i + 1].std()
return out
def river_flood_plain(closes, n=30):
"""The River, Flood Plain and Red Line - 30-period Bollinger zones around
the Mean (Z0 = the 30-period SMA). River = +/-1 SD (Z1), Flood Plain =
+/-2 SD (Z2), Red Line = +/-3 SD (Z3). Returns the Mean plus each (upper,
lower) band pair."""
mean = _sma(closes, n)
sd = _rolling_std(closes, n)
band = lambda k: (mean + k * sd, mean - k * sd)
return {"mean": mean,
"river": band(1), # Z1 / Z-1
"flood_plain": band(2), # Z2 / Z-2
"red_line": band(3)} # Z3 / Z-3
JavaScriptpermalink →
// The River, Flood Plain and Red Line - 30-period Bollinger zones around the
// Mean (Z0 = the 30-period SMA). River = +/-1 SD (Z1), Flood Plain = +/-2 SD
// (Z2), Red Line = +/-3 SD (Z3).
function sma(values, n = 30) {
const len = values.length, out = new Array(len).fill(null);
let sum = 0, count = 0;
for (let i = 0; i < len; i++) {
const v = values[i];
if (v != null && !Number.isNaN(v)) { sum += v; count++; }
if (i >= n) { const old = values[i - n]; if (old != null && !Number.isNaN(old)) { sum -= old; count--; } }
if (i >= n - 1 && count === n) out[i] = sum / n;
}
return out;
}
function rollingStd(values, n) { // population std
const len = values.length, out = new Array(len).fill(null);
for (let i = n - 1; i < len; i++) {
let s = 0, c = 0;
for (let j = i - n + 1; j <= i; j++) { const v = values[j]; if (v != null && !Number.isNaN(v)) { s += v; c++; } }
if (!c) continue;
const m = s / c; let sq = 0;
for (let j = i - n + 1; j <= i; j++) { const v = values[j]; if (v != null && !Number.isNaN(v)) sq += (v - m) * (v - m); }
out[i] = Math.sqrt(sq / c);
}
return out;
}
export function riverFloodPlain(closes, n = 30) {
const mean = sma(closes, n), sd = rollingStd(closes, n);
const band = (k) => ({
upper: mean.map((m, i) => (m != null && sd[i] != null) ? m + k * sd[i] : null),
lower: mean.map((m, i) => (m != null && sd[i] != null) ? m - k * sd[i] : null),
});
return { mean, river: band(1), floodPlain: band(2), redLine: band(3) };
}
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.