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 Dragon (spine, belly & northern skin)

A fast +/-0.5 SD band on the 10-period mean of RL10. The spine is the centerline, the belly is the lower (southern) skin, and the northern skin is the upper band.

Concept: what DRAGON means →

The Dragon is a tight band built on the 10-period mean of the RL10 line. Its spine is the centerline itself - the SMA(10) of RL10. The belly (the lower, or southern, skin) sits 0.5 SD below the spine, and the northern skin sits 0.5 SD above it.

Because it is built on a short, smoothed base, the Dragon hugs price as a noise boundary during trends and flips about six times faster than the River at trend inflection points - which is what makes price crossing the belly or the northern skin a meaningful early signal.

Verified. Python and JavaScript implementations agree to 7.11e-14 on a 60-bar reference price series (Python vs JavaScript across the Dragon spine (centerline), belly (lower skin), and northern skin (upper) - float64 round-off only).

Pythonpermalink →

import numpy as np

def _rl(values, n=10):                     # RL10 endpoint (see the RL10 page)
    v = np.asarray(values, float); out = np.full(len(v), np.nan)
    if n <= 0 or len(v) < n: return out
    x = np.arange(n); xm = (n - 1) / 2.0; xd = x - xm
    denom = float(np.sum(xd ** 2))
    if denom == 0: return out
    w = 1.0 / n + xm * xd / denom
    out[n - 1:] = np.correlate(v, w, mode="valid")
    return out

def _sma(values, n):
    v = np.asarray(values, float); out = np.full(len(v), np.nan)
    for i in range(n - 1, len(v)):
        win = v[i - n + 1:i + 1]
        if not np.isnan(win).any(): out[i] = win.mean()
    return out

def _rolling_std(values, n):
    v = np.asarray(values, float); out = np.full(len(v), np.nan)
    for i in range(n - 1, len(v)):
        win = v[i - n + 1:i + 1]
        if not np.isnan(win).any(): out[i] = win.std()   # population std
    return out

def dragon(closes):
    """The Dragon - a fast +/-0.5 SD band on the 10-period mean of RL10.

      spine         = the centerline itself, SMA(10) of RL10
      belly         = spine - 0.5 SD   (lower / southern skin)
      northern_skin = spine + 0.5 SD   (upper skin)

    The Dragon hugs price as a noise boundary during trends and flips about six
    times faster than the River at trend inflection points."""
    spine = _sma(_rl(closes, 10), 10)             # centerline
    sd = _rolling_std(spine, 10)
    return {"spine": spine,                         # centerline
            "belly": spine - 0.5 * sd,              # lower / southern skin
            "northern_skin": spine + 0.5 * sd}      # upper skin

JavaScriptpermalink →

// The Dragon - a fast +/-0.5 SD band on the 10-period mean of RL10.
//   spine         = the centerline itself, SMA(10) of RL10
//   belly         = spine - 0.5 SD   (lower / southern skin)
//   northern skin = spine + 0.5 SD   (upper skin)
// Flips ~6x faster than the River at trend inflections.
function rl(closes, n = 10) {              // RL10 endpoint (see the RL10 page)
  const len = closes.length, out = new Array(len).fill(null);
  if (n <= 0 || len < n) return out;
  const sx = (n * (n - 1)) / 2, sx2 = (n * (n - 1) * (2 * n - 1)) / 6, d = n * sx2 - sx * sx;
  if (d === 0) return out;
  for (let e = n; e <= len; e++) {
    let sy = 0, sxy = 0, bad = false;
    for (let j = 0; j < n; j++) { const v = closes[e - n + j]; if (v == null || Number.isNaN(v)) { bad = true; break; } sy += v; sxy += j * v; }
    if (bad) continue;
    const m = (n * sxy - sx * sy) / d, b = (sy - m * sx) / n;
    out[e - 1] = m * (n - 1) + b;
  }
  return out;
}
function sma(values, n) {
  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 o = values[i - n]; if (o != null && !Number.isNaN(o)) { sum -= o; count--; } }
    if (i >= n - 1 && count === n) out[i] = sum / n;
  }
  return out;
}
function rollingStd(values, n) {
  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 mn = 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 - mn) * (v - mn); }
    out[i] = Math.sqrt(sq / c);
  }
  return out;
}
export function dragon(closes) {
  const spine = sma(rl(closes, 10), 10);   // centerline
  const sd = rollingStd(spine, 10);
  return {
    spine,                                                                                  // centerline
    belly:       spine.map((c, i) => (c != null && sd[i] != null) ? c - 0.5 * sd[i] : null), // lower / southern skin
    northernSkin: spine.map((c, i) => (c != null && sd[i] != null) ? c + 0.5 * sd[i] : null), // upper skin
  };
}