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

R10 / Range Stat

One-tenth of the typical daily range (mean+stddev of high-low over 30 prior bars), the standard minimum-manageable-risk sizing box.

Concept: what R10 means →

R10 is the range stat divided by 10: (mean + population standard deviation of the daily high-low range) over the 30 bars strictly before the current day, then divided by 10. It represents one-tenth of the typical daily range — the standard minimum-manageable-risk box used to size positions. The strictly-before window keeps the value free of look-ahead so it can drive next-session sizing.

Verified. Python and JavaScript implementations agree to 1.11e-16 on a 60-bar reference high/low series (canonical Python (edge-scan) vs JavaScript, comparable positions).

Pythonpermalink →

import numpy as np


def r10(high, low):
    """R10 / Range Stat: one-tenth of the typical daily range.

    R10 = (mean(range) + population_stddev(range)) / 10 computed over the 30
    EOD bars STRICTLY BEFORE the current bar (the current bar is excluded so the
    value is safe for no-look-ahead sizing). ``range`` is high - low per bar.

    This is the standard minimum-manageable-risk box used for position sizing:
    a risk unit of one-tenth of the typical daily range.

    Faithful to edge-scan ``_rangestat_from_window`` / ``compute_rangestat_for_date``
    (n_div=10, fixed 30-bar strictly-before lookback).

    Args:
        high: sequence of bar highs, ascending by date.
        low:  sequence of bar lows, ascending by date.

    Returns:
        list aligned to the input: r10[i] uses the 30 bars before index i;
        None where fewer than 2 prior bars are available.
    """
    high = np.asarray(high, dtype=float)
    low = np.asarray(low, dtype=float)
    ranges = high - low
    out = []
    for i in range(len(ranges)):
        window = ranges[max(0, i - 30):i]   # 30 bars STRICTLY BEFORE bar i
        if len(window) < 2:
            out.append(None)
            continue
        mean = float(np.mean(window))
        std_pop = float(np.std(window, ddof=0))   # population stddev
        out.append((mean + std_pop) / 10.0)
    return out

JavaScriptpermalink →

/**
 * R10 / Range Stat: one-tenth of the typical daily range.
 *
 * R10 = (mean(range) + populationStdDev(range)) / 10 over the 30 EOD bars
 * STRICTLY BEFORE the current bar (current bar excluded -> no look-ahead).
 * `range` is high - low per bar. This is the standard minimum-manageable-risk
 * box used for position sizing.
 *
 * Faithful to edge-canvas calcR10 (window = slice(-(n+1), -1), n<=30).
 *
 * @param {number[]} high - bar highs, ascending by date
 * @param {number[]} low  - bar lows, ascending by date
 * @returns {(number|null)[]} r10[i] uses the 30 bars before index i;
 *                            null where fewer than 2 prior bars exist
 */
function r10(high, low) {
  const out = [];
  for (let i = 0; i < high.length; i++) {
    const n = Math.min(i, 30);            // bars available STRICTLY BEFORE i
    if (n < 2) { out.push(null); continue; }
    const ranges = [];
    for (let j = i - n; j < i; j++) ranges.push(high[j] - low[j]);
    const mean = ranges.reduce((s, v) => s + v, 0) / ranges.length;
    const variance =
      ranges.reduce((s, v) => s + (v - mean) ** 2, 0) / ranges.length;
    out.push((mean + Math.sqrt(variance)) / 10.0);  // population stddev
  }
  return out;
}