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

The Dragon (spine, belly & northern skin) in JavaScript

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 →

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).

JavaScript

// 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
  };
}