{
 "name": "Ora Rules",
 "version": "1.0.0",
 "label": "Ora Rules v1",
 "date": "2026-10-31",
 "url": "https://oramodel.com/spec/v1",
 "json": "https://oramodel.com/spec/v1.json",
 "license": {
  "name": "CC BY 4.0",
  "url": "https://creativecommons.org/licenses/by/4.0/",
  "attribution": "Ora Rules v1 by KlickAnalytics (oramodel.com), CC BY 4.0"
 },
 "editor": "KlickAnalytics (Ora)",
 "grammar": "rule     := or\nor       := and ( \"or\" and )*\nand      := not ( \"and\" not )*\nnot      := ( \"not\" | \"!\" ) not | compare\ncompare  := sum ( (\">\" | \">=\" | \"<\" | \"<=\" | \"==\" | \"=\" | \"!=\") sum )*\nsum      := product ( (\"+\" | \"-\") product )*\nproduct  := power ( (\"*\" | \"/\" | \"%\") power )*\npower    := unary ( \"^\" unary )*\nunary    := (\"+\" | \"-\") unary | primary\nprimary  := \"(\" rule \")\" | number | \"true\" | \"false\"\n          | price [ \"[\" offset \"]\" ] | function \"(\" [ args ] \")\" | fact\nargs     := rule ( \",\" rule )*\nprice    := \"O\" | \"H\" | \"L\" | \"C\" | \"V\"\noffset   := [ \"-\" ] digits\nnumber   := digits [ \".\" digits ] | \".\" digits\nfact     := name                       (a name that is not a price, keyword or function)\nname     := letter ( letter | digit | \"_\" )*",
 "precedence": [
  [
   "1",
   "( ) function calls, offsets"
  ],
  [
   "2",
   "^"
  ],
  [
   "3",
   "* / %"
  ],
  [
   "4",
   "+ -"
  ],
  [
   "5",
   "> >= < <= == = !="
  ],
  [
   "6",
   "not !"
  ],
  [
   "7",
   "and"
  ],
  [
   "8",
   "or"
  ]
 ],
 "beliefs": {
  "range": [
   0.05,
   0.95
  ],
  "margin": "left - right for > and >=; right - left for < and <= (computed on the latest bar)",
  "scale": "sample standard deviation (ddof 1) of (left - right) over the last 60 bars, skipping not-known values; fewer than 8 values or zero: the declared scale of a fact on either side, else 0.1 * |left| at the latest bar",
  "rounding": 4,
  "formula": "r = min(|margin| / scale, 3); b = 0.5 + 0.45 * (1 - exp(-1.6 * r)) when the margin is on the true side, else 1 - b",
  "ties": {
   ">=": 0.55,
   "<=": 0.55,
   ">": 0.45,
   "<": 0.45
  },
  "unknown": 0.5,
  "bare_expression": [
   0.95,
   0.05
  ],
  "and": "min",
  "or": "max",
  "not": "1 - x",
  "equal": "1.05 - b(|left - right|)",
  "not_equal": "b(|left - right|)",
  "facts": "a fact has no history, so each declares its own scale (3 days for day counts, 0.5 for flags, ...)"
 },
 "functions": [
  {
   "name": "sum",
   "group": "Windows",
   "level": "core",
   "signature": "sum(x, n)",
   "description": "Rolling sum of the last n bars. Summing a comparison counts the bars where it held.",
   "example": "sum(C > C[-1], 10) >= 6",
   "value_example": "sum(C > C[-1], 10)",
   "example_reads": "Up on at least six of the last ten sessions."
  },
  {
   "name": "avg",
   "group": "Windows",
   "level": "core",
   "signature": "avg(x, n)",
   "description": "Another name for sma(). Same result.",
   "example": "V > avg(V, 20)",
   "value_example": "avg(V, 20)",
   "example_reads": "Volume above its 20-day average."
  },
  {
   "name": "mean",
   "group": "Windows",
   "level": "core",
   "signature": "mean(x, n)",
   "description": "A third name for sma().",
   "example": "C > mean(C, 100)",
   "value_example": "mean(C, 100)",
   "example_reads": "Above the 100-day average."
  },
  {
   "name": "sma",
   "group": "Windows",
   "level": "core",
   "signature": "sma(x, n)",
   "description": "Simple moving average of the last n bars, including the current one.",
   "example": "C > sma(C, 50)",
   "value_example": "sma(C, 50)",
   "example_reads": "Above the 50-day average."
  },
  {
   "name": "min",
   "group": "Windows",
   "level": "core",
   "signature": "min(x, n)",
   "description": "Lowest value of the last n bars, INCLUDING the current one.",
   "example": "C < 1.02 * min(L, 20)",
   "value_example": "min(L, 20)",
   "example_reads": "Closed within 2% of the 20-day low. (L <= min(L, 20) would be a poor rule: min includes today, so the two sides are at best equal, and an exact tie answers only 0.55.)"
  },
  {
   "name": "max",
   "group": "Windows",
   "level": "core",
   "signature": "max(x, n)",
   "description": "Highest value of the last n bars, INCLUDING the current one. So C > max(C, n) can never be true.",
   "example": "C >= 0.98 * max(H, 20)",
   "value_example": "max(H, 20)",
   "example_reads": "Closed within 2% of the 20-day high."
  },
  {
   "name": "highest",
   "group": "Windows",
   "level": "core",
   "signature": "highest(x, n)",
   "description": "Highest value of the n bars BEFORE the current one. This is the breakout form.",
   "example": "C > highest(C, 20)",
   "value_example": "highest(C, 20)",
   "example_reads": "Closed above every close of the previous 20 sessions."
  },
  {
   "name": "lowest",
   "group": "Windows",
   "level": "core",
   "signature": "lowest(x, n)",
   "description": "Lowest value of the n bars before the current one.",
   "example": "C < lowest(C, 20)",
   "value_example": "lowest(C, 20)",
   "example_reads": "Closed below every close of the previous 20 sessions."
  },
  {
   "name": "std",
   "group": "Windows",
   "level": "core",
   "signature": "std(x, n)",
   "description": "Standard deviation over the window (population form, like most charting tools).",
   "example": "std(C, 10) < 0.5 * std(C, 50)",
   "value_example": "std(C, 20)",
   "example_reads": "Recent swings are less than half their longer-run size: compression."
  },
  {
   "name": "zscore",
   "group": "Windows",
   "level": "core",
   "signature": "zscore(x, n)",
   "description": "How many standard deviations the current value sits from its window mean. A flat window scores 0.",
   "example": "zscore(C, 20) < -2",
   "value_example": "zscore(C, 20)",
   "example_reads": "More than two deviations under the 20-day mean."
  },
  {
   "name": "ema",
   "group": "Windows",
   "level": "core",
   "signature": "ema(x, n)",
   "description": "Exponential moving average, smoothing 2 / (n + 1).",
   "example": "C > ema(C, 21)",
   "value_example": "ema(C, 20)",
   "example_reads": "Above the 21-day EMA."
  },
  {
   "name": "rsi",
   "group": "Indicators",
   "level": "core",
   "signature": "rsi(x, n)",
   "description": "Relative strength index, 0 to 100 (Wilder). rsi(C, 14) is the usual one.",
   "example": "rsi(C, 14) > 50",
   "value_example": "rsi(C, 14)",
   "example_reads": "Momentum on the up side."
  },
  {
   "name": "roc",
   "group": "Windows",
   "level": "core",
   "signature": "roc(x, n)",
   "description": "Percent change over n bars: roc(C, 20) = 5 means up 5% in 20 sessions.",
   "example": "roc(C, 20) > 5",
   "value_example": "roc(C, 20)",
   "example_reads": "Up more than 5% over the last 20 sessions."
  },
  {
   "name": "atr",
   "group": "Indicators",
   "level": "core",
   "signature": "atr(n)",
   "description": "Average true range over n bars, in price.",
   "example": "H - L > 2 * atr(14)",
   "value_example": "atr(14)",
   "example_reads": "Today's range is more than twice the average."
  },
  {
   "name": "atr_pct",
   "group": "Indicators",
   "level": "core",
   "signature": "atr_pct(n)",
   "description": "ATR as a percent of the close: 3 means 3%. Comparable across symbols.",
   "example": "atr_pct(14) > 3",
   "value_example": "atr_pct(14)",
   "example_reads": "A typical day moves more than 3%."
  },
  {
   "name": "macd",
   "group": "Indicators",
   "level": "core",
   "signature": "macd(x [, fast, slow, signal])",
   "description": "MACD line: ema(fast) − ema(slow). Defaults 12, 26, 9.",
   "example": "macd(C) > 0",
   "value_example": "macd(C)",
   "example_reads": "The 12-day EMA is above the 26-day."
  },
  {
   "name": "macdsignal",
   "group": "Indicators",
   "level": "core",
   "signature": "macdsignal(x [, fast, slow, signal])",
   "description": "The MACD signal line: an EMA of the MACD line.",
   "example": "macd(C) > macdsignal(C)",
   "value_example": "macdsignal(C)",
   "example_reads": "MACD above its signal line."
  },
  {
   "name": "macdhist",
   "group": "Indicators",
   "level": "core",
   "signature": "macdhist(x [, fast, slow, signal])",
   "description": "MACD histogram: line minus signal.",
   "example": "macdhist(C) > ref(macdhist(C), -1)",
   "value_example": "macdhist(C)",
   "example_reads": "The histogram is rising."
  },
  {
   "name": "bbupper",
   "group": "Indicators",
   "level": "core",
   "signature": "bbupper(x, n [, up, dn])",
   "description": "Upper Bollinger band: sma + up × std. Default 2.",
   "example": "C > bbupper(C, 20, 2)",
   "value_example": "bbupper(C, 20)",
   "example_reads": "A close above the upper band."
  },
  {
   "name": "bbmid",
   "group": "Indicators",
   "level": "core",
   "signature": "bbmid(x, n)",
   "description": "Middle band: the simple average.",
   "example": "C > bbmid(C, 20)",
   "value_example": "bbmid(C, 20)",
   "example_reads": "Above the middle band (the 20-day average)."
  },
  {
   "name": "bblower",
   "group": "Indicators",
   "level": "core",
   "signature": "bblower(x, n [, up, dn])",
   "description": "Lower Bollinger band: sma − dn × std (dn defaults to up).",
   "example": "C < bblower(C, 20, 2)",
   "value_example": "bblower(C, 20)",
   "example_reads": "A close under the lower band."
  },
  {
   "name": "adx",
   "group": "Indicators",
   "level": "core",
   "signature": "adx(n)",
   "description": "Trend strength, 0 to 100, with no direction. Above 25 is usually called trending.",
   "example": "adx(14) > 25",
   "value_example": "adx(14)",
   "example_reads": "A trending market, up or down."
  },
  {
   "name": "diplus",
   "group": "Indicators",
   "level": "core",
   "signature": "diplus(n)",
   "description": "+DI: the upward directional movement ADX is built from.",
   "example": "diplus(14) > diminus(14)",
   "value_example": "diplus(14)",
   "example_reads": "Up moves have been stronger than down moves."
  },
  {
   "name": "diminus",
   "group": "Indicators",
   "level": "core",
   "signature": "diminus(n)",
   "description": "−DI: the downward directional movement.",
   "example": "diminus(14) > 25",
   "value_example": "diminus(14)",
   "example_reads": "Strong downward movement."
  },
  {
   "name": "stochk",
   "group": "Indicators",
   "level": "core",
   "signature": "stochk(n [, d, slow])",
   "description": "Stochastic %K, 0 to 100: where the close sits in the n-bar range, smoothed over slow (default 3).",
   "example": "stochk(14) < 20",
   "value_example": "stochk(14)",
   "example_reads": "Near the bottom of its 14-day range."
  },
  {
   "name": "stochd",
   "group": "Indicators",
   "level": "core",
   "signature": "stochd(n [, d, slow])",
   "description": "Stochastic %D: the d-bar average of %K (default 3).",
   "example": "stochk(14) > stochd(14)",
   "value_example": "stochd(14)",
   "example_reads": "%K above %D."
  },
  {
   "name": "cci",
   "group": "Indicators",
   "level": "core",
   "signature": "cci(n)",
   "description": "Commodity channel index: typical price against its average, in units of mean deviation.",
   "example": "cci(20) > 100",
   "value_example": "cci(20)",
   "example_reads": "Strong upward deviation."
  },
  {
   "name": "willr",
   "group": "Indicators",
   "level": "core",
   "signature": "willr(n)",
   "description": "Williams %R, −100 to 0: 0 is the top of the n-bar range.",
   "example": "willr(14) > -20",
   "value_example": "willr(14)",
   "example_reads": "In the top fifth of the 14-day range."
  },
  {
   "name": "obv",
   "group": "Indicators",
   "level": "core",
   "signature": "obv()",
   "description": "On-balance volume: adds the day's volume on an up close, subtracts it on a down close.",
   "example": "obv() > sma(obv(), 20)",
   "value_example": "obv()",
   "example_reads": "Volume flow above its 20-day average."
  },
  {
   "name": "abs",
   "group": "Maths",
   "level": "core",
   "signature": "abs(x)",
   "description": "Absolute value.",
   "example": "abs(C / C[-1] - 1) > 0.02",
   "value_example": "abs(roc(C, 1))",
   "example_reads": "Moved more than 2% either way today."
  },
  {
   "name": "sqrt",
   "group": "Maths",
   "level": "core",
   "signature": "sqrt(x)",
   "description": "Square root (negative input is not a number).",
   "example": "std(log(C / C[-1]), 20) * sqrt(252) > 0.4",
   "value_example": "sqrt(V)",
   "example_reads": "Annualized 20-day volatility above 40%."
  },
  {
   "name": "log",
   "group": "Maths",
   "level": "core",
   "signature": "log(x)",
   "description": "Natural logarithm (zero or negative input is not a number).",
   "example": "log(C / C[-20]) > 0.05",
   "value_example": "log(C)",
   "example_reads": "A 20-session log return above 5%."
  },
  {
   "name": "sign",
   "group": "Maths",
   "level": "core",
   "signature": "sign(x)",
   "description": "−1, 0 or 1.",
   "example": "sum(sign(C - C[-1]), 10) > 2",
   "value_example": "sign(C - C[-1])",
   "example_reads": "More up days than down days over ten sessions, by at least three."
  },
  {
   "name": "iif",
   "group": "Maths",
   "level": "core",
   "signature": "iif(cond, a, b)",
   "description": "a where the condition holds, b otherwise. Use it to switch a threshold by regime.",
   "example": "rsi(C, 14) < iif(C > sma(C, 200), 40, 30)",
   "value_example": "iif(C > O, 1, -1)",
   "example_reads": "Oversold, with a looser line (40) in an uptrend and a stricter one (30) below the 200-day."
  },
  {
   "name": "ref",
   "group": "Maths",
   "level": "core",
   "signature": "ref(x, -n)",
   "description": "Any series n bars back: the offset for things that are not a price letter.",
   "example": "sma(C, 50) > ref(sma(C, 50), -10)",
   "value_example": "ref(C, -5)",
   "example_reads": "The 50-day average is higher than ten sessions ago: rising."
  },
  {
   "name": "between",
   "group": "Maths",
   "level": "core",
   "signature": "between(x, a, b)",
   "description": "1 when x is inside [a, b] (inclusive, either order), else 0.",
   "example": "between(rsi(C, 14), 40, 60)",
   "value_example": "between(rsi(C, 14), 40, 60)",
   "example_reads": "RSI in the neutral band. As a whole rule it answers 0.95 or 0.05."
  },
  {
   "name": "least",
   "group": "Maths",
   "level": "core",
   "signature": "least(a, b)",
   "description": "The smaller of two series, bar by bar.",
   "example": "least(O, C) - L > 2 * abs(C - O)",
   "value_example": "least(O, C)",
   "example_reads": "A long lower wick: twice the body."
  },
  {
   "name": "greatest",
   "group": "Maths",
   "level": "core",
   "signature": "greatest(a, b)",
   "description": "The larger of two series, bar by bar.",
   "example": "H - greatest(O, C) > 2 * abs(C - O)",
   "value_example": "greatest(O, C)",
   "example_reads": "A long upper wick: twice the body."
  },
  {
   "name": "hl2",
   "group": "Prices",
   "level": "core",
   "signature": "hl2()",
   "description": "The middle of the bar: (H + L) / 2.",
   "example": "C > hl2()",
   "value_example": "hl2()",
   "example_reads": "Closed in the upper half of the day's range."
  },
  {
   "name": "hlc3",
   "group": "Prices",
   "level": "core",
   "signature": "hlc3()",
   "description": "The typical price: (H + L + C) / 3.",
   "example": "hlc3() > sma(hlc3(), 20)",
   "value_example": "hlc3()",
   "example_reads": "Typical price above its own 20-day average."
  },
  {
   "name": "ohlc4",
   "group": "Prices",
   "level": "core",
   "signature": "ohlc4()",
   "description": "The average of the four prices: (O + H + L + C) / 4.",
   "example": "ohlc4() > sma(C, 10)",
   "value_example": "ohlc4()",
   "example_reads": "The day's average price is above the 10-day average close."
  },
  {
   "name": "tr",
   "group": "Prices",
   "level": "core",
   "signature": "tr()",
   "description": "True range: the bar's high minus low, extended to include a gap from the previous close.",
   "example": "tr() > 1.5 * ref(atr(14), -1)",
   "value_example": "tr()",
   "example_reads": "Today's true range is half again yesterday's ATR: a wide day."
  },
  {
   "name": "wma",
   "group": "Windows",
   "level": "core",
   "signature": "wma(x, n)",
   "description": "Linearly weighted moving average: recent bars count more.",
   "example": "wma(C, 10) > wma(C, 30)",
   "value_example": "wma(C, 20)",
   "example_reads": "Fast weighted average above the slow one."
  },
  {
   "name": "rma",
   "group": "Windows",
   "level": "core",
   "signature": "rma(x, n)",
   "description": "Wilder's smoothing, 1 / n: what RSI and ATR are built from.",
   "example": "C > rma(C, 14)",
   "value_example": "rma(C, 14)",
   "example_reads": "Above a Wilder-smoothed 14-day average."
  },
  {
   "name": "linreg",
   "group": "Windows",
   "level": "core",
   "signature": "linreg(x, n)",
   "description": "The least-squares line through the window, read at the current bar: a smoothed level, not the slope.",
   "example": "linreg(C, 20) > ref(linreg(C, 20), -5)",
   "value_example": "linreg(C, 20)",
   "example_reads": "The 20-day regression level is higher than a week ago: rising."
  },
  {
   "name": "median",
   "group": "Windows",
   "level": "core",
   "signature": "median(x, n)",
   "description": "Middle value of the window. Less pulled by one wild day than an average.",
   "example": "V > 2 * median(V, 20)",
   "value_example": "median(C, 20)",
   "example_reads": "Volume more than twice its typical (median) day."
  },
  {
   "name": "percentrank",
   "group": "Windows",
   "level": "core",
   "signature": "percentrank(x, n)",
   "description": "Where the current value ranks among the n − 1 bars before it, 0 to 100.",
   "example": "percentrank(V, 50) > 90",
   "value_example": "percentrank(C, 100)",
   "example_reads": "Volume in the top tenth of the last 50 sessions."
  },
  {
   "name": "cum",
   "group": "Windows",
   "level": "core",
   "signature": "cum(x)",
   "description": "Running total from the first bar loaded.",
   "example": "cum(C > C[-1]) > cum(C < C[-1])",
   "value_example": "cum(C > C[-1])",
   "example_reads": "More up days than down days across all the history loaded (about 1,500 sessions)."
  },
  {
   "name": "highestbars",
   "group": "Windows",
   "level": "core",
   "signature": "highestbars(x, n)",
   "description": "How many bars ago the window's high was set, 0 to n − 1 (0 = this bar). The window includes the current bar.",
   "example": "highestbars(H, 20) <= 3",
   "value_example": "highestbars(H, 252)",
   "example_reads": "The 20-day high was set in the last four sessions."
  },
  {
   "name": "lowestbars",
   "group": "Windows",
   "level": "core",
   "signature": "lowestbars(x, n)",
   "description": "How many bars ago the window's low was set (0 = this bar).",
   "example": "lowestbars(L, 20) >= 15",
   "value_example": "lowestbars(L, 252)",
   "example_reads": "The 20-day low is at least 15 sessions old."
  },
  {
   "name": "rec",
   "group": "Recursion",
   "level": "core",
   "signature": "rec(seed, step)",
   "description": "A series built from its own previous value: inside step, self is the previous bar's result (seed before the first bar). Only arithmetic, comparisons and abs/sqrt/log/sign/iif/least/greatest/between may touch self.",
   "example": "C > rec(C, self + 0.1 * (C - self))",
   "value_example": "rec(C, greatest(self, C))",
   "example_reads": "Above a home-made exponential average (smoothing 0.1)."
  },
  {
   "name": "crossup",
   "group": "Signals",
   "level": "core",
   "signature": "crossup(a, b)",
   "description": "1 on the bar where a crosses above b (it was at or below b on the bar before), 0 otherwise. As a whole rule it answers 0.95 or 0.05.",
   "example": "crossup(C, sma(C, 50))",
   "value_example": "crossup(C, sma(C, 50))",
   "example_reads": "Crossed above the 50-day today."
  },
  {
   "name": "crossdown",
   "group": "Signals",
   "level": "core",
   "signature": "crossdown(a, b)",
   "description": "1 on the bar where a crosses below b.",
   "example": "crossdown(sma(C, 20), sma(C, 50))",
   "value_example": "crossdown(C, sma(C, 50))",
   "example_reads": "The 20-day crossed under the 50-day today."
  },
  {
   "name": "barssince",
   "group": "Signals",
   "level": "core",
   "signature": "barssince(cond)",
   "description": "Bars since a condition was last true (0 = true on this bar). Not known until it has been true once.",
   "example": "barssince(C > highest(C, 20)) > 30",
   "value_example": "barssince(crossup(C, sma(C, 50)))",
   "example_reads": "More than 30 sessions since the last 20-day closing breakout."
  },
  {
   "name": "vwap",
   "group": "Intraday",
   "level": "core",
   "signature": "vwap()",
   "description": "Volume-weighted average price of the session so far (typical price), reset every session.",
   "example": "C > vwap()",
   "requires": "intraday bars: send state.bars with an interval such as \"5m\""
  },
  {
   "name": "opening_range_high",
   "group": "Intraday",
   "level": "core",
   "signature": "opening_range_high([m])",
   "description": "Highest price of the session's first m minutes (default 30).",
   "example": "C > opening_range_high(30)",
   "requires": "intraday bars: send state.bars with an interval such as \"5m\""
  },
  {
   "name": "opening_range_low",
   "group": "Intraday",
   "level": "core",
   "signature": "opening_range_low([m])",
   "description": "Lowest price of the session's first m minutes.",
   "example": "C < opening_range_low(30)",
   "requires": "intraday bars: send state.bars with an interval such as \"5m\""
  },
  {
   "name": "minutes_since_open",
   "group": "Intraday",
   "level": "core",
   "signature": "minutes_since_open()",
   "description": "Minutes since the session's first bar.",
   "example": "minutes_since_open() >= 30",
   "requires": "intraday bars: send state.bars with an interval such as \"5m\""
  },
  {
   "name": "time_of_day",
   "group": "Intraday",
   "level": "core",
   "signature": "time_of_day()",
   "description": "The bar's clock time as HHMM (1030 = 10:30), in the timestamps' own time zone.",
   "example": "time_of_day() >= 1000",
   "requires": "intraday bars: send state.bars with an interval such as \"5m\""
  },
  {
   "name": "rvol",
   "group": "Windows",
   "level": "core",
   "signature": "rvol(n)",
   "description": "Relative volume: this bar's volume over the average of the n bars before it.",
   "example": "rvol(20) > 2",
   "value_example": "rvol(20)"
  },
  {
   "name": "gap",
   "group": "Prices",
   "level": "core",
   "signature": "gap()",
   "description": "Percent from the previous close to this bar's open.",
   "example": "gap() > 2",
   "value_example": "gap()"
  },
  {
   "name": "swinghigh",
   "group": "Price structure",
   "level": "core",
   "signature": "swinghigh(n [, k])",
   "description": "The k-th most recent confirmed swing high (default k = 1): a high strictly above the n bars on each side. It is only known n bars later, so it never looks ahead.",
   "example": "C > swinghigh(5)",
   "value_example": "swinghigh(5)",
   "example_reads": "Closed above the last confirmed 5-bar swing high: resistance taken out."
  },
  {
   "name": "swinglow",
   "group": "Price structure",
   "level": "core",
   "signature": "swinglow(n [, k])",
   "description": "The k-th most recent confirmed swing low (default k = 1).",
   "example": "swinglow(3) > swinglow(3, 2)",
   "value_example": "swinglow(5)",
   "example_reads": "The last swing low is higher than the one before: higher lows."
  },
  {
   "name": "dema",
   "group": "Moving averages",
   "level": "library",
   "signature": "dema(x, n=20)",
   "description": "Double exponential moving average: 2*ema - ema(ema). Less lag than an ema of the same length.",
   "example": "C > dema(C, 20)",
   "value_example": "dema(C)"
  },
  {
   "name": "tema",
   "group": "Moving averages",
   "level": "library",
   "signature": "tema(x, n=20)",
   "description": "Triple exponential moving average: 3*e1 - 3*e2 + e3 over three nested emas.",
   "example": "C > tema(C, 20)",
   "value_example": "tema(C)"
  },
  {
   "name": "trima",
   "group": "Moving averages",
   "level": "library",
   "signature": "trima(x, n=20)",
   "description": "Triangular moving average: an sma of an sma, weighting the middle of the window most.",
   "example": "C > trima(C, 20)",
   "value_example": "trima(C)"
  },
  {
   "name": "kama",
   "group": "Moving averages",
   "level": "library",
   "signature": "kama(x, n=10, fast=2, slow=30)",
   "description": "Kaufman adaptive moving average: fast in a clean trend, nearly flat in chop (efficiency-ratio driven).",
   "example": "C > kama(C, 10)",
   "value_example": "kama(C)"
  },
  {
   "name": "hma",
   "group": "Moving averages",
   "level": "library",
   "signature": "hma(x, n=20)",
   "description": "Hull moving average: wma(2*wma(x, n/2) - wma(x, n), sqrt(n)). Very little lag.",
   "example": "hma(C, 20) > ref(hma(C, 20), -1)",
   "value_example": "hma(C)"
  },
  {
   "name": "vwma",
   "group": "Moving averages",
   "level": "library",
   "signature": "vwma(x, n=20)",
   "description": "Volume-weighted moving average: sum(x*V, n) / sum(V, n).",
   "example": "C > vwma(C, 20)",
   "value_example": "vwma(C)"
  },
  {
   "name": "alma",
   "group": "Moving averages",
   "level": "library",
   "signature": "alma(x, n=9, offset=0.85, sigma=6)",
   "description": "Arnaud Legoux moving average: a Gaussian-weighted window centred near the newest bar (offset 0.85).",
   "example": "C > alma(C, 20)",
   "value_example": "alma(C)"
  },
  {
   "name": "zlema",
   "group": "Moving averages",
   "level": "library",
   "signature": "zlema(x, n=20)",
   "description": "Zero-lag ema: an ema of x + (x - x[-(n-1)/2]), which cancels most of the lag.",
   "example": "C > zlema(C, 20)",
   "value_example": "zlema(C)"
  },
  {
   "name": "t3",
   "group": "Moving averages",
   "level": "library",
   "signature": "t3(x, n=5, vfactor=0.7)",
   "description": "Tillson T3: six chained emas blended by the volume factor. Smooth and responsive.",
   "example": "C > t3(C, 10)",
   "value_example": "t3(C)"
  },
  {
   "name": "smma",
   "group": "Moving averages",
   "level": "library",
   "signature": "smma(x, n=14)",
   "description": "Smoothed moving average (Wilder's, alpha 1/n). The same as rma().",
   "example": "C > smma(C, 20)",
   "value_example": "smma(C)"
  },
  {
   "name": "swma",
   "group": "Moving averages",
   "level": "library",
   "signature": "swma(x)",
   "description": "Symmetric 4-bar weighted average: (x[-3] + 2*x[-2] + 2*x[-1] + x) / 6.",
   "example": "C > swma(C)",
   "value_example": "swma(C)"
  },
  {
   "name": "vidya",
   "group": "Moving averages",
   "level": "library",
   "signature": "vidya(x, n=20, cmo=9)",
   "description": "Variable index dynamic average: an ema whose speed scales with |CMO(cmo)|/100, so it hugs price in strong moves.",
   "example": "C > vidya(C, 20)",
   "value_example": "vidya(C)"
  },
  {
   "name": "mcginley",
   "group": "Moving averages",
   "level": "library",
   "signature": "mcginley(x, n=14)",
   "description": "McGinley dynamic: an average that speeds up when price runs away from it.",
   "example": "C > mcginley(C, 14)",
   "value_example": "mcginley(C)"
  },
  {
   "name": "midpoint",
   "group": "Moving averages",
   "level": "library",
   "signature": "midpoint(x, n=14)",
   "description": "Middle of the window's range: (max(x, n) + min(x, n)) / 2.",
   "example": "C > midpoint(C, 20)",
   "value_example": "midpoint(C)"
  },
  {
   "name": "midprice",
   "group": "Moving averages",
   "level": "library",
   "signature": "midprice(n=14)",
   "description": "Middle of the n-bar high-low range: (max(H, n) + min(L, n)) / 2.",
   "example": "C > midprice(20)",
   "value_example": "midprice()"
  },
  {
   "name": "stochrsi",
   "group": "Oscillators",
   "level": "library",
   "signature": "stochrsi(x, n=14, m=14, k=3)",
   "description": "Stochastic RSI %K, 0-100: where rsi(x, n) sits in its own m-bar range, smoothed over k bars.",
   "example": "stochrsi(C) < 20",
   "value_example": "stochrsi(C)"
  },
  {
   "name": "stochrsid",
   "group": "Oscillators",
   "level": "library",
   "signature": "stochrsid(x, n=14, m=14, k=3, d=3)",
   "description": "Stochastic RSI %D: the d-bar average of stochrsi().",
   "example": "crossup(stochrsi(C), stochrsid(C))",
   "value_example": "stochrsid(C)"
  },
  {
   "name": "mfi",
   "group": "Oscillators",
   "level": "library",
   "signature": "mfi(n=14)",
   "description": "Money flow index, 0-100: a volume-weighted RSI of the typical price.",
   "example": "mfi(14) < 20",
   "value_example": "mfi()"
  },
  {
   "name": "cmo",
   "group": "Oscillators",
   "level": "library",
   "signature": "cmo(x, n=14)",
   "description": "Chande momentum oscillator, -100 to 100: (sum of gains - sum of losses) / their total, over plain n-bar sums (Chande's definition).",
   "example": "cmo(C, 14) > 50",
   "value_example": "cmo(C)"
  },
  {
   "name": "tsi",
   "group": "Oscillators",
   "level": "library",
   "signature": "tsi(x, long=25, short=13)",
   "description": "True strength index, -100 to 100: double-smoothed momentum over double-smoothed absolute momentum.",
   "example": "tsi(C) > 0",
   "value_example": "tsi(C)"
  },
  {
   "name": "uo",
   "group": "Oscillators",
   "level": "library",
   "signature": "uo(fast=7, mid=14, slow=28)",
   "description": "Ultimate oscillator, 0-100: buying pressure over three windows, weighted 4:2:1.",
   "example": "uo() < 30",
   "value_example": "uo()"
  },
  {
   "name": "dpo",
   "group": "Oscillators",
   "level": "library",
   "signature": "dpo(x, n=20)",
   "description": "Detrended price oscillator: x minus the sma(x, n) of n/2+1 bars ago (the non-centred form, so it never looks ahead).",
   "example": "dpo(C, 20) > 0",
   "value_example": "dpo(C)"
  },
  {
   "name": "ppo",
   "group": "Oscillators",
   "level": "library",
   "signature": "ppo(x, fast=12, slow=26)",
   "description": "Percentage price oscillator: (ema fast - ema slow) / ema slow * 100. A MACD you can compare across symbols.",
   "example": "ppo(C) > 0",
   "value_example": "ppo(C)"
  },
  {
   "name": "pposignal",
   "group": "Oscillators",
   "level": "library",
   "signature": "pposignal(x, fast=12, slow=26, signal=9)",
   "description": "Signal line of ppo(): its ema over `signal` bars.",
   "example": "crossup(ppo(C), pposignal(C))",
   "value_example": "pposignal(C)"
  },
  {
   "name": "ppohist",
   "group": "Oscillators",
   "level": "library",
   "signature": "ppohist(x, fast=12, slow=26, signal=9)",
   "description": "ppo() minus its signal line.",
   "example": "ppohist(C) > 0",
   "value_example": "ppohist(C)"
  },
  {
   "name": "apo",
   "group": "Oscillators",
   "level": "library",
   "signature": "apo(x, fast=12, slow=26)",
   "description": "Absolute price oscillator: ema(x, fast) - ema(x, slow), in price.",
   "example": "apo(C) > 0",
   "value_example": "apo(C)"
  },
  {
   "name": "kst",
   "group": "Oscillators",
   "level": "library",
   "signature": "kst(x)",
   "description": "Know Sure Thing (Pring): weighted sum of four smoothed rates of change (10/15/20/30).",
   "example": "kst(C) > kstsignal(C)",
   "value_example": "kst(C)"
  },
  {
   "name": "kstsignal",
   "group": "Oscillators",
   "level": "library",
   "signature": "kstsignal(x)",
   "description": "Signal line of kst(): its 9-bar sma.",
   "example": "crossup(kst(C), kstsignal(C))",
   "value_example": "kstsignal(C)"
  },
  {
   "name": "trix",
   "group": "Oscillators",
   "level": "library",
   "signature": "trix(x, n=15)",
   "description": "TRIX: 1-bar percent change of a triple-smoothed ema.",
   "example": "trix(C, 15) > 0",
   "value_example": "trix(C)"
  },
  {
   "name": "ao",
   "group": "Oscillators",
   "level": "library",
   "signature": "ao()",
   "description": "Awesome oscillator (Bill Williams): sma(hl2, 5) - sma(hl2, 34).",
   "example": "ao() > 0",
   "value_example": "ao()"
  },
  {
   "name": "ac",
   "group": "Oscillators",
   "level": "library",
   "signature": "ac()",
   "description": "Accelerator oscillator: ao() minus its 5-bar sma.",
   "example": "ac() > 0",
   "value_example": "ac()"
  },
  {
   "name": "mom",
   "group": "Oscillators",
   "level": "library",
   "signature": "mom(x, n=10)",
   "description": "Momentum: x - x n bars ago, in the units of x.",
   "example": "mom(C, 10) > 0",
   "value_example": "mom(C)"
  },
  {
   "name": "rocr",
   "group": "Oscillators",
   "level": "library",
   "signature": "rocr(x, n=10)",
   "description": "Rate of change as a ratio: x / x n bars ago (1.05 = up 5%).",
   "example": "rocr(C, 20) > 1.05",
   "value_example": "rocr(C)"
  },
  {
   "name": "bop",
   "group": "Oscillators",
   "level": "library",
   "signature": "bop()",
   "description": "Balance of power, -1 to 1: (C - O) / (H - L). 0 on a bar with no range.",
   "example": "sma(bop(), 14) > 0",
   "value_example": "bop()"
  },
  {
   "name": "aroonup",
   "group": "Oscillators",
   "level": "library",
   "signature": "aroonup(n=25)",
   "description": "Aroon up, 0-100: 100 when the n-bar high is today, falling as it ages.",
   "example": "aroonup(25) > 70",
   "value_example": "aroonup()"
  },
  {
   "name": "aroondown",
   "group": "Oscillators",
   "level": "library",
   "signature": "aroondown(n=25)",
   "description": "Aroon down, 0-100: 100 when the n-bar low is today.",
   "example": "aroondown(25) > 70",
   "value_example": "aroondown()"
  },
  {
   "name": "aroonosc",
   "group": "Oscillators",
   "level": "library",
   "signature": "aroonosc(n=25)",
   "description": "Aroon oscillator, -100 to 100: aroonup - aroondown.",
   "example": "aroonosc(25) > 50",
   "value_example": "aroonosc()"
  },
  {
   "name": "fisher",
   "group": "Oscillators",
   "level": "library",
   "signature": "fisher(n=9)",
   "description": "Ehlers Fisher transform of the bar's position in its n-bar range: sharp turning points.",
   "example": "fisher(9) > 1.5",
   "value_example": "fisher()"
  },
  {
   "name": "rvgi",
   "group": "Oscillators",
   "level": "library",
   "signature": "rvgi(n=10)",
   "description": "Relative vigor index: smoothed (C - O) over smoothed (H - L), summed over n bars.",
   "example": "rvgi(10) > rvgisignal(10)",
   "value_example": "rvgi()"
  },
  {
   "name": "rvgisignal",
   "group": "Oscillators",
   "level": "library",
   "signature": "rvgisignal(n=10)",
   "description": "Signal line of rvgi(): its symmetric 4-bar average.",
   "example": "crossup(rvgi(10), rvgisignal(10))",
   "value_example": "rvgisignal()"
  },
  {
   "name": "coppock",
   "group": "Oscillators",
   "level": "library",
   "signature": "coppock(x, wma=10, long=14, short=11)",
   "description": "Coppock curve: wma of the sum of two rates of change. A turn up from below zero is the classic long-term buy signal.",
   "example": "coppock(C) > ref(coppock(C), -1)",
   "value_example": "coppock(C)"
  },
  {
   "name": "crsi",
   "group": "Oscillators",
   "level": "library",
   "signature": "crsi(x, rsi=3, streak=2, rank=100)",
   "description": "Connors RSI, 0-100: the average of rsi(x, 3), the RSI of the up/down streak, and the percent rank of today's 1-day change among the previous 100.",
   "example": "crsi(C) < 10",
   "value_example": "crsi(C)"
  },
  {
   "name": "smi",
   "group": "Oscillators",
   "level": "library",
   "signature": "smi(n=10, s1=3, s2=3)",
   "description": "Stochastic momentum index, -100 to 100: the close against the middle of its n-bar range, double-smoothed.",
   "example": "smi(10) < -40",
   "value_example": "smi()"
  },
  {
   "name": "cfo",
   "group": "Oscillators",
   "level": "library",
   "signature": "cfo(x, n=14)",
   "description": "Chande forecast oscillator: percent gap between x and its n-bar linear-regression value.",
   "example": "cfo(C, 14) > 0",
   "value_example": "cfo(C)"
  },
  {
   "name": "qstick",
   "group": "Oscillators",
   "level": "library",
   "signature": "qstick(n=10)",
   "description": "Qstick: the n-bar average of (C - O). Positive = mostly green bodies.",
   "example": "qstick(10) > 0",
   "value_example": "qstick()"
  },
  {
   "name": "bullpower",
   "group": "Oscillators",
   "level": "library",
   "signature": "bullpower(n=13)",
   "description": "Elder bull power: H - ema(C, n).",
   "example": "bullpower(13) > 0",
   "value_example": "bullpower()"
  },
  {
   "name": "bearpower",
   "group": "Oscillators",
   "level": "library",
   "signature": "bearpower(n=13)",
   "description": "Elder bear power: L - ema(C, n).",
   "example": "bearpower(13) < 0",
   "value_example": "bearpower()"
  },
  {
   "name": "imi",
   "group": "Oscillators",
   "level": "library",
   "signature": "imi(n=14)",
   "description": "Intraday momentum index, 0-100: an RSI built from bar bodies (C - O) instead of closes.",
   "example": "imi(14) > 70",
   "value_example": "imi()"
  },
  {
   "name": "adxr",
   "group": "Oscillators",
   "level": "library",
   "signature": "adxr(n=14)",
   "description": "ADX rating: (adx + adx n-1 bars ago) / 2. A slower trend-strength reading.",
   "example": "adxr(14) > 25",
   "value_example": "adxr()"
  },
  {
   "name": "dx",
   "group": "Oscillators",
   "level": "library",
   "signature": "dx(n=14)",
   "description": "Directional movement index: 100 * |DI+ - DI-| / (DI+ + DI-), the unsmoothed ADX.",
   "example": "dx(14) > 30",
   "value_example": "dx()"
  },
  {
   "name": "plusdm",
   "group": "Oscillators",
   "level": "library",
   "signature": "plusdm(n=14)",
   "description": "Plus directional movement, Wilder-smoothed (a running sum).",
   "example": "plusdm(14) > minusdm(14)",
   "value_example": "plusdm()"
  },
  {
   "name": "minusdm",
   "group": "Oscillators",
   "level": "library",
   "signature": "minusdm(n=14)",
   "description": "Minus directional movement, Wilder-smoothed.",
   "example": "minusdm(14) > plusdm(14)",
   "value_example": "minusdm()"
  },
  {
   "name": "wavetrend",
   "group": "Oscillators",
   "level": "library",
   "signature": "wavetrend(channel=10, avg=21)",
   "description": "WaveTrend (LazyBear) WT1: a CCI-like channel index of hlc3, ema-smoothed. Below -60 is oversold.",
   "example": "wavetrend() < -60",
   "value_example": "wavetrend()"
  },
  {
   "name": "wavetrendsignal",
   "group": "Oscillators",
   "level": "library",
   "signature": "wavetrendsignal(channel=10, avg=21)",
   "description": "WaveTrend WT2: the 4-bar sma of wavetrend().",
   "example": "crossup(wavetrend(), wavetrendsignal())",
   "value_example": "wavetrendsignal()"
  },
  {
   "name": "supertrend",
   "group": "Trend",
   "level": "library",
   "signature": "supertrend(n=10, mult=3)",
   "description": "Supertrend line: an ATR band that trails below price in an uptrend and above it in a downtrend.",
   "example": "C > supertrend(10, 3)",
   "value_example": "supertrend()"
  },
  {
   "name": "supertrenddir",
   "group": "Trend",
   "level": "library",
   "signature": "supertrenddir(n=10, mult=3)",
   "description": "Supertrend direction: 1 in an uptrend, -1 in a downtrend.",
   "example": "supertrenddir(10, 3) > 0",
   "value_example": "supertrenddir()"
  },
  {
   "name": "psar",
   "group": "Trend",
   "level": "library",
   "signature": "psar(step=0.02, max=0.2)",
   "description": "Parabolic SAR (Wilder): a stop that accelerates toward price. Price above it = long side.",
   "example": "C > psar()",
   "value_example": "psar()"
  },
  {
   "name": "tenkan",
   "group": "Trend",
   "level": "library",
   "signature": "tenkan(n=9)",
   "description": "Ichimoku conversion line (tenkan-sen): the middle of the n-bar high-low range.",
   "example": "tenkan() > kijun()",
   "value_example": "tenkan()"
  },
  {
   "name": "kijun",
   "group": "Trend",
   "level": "library",
   "signature": "kijun(n=26)",
   "description": "Ichimoku base line (kijun-sen): the middle of the 26-bar range.",
   "example": "C > kijun()",
   "value_example": "kijun()"
  },
  {
   "name": "senkou_a",
   "group": "Trend",
   "level": "library",
   "signature": "senkou_a(tenkan=9, kijun=26, shift=26)",
   "description": "Ichimoku leading span A as it stands under TODAY's bar: (tenkan + kijun) / 2 computed `shift` bars ago. No look-ahead.",
   "example": "C > senkou_a()",
   "value_example": "senkou_a()"
  },
  {
   "name": "senkou_b",
   "group": "Trend",
   "level": "library",
   "signature": "senkou_b(n=52, shift=26)",
   "description": "Ichimoku leading span B under today's bar: the 52-bar midprice from `shift` bars ago.",
   "example": "C > senkou_b()",
   "value_example": "senkou_b()"
  },
  {
   "name": "cloud_top",
   "group": "Trend",
   "level": "library",
   "signature": "cloud_top()",
   "description": "Top of the Ichimoku cloud under today's bar: greatest(senkou_a(), senkou_b()).",
   "example": "C > cloud_top()",
   "value_example": "cloud_top()"
  },
  {
   "name": "cloud_bottom",
   "group": "Trend",
   "level": "library",
   "signature": "cloud_bottom()",
   "description": "Bottom of the Ichimoku cloud under today's bar.",
   "example": "C < cloud_bottom()",
   "value_example": "cloud_bottom()"
  },
  {
   "name": "viplus",
   "group": "Trend",
   "level": "library",
   "signature": "viplus(n=14)",
   "description": "Vortex indicator VI+: upward vortex movement over true range, n bars.",
   "example": "viplus(14) > viminus(14)",
   "value_example": "viplus()"
  },
  {
   "name": "viminus",
   "group": "Trend",
   "level": "library",
   "signature": "viminus(n=14)",
   "description": "Vortex indicator VI-.",
   "example": "viminus(14) > viplus(14)",
   "value_example": "viminus()"
  },
  {
   "name": "linreg_slope",
   "group": "Trend",
   "level": "library",
   "signature": "linreg_slope(x, n=14)",
   "description": "Slope of the n-bar least-squares line, in x units per bar.",
   "example": "linreg_slope(C, 20) > 0",
   "value_example": "linreg_slope(C)"
  },
  {
   "name": "linreg_intercept",
   "group": "Trend",
   "level": "library",
   "signature": "linreg_intercept(x, n=14)",
   "description": "Value of the n-bar regression line at the window's FIRST bar.",
   "example": "C > linreg_intercept(C, 20)",
   "value_example": "linreg_intercept(C)"
  },
  {
   "name": "linreg_angle",
   "group": "Trend",
   "level": "library",
   "signature": "linreg_angle(x, n=14)",
   "description": "Angle of the regression line in degrees: atan(slope). Scale-dependent, so compare it on the same symbol.",
   "example": "linreg_angle(C, 20) > 0",
   "value_example": "linreg_angle(C)"
  },
  {
   "name": "linreg_r2",
   "group": "Trend",
   "level": "library",
   "signature": "linreg_r2(x, n=20)",
   "description": "R-squared of the n-bar regression line, 0-1: how straight the trend is.",
   "example": "linreg_r2(C, 50) > 0.8 and linreg_slope(C, 50) > 0",
   "value_example": "linreg_r2(C)"
  },
  {
   "name": "tsf",
   "group": "Trend",
   "level": "library",
   "signature": "tsf(x, n=14)",
   "description": "Time series forecast: the regression line extended one bar ahead.",
   "example": "C > tsf(C, 14)",
   "value_example": "tsf(C)"
  },
  {
   "name": "chop",
   "group": "Trend",
   "level": "library",
   "signature": "chop(n=14)",
   "description": "Choppiness index, 0-100: high (over 61.8) in a sideways market, low (under 38.2) in a trend.",
   "example": "chop(14) > 61.8",
   "value_example": "chop()"
  },
  {
   "name": "er",
   "group": "Trend",
   "level": "library",
   "signature": "er(x, n=10)",
   "description": "Kaufman efficiency ratio, 0-1: net move over the sum of bar-to-bar moves. 1 = a straight line.",
   "example": "er(C, 10) > 0.5",
   "value_example": "er(C)"
  },
  {
   "name": "massindex",
   "group": "Trend",
   "level": "library",
   "signature": "massindex(n=9, sum=25)",
   "description": "Mass index: range expansion. A 'reversal bulge' rises above 27 then drops below 26.5.",
   "example": "massindex() > 27",
   "value_example": "massindex()"
  },
  {
   "name": "vhf",
   "group": "Trend",
   "level": "library",
   "signature": "vhf(x, n=28)",
   "description": "Vertical horizontal filter: n-bar range over the sum of bar moves. High = trending.",
   "example": "vhf(C, 28) > 0.35",
   "value_example": "vhf(C)"
  },
  {
   "name": "natr",
   "group": "Volatility",
   "level": "library",
   "signature": "natr(n=14)",
   "description": "Normalized ATR: atr(n) / C * 100. The same as atr_pct().",
   "example": "natr(14) > 3",
   "value_example": "natr()"
  },
  {
   "name": "kcupper",
   "group": "Volatility",
   "level": "library",
   "signature": "kcupper(n=20, mult=2, atr=10)",
   "description": "Upper Keltner channel: ema(C, n) + mult * atr(atr).",
   "example": "C > kcupper(20, 2)",
   "value_example": "kcupper()"
  },
  {
   "name": "kcmid",
   "group": "Volatility",
   "level": "library",
   "signature": "kcmid(n=20)",
   "description": "Middle Keltner line: ema(C, n).",
   "example": "C > kcmid(20)",
   "value_example": "kcmid()"
  },
  {
   "name": "kclower",
   "group": "Volatility",
   "level": "library",
   "signature": "kclower(n=20, mult=2, atr=10)",
   "description": "Lower Keltner channel: ema(C, n) - mult * atr(atr).",
   "example": "C < kclower(20, 2)",
   "value_example": "kclower()"
  },
  {
   "name": "dcupper",
   "group": "Volatility",
   "level": "library",
   "signature": "dcupper(n=20)",
   "description": "Upper Donchian channel: highest high of the last n bars, including today.",
   "example": "H >= dcupper(20)",
   "value_example": "dcupper()"
  },
  {
   "name": "dclower",
   "group": "Volatility",
   "level": "library",
   "signature": "dclower(n=20)",
   "description": "Lower Donchian channel: lowest low of the last n bars, including today.",
   "example": "L <= dclower(20)",
   "value_example": "dclower()"
  },
  {
   "name": "dcmid",
   "group": "Volatility",
   "level": "library",
   "signature": "dcmid(n=20)",
   "description": "Middle of the Donchian channel.",
   "example": "C > dcmid(20)",
   "value_example": "dcmid()"
  },
  {
   "name": "dcwidth",
   "group": "Volatility",
   "level": "library",
   "signature": "dcwidth(n=20)",
   "description": "Donchian width as a percent of its middle: how wide the n-bar range is.",
   "example": "dcwidth(20) < 8",
   "value_example": "dcwidth()"
  },
  {
   "name": "bbwidth",
   "group": "Volatility",
   "level": "library",
   "signature": "bbwidth(x, n=20, k=2)",
   "description": "Bollinger band width as a percent of the middle band: (upper - lower) / mid * 100.",
   "example": "bbwidth(C, 20) < percentile(bbwidth(C, 20), 252, 10)",
   "value_example": "bbwidth(C, 20)"
  },
  {
   "name": "bbpct",
   "group": "Volatility",
   "level": "library",
   "signature": "bbpct(x, n=20, k=2)",
   "description": "Bollinger %B: where x sits between the bands, 0 = lower band, 1 = upper band.",
   "example": "bbpct(C, 20) > 1",
   "value_example": "bbpct(C, 20)"
  },
  {
   "name": "chaikinvol",
   "group": "Volatility",
   "level": "library",
   "signature": "chaikinvol(n=10, roc=10)",
   "description": "Chaikin volatility: percent change over `roc` bars of the ema of the high-low range.",
   "example": "chaikinvol(10, 10) > 20",
   "value_example": "chaikinvol()"
  },
  {
   "name": "ulcer",
   "group": "Volatility",
   "level": "library",
   "signature": "ulcer(x, n=14)",
   "description": "Ulcer index: root-mean-square of the percent drawdown from the n-bar high. Downside volatility only.",
   "example": "ulcer(C, 14) < 3",
   "value_example": "ulcer(C)"
  },
  {
   "name": "hv",
   "group": "Volatility",
   "level": "library",
   "signature": "hv(x, n=20)",
   "description": "Historical volatility: annualised sample standard deviation of n daily log returns, in percent.",
   "example": "hv(C, 20) > hv(C, 100)",
   "value_example": "hv(C)"
  },
  {
   "name": "parkinson",
   "group": "Volatility",
   "level": "library",
   "signature": "parkinson(n=20)",
   "description": "Parkinson volatility from the high-low range, annualised percent.",
   "example": "parkinson(20) > 30",
   "value_example": "parkinson()"
  },
  {
   "name": "garmanklass",
   "group": "Volatility",
   "level": "library",
   "signature": "garmanklass(n=20)",
   "description": "Garman-Klass volatility from open, high, low and close, annualised percent.",
   "example": "garmanklass(20) > 30",
   "value_example": "garmanklass()"
  },
  {
   "name": "rogerssatchell",
   "group": "Volatility",
   "level": "library",
   "signature": "rogerssatchell(n=20)",
   "description": "Rogers-Satchell volatility (drift-independent), annualised percent.",
   "example": "rogerssatchell(20) > 30",
   "value_example": "rogerssatchell()"
  },
  {
   "name": "yangzhang",
   "group": "Volatility",
   "level": "library",
   "signature": "yangzhang(n=20)",
   "description": "Yang-Zhang volatility: overnight, open-to-close and Rogers-Satchell parts combined, annualised percent. The most efficient OHLC estimator.",
   "example": "yangzhang(20) > 30",
   "value_example": "yangzhang()"
  },
  {
   "name": "range_pct",
   "group": "Volatility",
   "level": "library",
   "signature": "range_pct()",
   "description": "Today's high-low range as a percent of the close.",
   "example": "range_pct() > 2 * adr(20)",
   "value_example": "range_pct()"
  },
  {
   "name": "adr",
   "group": "Volatility",
   "level": "library",
   "signature": "adr(n=20)",
   "description": "Average daily range percent: the n-bar average of (H / L - 1) * 100.",
   "example": "adr(20) > 4",
   "value_example": "adr()"
  },
  {
   "name": "squeeze",
   "group": "Volatility",
   "level": "library",
   "signature": "squeeze(n=20, bb=2, kc=1.5)",
   "description": "TTM-style squeeze: 1 while the Bollinger bands (k=bb) sit inside the Keltner channel (sma mid, kc * atr(n)).",
   "example": "squeeze(20)",
   "value_example": "squeeze()"
  },
  {
   "name": "chandelier_long",
   "group": "Volatility",
   "level": "library",
   "signature": "chandelier_long(n=22, mult=3)",
   "description": "Chandelier exit for longs: max(H, n) - mult * atr(n).",
   "example": "C < chandelier_long(22, 3)",
   "value_example": "chandelier_long()"
  },
  {
   "name": "chandelier_short",
   "group": "Volatility",
   "level": "library",
   "signature": "chandelier_short(n=22, mult=3)",
   "description": "Chandelier exit for shorts: min(L, n) + mult * atr(n).",
   "example": "C > chandelier_short(22, 3)",
   "value_example": "chandelier_short()"
  },
  {
   "name": "ad",
   "group": "Volume",
   "level": "library",
   "signature": "ad()",
   "description": "Accumulation/distribution line: running total of volume weighted by where each bar closed in its range.",
   "example": "ad() > sma(ad(), 20)",
   "value_example": "ad()"
  },
  {
   "name": "adosc",
   "group": "Volume",
   "level": "library",
   "signature": "adosc(fast=3, slow=10)",
   "description": "Chaikin oscillator: ema(ad, fast) - ema(ad, slow).",
   "example": "adosc() > 0",
   "value_example": "adosc()"
  },
  {
   "name": "cmf",
   "group": "Volume",
   "level": "library",
   "signature": "cmf(n=20)",
   "description": "Chaikin money flow, -1 to 1: sum of money-flow volume over sum of volume, n bars.",
   "example": "cmf(20) > 0.1",
   "value_example": "cmf()"
  },
  {
   "name": "force",
   "group": "Volume",
   "level": "library",
   "signature": "force(n=13)",
   "description": "Elder force index: ema of (C - C[-1]) * V.",
   "example": "force(13) > 0",
   "value_example": "force()"
  },
  {
   "name": "eom",
   "group": "Volume",
   "level": "library",
   "signature": "eom(n=14)",
   "description": "Ease of movement: n-bar average of 10000 * (hl2 change) * (H - L) / V. Positive = price rises on light volume.",
   "example": "eom(14) > 0",
   "value_example": "eom()"
  },
  {
   "name": "pvt",
   "group": "Volume",
   "level": "library",
   "signature": "pvt()",
   "description": "Price volume trend: running total of V times each bar's fractional change.",
   "example": "pvt() > sma(pvt(), 50)",
   "value_example": "pvt()"
  },
  {
   "name": "nvi",
   "group": "Volume",
   "level": "library",
   "signature": "nvi()",
   "description": "Negative volume index (starts at 1000): moves only on days volume falls. Above its 255-day ema is the classic bull signal.",
   "example": "nvi() > ema(nvi(), 255)",
   "value_example": "nvi()"
  },
  {
   "name": "pvi",
   "group": "Volume",
   "level": "library",
   "signature": "pvi()",
   "description": "Positive volume index (starts at 1000): moves only on days volume rises.",
   "example": "pvi() > ema(pvi(), 255)",
   "value_example": "pvi()"
  },
  {
   "name": "rvwap",
   "group": "Volume",
   "level": "library",
   "signature": "rvwap(n=20)",
   "description": "Rolling VWAP over the last n bars, from the typical price. Works on daily bars.",
   "example": "C > rvwap(20)",
   "value_example": "rvwap()"
  },
  {
   "name": "avwap",
   "group": "Volume",
   "level": "library",
   "signature": "avwap(c)",
   "description": "Anchored VWAP since the most recent bar where the condition was true (that bar included). Anchor on anything: an earnings gap, a crossover, a swing low.",
   "example": "C > avwap(crossup(C, sma(C, 50)))",
   "value_example": "avwap(crossup(C, sma(C, 50)))"
  },
  {
   "name": "wvwap",
   "group": "Volume",
   "level": "library",
   "signature": "wvwap()",
   "description": "VWAP anchored to the first bar of the week.",
   "example": "C > wvwap()",
   "value_example": "wvwap()",
   "requires": "dated bars"
  },
  {
   "name": "mvwap",
   "group": "Volume",
   "level": "library",
   "signature": "mvwap()",
   "description": "VWAP anchored to the first bar of the month.",
   "example": "C > mvwap()",
   "value_example": "mvwap()",
   "requires": "dated bars"
  },
  {
   "name": "qvwap",
   "group": "Volume",
   "level": "library",
   "signature": "qvwap()",
   "description": "VWAP anchored to the first bar of the quarter.",
   "example": "C > qvwap()",
   "value_example": "qvwap()",
   "requires": "dated bars"
  },
  {
   "name": "yvwap",
   "group": "Volume",
   "level": "library",
   "signature": "yvwap()",
   "description": "VWAP anchored to the first bar of the year.",
   "example": "C > yvwap()",
   "value_example": "yvwap()",
   "requires": "dated bars"
  },
  {
   "name": "volosc",
   "group": "Volume",
   "level": "library",
   "signature": "volosc(fast=5, slow=10)",
   "description": "Volume oscillator: percent gap between a fast and a slow ema of volume.",
   "example": "volosc(5, 20) > 25",
   "value_example": "volosc()"
  },
  {
   "name": "kvo",
   "group": "Volume",
   "level": "library",
   "signature": "kvo(fast=34, slow=55)",
   "description": "Klinger volume oscillator (simple form): ema(sv, fast) - ema(sv, slow), where sv is the volume signed by the direction of hlc3.",
   "example": "kvo() > ema(kvo(), 13)",
   "value_example": "kvo()"
  },
  {
   "name": "dollarvol",
   "group": "Volume",
   "level": "library",
   "signature": "dollarvol(n=1)",
   "description": "Average dollar volume (C * V) over n bars.",
   "example": "dollarvol(20) > 50000000",
   "value_example": "dollarvol()"
  },
  {
   "name": "updownvol",
   "group": "Volume",
   "level": "library",
   "signature": "updownvol(n=20)",
   "description": "Up/down volume ratio: volume on up closes over volume on down closes, n bars.",
   "example": "updownvol(50) > 1.5",
   "value_example": "updownvol()"
  },
  {
   "name": "wad",
   "group": "Volume",
   "level": "library",
   "signature": "wad()",
   "description": "Williams accumulation/distribution: running total of the close's move against the true range edge (no volume).",
   "example": "wad() > sma(wad(), 20)",
   "value_example": "wad()"
  },
  {
   "name": "variance",
   "group": "Statistics",
   "level": "library",
   "signature": "variance(x, n=20)",
   "description": "Population variance over the window (the square of std()).",
   "example": "variance(roc(C, 1), 20) > 4",
   "value_example": "variance(roc(C, 1), 20)"
  },
  {
   "name": "var",
   "group": "Statistics",
   "level": "library",
   "signature": "var(x, n=20)",
   "description": "Another name for variance().",
   "example": "var(roc(C, 1), 20) > 4",
   "value_example": "var(roc(C, 1), 20)"
  },
  {
   "name": "skew",
   "group": "Statistics",
   "level": "library",
   "signature": "skew(x, n=20)",
   "description": "Sample skewness of the window (bias-corrected). Negative = a long left tail.",
   "example": "skew(roc(C, 1), 60) < -0.5",
   "value_example": "skew(roc(C, 1), 60)"
  },
  {
   "name": "kurt",
   "group": "Statistics",
   "level": "library",
   "signature": "kurt(x, n=20)",
   "description": "Sample excess kurtosis of the window (bias-corrected). Above 0 = fatter tails than normal.",
   "example": "kurt(roc(C, 1), 60) > 3",
   "value_example": "kurt(roc(C, 1), 60)"
  },
  {
   "name": "corr",
   "group": "Statistics",
   "level": "library",
   "signature": "corr(x, y, n=20)",
   "description": "Pearson correlation of two series over n bars, -1 to 1.",
   "example": "corr(C, V, 20) < 0",
   "value_example": "corr(C, V, 20)"
  },
  {
   "name": "covar",
   "group": "Statistics",
   "level": "library",
   "signature": "covar(x, y, n=20)",
   "description": "Sample covariance of two series over n bars.",
   "example": "covar(roc(C, 1), roc(V, 1), 20) > 0",
   "value_example": "covar(roc(C, 1), roc(V, 1), 20)"
  },
  {
   "name": "percentile",
   "group": "Statistics",
   "level": "library",
   "signature": "percentile(x, n=252, p=50)",
   "description": "The p-th percentile (0-100) of the last n values, linearly interpolated.",
   "example": "atr_pct(14) < percentile(atr_pct(14), 252, 10)",
   "value_example": "percentile(C, 252, 50)"
  },
  {
   "name": "mad",
   "group": "Statistics",
   "level": "library",
   "signature": "mad(x, n=20)",
   "description": "Mean absolute deviation from the window mean.",
   "example": "abs(C - sma(C, 20)) > 2 * mad(C, 20)",
   "value_example": "mad(C, 20)"
  },
  {
   "name": "drawdown",
   "group": "Statistics",
   "level": "library",
   "signature": "drawdown(x, n=0)",
   "description": "Percent below the running peak of x (0 = at the peak). n=0 uses all the history loaded; n > 0 the peak of the last n bars.",
   "example": "drawdown(C) > 20",
   "value_example": "drawdown(C)"
  },
  {
   "name": "maxdrawdown",
   "group": "Statistics",
   "level": "library",
   "signature": "maxdrawdown(x, n=252)",
   "description": "Largest peak-to-trough fall inside the last n bars, in percent (a positive number).",
   "example": "maxdrawdown(C, 252) < 15",
   "value_example": "maxdrawdown(C, 252)"
  },
  {
   "name": "runup",
   "group": "Statistics",
   "level": "library",
   "signature": "runup(x, n=252)",
   "description": "Largest trough-to-peak rise inside the last n bars, in percent.",
   "example": "runup(C, 20) > 25",
   "value_example": "runup(C, 252)"
  },
  {
   "name": "barssincehigh",
   "group": "Statistics",
   "level": "library",
   "signature": "barssincehigh(n=252)",
   "description": "Bars since the highest high of the last n bars (0 = today). Ties count the most recent.",
   "example": "barssincehigh(252) <= 5",
   "value_example": "barssincehigh()"
  },
  {
   "name": "barssincelow",
   "group": "Statistics",
   "level": "library",
   "signature": "barssincelow(n=252)",
   "description": "Bars since the lowest low of the last n bars (0 = today).",
   "example": "barssincelow(252) > 100",
   "value_example": "barssincelow()"
  },
  {
   "name": "streak",
   "group": "Statistics",
   "level": "library",
   "signature": "streak(c)",
   "description": "How many bars in a row the condition has held, ending today (0 = not today).",
   "example": "streak(C > sma(C, 20)) >= 10",
   "value_example": "streak(C > sma(C, 20))"
  },
  {
   "name": "upstreak",
   "group": "Statistics",
   "level": "library",
   "signature": "upstreak()",
   "description": "Consecutive higher closes ending today.",
   "example": "upstreak() >= 4",
   "value_example": "upstreak()"
  },
  {
   "name": "downstreak",
   "group": "Statistics",
   "level": "library",
   "signature": "downstreak()",
   "description": "Consecutive lower closes ending today.",
   "example": "downstreak() >= 4",
   "value_example": "downstreak()"
  },
  {
   "name": "count",
   "group": "Statistics",
   "level": "library",
   "signature": "count(c, n=20)",
   "description": "How many of the last n bars the condition held.",
   "example": "count(C > O, 10) >= 7",
   "value_example": "count(C > O, 20)"
  },
  {
   "name": "freq",
   "group": "Statistics",
   "level": "library",
   "signature": "freq(c, n=20)",
   "description": "Percent of the last n bars on which the condition held.",
   "example": "freq(C > C[-1], 20) > 60",
   "value_example": "freq(C > C[-1], 20)"
  },
  {
   "name": "any",
   "group": "Statistics",
   "level": "library",
   "signature": "any(c, n=20)",
   "description": "1 if the condition held on at least one of the last n bars.",
   "example": "any(crossup(C, sma(C, 50)), 5)",
   "value_example": "any(crossup(C, sma(C, 20)), 20)"
  },
  {
   "name": "all",
   "group": "Statistics",
   "level": "library",
   "signature": "all(c, n=20)",
   "description": "1 if the condition held on every one of the last n bars.",
   "example": "all(C > sma(C, 50), 20)",
   "value_example": "all(C > sma(C, 200), 20)"
  },
  {
   "name": "valuewhen",
   "group": "Statistics",
   "level": "library",
   "signature": "valuewhen(c, x, nth=0)",
   "description": "The value of x on the nth most recent bar where the condition held (nth=0: the latest, today included).",
   "example": "C > valuewhen(crossup(C, sma(C, 50)), C)",
   "value_example": "valuewhen(crossup(C, sma(C, 50)), C)"
  },
  {
   "name": "cross",
   "group": "Statistics",
   "level": "library",
   "signature": "cross(x, y)",
   "description": "1 on the bar where x crosses y in either direction.",
   "example": "cross(C, sma(C, 50))",
   "value_example": "cross(C, sma(C, 20))"
  },
  {
   "name": "crossover",
   "group": "Statistics",
   "level": "library",
   "signature": "crossover(x, y)",
   "description": "1 on the bar where x crosses ABOVE y: the same as crossup().",
   "example": "crossover(C, sma(C, 50))",
   "value_example": "crossover(C, sma(C, 20))"
  },
  {
   "name": "crossunder",
   "group": "Statistics",
   "level": "library",
   "signature": "crossunder(x, y)",
   "description": "1 on the bar where x crosses BELOW y: the same as crossdown().",
   "example": "crossunder(C, sma(C, 50))",
   "value_example": "crossunder(C, sma(C, 20))"
  },
  {
   "name": "rising",
   "group": "Statistics",
   "level": "library",
   "signature": "rising(x, n=3)",
   "description": "1 if x rose on each of the last n bars (n higher values in a row).",
   "example": "rising(sma(C, 20), 5)",
   "value_example": "rising(sma(C, 20), 5)"
  },
  {
   "name": "falling",
   "group": "Statistics",
   "level": "library",
   "signature": "falling(x, n=3)",
   "description": "1 if x fell on each of the last n bars.",
   "example": "falling(sma(C, 20), 5)",
   "value_example": "falling(sma(C, 20), 5)"
  },
  {
   "name": "change",
   "group": "Statistics",
   "level": "library",
   "signature": "change(x, n=1)",
   "description": "x minus its value n bars ago.",
   "example": "change(C, 1) > 0",
   "value_example": "change(C, 5)"
  },
  {
   "name": "normalize",
   "group": "Statistics",
   "level": "library",
   "signature": "normalize(x, n=20)",
   "description": "Where x sits in its own n-bar range, 0-100 (a stochastic of any series).",
   "example": "normalize(rsi(C, 14), 100) > 90",
   "value_example": "normalize(rsi(C, 14), 100)"
  },
  {
   "name": "cummax",
   "group": "Statistics",
   "level": "library",
   "signature": "cummax(x)",
   "description": "Running maximum of x over all bars loaded.",
   "example": "C >= cummax(C)",
   "value_example": "cummax(C)"
  },
  {
   "name": "cummin",
   "group": "Statistics",
   "level": "library",
   "signature": "cummin(x)",
   "description": "Running minimum of x over all bars loaded.",
   "example": "L <= cummin(L)",
   "value_example": "cummin(C)"
  },
  {
   "name": "autocorr",
   "group": "Statistics",
   "level": "library",
   "signature": "autocorr(x, n=20, lag=1)",
   "description": "Correlation of x with itself `lag` bars earlier, over n bars. Positive on returns = momentum, negative = mean reversion.",
   "example": "autocorr(roc(C, 1), 60) < -0.2",
   "value_example": "autocorr(roc(C, 1), 60)"
  },
  {
   "name": "nz",
   "group": "Maths",
   "level": "library",
   "signature": "nz(x, default=0)",
   "description": "x, with 'not known yet' (NaN) replaced by the default.",
   "example": "nz(ref(C, -300), C) > 0",
   "value_example": "nz(ref(C, -1), C)"
  },
  {
   "name": "na",
   "group": "Maths",
   "level": "library",
   "signature": "na(x)",
   "description": "1 where x is not known yet (still warming up), 0 otherwise.",
   "example": "na(sma(C, 2000))",
   "value_example": "na(sma(C, 2000))"
  },
  {
   "name": "clamp",
   "group": "Maths",
   "level": "library",
   "signature": "clamp(x, lo, hi)",
   "description": "x limited to the range lo..hi.",
   "example": "clamp(rsi(C, 14), 30, 70) == 70",
   "value_example": "clamp(rsi(C, 14), 30, 70)"
  },
  {
   "name": "round",
   "group": "Maths",
   "level": "library",
   "signature": "round(x, digits=0)",
   "description": "x rounded to `digits` decimals (half away from zero).",
   "example": "round(rsi(C, 14)) >= 50",
   "value_example": "round(C)"
  },
  {
   "name": "floor",
   "group": "Maths",
   "level": "library",
   "signature": "floor(x)",
   "description": "The largest whole number at or below x.",
   "example": "floor(C) == floor(C[-1])",
   "value_example": "floor(C)"
  },
  {
   "name": "ceil",
   "group": "Maths",
   "level": "library",
   "signature": "ceil(x)",
   "description": "The smallest whole number at or above x.",
   "example": "ceil(C) > ceil(C[-1])",
   "value_example": "ceil(C)"
  },
  {
   "name": "exp",
   "group": "Maths",
   "level": "library",
   "signature": "exp(x)",
   "description": "e to the power x.",
   "example": "exp(log(C / C[-20])) > 1.1",
   "value_example": "exp(roc(C, 1) / 100)"
  },
  {
   "name": "pow",
   "group": "Maths",
   "level": "library",
   "signature": "pow(x, y)",
   "description": "x to the power y (the same as x ^ y).",
   "example": "pow(C / C[-252], 0.5) > 1.1",
   "value_example": "pow(C, 0.5)"
  },
  {
   "name": "log10",
   "group": "Maths",
   "level": "library",
   "signature": "log10(x)",
   "description": "Base-10 logarithm. NaN for x <= 0.",
   "example": "log10(V) > 7",
   "value_example": "log10(V)"
  },
  {
   "name": "ret",
   "group": "Returns and risk",
   "level": "library",
   "signature": "ret(n=1)",
   "description": "Simple return of the close over n bars, in percent.",
   "example": "ret(20) > 10",
   "value_example": "ret()"
  },
  {
   "name": "logret",
   "group": "Returns and risk",
   "level": "library",
   "signature": "logret(n=1)",
   "description": "Log return of the close over n bars, in percent (100 * ln(C / C[-n])).",
   "example": "logret(5) < -5",
   "value_example": "logret()"
  },
  {
   "name": "cagr",
   "group": "Returns and risk",
   "level": "library",
   "signature": "cagr(n=252)",
   "description": "Compound annual growth rate of the close over the last n bars, percent (252 bars = a year).",
   "example": "cagr(756) > 15",
   "value_example": "cagr()"
  },
  {
   "name": "sharpe",
   "group": "Returns and risk",
   "level": "library",
   "signature": "sharpe(n=252)",
   "description": "Annualised Sharpe ratio of daily returns over n bars (risk-free rate 0).",
   "example": "sharpe(252) > 1",
   "value_example": "sharpe()"
  },
  {
   "name": "sortino",
   "group": "Returns and risk",
   "level": "library",
   "signature": "sortino(n=252)",
   "description": "Annualised Sortino ratio: mean daily return over downside deviation (target 0).",
   "example": "sortino(252) > 1.5",
   "value_example": "sortino()"
  },
  {
   "name": "calmar",
   "group": "Returns and risk",
   "level": "library",
   "signature": "calmar(n=252)",
   "description": "Calmar ratio: cagr(n) / maxdrawdown(C, n).",
   "example": "calmar(756) > 1",
   "value_example": "calmar()"
  },
  {
   "name": "hvar",
   "group": "Returns and risk",
   "level": "library",
   "signature": "hvar(n=252, p=95)",
   "description": "Historical value at risk: the daily loss, in percent, exceeded on only (100 - p)% of the last n days (a positive number).",
   "example": "hvar(252, 95) > 3",
   "value_example": "hvar()"
  },
  {
   "name": "cvar",
   "group": "Returns and risk",
   "level": "library",
   "signature": "cvar(n=252, p=95)",
   "description": "Conditional VaR (expected shortfall): the average daily loss, in percent, on the days at or beyond hvar(n, p).",
   "example": "cvar(252, 95) > 4",
   "value_example": "cvar()"
  },
  {
   "name": "winrate",
   "group": "Returns and risk",
   "level": "library",
   "signature": "winrate(n=20)",
   "description": "Percent of the last n bars that closed higher than the bar before.",
   "example": "winrate(20) > 60",
   "value_example": "winrate()"
  },
  {
   "name": "omega",
   "group": "Returns and risk",
   "level": "library",
   "signature": "omega(n=252)",
   "description": "Omega ratio (threshold 0): total daily gains over total daily losses, n bars.",
   "example": "omega(252) > 1.2",
   "value_example": "omega()"
  },
  {
   "name": "gainloss",
   "group": "Returns and risk",
   "level": "library",
   "signature": "gainloss(n=20)",
   "description": "Average gain on up days over average loss on down days, n bars.",
   "example": "gainloss(50) > 1.2",
   "value_example": "gainloss()"
  },
  {
   "name": "dayofweek",
   "group": "Calendar",
   "level": "library",
   "signature": "dayofweek()",
   "description": "Day of the week: 1 = Monday ... 5 = Friday.",
   "example": "dayofweek() == 1",
   "value_example": "dayofweek()",
   "requires": "dated bars"
  },
  {
   "name": "dayofmonth",
   "group": "Calendar",
   "level": "library",
   "signature": "dayofmonth()",
   "description": "Calendar day of the month, 1-31.",
   "example": "dayofmonth() <= 5",
   "value_example": "dayofmonth()",
   "requires": "dated bars"
  },
  {
   "name": "month",
   "group": "Calendar",
   "level": "library",
   "signature": "month()",
   "description": "Month, 1-12.",
   "example": "month() == 12",
   "value_example": "month()",
   "requires": "dated bars"
  },
  {
   "name": "quarter",
   "group": "Calendar",
   "level": "library",
   "signature": "quarter()",
   "description": "Quarter, 1-4.",
   "example": "quarter() == 4",
   "value_example": "quarter()",
   "requires": "dated bars"
  },
  {
   "name": "year",
   "group": "Calendar",
   "level": "library",
   "signature": "year()",
   "description": "Calendar year.",
   "example": "year() >= 2026",
   "value_example": "year()",
   "requires": "dated bars"
  },
  {
   "name": "dayofyear",
   "group": "Calendar",
   "level": "library",
   "signature": "dayofyear()",
   "description": "Day of the year, 1-366.",
   "example": "dayofyear() > 350",
   "value_example": "dayofyear()",
   "requires": "dated bars"
  },
  {
   "name": "weekofyear",
   "group": "Calendar",
   "level": "library",
   "signature": "weekofyear()",
   "description": "ISO week number, 1-53.",
   "example": "weekofyear() >= 52",
   "value_example": "weekofyear()",
   "requires": "dated bars"
  },
  {
   "name": "days_to_month_end",
   "group": "Calendar",
   "level": "library",
   "signature": "days_to_month_end()",
   "description": "Weekdays left in the month after today (0 on the last weekday of the month). Exchange holidays are not counted out.",
   "example": "days_to_month_end() <= 2",
   "value_example": "days_to_month_end()",
   "requires": "dated bars"
  },
  {
   "name": "is_month_end",
   "group": "Calendar",
   "level": "library",
   "signature": "is_month_end()",
   "description": "1 on the last weekday of the month.",
   "example": "is_month_end()",
   "value_example": "is_month_end()",
   "requires": "dated bars"
  },
  {
   "name": "is_month_start",
   "group": "Calendar",
   "level": "library",
   "signature": "is_month_start()",
   "description": "1 on the first bar of a new month.",
   "example": "is_month_start()",
   "value_example": "is_month_start()",
   "requires": "dated bars"
  },
  {
   "name": "is_week_start",
   "group": "Calendar",
   "level": "library",
   "signature": "is_week_start()",
   "description": "1 on the first bar of a new week.",
   "example": "is_week_start()",
   "value_example": "is_week_start()",
   "requires": "dated bars"
  },
  {
   "name": "is_week_end",
   "group": "Calendar",
   "level": "library",
   "signature": "is_week_end()",
   "description": "1 on the last weekday of the week (Friday).",
   "example": "is_week_end()",
   "value_example": "is_week_end()",
   "requires": "dated bars"
  },
  {
   "name": "is_quarter_end",
   "group": "Calendar",
   "level": "library",
   "signature": "is_quarter_end()",
   "description": "1 on the last weekday of the quarter.",
   "example": "is_quarter_end()",
   "value_example": "is_quarter_end()",
   "requires": "dated bars"
  },
  {
   "name": "is_quarter_start",
   "group": "Calendar",
   "level": "library",
   "signature": "is_quarter_start()",
   "description": "1 on the first bar of a new quarter.",
   "example": "is_quarter_start()",
   "value_example": "is_quarter_start()",
   "requires": "dated bars"
  },
  {
   "name": "is_year_end",
   "group": "Calendar",
   "level": "library",
   "signature": "is_year_end()",
   "description": "1 on the last weekday of the year.",
   "example": "is_year_end()",
   "value_example": "is_year_end()",
   "requires": "dated bars"
  },
  {
   "name": "is_year_start",
   "group": "Calendar",
   "level": "library",
   "signature": "is_year_start()",
   "description": "1 on the first bar of a new year.",
   "example": "is_year_start()",
   "value_example": "is_year_start()",
   "requires": "dated bars"
  },
  {
   "name": "tdom",
   "group": "Calendar",
   "level": "library",
   "signature": "tdom()",
   "description": "Trading day of the month: 1 on the month's first bar, 2 on the second ... (not known for the first, partial month loaded).",
   "example": "tdom() <= 3",
   "value_example": "tdom()",
   "requires": "dated bars"
  },
  {
   "name": "is_opex",
   "group": "Calendar",
   "level": "library",
   "signature": "is_opex()",
   "description": "1 on the monthly options expiration day: the third Friday of the month.",
   "example": "is_opex()",
   "value_example": "is_opex()",
   "requires": "dated bars"
  },
  {
   "name": "days_to_opex",
   "group": "Calendar",
   "level": "library",
   "signature": "days_to_opex()",
   "description": "Weekdays from today to the next monthly options expiration (0 on the day itself).",
   "example": "days_to_opex() <= 3",
   "value_example": "days_to_opex()",
   "requires": "dated bars"
  },
  {
   "name": "pp",
   "group": "Price structure",
   "level": "library",
   "signature": "pp()",
   "description": "Classic floor pivot from the previous bar: (H + L + C) / 3.",
   "example": "C > pp()",
   "value_example": "pp()"
  },
  {
   "name": "r1",
   "group": "Price structure",
   "level": "library",
   "signature": "r1()",
   "description": "Classic resistance 1: 2 * pivot - previous low.",
   "example": "H > r1()",
   "value_example": "r1()"
  },
  {
   "name": "r2",
   "group": "Price structure",
   "level": "library",
   "signature": "r2()",
   "description": "Classic resistance 2: pivot + previous range.",
   "example": "H > r2()",
   "value_example": "r2()"
  },
  {
   "name": "r3",
   "group": "Price structure",
   "level": "library",
   "signature": "r3()",
   "description": "Classic resistance 3: previous high + 2 * (pivot - previous low).",
   "example": "H > r3()",
   "value_example": "r3()"
  },
  {
   "name": "s1",
   "group": "Price structure",
   "level": "library",
   "signature": "s1()",
   "description": "Classic support 1: 2 * pivot - previous high.",
   "example": "L < s1()",
   "value_example": "s1()"
  },
  {
   "name": "s2",
   "group": "Price structure",
   "level": "library",
   "signature": "s2()",
   "description": "Classic support 2: pivot - previous range.",
   "example": "L < s2()",
   "value_example": "s2()"
  },
  {
   "name": "s3",
   "group": "Price structure",
   "level": "library",
   "signature": "s3()",
   "description": "Classic support 3: previous low - 2 * (previous high - pivot).",
   "example": "L < s3()",
   "value_example": "s3()"
  },
  {
   "name": "fib_r1",
   "group": "Price structure",
   "level": "library",
   "signature": "fib_r1()",
   "description": "Fibonacci pivot resistance 1: pivot + 0.382 * previous range.",
   "example": "H > fib_r1()",
   "value_example": "fib_r1()"
  },
  {
   "name": "fib_r2",
   "group": "Price structure",
   "level": "library",
   "signature": "fib_r2()",
   "description": "Fibonacci resistance 2: pivot + 0.618 * range.",
   "example": "H > fib_r2()",
   "value_example": "fib_r2()"
  },
  {
   "name": "fib_r3",
   "group": "Price structure",
   "level": "library",
   "signature": "fib_r3()",
   "description": "Fibonacci resistance 3: pivot + 1.0 * range.",
   "example": "H > fib_r3()",
   "value_example": "fib_r3()"
  },
  {
   "name": "fib_s1",
   "group": "Price structure",
   "level": "library",
   "signature": "fib_s1()",
   "description": "Fibonacci support 1: pivot - 0.382 * range.",
   "example": "L < fib_s1()",
   "value_example": "fib_s1()"
  },
  {
   "name": "fib_s2",
   "group": "Price structure",
   "level": "library",
   "signature": "fib_s2()",
   "description": "Fibonacci support 2: pivot - 0.618 * range.",
   "example": "L < fib_s2()",
   "value_example": "fib_s2()"
  },
  {
   "name": "fib_s3",
   "group": "Price structure",
   "level": "library",
   "signature": "fib_s3()",
   "description": "Fibonacci support 3: pivot - 1.0 * range.",
   "example": "L < fib_s3()",
   "value_example": "fib_s3()"
  },
  {
   "name": "cam_r1",
   "group": "Price structure",
   "level": "library",
   "signature": "cam_r1()",
   "description": "Camarilla R1: previous close + 1.1/12 * range.",
   "example": "C > cam_r1()",
   "value_example": "cam_r1()"
  },
  {
   "name": "cam_r2",
   "group": "Price structure",
   "level": "library",
   "signature": "cam_r2()",
   "description": "Camarilla R2: previous close + 1.1/6 * range.",
   "example": "C > cam_r2()",
   "value_example": "cam_r2()"
  },
  {
   "name": "cam_r3",
   "group": "Price structure",
   "level": "library",
   "signature": "cam_r3()",
   "description": "Camarilla R3: previous close + 1.1/4 * range. A fade level.",
   "example": "H > cam_r3() and C < cam_r3()",
   "value_example": "cam_r3()"
  },
  {
   "name": "cam_r4",
   "group": "Price structure",
   "level": "library",
   "signature": "cam_r4()",
   "description": "Camarilla R4: previous close + 1.1/2 * range. A breakout level.",
   "example": "C > cam_r4()",
   "value_example": "cam_r4()"
  },
  {
   "name": "cam_s1",
   "group": "Price structure",
   "level": "library",
   "signature": "cam_s1()",
   "description": "Camarilla S1: previous close - 1.1/12 * range.",
   "example": "C < cam_s1()",
   "value_example": "cam_s1()"
  },
  {
   "name": "cam_s2",
   "group": "Price structure",
   "level": "library",
   "signature": "cam_s2()",
   "description": "Camarilla S2: previous close - 1.1/6 * range.",
   "example": "C < cam_s2()",
   "value_example": "cam_s2()"
  },
  {
   "name": "cam_s3",
   "group": "Price structure",
   "level": "library",
   "signature": "cam_s3()",
   "description": "Camarilla S3: previous close - 1.1/4 * range. A fade level.",
   "example": "L < cam_s3() and C > cam_s3()",
   "value_example": "cam_s3()"
  },
  {
   "name": "cam_s4",
   "group": "Price structure",
   "level": "library",
   "signature": "cam_s4()",
   "description": "Camarilla S4: previous close - 1.1/2 * range. A breakdown level.",
   "example": "C < cam_s4()",
   "value_example": "cam_s4()"
  },
  {
   "name": "gap_filled",
   "group": "Price structure",
   "level": "library",
   "signature": "gap_filled()",
   "description": "1 if today opened with a gap and traded back to the previous close (gap up and L <= C[-1], or gap down and H >= C[-1]).",
   "example": "gap_filled()",
   "value_example": "gap_filled()"
  },
  {
   "name": "insidebars",
   "group": "Price structure",
   "level": "library",
   "signature": "insidebars(n=5)",
   "description": "How many of the last n bars were inside bars (range inside the bar before).",
   "example": "insidebars(5) >= 2",
   "value_example": "insidebars()"
  },
  {
   "name": "outsidebars",
   "group": "Price structure",
   "level": "library",
   "signature": "outsidebars(n=5)",
   "description": "How many of the last n bars were outside bars.",
   "example": "outsidebars(10) >= 3",
   "value_example": "outsidebars()"
  },
  {
   "name": "nr",
   "group": "Price structure",
   "level": "library",
   "signature": "nr(n=7)",
   "description": "1 if today's range (H - L) is the narrowest of the last n bars (NR7 = nr(7)). Ties count.",
   "example": "nr(7)",
   "value_example": "nr()"
  },
  {
   "name": "wr",
   "group": "Price structure",
   "level": "library",
   "signature": "wr(n=7)",
   "description": "1 if today's range is the widest of the last n bars (WR7 = wr(7)). Ties count.",
   "example": "wr(7)",
   "value_example": "wr()"
  },
  {
   "name": "hhv",
   "group": "Price structure",
   "level": "library",
   "signature": "hhv(x, n=20)",
   "description": "Highest value of the last n bars, including today (MetaStock's name for max()).",
   "example": "C >= 0.97 * hhv(H, 52)",
   "value_example": "hhv(H, 52)"
  },
  {
   "name": "llv",
   "group": "Price structure",
   "level": "library",
   "signature": "llv(x, n=20)",
   "description": "Lowest value of the last n bars, including today (MetaStock's name for min()).",
   "example": "C <= 1.03 * llv(L, 52)",
   "value_example": "llv(L, 52)"
  },
  {
   "name": "body",
   "group": "Price structure",
   "level": "library",
   "signature": "body()",
   "description": "Size of the candle body: |C - O|.",
   "example": "body() > 2 * sma(body(), 20)",
   "value_example": "body()"
  },
  {
   "name": "upperwick",
   "group": "Price structure",
   "level": "library",
   "signature": "upperwick()",
   "description": "Upper shadow: H - max(O, C).",
   "example": "upperwick() > 2 * body()",
   "value_example": "upperwick()"
  },
  {
   "name": "lowerwick",
   "group": "Price structure",
   "level": "library",
   "signature": "lowerwick()",
   "description": "Lower shadow: min(O, C) - L.",
   "example": "lowerwick() > 2 * body()",
   "value_example": "lowerwick()"
  },
  {
   "name": "barrange",
   "group": "Price structure",
   "level": "library",
   "signature": "barrange()",
   "description": "The bar's range: H - L.",
   "example": "barrange() > 1.5 * atr(14)",
   "value_example": "barrange()"
  },
  {
   "name": "bodypct",
   "group": "Price structure",
   "level": "library",
   "signature": "bodypct()",
   "description": "Body as a percent of the bar's range (0 on a bar with no range).",
   "example": "bodypct() > 80",
   "value_example": "bodypct()"
  },
  {
   "name": "clv",
   "group": "Price structure",
   "level": "library",
   "signature": "clv()",
   "description": "Close location value, -1 to 1: ((C - L) - (H - C)) / (H - L). 1 = closed on the high.",
   "example": "clv() > 0.8",
   "value_example": "clv()"
  },
  {
   "name": "closepos",
   "group": "Price structure",
   "level": "library",
   "signature": "closepos()",
   "description": "Where the close sits in the bar's range, 0-100 (100 = on the high).",
   "example": "closepos() > 90",
   "value_example": "closepos()"
  },
  {
   "name": "pctfromhigh",
   "group": "Price structure",
   "level": "library",
   "signature": "pctfromhigh(n=252)",
   "description": "Percent the close sits below the highest high of the last n bars (0 = at the high).",
   "example": "pctfromhigh(252) < 3",
   "value_example": "pctfromhigh()"
  },
  {
   "name": "pctfromlow",
   "group": "Price structure",
   "level": "library",
   "signature": "pctfromlow(n=252)",
   "description": "Percent the close sits above the lowest low of the last n bars.",
   "example": "pctfromlow(252) < 5",
   "value_example": "pctfromlow()"
  },
  {
   "name": "rangepos",
   "group": "Price structure",
   "level": "library",
   "signature": "rangepos(n=20)",
   "description": "Where the close sits in the n-bar high-low range, 0-100.",
   "example": "rangepos(20) > 80",
   "value_example": "rangepos()"
  },
  {
   "name": "rangewidth",
   "group": "Price structure",
   "level": "library",
   "signature": "rangewidth(n=20)",
   "description": "Width of the n-bar high-low range in percent: (max(H, n) / min(L, n) - 1) * 100.",
   "example": "rangewidth(20) < 10",
   "value_example": "rangewidth()"
  },
  {
   "name": "close_of",
   "group": "Relative strength",
   "level": "library",
   "signature": "close_of(symbol)",
   "description": "Another symbol's daily close (C), on the same dates, e.g. close_of('SPY'). Ora symbols only; a date the other symbol did not trade carries its last close.",
   "example": "C / close_of('SPY') > ref(C / close_of('SPY'), -20)",
   "value_example": "close_of('SPY')",
   "requires": "dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars"
  },
  {
   "name": "open_of",
   "group": "Relative strength",
   "level": "library",
   "signature": "open_of(symbol)",
   "description": "Another symbol's daily open (O), on the same dates, e.g. open_of('SPY'). Ora symbols only; a date the other symbol did not trade carries its last open.",
   "example": "C > O and open_of('SPY') > 0",
   "value_example": "open_of('SPY')",
   "requires": "dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars"
  },
  {
   "name": "high_of",
   "group": "Relative strength",
   "level": "library",
   "signature": "high_of(symbol)",
   "description": "Another symbol's daily high (H), on the same dates, e.g. high_of('SPY'). Ora symbols only; a date the other symbol did not trade carries its last high.",
   "example": "C > O and high_of('SPY') > 0",
   "value_example": "high_of('SPY')",
   "requires": "dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars"
  },
  {
   "name": "low_of",
   "group": "Relative strength",
   "level": "library",
   "signature": "low_of(symbol)",
   "description": "Another symbol's daily low (L), on the same dates, e.g. low_of('SPY'). Ora symbols only; a date the other symbol did not trade carries its last low.",
   "example": "C > O and low_of('SPY') > 0",
   "value_example": "low_of('SPY')",
   "requires": "dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars"
  },
  {
   "name": "volume_of",
   "group": "Relative strength",
   "level": "library",
   "signature": "volume_of(symbol)",
   "description": "Another symbol's daily volume (V), on the same dates, e.g. volume_of('SPY'). Ora symbols only; a date the other symbol did not trade carries its last volume.",
   "example": "volume_of('SPY') > 1.5 * sma(volume_of('SPY'), 20)",
   "value_example": "volume_of('SPY')",
   "requires": "dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars"
  },
  {
   "name": "rs",
   "group": "Relative strength",
   "level": "library",
   "signature": "rs(n=63, symbol='SPY')",
   "description": "Relative strength: percent the symbol out- or under-performed the benchmark over n bars, ((C / C[-n]) / (B / B[-n]) - 1) * 100.",
   "example": "rs(63) > 0",
   "value_example": "rs()",
   "requires": "dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars"
  },
  {
   "name": "rsline",
   "group": "Relative strength",
   "level": "library",
   "signature": "rsline(symbol='SPY')",
   "description": "Relative-strength line: C / the benchmark's close. Compare it with its own past.",
   "example": "rsline() > max(ref(rsline(), -1), 252)",
   "value_example": "rsline()",
   "requires": "dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars"
  },
  {
   "name": "beta",
   "group": "Relative strength",
   "level": "library",
   "signature": "beta(n=60, symbol='SPY')",
   "description": "Beta of daily returns against the benchmark over n bars: cov(r, rb) / var(rb).",
   "example": "beta(60) > 1.5",
   "value_example": "beta()",
   "requires": "dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars"
  },
  {
   "name": "correl",
   "group": "Relative strength",
   "level": "library",
   "signature": "correl(n=60, symbol='SPY')",
   "description": "Correlation of daily returns with the benchmark over n bars, -1 to 1.",
   "example": "correl(60) < 0.3",
   "value_example": "correl()",
   "requires": "dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars"
  },
  {
   "name": "alpha",
   "group": "Relative strength",
   "level": "library",
   "signature": "alpha(n=252, symbol='SPY')",
   "description": "Annualised alpha against the benchmark over n bars, percent: (mean r - beta * mean rb) * 252 * 100.",
   "example": "alpha(252) > 5",
   "value_example": "alpha()",
   "requires": "dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars"
  },
  {
   "name": "doji",
   "group": "Patterns",
   "level": "pattern",
   "signature": "doji()",
   "description": "Body at most 10% of the day's range.",
   "example": "doji()",
   "expands_to": "(H > L) and (abs(C - O) <= 0.1 * (H - L))"
  },
  {
   "name": "dragonfly_doji",
   "group": "Patterns",
   "level": "pattern",
   "signature": "dragonfly_doji()",
   "description": "Doji with a long lower wick and almost no upper wick.",
   "example": "dragonfly_doji()",
   "expands_to": "((H > L) and (abs(C - O) <= 0.1 * (H - L))) and ((H - greatest(O, C)) <= 0.1 * (H - L)) and ((least(O, C) - L) >= 0.6 * (H - L))"
  },
  {
   "name": "gravestone_doji",
   "group": "Patterns",
   "level": "pattern",
   "signature": "gravestone_doji()",
   "description": "Doji with a long upper wick and almost no lower wick.",
   "example": "gravestone_doji()",
   "expands_to": "((H > L) and (abs(C - O) <= 0.1 * (H - L))) and ((least(O, C) - L) <= 0.1 * (H - L)) and ((H - greatest(O, C)) >= 0.6 * (H - L))"
  },
  {
   "name": "spinning_top",
   "group": "Patterns",
   "level": "pattern",
   "signature": "spinning_top()",
   "description": "Small body (10-30% of range) with wicks on both sides.",
   "example": "spinning_top()",
   "expands_to": "(H > L) and (abs(C - O) <= 0.3 * (H - L)) and (abs(C - O) > 0.1 * (H - L)) and ((H - greatest(O, C)) >= 0.25 * (H - L)) and ((least(O, C) - L) >= 0.25 * (H - L))"
  },
  {
   "name": "marubozu",
   "group": "Patterns",
   "level": "pattern",
   "signature": "marubozu()",
   "description": "Body at least 90% of the range: almost no wicks.",
   "example": "marubozu()",
   "expands_to": "(H > L) and (abs(C - O) >= 0.9 * (H - L))"
  },
  {
   "name": "hammer",
   "group": "Patterns",
   "level": "pattern",
   "signature": "hammer()",
   "description": "Long lower wick (2x the body), tiny upper wick, after a 5-bar decline.",
   "example": "hammer()",
   "expands_to": "((H > L) and (abs(C - O) > 0) and ((least(O, C) - L) >= 2 * abs(C - O)) and ((H - greatest(O, C)) <= 0.1 * (H - L))) and (C < C[-5])"
  },
  {
   "name": "hanging_man",
   "group": "Patterns",
   "level": "pattern",
   "signature": "hanging_man()",
   "description": "Hammer shape after a 5-bar rise.",
   "example": "hanging_man()",
   "expands_to": "((H > L) and (abs(C - O) > 0) and ((least(O, C) - L) >= 2 * abs(C - O)) and ((H - greatest(O, C)) <= 0.1 * (H - L))) and (C > C[-5])"
  },
  {
   "name": "shooting_star",
   "group": "Patterns",
   "level": "pattern",
   "signature": "shooting_star()",
   "description": "Long upper wick (2x the body), tiny lower wick, after a 5-bar rise.",
   "example": "shooting_star()",
   "expands_to": "((H > L) and (abs(C - O) > 0) and ((H - greatest(O, C)) >= 2 * abs(C - O)) and ((least(O, C) - L) <= 0.1 * (H - L))) and (C > C[-5])"
  },
  {
   "name": "inverted_hammer",
   "group": "Patterns",
   "level": "pattern",
   "signature": "inverted_hammer()",
   "description": "Shooting-star shape after a 5-bar decline.",
   "example": "inverted_hammer()",
   "expands_to": "((H > L) and (abs(C - O) > 0) and ((H - greatest(O, C)) >= 2 * abs(C - O)) and ((least(O, C) - L) <= 0.1 * (H - L))) and (C < C[-5])"
  },
  {
   "name": "engulfing_bull",
   "group": "Patterns",
   "level": "pattern",
   "signature": "engulfing_bull()",
   "description": "Green body that engulfs yesterday's red body.",
   "example": "engulfing_bull()",
   "expands_to": "(C[-1] < O[-1]) and (C > O) and (O <= C[-1]) and (C >= O[-1]) and (abs(C - O) > abs(C[-1] - O[-1]))"
  },
  {
   "name": "engulfing_bear",
   "group": "Patterns",
   "level": "pattern",
   "signature": "engulfing_bear()",
   "description": "Red body that engulfs yesterday's green body.",
   "example": "engulfing_bear()",
   "expands_to": "(C[-1] > O[-1]) and (C < O) and (O >= C[-1]) and (C <= O[-1]) and (abs(C - O) > abs(C[-1] - O[-1]))"
  },
  {
   "name": "harami_bull",
   "group": "Patterns",
   "level": "pattern",
   "signature": "harami_bull()",
   "description": "Small green body inside yesterday's red body.",
   "example": "harami_bull()",
   "expands_to": "(C[-1] < O[-1]) and (C > O) and (abs(C - O) < abs(C[-1] - O[-1])) and (O >= C[-1]) and (C <= O[-1])"
  },
  {
   "name": "harami_bear",
   "group": "Patterns",
   "level": "pattern",
   "signature": "harami_bear()",
   "description": "Small red body inside yesterday's green body.",
   "example": "harami_bear()",
   "expands_to": "(C[-1] > O[-1]) and (C < O) and (abs(C - O) < abs(C[-1] - O[-1])) and (O <= C[-1]) and (C >= O[-1])"
  },
  {
   "name": "piercing_line",
   "group": "Patterns",
   "level": "pattern",
   "signature": "piercing_line()",
   "description": "Opens below yesterday's close, closes above the middle of its red body.",
   "example": "piercing_line()",
   "expands_to": "(C[-1] < O[-1]) and (C > O) and (O < C[-1]) and (C > (O[-1] + C[-1]) / 2) and (C < O[-1])"
  },
  {
   "name": "dark_cloud",
   "group": "Patterns",
   "level": "pattern",
   "signature": "dark_cloud()",
   "description": "Opens above yesterday's close, closes below the middle of its green body.",
   "example": "dark_cloud()",
   "expands_to": "(C[-1] > O[-1]) and (C < O) and (O > C[-1]) and (C < (O[-1] + C[-1]) / 2) and (C > O[-1])"
  },
  {
   "name": "morning_star",
   "group": "Patterns",
   "level": "pattern",
   "signature": "morning_star()",
   "description": "Red bar, small bar below it, then a green bar closing above the middle of the first.",
   "example": "morning_star()",
   "expands_to": "(C[-2] < O[-2]) and (abs(C[-1] - O[-1]) <= 0.5 * abs(C[-2] - O[-2])) and (greatest(O[-1], C[-1]) <= C[-2]) and (C > O) and (C > (O[-2] + C[-2]) / 2)"
  },
  {
   "name": "evening_star",
   "group": "Patterns",
   "level": "pattern",
   "signature": "evening_star()",
   "description": "Green bar, small bar above it, then a red bar closing below the middle of the first.",
   "example": "evening_star()",
   "expands_to": "(C[-2] > O[-2]) and (abs(C[-1] - O[-1]) <= 0.5 * abs(C[-2] - O[-2])) and (least(O[-1], C[-1]) >= C[-2]) and (C < O) and (C < (O[-2] + C[-2]) / 2)"
  },
  {
   "name": "three_white_soldiers",
   "group": "Patterns",
   "level": "pattern",
   "signature": "three_white_soldiers()",
   "description": "Three green bars, each closing higher and opening inside the previous body.",
   "example": "three_white_soldiers()",
   "expands_to": "(C > O) and (C[-1] > O[-1]) and (C[-2] > O[-2]) and (C > C[-1]) and (C[-1] > C[-2]) and (O >= O[-1]) and (O <= C[-1]) and (O[-1] >= O[-2]) and (O[-1] <= C[-2])"
  },
  {
   "name": "three_black_crows",
   "group": "Patterns",
   "level": "pattern",
   "signature": "three_black_crows()",
   "description": "Three red bars, each closing lower and opening inside the previous body.",
   "example": "three_black_crows()",
   "expands_to": "(C < O) and (C[-1] < O[-1]) and (C[-2] < O[-2]) and (C < C[-1]) and (C[-1] < C[-2]) and (O <= O[-1]) and (O >= C[-1]) and (O[-1] <= O[-2]) and (O[-1] >= C[-2])"
  },
  {
   "name": "tweezer_bottom",
   "group": "Patterns",
   "level": "pattern",
   "signature": "tweezer_bottom()",
   "description": "Red then green with lows within 0.1%.",
   "example": "tweezer_bottom()",
   "expands_to": "(C[-1] < O[-1]) and (C > O) and (abs(L - L[-1]) <= 0.001 * C[-1])"
  },
  {
   "name": "tweezer_top",
   "group": "Patterns",
   "level": "pattern",
   "signature": "tweezer_top()",
   "description": "Green then red with highs within 0.1%.",
   "example": "tweezer_top()",
   "expands_to": "(C[-1] > O[-1]) and (C < O) and (abs(H - H[-1]) <= 0.001 * C[-1])"
  },
  {
   "name": "inside_bar",
   "group": "Patterns",
   "level": "pattern",
   "signature": "inside_bar()",
   "description": "Today's range inside yesterday's.",
   "example": "inside_bar()",
   "expands_to": "(H <= H[-1]) and (L >= L[-1])"
  },
  {
   "name": "outside_bar",
   "group": "Patterns",
   "level": "pattern",
   "signature": "outside_bar()",
   "description": "Today's range beyond yesterday's high and low.",
   "example": "outside_bar()",
   "expands_to": "(H > H[-1]) and (L < L[-1])"
  },
  {
   "name": "gap_up",
   "group": "Patterns",
   "level": "pattern",
   "signature": "gap_up()",
   "description": "Today's whole range above yesterday's high (unfilled gap up).",
   "example": "gap_up()",
   "expands_to": "L > H[-1]"
  },
  {
   "name": "gap_down",
   "group": "Patterns",
   "level": "pattern",
   "signature": "gap_down()",
   "description": "Today's whole range below yesterday's low (unfilled gap down).",
   "example": "gap_down()",
   "expands_to": "H < L[-1]"
  }
 ],
 "facts": [
  {
   "name": "days_to_earnings",
   "category": "Earnings",
   "units": "days",
   "scale": 3,
   "meaning": "Calendar days to the next scheduled earnings report (0 = reports today after the close or at an unknown time). Not known if no date is on the calendar.",
   "example": "days_to_earnings <= 10"
  },
  {
   "name": "days_since_earnings",
   "category": "Earnings",
   "units": "days",
   "scale": 3,
   "meaning": "Calendar days since the last report whose result is known (0 = reported before today's open).",
   "example": "days_since_earnings <= 5"
  },
  {
   "name": "next_report_bmo",
   "category": "Earnings",
   "units": "flag",
   "scale": 0.5,
   "meaning": "1 if the next report is scheduled before the open, 0 if after the close; not known if the time is not given.",
   "example": "next_report_bmo > 0.5"
  },
  {
   "name": "next_report_amc",
   "category": "Earnings",
   "units": "flag",
   "scale": 0.5,
   "meaning": "1 if the next report is scheduled after the close, 0 if before the open; not known if the time is not given.",
   "example": "next_report_amc > 0.5"
  },
  {
   "name": "last_eps",
   "category": "Earnings",
   "units": "$/share",
   "scale": null,
   "meaning": "EPS of the last known report.",
   "example": "last_eps > 0"
  },
  {
   "name": "last_eps_est",
   "category": "Earnings",
   "units": "$/share",
   "scale": null,
   "meaning": "Consensus EPS estimate for that report (as stored; estimates may have been revised).",
   "example": "last_eps > last_eps_est"
  },
  {
   "name": "eps_surprise_pct",
   "category": "Earnings",
   "units": "%",
   "scale": 5,
   "meaning": "(EPS − estimate) / |estimate| × 100 for the last report. Not known if there is no estimate or it is 0.",
   "example": "eps_surprise_pct > 5"
  },
  {
   "name": "beat_eps_last",
   "category": "Earnings",
   "units": "flag",
   "scale": 0.5,
   "meaning": "1 if the last EPS was above the estimate, else 0 (in line is not a beat).",
   "example": "beat_eps_last > 0.5"
  },
  {
   "name": "miss_eps_last",
   "category": "Earnings",
   "units": "flag",
   "scale": 0.5,
   "meaning": "1 if the last EPS was below the estimate, else 0.",
   "example": "miss_eps_last > 0.5"
  },
  {
   "name": "last_revenue",
   "category": "Earnings",
   "units": "$",
   "scale": null,
   "meaning": "Revenue of the last known report.",
   "example": "last_revenue > revenue_year_ago"
  },
  {
   "name": "revenue_surprise_pct",
   "category": "Earnings",
   "units": "%",
   "scale": 2,
   "meaning": "(revenue − estimate) / |estimate| × 100 for the last report.",
   "example": "revenue_surprise_pct > 1"
  },
  {
   "name": "beat_revenue_last",
   "category": "Earnings",
   "units": "flag",
   "scale": 0.5,
   "meaning": "1 if the last revenue was above the estimate, else 0.",
   "example": "beat_revenue_last > 0.5"
  },
  {
   "name": "miss_revenue_last",
   "category": "Earnings",
   "units": "flag",
   "scale": 0.5,
   "meaning": "1 if the last revenue was below the estimate, else 0.",
   "example": "miss_revenue_last > 0.5"
  },
  {
   "name": "beat_streak",
   "category": "Earnings",
   "units": "quarters",
   "scale": 1,
   "meaning": "Consecutive EPS beats ending with the last report (0 if it was not a beat). Not known when a missing estimate interrupts the run.",
   "example": "beat_streak >= 4"
  },
  {
   "name": "beats_last4",
   "category": "Earnings",
   "units": "count",
   "scale": 1,
   "meaning": "EPS beats among the last 4 known reports (not known unless all 4 have an estimate).",
   "example": "beats_last4 >= 3"
  },
  {
   "name": "misses_last4",
   "category": "Earnings",
   "units": "count",
   "scale": 1,
   "meaning": "EPS misses among the last 4 known reports (not known unless all 4 have an estimate).",
   "example": "misses_last4 >= 1"
  },
  {
   "name": "eps_year_ago",
   "category": "Earnings",
   "units": "$/share",
   "scale": null,
   "meaning": "EPS of the report about a year before the last one (365 ± 45 days).",
   "example": "last_eps > eps_year_ago"
  },
  {
   "name": "eps_growth_yoy",
   "category": "Earnings",
   "units": "%",
   "scale": 10,
   "meaning": "(last EPS − EPS a year ago) / |EPS a year ago| × 100.",
   "example": "eps_growth_yoy > 10"
  },
  {
   "name": "revenue_year_ago",
   "category": "Earnings",
   "units": "$",
   "scale": null,
   "meaning": "Revenue of the report about a year before the last one.",
   "example": "last_revenue > 1.1 * revenue_year_ago"
  },
  {
   "name": "revenue_growth_yoy",
   "category": "Earnings",
   "units": "%",
   "scale": 5,
   "meaning": "(last revenue − revenue a year ago) / |revenue a year ago| × 100.",
   "example": "revenue_growth_yoy > 5"
  },
  {
   "name": "last_earnings_move",
   "category": "Earnings",
   "units": "%",
   "scale": 2,
   "meaning": "Close-to-close return of the reaction session of the last report. Not known if the report time is blank.",
   "example": "abs(last_earnings_move) > 5"
  },
  {
   "name": "last_earnings_gap",
   "category": "Earnings",
   "units": "%",
   "scale": 2,
   "meaning": "Open of the reaction session against the close before it. Not known if the report time is blank.",
   "example": "last_earnings_gap > 0"
  },
  {
   "name": "pre_earnings_close",
   "category": "Earnings",
   "units": "price",
   "scale": null,
   "meaning": "Close of the session before the last report's reaction session (same price basis as C).",
   "example": "C > pre_earnings_close"
  },
  {
   "name": "pays_dividend",
   "category": "Dividends",
   "units": "flag",
   "scale": 0.5,
   "meaning": "1 if an ex-date fell in the last 400 days or one is declared, else 0.",
   "example": "pays_dividend > 0.5"
  },
  {
   "name": "div_ttm",
   "category": "Dividends",
   "units": "$/share",
   "scale": null,
   "meaning": "Sum of split-adjusted dividends with ex-dates in the last 365 days.",
   "example": "div_ttm > 1"
  },
  {
   "name": "div_yield_ttm",
   "category": "Dividends",
   "units": "%",
   "scale": 0.5,
   "meaning": "div_ttm / today's close × 100 (split-adjusted, not dividend-adjusted close).",
   "example": "div_yield_ttm > 2"
  },
  {
   "name": "div_count_ttm",
   "category": "Dividends",
   "units": "count",
   "scale": 1,
   "meaning": "Number of ex-dates in the last 365 days.",
   "example": "div_count_ttm >= 4"
  },
  {
   "name": "last_div_amount",
   "category": "Dividends",
   "units": "$/share",
   "scale": null,
   "meaning": "Split-adjusted amount of the last ex-date on or before today.",
   "example": "last_div_amount > 0.5"
  },
  {
   "name": "prev_div_amount",
   "category": "Dividends",
   "units": "$/share",
   "scale": null,
   "meaning": "Split-adjusted amount of the ex-date before that.",
   "example": "last_div_amount > prev_div_amount"
  },
  {
   "name": "div_change_pct",
   "category": "Dividends",
   "units": "%",
   "scale": 5,
   "meaning": "(last / previous payment − 1) × 100.",
   "example": "div_change_pct > 3"
  },
  {
   "name": "raised_last",
   "category": "Dividends",
   "units": "flag",
   "scale": 0.5,
   "meaning": "1 if the last payment was more than 0.5% above the previous one, else 0.",
   "example": "raised_last > 0.5"
  },
  {
   "name": "cut_last",
   "category": "Dividends",
   "units": "flag",
   "scale": 0.5,
   "meaning": "1 if the last payment was more than 0.5% below the previous one, else 0.",
   "example": "cut_last > 0.5"
  },
  {
   "name": "days_since_ex_div",
   "category": "Dividends",
   "units": "days",
   "scale": 3,
   "meaning": "Calendar days since the last ex-date (99999 if it never paid).",
   "example": "days_since_ex_div <= 45"
  },
  {
   "name": "days_to_ex_div",
   "category": "Dividends",
   "units": "days",
   "scale": 3,
   "meaning": "Calendar days to the next DECLARED ex-date (99999 if none is declared).",
   "example": "days_to_ex_div <= 30"
  },
  {
   "name": "next_div_amount",
   "category": "Dividends",
   "units": "$/share",
   "scale": null,
   "meaning": "Split-adjusted amount of the next declared dividend.",
   "example": "next_div_amount > last_div_amount"
  },
  {
   "name": "years_of_growth",
   "category": "Dividends",
   "units": "years",
   "scale": 1,
   "meaning": "Consecutive calendar years, ending last year, whose total dividend was more than 0.5% above the year before.",
   "example": "years_of_growth >= 10"
  },
  {
   "name": "days_since_split",
   "category": "Splits",
   "units": "days",
   "scale": 30,
   "meaning": "Calendar days since the last share split (99999 if none on record).",
   "example": "days_since_split <= 1095"
  },
  {
   "name": "last_split_ratio",
   "category": "Splits",
   "units": "ratio",
   "scale": 0.5,
   "meaning": "Shares after / before for the last split: 10 = 10-for-1, 0.05 = 1-for-20 reverse.",
   "example": "last_split_ratio >= 4"
  },
  {
   "name": "reverse_split_last",
   "category": "Splits",
   "units": "flag",
   "scale": 0.5,
   "meaning": "1 if the last split was a reverse split, 0 if forward; not known if it never split.",
   "example": "reverse_split_last > 0.5"
  },
  {
   "name": "days_since_reverse_split",
   "category": "Splits",
   "units": "days",
   "scale": 30,
   "meaning": "Calendar days since the last reverse split (99999 if none on record).",
   "example": "days_since_reverse_split <= 365"
  },
  {
   "name": "splits_5y",
   "category": "Splits",
   "units": "count",
   "scale": 1,
   "meaning": "Number of splits in the last 5 years (1,825 days).",
   "example": "splits_5y >= 1"
  },
  {
   "name": "days_to_split",
   "category": "Splits",
   "units": "days",
   "scale": 3,
   "meaning": "Calendar days to the next announced split (99999 if none is announced).",
   "example": "days_to_split <= 365"
  },
  {
   "name": "next_split_ratio",
   "category": "Splits",
   "units": "ratio",
   "scale": 0.5,
   "meaning": "Ratio of the next announced split.",
   "example": "next_split_ratio >= 2"
  }
 ],
 "examples": [
  {
   "group": "Price relations",
   "id": "close_above_prior_high",
   "english": "Closed above yesterday's high",
   "rule": "C > H[-1]",
   "why": "Offsets count back from the latest bar, so H[-1] is yesterday's high. Comparing the close (not the high) asks that the strength held into the end of the day."
  },
  {
   "group": "Price relations",
   "id": "open_below_prior_low",
   "english": "Opened below yesterday's low",
   "rule": "O < L[-1]",
   "why": "An opening break of the prior range. Use O for the open; the rest of the bar does not matter to this rule."
  },
  {
   "group": "Price relations",
   "id": "up_close",
   "english": "Closed higher than yesterday",
   "rule": "C > C[-1]",
   "why": "The simplest relation. The belief grows with the size of the move measured against this symbol's normal daily change."
  },
  {
   "group": "Price relations",
   "id": "green_day",
   "english": "Closed above its open",
   "rule": "C > O",
   "why": "An intraday gain, regardless of where yesterday closed."
  },
  {
   "group": "Price relations",
   "id": "higher_high_low",
   "english": "Higher high and higher low",
   "rule": "H > H[-1] and L > L[-1]",
   "why": "Both halves must hold; and takes the weaker of the two, so a barely higher low keeps the answer close to the middle."
  },
  {
   "group": "Price relations",
   "id": "lower_high_low",
   "english": "Lower high and lower low",
   "rule": "H < H[-1] and L < L[-1]",
   "why": "The mirror image: a down-trending bar."
  },
  {
   "group": "Price relations",
   "id": "two_up_closes",
   "english": "Two higher closes in a row",
   "rule": "C > C[-1] and C[-1] > C[-2]",
   "why": "Chain offsets to walk back bar by bar."
  },
  {
   "group": "Price relations",
   "id": "close_top_quarter",
   "english": "Closed in the top quarter of the day's range",
   "rule": "(C - L) / (H - L) > 0.75",
   "why": "Where the close sits in the bar, 0 at the low and 1 at the high. A bar with no range divides by zero and answers 0.50."
  },
  {
   "group": "Price relations",
   "id": "close_bottom_quarter",
   "english": "Closed in the bottom quarter of the day's range",
   "rule": "(C - L) / (H - L) < 0.25",
   "why": "Selling into the close."
  },
  {
   "group": "Gaps",
   "id": "gap_up_1pct",
   "english": "Opened more than 1% above yesterday's close",
   "rule": "O > C[-1] * 1.01",
   "why": "Multiply instead of subtracting to express the gap as a percentage."
  },
  {
   "group": "Gaps",
   "id": "gap_down_1pct",
   "english": "Opened more than 1% below yesterday's close",
   "rule": "O < C[-1] * 0.99",
   "why": "The down-side version."
  },
  {
   "group": "Gaps",
   "id": "unfilled_gap_up",
   "english": "An unfilled gap up",
   "rule": "L > H[-1]",
   "why": "The whole bar stayed above yesterday's high. Same as gap_up()."
  },
  {
   "group": "Gaps",
   "id": "gap_up_faded",
   "english": "Gapped up but closed under yesterday's close",
   "rule": "O > C[-1] and C < C[-1]",
   "why": "A failed gap: two conditions on the same bar."
  },
  {
   "group": "Gaps",
   "id": "gap_down_reclaimed",
   "english": "Opened under yesterday's low, closed up on the day",
   "rule": "O < L[-1] and C > C[-1]",
   "why": "Sellers had the open; buyers had the close."
  },
  {
   "group": "Gaps",
   "id": "gap_vs_atr",
   "english": "Gap bigger than half an ATR",
   "rule": "abs(O - C[-1]) > 0.5 * ref(atr(14), -1)",
   "why": "ref(atr(14), -1) is yesterday's ATR, so today's own wide bar does not inflate the yardstick it is measured against."
  },
  {
   "group": "Ranges",
   "id": "inside_day",
   "english": "An inside day",
   "rule": "H < H[-1] and L > L[-1]",
   "why": "Strictly inside. inside_bar() allows equal highs or lows."
  },
  {
   "group": "Ranges",
   "id": "nr7",
   "english": "Narrowest range of the last seven sessions (NR7)",
   "rule": "(H - L) < lowest(H - L, 6)",
   "why": "lowest() looks at the six bars before today, so today plus six is the seven-bar window."
  },
  {
   "group": "Ranges",
   "id": "wide_range",
   "english": "Range more than twice the 20-day average range",
   "rule": "(H - L) > 2 * avg(H - L, 20)",
   "why": "Window functions take any expression, not just a price."
  },
  {
   "group": "Ranges",
   "id": "tr_vs_atr",
   "english": "True range 1.5× yesterday's ATR",
   "rule": "tr() > 1.5 * ref(atr(14), -1)",
   "why": "tr() counts an overnight gap as part of the range, unlike H − L."
  },
  {
   "group": "Ranges",
   "id": "lower_third_20d",
   "english": "In the lower third of its 20-day range",
   "rule": "C < lowest(L, 20) + (highest(H, 20) - lowest(L, 20)) / 3",
   "why": "Build the range from highest and lowest of the previous 20 bars, then place the close in it."
  },
  {
   "group": "Ranges",
   "id": "tight_base",
   "english": "A tight 10-day base (under 5% of price)",
   "rule": "(max(H, 10) - min(L, 10)) / C < 0.05",
   "why": "max and min include today, which is what a base measured up to now should do."
  },
  {
   "group": "Trend",
   "id": "above_50d",
   "english": "Above the 50-day average",
   "rule": "C > sma(C, 50)",
   "why": "The everyday trend filter."
  },
  {
   "group": "Trend",
   "id": "above_200d_10pct",
   "english": "More than 10% above the 200-day",
   "rule": "C > sma(C, 200) * 1.10",
   "why": "Scale one side to ask \"by how much\", not just \"above\"."
  },
  {
   "group": "Trend",
   "id": "golden_alignment",
   "english": "50-day above the 200-day",
   "rule": "sma(C, 50) > sma(C, 200)",
   "why": "Compares two averages; the belief is scaled by how much their gap normally moves."
  },
  {
   "group": "Trend",
   "id": "stacked_averages",
   "english": "Stacked averages: close > 20 EMA > 50 > 200",
   "rule": "C > ema(C, 20) and ema(C, 20) > sma(C, 50) and sma(C, 50) > sma(C, 200)",
   "why": "Four links in one chain; the weakest link sets the answer."
  },
  {
   "group": "Trend",
   "id": "sma50_rising",
   "english": "The 50-day average is rising",
   "rule": "sma(C, 50) > ref(sma(C, 50), -10)",
   "why": "ref() is how you look back at an indicator; sma(C, 50)[-10] is not valid."
  },
  {
   "group": "Trend",
   "id": "sma200_rising",
   "english": "The 200-day average is rising",
   "rule": "sma(C, 200) > ref(sma(C, 200), -20)",
   "why": "A slow average needs a longer look-back to show a slope."
  },
  {
   "group": "Trend",
   "id": "adx_trend",
   "english": "Trending (ADX above 25)",
   "rule": "adx(14) > 25",
   "why": "ADX has no direction; pair it with diplus/diminus for one."
  },
  {
   "group": "Trend",
   "id": "adx_uptrend",
   "english": "Trending up (ADX over 20 and +DI over −DI)",
   "rule": "adx(14) > 20 and diplus(14) > diminus(14)",
   "why": "Strength and direction as two separate conditions."
  },
  {
   "group": "Trend",
   "id": "mostly_up",
   "english": "Up on at least six of the last ten days",
   "rule": "sum(C > C[-1], 10) >= 6",
   "why": "A comparison is 1 or 0 inside a window, so sum() counts the days it held."
  },
  {
   "group": "Momentum",
   "id": "rsi_above_50",
   "english": "RSI above 50",
   "rule": "rsi(C, 14) > 50",
   "why": "The midline: more up pressure than down."
  },
  {
   "group": "Momentum",
   "id": "rsi_overbought",
   "english": "RSI above 70",
   "rule": "rsi(C, 14) > 70",
   "why": "The classic overbought line. The measured scale means 71 answers near 0.55 and 85 near 0.9."
  },
  {
   "group": "Momentum",
   "id": "macd_over_signal",
   "english": "MACD above its signal line",
   "rule": "macd(C) > macdsignal(C)",
   "why": "Defaults 12, 26, 9; pass periods to change them: macd(C, 8, 21, 5)."
  },
  {
   "group": "Momentum",
   "id": "macd_hist_rising",
   "english": "MACD histogram rising",
   "rule": "macdhist(C) > ref(macdhist(C), -1)",
   "why": "Momentum of momentum, read one bar back with ref()."
  },
  {
   "group": "Momentum",
   "id": "roc20_5",
   "english": "Up more than 5% in 20 sessions",
   "rule": "roc(C, 20) > 5",
   "why": "roc() is in percent, so 5 means 5%."
  },
  {
   "group": "Momentum",
   "id": "three_month_up",
   "english": "Higher than three months ago",
   "rule": "C > C[-63]",
   "why": "About 63 sessions in three months."
  },
  {
   "group": "Momentum",
   "id": "stoch_oversold",
   "english": "Stochastic %K under 20",
   "rule": "stochk(14) < 20",
   "why": "Near the bottom of the 14-day range."
  },
  {
   "group": "Momentum",
   "id": "cci_strong",
   "english": "CCI above 100",
   "rule": "cci(20) > 100",
   "why": "A strong push above the typical price's average."
  },
  {
   "group": "Momentum",
   "id": "willr_top",
   "english": "Williams %R above −20",
   "rule": "willr(14) > -20",
   "why": "Negative numbers work as written."
  },
  {
   "group": "Volatility",
   "id": "atr_pct_3",
   "english": "A typical day moves more than 3%",
   "rule": "atr_pct(14) > 3",
   "why": "Percent of price, so one line works across cheap and expensive stocks."
  },
  {
   "group": "Volatility",
   "id": "atr_expanding",
   "english": "ATR up 20% in two weeks",
   "rule": "atr(14) > 1.2 * ref(atr(14), -10)",
   "why": "Compare an indicator with its own past to see a regime change."
  },
  {
   "group": "Volatility",
   "id": "bb_squeeze",
   "english": "Bollinger bands narrower than 6% of price",
   "rule": "(bbupper(C, 20, 2) - bblower(C, 20, 2)) / bbmid(C, 20) < 0.06",
   "why": "Band width as a fraction of the middle band: a squeeze measure."
  },
  {
   "group": "Volatility",
   "id": "hv_40",
   "english": "Annualized 20-day volatility above 40%",
   "rule": "std(log(C / C[-1]), 20) * sqrt(252) > 0.4",
   "why": "Daily log returns, their deviation, scaled by the square root of 252 sessions."
  },
  {
   "group": "Volatility",
   "id": "compression",
   "english": "Swings less than half their 50-day size",
   "rule": "std(C, 10) < 0.5 * std(C, 50)",
   "why": "Short window against long window of the same statistic."
  },
  {
   "group": "Volatility",
   "id": "return_shock",
   "english": "A two-sigma down day",
   "rule": "zscore(C / C[-1] - 1, 20) < -2",
   "why": "zscore() of the daily return, against its own last 20 sessions."
  },
  {
   "group": "Volume",
   "id": "vol_above_avg",
   "english": "Volume above its 20-day average",
   "rule": "V > avg(V, 20)",
   "why": "The average includes today; see the next rule for the stricter form."
  },
  {
   "group": "Volume",
   "id": "vol_spike",
   "english": "Volume more than twice the previous 20-day average",
   "rule": "V > 2 * ref(avg(V, 20), -1)",
   "why": "ref(…, -1) keeps today's spike out of the average it is compared with."
  },
  {
   "group": "Volume",
   "id": "up_on_volume",
   "english": "Up on heavy volume",
   "rule": "C > C[-1] and V > 1.5 * ref(avg(V, 20), -1)",
   "why": "Price and participation together."
  },
  {
   "group": "Volume",
   "id": "down_on_volume",
   "english": "Down on heavy volume",
   "rule": "C < C[-1] and V > 1.5 * ref(avg(V, 20), -1)",
   "why": "Distribution: selling with size."
  },
  {
   "group": "Volume",
   "id": "obv_rising",
   "english": "On-balance volume above its 20-day average",
   "rule": "obv() > sma(obv(), 20)",
   "why": "Window functions accept another function as input."
  },
  {
   "group": "Volume",
   "id": "vol_dry_up",
   "english": "Volume under 60% of normal",
   "rule": "V < 0.6 * avg(V, 20)",
   "why": "Quiet sessions, often inside a base."
  },
  {
   "group": "Volume",
   "id": "vol_rank_90",
   "english": "Volume in the top tenth of 50 sessions",
   "rule": "percentrank(V, 50) > 90",
   "why": "A rank needs no threshold in shares, so it works on any symbol."
  },
  {
   "group": "Breakouts",
   "id": "close_20d_high",
   "english": "20-day closing breakout",
   "rule": "C > highest(C, 20)",
   "why": "highest() excludes today: the form that can actually be true."
  },
  {
   "group": "Breakouts",
   "id": "high_20d_break",
   "english": "Took out the 20-day high intraday",
   "rule": "H > highest(H, 20)",
   "why": "Highs against highs: an intraday break that may not have held."
  },
  {
   "group": "Breakouts",
   "id": "new_52w_high",
   "english": "New 52-week high",
   "rule": "H > highest(H, 252)",
   "why": "About 252 sessions in a year."
  },
  {
   "group": "Breakouts",
   "id": "near_52w_high",
   "english": "Within 2% of the 52-week high",
   "rule": "C >= 0.98 * max(H, 252)",
   "why": "max() includes today, so a stock at its high still qualifies."
  },
  {
   "group": "Breakouts",
   "id": "donchian_55",
   "english": "55-day closing breakout",
   "rule": "C > highest(C, 55)",
   "why": "A slower channel breakout."
  },
  {
   "group": "Breakouts",
   "id": "breakdown_20d",
   "english": "Closed under the 20-day low",
   "rule": "C < lowest(L, 20)",
   "why": "Close against the lows of the previous 20 bars."
  },
  {
   "group": "Breakouts",
   "id": "breakout_volume",
   "english": "20-day breakout on 1.5× volume",
   "rule": "C > highest(C, 20) and V > 1.5 * ref(avg(V, 20), -1)",
   "why": "Ask the parts as separate questions too: they show which half held it back."
  },
  {
   "group": "Breakouts",
   "id": "recent_high",
   "english": "Set its 20-day high in the last four sessions",
   "rule": "highestbars(H, 20) <= 3",
   "why": "highestbars() is 0 on the bar that set the high."
  },
  {
   "group": "Breakouts",
   "id": "cross_50d",
   "english": "Crossed above the 50-day today",
   "rule": "crossup(C, sma(C, 50))",
   "why": "An event on this bar only: 0.95 when it happened, 0.05 otherwise."
  },
  {
   "group": "Breakouts",
   "id": "above_upper_band",
   "english": "Closed above the upper Bollinger band",
   "rule": "C > bbupper(C, 20, 2)",
   "why": "A volatility-scaled breakout."
  },
  {
   "group": "Mean reversion",
   "id": "rsi2_oversold",
   "english": "Two-day RSI under 10",
   "rule": "rsi(C, 2) < 10",
   "why": "A very short RSI reacts to two or three down days."
  },
  {
   "group": "Mean reversion",
   "id": "below_lower_band",
   "english": "Closed under the lower Bollinger band",
   "rule": "C < bblower(C, 20, 2)",
   "why": "Two deviations under the 20-day mean."
  },
  {
   "group": "Mean reversion",
   "id": "zscore_low",
   "english": "Two deviations under the 20-day mean",
   "rule": "zscore(C, 20) < -2",
   "why": "The same idea as the band, as one number."
  },
  {
   "group": "Mean reversion",
   "id": "pullback_in_uptrend",
   "english": "Pullback in an uptrend: above the 200-day, RSI under 40",
   "rule": "C > sma(C, 200) and rsi(C, 14) < 40",
   "why": "A trend filter plus an oversold trigger."
  },
  {
   "group": "Mean reversion",
   "id": "stretched_up",
   "english": "Stretched: more than 2 ATR above the 20-day",
   "rule": "C > sma(C, 20) + 2 * atr(14)",
   "why": "Distance in ATR units is comparable across symbols."
  },
  {
   "group": "Mean reversion",
   "id": "three_down_in_uptrend",
   "english": "Three lower closes while above the 200-day",
   "rule": "C < C[-1] and C[-1] < C[-2] and C[-2] < C[-3] and C > sma(C, 200)",
   "why": "Short-term weakness inside long-term strength."
  },
  {
   "group": "Mean reversion",
   "id": "back_inside_band",
   "english": "Crossed back above the lower band",
   "rule": "crossup(C, bblower(C, 20, 2))",
   "why": "The re-entry signal after a band break."
  },
  {
   "group": "Patterns",
   "id": "hammer_at_low",
   "english": "Hammer at a 20-day low",
   "rule": "hammer() and L <= 1.01 * lowest(L, 20)",
   "why": "A pattern is a rule, so it combines with and like any other."
  },
  {
   "group": "Patterns",
   "id": "engulfing_in_uptrend",
   "english": "Bullish engulfing above the 50-day",
   "rule": "engulfing_bull() and C > sma(C, 50)",
   "why": "Context matters more than the candle."
  },
  {
   "group": "Patterns",
   "id": "star_or_engulf",
   "english": "Morning star or bullish engulfing",
   "rule": "morning_star() or engulfing_bull()",
   "why": "or takes the stronger of the two."
  },
  {
   "group": "Patterns",
   "id": "shooting_star_at_high",
   "english": "Shooting star at a 20-day high",
   "rule": "shooting_star() and H > highest(H, 20)",
   "why": "A reversal shape where it matters."
  },
  {
   "group": "Patterns",
   "id": "doji_after_run",
   "english": "Doji after a 10% month",
   "rule": "doji() and roc(C, 20) > 10",
   "why": "Indecision after a run."
  },
  {
   "group": "Price action",
   "id": "above_swing_high",
   "english": "Above its last confirmed swing high",
   "rule": "C > swinghigh(5)",
   "why": "swinghigh() is confirmed five bars after the pivot, so it never peeks ahead."
  },
  {
   "group": "Price action",
   "id": "higher_swing_lows",
   "english": "Higher swing lows",
   "rule": "swinglow(3) > swinglow(3, 2)",
   "why": "The second argument picks an earlier pivot."
  },
  {
   "group": "Price action",
   "id": "hh_hl",
   "english": "Higher swing highs and higher swing lows",
   "rule": "(swinghigh(3) > swinghigh(3, 2)) and (swinglow(3) > swinglow(3, 2))",
   "why": "The textbook definition of an uptrend, in pivots."
  },
  {
   "group": "Price action",
   "id": "near_resistance",
   "english": "Within 3% under the last swing high",
   "rule": "C <= swinghigh(5) and C >= 0.97 * swinghigh(5)",
   "why": "Two comparisons on one level make a band."
  },
  {
   "group": "Price action",
   "id": "near_support",
   "english": "Within 3% above the last swing low",
   "rule": "C >= swinglow(5) and C <= 1.03 * swinglow(5)",
   "why": "The support version of the band."
  },
  {
   "group": "Price action",
   "id": "break_resistance",
   "english": "Closed above the last swing high today",
   "rule": "C > swinghigh(5) and C[-1] <= ref(swinghigh(5), -1)",
   "why": "Above it today, not above it yesterday: the break is new."
  },
  {
   "group": "Price action",
   "id": "trend_day_up",
   "english": "A trend day up: opened near the low, closed near the high",
   "rule": "(O <= L + 0.2 * (H - L)) and (C >= H - 0.2 * (H - L))",
   "why": "Both ends of the bar, placed in its own range."
  },
  {
   "group": "Price action",
   "id": "rolling_vwap",
   "english": "Above its 20-day volume-weighted price",
   "rule": "C > sum(hlc3() * V, 20) / sum(V, 20)",
   "why": "A rolling VWAP from daily bars; the intraday vwap() is not available on Ora."
  },
  {
   "group": "Price action",
   "id": "key_reversal_up",
   "english": "New 10-day low, but closed up on the day",
   "rule": "L < lowest(L, 10) and C > C[-1]",
   "why": "A key reversal: the low broke, the close did not follow."
  },
  {
   "group": "Multi-condition setups",
   "id": "trend_pullback_entry",
   "english": "Uptrend, under the 20-day, RSI still above 40",
   "rule": "C > sma(C, 200) and C < sma(C, 20) and rsi(C, 14) > 40",
   "why": "A pullback that has not broken momentum."
  },
  {
   "group": "Multi-condition setups",
   "id": "squeeze_breakout",
   "english": "Squeeze, then a breakout",
   "rule": "std(C, 20) < 0.7 * std(C, 100) and C > highest(C, 20)",
   "why": "Quiet first, then a break: both must hold today."
  },
  {
   "group": "Multi-condition setups",
   "id": "momentum_participation",
   "english": "Momentum with participation",
   "rule": "roc(C, 20) > 5 and V > avg(V, 50) and C > sma(C, 50)",
   "why": "Three independent pieces of evidence."
  },
  {
   "group": "Multi-condition setups",
   "id": "failed_breakdown",
   "english": "Undercut the 20-day low and closed back above it",
   "rule": "L < lowest(L, 20) and C > lowest(L, 20)",
   "why": "The same level on both sides of one bar: a spring."
  },
  {
   "group": "Multi-condition setups",
   "id": "regime_oversold",
   "english": "Oversold, with the line set by the trend",
   "rule": "(C > sma(C, 200) and rsi(C, 14) < 40) or (C < sma(C, 200) and rsi(C, 14) < 25)",
   "why": "Two regimes joined by or, each with its own threshold. iif() can do the same in one comparison."
  },
  {
   "group": "Multi-condition setups",
   "id": "chandelier_exit",
   "english": "Chandelier exit: 3 ATR under the 22-day high",
   "rule": "C < highest(H, 22) - 3 * atr(22)",
   "why": "A volatility stop written as a rule: true means the stop is hit."
  },
  {
   "group": "Scanning ideas",
   "id": "drawdown_20",
   "english": "More than 20% off the 52-week high",
   "rule": "C < 0.8 * max(H, 252)",
   "why": "Run it on a watchlist to find names in a drawdown."
  },
  {
   "group": "Scanning ideas",
   "id": "near_200d",
   "english": "Within 3% of the 200-day",
   "rule": "abs(C / sma(C, 200) - 1) < 0.03",
   "why": "abs() makes it two-sided: just above or just below."
  },
  {
   "group": "Scanning ideas",
   "id": "quiet_since_breakout",
   "english": "No 20-day breakout for 30 sessions",
   "rule": "barssince(C > highest(C, 20)) > 30",
   "why": "barssince() turns an event into an age."
  },
  {
   "group": "Scanning ideas",
   "id": "higher_weekly_low",
   "english": "This week's low is above last week's",
   "rule": "min(L, 5) > ref(min(L, 5), -5)",
   "why": "ref() shifts a whole window back: the five sessions before these five."
  },
  {
   "group": "Scanning ideas",
   "id": "range_expansion_week",
   "english": "This week's range is 1.5× last week's",
   "rule": "(max(H, 5) - min(L, 5)) > 1.5 * ref(max(H, 5) - min(L, 5), -5)",
   "why": "Any expression can go inside ref(), here a whole five-day range."
  },
  {
   "group": "Scanning ideas",
   "id": "quarter_leader",
   "english": "Up more than 10% in a quarter, near the high",
   "rule": "roc(C, 63) > 10 and C >= 0.95 * max(H, 63)",
   "why": "Strength that has not faded. Rules read one symbol's own history; to compare against an index, rank the answers in your code."
  },
  {
   "group": "Trading actions",
   "id": "stop_prior_low",
   "english": "A stop under yesterday's low is less than 1 ATR away",
   "rule": "C > L[-1] and C - L[-1] < atr(14)",
   "why": "Risk in ATR units, before you place the stop."
  },
  {
   "group": "Trading actions",
   "id": "stop_swing_low",
   "english": "A stop under the last swing low is within 1.5 ATR",
   "rule": "C > swinglow(5) and C - swinglow(5) < 1.5 * atr(14)",
   "why": "A structural stop that is still tight enough to trade."
  },
  {
   "group": "Trading actions",
   "id": "reward_2to1",
   "english": "At least 2:1 to the last swing high, stop at the last swing low",
   "rule": "C > swinglow(5) and C < swinghigh(5) and swinghigh(5) - C >= 2 * (C - swinglow(5))",
   "why": "Reward and risk from the same two pivots."
  },
  {
   "group": "Trading actions",
   "id": "pullback_entry",
   "english": "Uptrend pullback to the 20-day (entry zone)",
   "rule": "(C > sma(C, 50)) and (sma(C, 50) > sma(C, 200)) and (L <= sma(C, 20) * 1.01) and (C >= sma(C, 20))",
   "why": "Trend, then the touch, then the hold."
  },
  {
   "group": "Trading actions",
   "id": "big_move_atr",
   "english": "Today's move was bigger than 1.5 ATR",
   "rule": "abs(C - C[-1]) > 1.5 * atr(14)",
   "why": "A move judged against the stock's own range, not a fixed percent."
  },
  {
   "group": "Trading actions",
   "id": "size_check",
   "english": "A 2-ATR stop risking 1% allows at least a 25% position",
   "rule": "0.01 / (2 * atr(14) / C) >= 0.25",
   "why": "Position-size arithmetic as a rule: account risk divided by stop distance in percent."
  },
  {
   "group": "Trading actions",
   "id": "liquid",
   "english": "Trades more than $100 million a day",
   "rule": "avg(C * V, 20) > 100000000",
   "why": "Dollar volume, averaged over 20 sessions."
  },
  {
   "group": "Trading actions",
   "id": "clear_of_earnings",
   "english": "Above the 50-day, no earnings in the next 5 days",
   "rule": "C > sma(C, 50) and days_to_earnings > 5",
   "why": "A technical condition and a calendar fact in one rule."
  },
  {
   "group": "Earnings",
   "id": "earn_within_10",
   "english": "Reports earnings within 10 days",
   "rule": "days_to_earnings <= 10",
   "why": "A bare fact name, compared like any price."
  },
  {
   "group": "Earnings",
   "id": "earn_recent",
   "english": "Reported in the last 5 days",
   "rule": "days_since_earnings <= 5",
   "why": "Days since the last report whose result is known."
  },
  {
   "group": "Earnings",
   "id": "beat_both",
   "english": "Beat on EPS and revenue last quarter",
   "rule": "beat_eps_last > 0.5 and beat_revenue_last > 0.5",
   "why": "Flags are 1 or 0; compare with 0.5."
  },
  {
   "group": "Earnings",
   "id": "big_surprise",
   "english": "EPS beat by more than 5%",
   "rule": "eps_surprise_pct > 5",
   "why": "Surprise in percent of the estimate."
  },
  {
   "group": "Earnings",
   "id": "streak_4",
   "english": "Beat four quarters in a row",
   "rule": "beat_streak >= 4",
   "why": "With >=, a streak of exactly 4 answers yes (0.55 at the line)."
  },
  {
   "group": "Earnings",
   "id": "eps_growth",
   "english": "EPS up more than 10% on the year",
   "rule": "eps_growth_yoy > 10",
   "why": "Against the report about a year earlier."
  },
  {
   "group": "Earnings",
   "id": "gap_up_after",
   "english": "Gapped up after the last report",
   "rule": "last_earnings_gap > 0",
   "why": "The reaction session's open against the close before it."
  },
  {
   "group": "Earnings",
   "id": "big_reaction",
   "english": "Last earnings move bigger than 5%",
   "rule": "abs(last_earnings_move) > 5",
   "why": "abs() makes it two-sided."
  },
  {
   "group": "Earnings",
   "id": "above_pre",
   "english": "Holding above the pre-earnings close",
   "rule": "C > pre_earnings_close",
   "why": "Today's price against a fact: did the reaction stick?"
  },
  {
   "group": "Earnings",
   "id": "drift",
   "english": "Post-earnings drift: beat, gapped up, still above the pre-earnings close",
   "rule": "beat_eps_last > 0.5 and last_earnings_gap > 0 and C > pre_earnings_close",
   "why": "Three facts about the last report and one price."
  },
  {
   "group": "Earnings",
   "id": "pre_earnings_trend",
   "english": "Earnings within 10 days, above the 50-day",
   "rule": "days_to_earnings <= 10 and C > sma(C, 50)",
   "why": "A pre-earnings check: who is going into the print strong."
  },
  {
   "group": "Dividends",
   "id": "pays",
   "english": "Pays a dividend",
   "rule": "pays_dividend > 0.5",
   "why": "An ex-date in the last 400 days, or one declared."
  },
  {
   "group": "Dividends",
   "id": "yield_3",
   "english": "Yield above 3%",
   "rule": "div_yield_ttm > 3",
   "why": "Trailing twelve months, in percent."
  },
  {
   "group": "Dividends",
   "id": "raised",
   "english": "Raised its last dividend",
   "rule": "raised_last > 0.5",
   "why": "Last payment more than 0.5% above the one before."
  },
  {
   "group": "Dividends",
   "id": "grower_10",
   "english": "Ten years of dividend growth",
   "rule": "years_of_growth >= 10",
   "why": "Calendar-year totals, ending last year."
  },
  {
   "group": "Dividends",
   "id": "ex_soon",
   "english": "Goes ex-dividend within 30 days",
   "rule": "days_to_ex_div <= 30",
   "why": "Only declared ex-dates count; none declared is 99999, a clean no."
  },
  {
   "group": "Dividends",
   "id": "quarterly",
   "english": "Pays at least quarterly",
   "rule": "div_count_ttm >= 4",
   "why": "Ex-dates in the last 365 days."
  },
  {
   "group": "Dividends",
   "id": "yield_in_trend",
   "english": "Yield above 2% and above the 200-day",
   "rule": "div_yield_ttm > 2 and C > sma(C, 200)",
   "why": "Income without a falling knife."
  },
  {
   "group": "Dividends",
   "id": "no_cut",
   "english": "Did not cut its last dividend",
   "rule": "cut_last < 0.5",
   "why": "The flag read the other way round."
  },
  {
   "group": "Splits",
   "id": "split_3y",
   "english": "Split in the last three years",
   "rule": "days_since_split <= 1095",
   "why": "Calendar days; 99999 if it never split, so the answer is a clean no."
  },
  {
   "group": "Splits",
   "id": "ratio_4",
   "english": "Last split at least 4-for-1",
   "rule": "last_split_ratio >= 4",
   "why": "Shares after per share before: 10 = 10-for-1."
  },
  {
   "group": "Splits",
   "id": "reverse_last",
   "english": "Last split was a reverse split",
   "rule": "reverse_split_last > 0.5",
   "why": "Not known (0.50) if it never split."
  },
  {
   "group": "Splits",
   "id": "split_announced",
   "english": "A split is announced",
   "rule": "days_to_split <= 365",
   "why": "Only announced splits count."
  },
  {
   "group": "Splits",
   "id": "multi_split",
   "english": "Two or more splits in five years",
   "rule": "splits_5y >= 2",
   "why": "A count over the last 1,825 days."
  }
 ]
}
