Ora

Open standard · version 1.0.0 · 2026-10-31

Ora Rules v1

A small, exact language for questions about a price series: C > sma(C, 50), rsi(C, 14) < 30, engulfing_bull(), days_to_earnings <= 10. A rule answers with a belief between 0.05 and 0.95: how clearly it holds on the latest bar, measured against how much that quantity normally moves. This document specifies the language completely, so anyone can implement it.

Status: stable. Editor: KlickAnalytics (Ora). Reference implementation: the Ora engine behind oramodel.com.

License: this specification is published under Creative Commons Attribution 4.0 (CC BY 4.0). You may copy, implement, translate and extend it, commercially or not, as long as you credit it: “Ora Rules v1 by KlickAnalytics (oramodel.com), CC BY 4.0”. The license covers this text and the JSON at /spec/v1.json; it grants no rights to the Ora service, its models or its data.

The key words MUST, MUST NOT, SHOULD and MAY are used as in RFC 2119.

1. Data model

A rule is evaluated against one series of bars, oldest first. Each bar has five numbers: open O, high H, low L, close C and volume V, and MAY have a timestamp. The rule is about the latest bar (the evaluation bar) unless it looks back with an offset or a window. Daily bars SHOULD be split- and dividend-adjusted. An implementation MAY also expose named facts: single numbers known at the evaluation bar (section 8).

Every expression denotes a series aligned with the bars: C is the series of closes, sma(C, 50) the series of 50-bar averages. A value that cannot be computed at a bar (too little history, division by zero, a bar that does not exist) is not known at that bar.

2. Lexical rules

RuleSpecification
CaseKeywords, function names and price letters are case-insensitive: AND, Sma(C, 50) and c are valid.
NumbersPlain decimals: 70, 1.5, .97. No exponents, no thousands separators. A leading minus is the unary operator: willr(14) > -20.
Literalstrue is 1 and false is 0.
Equality= and == are the same operator. ! is the same as not.
WhitespaceIgnored between tokens: C>H[-1] and C > H[-1] are one rule.
OffsetsOnly on the five price letters: C[-1] is the previous bar. A positive offset looks forward and is not known at the latest bar. For any other expression use ref(x, -n).
NamesA name that is not a price letter, keyword or function is a fact. An unknown name MUST be rejected (section 10).

3. Grammar

rule     := or
or       := and ( "or" and )*
and      := not ( "and" not )*
not      := ( "not" | "!" ) not | compare
compare  := sum ( (">" | ">=" | "<" | "<=" | "==" | "=" | "!=") sum )*
sum      := product ( ("+" | "-") product )*
product  := power ( ("*" | "/" | "%") power )*
power    := unary ( "^" unary )*
unary    := ("+" | "-") unary | primary
primary  := "(" rule ")" | number | "true" | "false"
          | price [ "[" offset "]" ] | function "(" [ args ] ")" | fact
args     := rule ( "," rule )*
price    := "O" | "H" | "L" | "C" | "V"
offset   := [ "-" ] digits
number   := digits [ "." digits ] | "." digits
fact     := name                       (a name that is not a price, keyword or function)
name     := letter ( letter | digit | "_" )*

Function arguments are full expressions, so windows take any series: avg(H - L, 20). Optional trailing arguments take the defaults shown in each signature (zlema(x, n=20)).

4. Precedence

Level (tightest first)OperatorsExample
1( ), function calls, offsetssma(C, 50), H[-1]
2^(C / C[-1]) ^ 2
3* / %1.5 * avg(V, 20)
4+ -sma(C, 20) + 2 * atr(14)
5> >= < <= == = !=C > O
6not, !not C > sma(C, 50)
7anda and b
8ora or b

and binds tighter than or: a or b and c is a or (b and c). Implementations SHOULD warn when the two are mixed without parentheses.

5. Evaluation

  1. Arithmetic is on series. + - * / % ^ apply bar by bar. Division by zero is not known at that bar.
  2. A comparison inside arithmetic or a function argument is 1 or 0 at each bar, so sum(C > C[-1], 10) counts up bars. A comparison with a side that is not known is not known.
  3. Only the outermost comparisons become beliefs: those joined by and, or and not at the top of the rule (section 6).
  4. Windows such as sma(x, n), max(x, n) and sum(x, n) cover the last n bars including the current one. highest(x, n) and lowest(x, n) cover the n bars before it, so C > highest(C, 20) is a breakout.
  5. Events such as crossup(a, b) are true only on the bar where they happen.
  6. Patterns (hammer(), engulfing_bull(), …) are macros: each is defined as a rule in the core language (section 9 lists the expansions) and MUST evaluate exactly as its expansion.

6. Beliefs

A rule does not answer true or false. It answers how far past the line the latest bar is, in units of how much that gap normally moves for this series, as a belief in [0.05, 0.95].

  1. Margin. For a > b and a >= b, the margin is a − b at the latest bar; for < and <= it is b − a.
  2. Scale. The sample standard deviation (n − 1) of (a − b) over the last 60 bars, skipping bars where it is not known. A constant on one side does not change it, so rsi(C, 14) > 30 and > 70 share one yardstick. With fewer than 8 known values, or a deviation of zero, the scale is the fact's declared scale (section 8) if one side is a fact, otherwise 10% of |a| at the latest bar.
  3. Belief. With r = min(|margin| ÷ scale, 3): b = 0.5 + 0.45 × (1 − e−1.6 r) when the margin is positive (the rule holds), 1 − b when it is negative, and the tie value when it is exactly zero.
Margin in scalesBelief if trueBelief if false
00.55 (>=, <=)0.45 (>, <)
0.250.64840.3516
0.50.74780.2522
10.85910.1409
20.93170.0683
3 or more0.94630.0537
CaseBelief
Exactly on the line (a tie)0.55 for >= and <=; 0.45 for > and <
A side not known at the latest bar, or a fact not known0.50
A bare expression with no comparison (an event such as crossup(), between(), a literal)0.95 if non-zero at the latest bar, else 0.05; 0.50 if not known
a == b1.05 − b(|a − b|), where b(·) is the belief formula above: 0.55 when equal, down to 0.1037 at three scales or more
a != bb(|a − b|): 0.50 when equal, up to 0.9463
a and bthe lower of the two beliefs
a or bthe higher of the two
not a1 − the belief

A belief is not a probability. It describes how clearly a condition that is already decided holds. Implementations MUST NOT present rule beliefs as forecasts. Beliefs are rounded to 4 decimal places, so a comparison ranges from 0.0537 to 0.9463 and only a bare expression reaches 0.05 or 0.95.

7. Values and formulas

An implementation SHOULD offer a value evaluation that returns the number an expression has at the latest bar (sma(C, 50) → 612.4), and MAY accept formulas: named inputs written as "=expression" whose values are computed before a request is answered ("stop": "=lowest(L, 20)"). Inputs given as plain numbers MAY be read by name in rules, like facts (C > stop).

8. Facts

A fact is a named number known at the evaluation bar, point in time: it uses only what was known at that bar's close. A fact that is not known makes its comparison 0.50; "none on record" (a stock that never split) is the number 99999, so "split in the last year" is a clean no. Each fact declares its scale. Ora Rules v1 names 43 facts in three groups; an implementation that offers a group MUST use these names and meanings.

Earnings

FactUnitsScaleMeaning
days_to_earningsdays3Calendar 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.
days_since_earningsdays3Calendar days since the last report whose result is known (0 = reported before today's open).
next_report_bmoflag0.51 if the next report is scheduled before the open, 0 if after the close; not known if the time is not given.
next_report_amcflag0.51 if the next report is scheduled after the close, 0 if before the open; not known if the time is not given.
last_eps$/sharemeasuredEPS of the last known report.
last_eps_est$/sharemeasuredConsensus EPS estimate for that report (as stored; estimates may have been revised).
eps_surprise_pct%5(EPS − estimate) / |estimate| × 100 for the last report. Not known if there is no estimate or it is 0.
beat_eps_lastflag0.51 if the last EPS was above the estimate, else 0 (in line is not a beat).
miss_eps_lastflag0.51 if the last EPS was below the estimate, else 0.
last_revenue$measuredRevenue of the last known report.
revenue_surprise_pct%2(revenue − estimate) / |estimate| × 100 for the last report.
beat_revenue_lastflag0.51 if the last revenue was above the estimate, else 0.
miss_revenue_lastflag0.51 if the last revenue was below the estimate, else 0.
beat_streakquarters1Consecutive EPS beats ending with the last report (0 if it was not a beat). Not known when a missing estimate interrupts the run.
beats_last4count1EPS beats among the last 4 known reports (not known unless all 4 have an estimate).
misses_last4count1EPS misses among the last 4 known reports (not known unless all 4 have an estimate).
eps_year_ago$/sharemeasuredEPS of the report about a year before the last one (365 ± 45 days).
eps_growth_yoy%10(last EPS − EPS a year ago) / |EPS a year ago| × 100.
revenue_year_ago$measuredRevenue of the report about a year before the last one.
revenue_growth_yoy%5(last revenue − revenue a year ago) / |revenue a year ago| × 100.
last_earnings_move%2Close-to-close return of the reaction session of the last report. Not known if the report time is blank.
last_earnings_gap%2Open of the reaction session against the close before it. Not known if the report time is blank.
pre_earnings_closepricemeasuredClose of the session before the last report's reaction session (same price basis as C).

Dividends

FactUnitsScaleMeaning
pays_dividendflag0.51 if an ex-date fell in the last 400 days or one is declared, else 0.
div_ttm$/sharemeasuredSum of split-adjusted dividends with ex-dates in the last 365 days.
div_yield_ttm%0.5div_ttm / today's close × 100 (split-adjusted, not dividend-adjusted close).
div_count_ttmcount1Number of ex-dates in the last 365 days.
last_div_amount$/sharemeasuredSplit-adjusted amount of the last ex-date on or before today.
prev_div_amount$/sharemeasuredSplit-adjusted amount of the ex-date before that.
div_change_pct%5(last / previous payment − 1) × 100.
raised_lastflag0.51 if the last payment was more than 0.5% above the previous one, else 0.
cut_lastflag0.51 if the last payment was more than 0.5% below the previous one, else 0.
days_since_ex_divdays3Calendar days since the last ex-date (99999 if it never paid).
days_to_ex_divdays3Calendar days to the next DECLARED ex-date (99999 if none is declared).
next_div_amount$/sharemeasuredSplit-adjusted amount of the next declared dividend.
years_of_growthyears1Consecutive calendar years, ending last year, whose total dividend was more than 0.5% above the year before.

Splits

FactUnitsScaleMeaning
days_since_splitdays30Calendar days since the last share split (99999 if none on record).
last_split_ratioratio0.5Shares after / before for the last split: 10 = 10-for-1, 0.05 = 1-for-20 reverse.
reverse_split_lastflag0.51 if the last split was a reverse split, 0 if forward; not known if it never split.
days_since_reverse_splitdays30Calendar days since the last reverse split (99999 if none on record).
splits_5ycount1Number of splits in the last 5 years (1,825 days).
days_to_splitdays3Calendar days to the next announced split (99999 if none is announced).
next_split_ratioratio0.5Ratio of the next announced split.

9. Functions

322 functions in 18 groups: 63 core (required), 234 library (optional, each defined exactly) and 25 patterns (macros over the core). Arguments named x take any series; n is a whole number of bars. This list is generated from the reference engine's own catalog.

Windows

SignatureLevelDefinitionExample
sum(x, n)CoreRolling sum of the last n bars. Summing a comparison counts the bars where it held.sum(C > C[-1], 10) >= 6
avg(x, n)CoreAnother name for sma(). Same result.V > avg(V, 20)
mean(x, n)CoreA third name for sma().C > mean(C, 100)
sma(x, n)CoreSimple moving average of the last n bars, including the current one.C > sma(C, 50)
min(x, n)CoreLowest value of the last n bars, INCLUDING the current one.C < 1.02 * min(L, 20)
max(x, n)CoreHighest value of the last n bars, INCLUDING the current one. So C > max(C, n) can never be true.C >= 0.98 * max(H, 20)
highest(x, n)CoreHighest value of the n bars BEFORE the current one. This is the breakout form.C > highest(C, 20)
lowest(x, n)CoreLowest value of the n bars before the current one.C < lowest(C, 20)
std(x, n)CoreStandard deviation over the window (population form, like most charting tools).std(C, 10) < 0.5 * std(C, 50)
zscore(x, n)CoreHow many standard deviations the current value sits from its window mean. A flat window scores 0.zscore(C, 20) < -2
ema(x, n)CoreExponential moving average, smoothing 2 / (n + 1).C > ema(C, 21)
roc(x, n)CorePercent change over n bars: roc(C, 20) = 5 means up 5% in 20 sessions.roc(C, 20) > 5
wma(x, n)CoreLinearly weighted moving average: recent bars count more.wma(C, 10) > wma(C, 30)
rma(x, n)CoreWilder's smoothing, 1 / n: what RSI and ATR are built from.C > rma(C, 14)
linreg(x, n)CoreThe least-squares line through the window, read at the current bar: a smoothed level, not the slope.linreg(C, 20) > ref(linreg(C, 20), -5)
median(x, n)CoreMiddle value of the window. Less pulled by one wild day than an average.V > 2 * median(V, 20)
percentrank(x, n)CoreWhere the current value ranks among the n − 1 bars before it, 0 to 100.percentrank(V, 50) > 90
cum(x)CoreRunning total from the first bar loaded.cum(C > C[-1]) > cum(C < C[-1])
highestbars(x, n)CoreHow many bars ago the window's high was set, 0 to n − 1 (0 = this bar). The window includes the current bar.highestbars(H, 20) <= 3
lowestbars(x, n)CoreHow many bars ago the window's low was set (0 = this bar).lowestbars(L, 20) >= 15
rvol(n)CoreRelative volume: this bar's volume over the average of the n bars before it.rvol(20) > 2

Indicators

SignatureLevelDefinitionExample
rsi(x, n)CoreRelative strength index, 0 to 100 (Wilder). rsi(C, 14) is the usual one.rsi(C, 14) > 50
atr(n)CoreAverage true range over n bars, in price.H - L > 2 * atr(14)
atr_pct(n)CoreATR as a percent of the close: 3 means 3%. Comparable across symbols.atr_pct(14) > 3
macd(x [, fast, slow, signal])CoreMACD line: ema(fast) − ema(slow). Defaults 12, 26, 9.macd(C) > 0
macdsignal(x [, fast, slow, signal])CoreThe MACD signal line: an EMA of the MACD line.macd(C) > macdsignal(C)
macdhist(x [, fast, slow, signal])CoreMACD histogram: line minus signal.macdhist(C) > ref(macdhist(C), -1)
bbupper(x, n [, up, dn])CoreUpper Bollinger band: sma + up × std. Default 2.C > bbupper(C, 20, 2)
bbmid(x, n)CoreMiddle band: the simple average.C > bbmid(C, 20)
bblower(x, n [, up, dn])CoreLower Bollinger band: sma − dn × std (dn defaults to up).C < bblower(C, 20, 2)
adx(n)CoreTrend strength, 0 to 100, with no direction. Above 25 is usually called trending.adx(14) > 25
diplus(n)Core+DI: the upward directional movement ADX is built from.diplus(14) > diminus(14)
diminus(n)Core−DI: the downward directional movement.diminus(14) > 25
stochk(n [, d, slow])CoreStochastic %K, 0 to 100: where the close sits in the n-bar range, smoothed over slow (default 3).stochk(14) < 20
stochd(n [, d, slow])CoreStochastic %D: the d-bar average of %K (default 3).stochk(14) > stochd(14)
cci(n)CoreCommodity channel index: typical price against its average, in units of mean deviation.cci(20) > 100
willr(n)CoreWilliams %R, −100 to 0: 0 is the top of the n-bar range.willr(14) > -20
obv()CoreOn-balance volume: adds the day's volume on an up close, subtracts it on a down close.obv() > sma(obv(), 20)

Maths

SignatureLevelDefinitionExample
abs(x)CoreAbsolute value.abs(C / C[-1] - 1) > 0.02
sqrt(x)CoreSquare root (negative input is not a number).std(log(C / C[-1]), 20) * sqrt(252) > 0.4
log(x)CoreNatural logarithm (zero or negative input is not a number).log(C / C[-20]) > 0.05
sign(x)Core−1, 0 or 1.sum(sign(C - C[-1]), 10) > 2
iif(cond, a, b)Corea where the condition holds, b otherwise. Use it to switch a threshold by regime.rsi(C, 14) < iif(C > sma(C, 200), 40, 30)
ref(x, -n)CoreAny series n bars back: the offset for things that are not a price letter.sma(C, 50) > ref(sma(C, 50), -10)
between(x, a, b)Core1 when x is inside [a, b] (inclusive, either order), else 0.between(rsi(C, 14), 40, 60)
least(a, b)CoreThe smaller of two series, bar by bar.least(O, C) - L > 2 * abs(C - O)
greatest(a, b)CoreThe larger of two series, bar by bar.H - greatest(O, C) > 2 * abs(C - O)
nz(x, default=0)Libraryx, with 'not known yet' (NaN) replaced by the default.nz(ref(C, -300), C) > 0
na(x)Library1 where x is not known yet (still warming up), 0 otherwise.na(sma(C, 2000))
clamp(x, lo, hi)Libraryx limited to the range lo..hi.clamp(rsi(C, 14), 30, 70) == 70
round(x, digits=0)Libraryx rounded to `digits` decimals (half away from zero).round(rsi(C, 14)) >= 50
floor(x)LibraryThe largest whole number at or below x.floor(C) == floor(C[-1])
ceil(x)LibraryThe smallest whole number at or above x.ceil(C) > ceil(C[-1])
exp(x)Librarye to the power x.exp(log(C / C[-20])) > 1.1
pow(x, y)Libraryx to the power y (the same as x ^ y).pow(C / C[-252], 0.5) > 1.1
log10(x)LibraryBase-10 logarithm. NaN for x <= 0.log10(V) > 7

Prices

SignatureLevelDefinitionExample
hl2()CoreThe middle of the bar: (H + L) / 2.C > hl2()
hlc3()CoreThe typical price: (H + L + C) / 3.hlc3() > sma(hlc3(), 20)
ohlc4()CoreThe average of the four prices: (O + H + L + C) / 4.ohlc4() > sma(C, 10)
tr()CoreTrue range: the bar's high minus low, extended to include a gap from the previous close.tr() > 1.5 * ref(atr(14), -1)
gap()CorePercent from the previous close to this bar's open.gap() > 2

Recursion

SignatureLevelDefinitionExample
rec(seed, step)CoreA 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.C > rec(C, self + 0.1 * (C - self))

Signals

SignatureLevelDefinitionExample
crossup(a, b)Core1 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.crossup(C, sma(C, 50))
crossdown(a, b)Core1 on the bar where a crosses below b.crossdown(sma(C, 20), sma(C, 50))
barssince(cond)CoreBars since a condition was last true (0 = true on this bar). Not known until it has been true once.barssince(C > highest(C, 20)) > 30

Intraday

SignatureLevelDefinitionExample
vwap()CoreVolume-weighted average price of the session so far (typical price), reset every session.
Requires intraday bars: send state.bars with an interval such as "5m".
C > vwap()
opening_range_high([m])CoreHighest price of the session's first m minutes (default 30).
Requires intraday bars: send state.bars with an interval such as "5m".
C > opening_range_high(30)
opening_range_low([m])CoreLowest price of the session's first m minutes.
Requires intraday bars: send state.bars with an interval such as "5m".
C < opening_range_low(30)
minutes_since_open()CoreMinutes since the session's first bar.
Requires intraday bars: send state.bars with an interval such as "5m".
minutes_since_open() >= 30
time_of_day()CoreThe bar's clock time as HHMM (1030 = 10:30), in the timestamps' own time zone.
Requires intraday bars: send state.bars with an interval such as "5m".
time_of_day() >= 1000

Price structure

SignatureLevelDefinitionExample
swinghigh(n [, k])CoreThe 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.C > swinghigh(5)
swinglow(n [, k])CoreThe k-th most recent confirmed swing low (default k = 1).swinglow(3) > swinglow(3, 2)
pp()LibraryClassic floor pivot from the previous bar: (H + L + C) / 3.C > pp()
r1()LibraryClassic resistance 1: 2 * pivot - previous low.H > r1()
r2()LibraryClassic resistance 2: pivot + previous range.H > r2()
r3()LibraryClassic resistance 3: previous high + 2 * (pivot - previous low).H > r3()
s1()LibraryClassic support 1: 2 * pivot - previous high.L < s1()
s2()LibraryClassic support 2: pivot - previous range.L < s2()
s3()LibraryClassic support 3: previous low - 2 * (previous high - pivot).L < s3()
fib_r1()LibraryFibonacci pivot resistance 1: pivot + 0.382 * previous range.H > fib_r1()
fib_r2()LibraryFibonacci resistance 2: pivot + 0.618 * range.H > fib_r2()
fib_r3()LibraryFibonacci resistance 3: pivot + 1.0 * range.H > fib_r3()
fib_s1()LibraryFibonacci support 1: pivot - 0.382 * range.L < fib_s1()
fib_s2()LibraryFibonacci support 2: pivot - 0.618 * range.L < fib_s2()
fib_s3()LibraryFibonacci support 3: pivot - 1.0 * range.L < fib_s3()
cam_r1()LibraryCamarilla R1: previous close + 1.1/12 * range.C > cam_r1()
cam_r2()LibraryCamarilla R2: previous close + 1.1/6 * range.C > cam_r2()
cam_r3()LibraryCamarilla R3: previous close + 1.1/4 * range. A fade level.H > cam_r3() and C < cam_r3()
cam_r4()LibraryCamarilla R4: previous close + 1.1/2 * range. A breakout level.C > cam_r4()
cam_s1()LibraryCamarilla S1: previous close - 1.1/12 * range.C < cam_s1()
cam_s2()LibraryCamarilla S2: previous close - 1.1/6 * range.C < cam_s2()
cam_s3()LibraryCamarilla S3: previous close - 1.1/4 * range. A fade level.L < cam_s3() and C > cam_s3()
cam_s4()LibraryCamarilla S4: previous close - 1.1/2 * range. A breakdown level.C < cam_s4()
gap_filled()Library1 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]).gap_filled()
insidebars(n=5)LibraryHow many of the last n bars were inside bars (range inside the bar before).insidebars(5) >= 2
outsidebars(n=5)LibraryHow many of the last n bars were outside bars.outsidebars(10) >= 3
nr(n=7)Library1 if today's range (H - L) is the narrowest of the last n bars (NR7 = nr(7)). Ties count.nr(7)
wr(n=7)Library1 if today's range is the widest of the last n bars (WR7 = wr(7)). Ties count.wr(7)
hhv(x, n=20)LibraryHighest value of the last n bars, including today (MetaStock's name for max()).C >= 0.97 * hhv(H, 52)
llv(x, n=20)LibraryLowest value of the last n bars, including today (MetaStock's name for min()).C <= 1.03 * llv(L, 52)
body()LibrarySize of the candle body: |C - O|.body() > 2 * sma(body(), 20)
upperwick()LibraryUpper shadow: H - max(O, C).upperwick() > 2 * body()
lowerwick()LibraryLower shadow: min(O, C) - L.lowerwick() > 2 * body()
barrange()LibraryThe bar's range: H - L.barrange() > 1.5 * atr(14)
bodypct()LibraryBody as a percent of the bar's range (0 on a bar with no range).bodypct() > 80
clv()LibraryClose location value, -1 to 1: ((C - L) - (H - C)) / (H - L). 1 = closed on the high.clv() > 0.8
closepos()LibraryWhere the close sits in the bar's range, 0-100 (100 = on the high).closepos() > 90
pctfromhigh(n=252)LibraryPercent the close sits below the highest high of the last n bars (0 = at the high).pctfromhigh(252) < 3
pctfromlow(n=252)LibraryPercent the close sits above the lowest low of the last n bars.pctfromlow(252) < 5
rangepos(n=20)LibraryWhere the close sits in the n-bar high-low range, 0-100.rangepos(20) > 80
rangewidth(n=20)LibraryWidth of the n-bar high-low range in percent: (max(H, n) / min(L, n) - 1) * 100.rangewidth(20) < 10

Moving averages

SignatureLevelDefinitionExample
dema(x, n=20)LibraryDouble exponential moving average: 2*ema - ema(ema). Less lag than an ema of the same length.C > dema(C, 20)
tema(x, n=20)LibraryTriple exponential moving average: 3*e1 - 3*e2 + e3 over three nested emas.C > tema(C, 20)
trima(x, n=20)LibraryTriangular moving average: an sma of an sma, weighting the middle of the window most.C > trima(C, 20)
kama(x, n=10, fast=2, slow=30)LibraryKaufman adaptive moving average: fast in a clean trend, nearly flat in chop (efficiency-ratio driven).C > kama(C, 10)
hma(x, n=20)LibraryHull moving average: wma(2*wma(x, n/2) - wma(x, n), sqrt(n)). Very little lag.hma(C, 20) > ref(hma(C, 20), -1)
vwma(x, n=20)LibraryVolume-weighted moving average: sum(x*V, n) / sum(V, n).C > vwma(C, 20)
alma(x, n=9, offset=0.85, sigma=6)LibraryArnaud Legoux moving average: a Gaussian-weighted window centred near the newest bar (offset 0.85).C > alma(C, 20)
zlema(x, n=20)LibraryZero-lag ema: an ema of x + (x - x[-(n-1)/2]), which cancels most of the lag.C > zlema(C, 20)
t3(x, n=5, vfactor=0.7)LibraryTillson T3: six chained emas blended by the volume factor. Smooth and responsive.C > t3(C, 10)
smma(x, n=14)LibrarySmoothed moving average (Wilder's, alpha 1/n). The same as rma().C > smma(C, 20)
swma(x)LibrarySymmetric 4-bar weighted average: (x[-3] + 2*x[-2] + 2*x[-1] + x) / 6.C > swma(C)
vidya(x, n=20, cmo=9)LibraryVariable index dynamic average: an ema whose speed scales with |CMO(cmo)|/100, so it hugs price in strong moves.C > vidya(C, 20)
mcginley(x, n=14)LibraryMcGinley dynamic: an average that speeds up when price runs away from it.C > mcginley(C, 14)
midpoint(x, n=14)LibraryMiddle of the window's range: (max(x, n) + min(x, n)) / 2.C > midpoint(C, 20)
midprice(n=14)LibraryMiddle of the n-bar high-low range: (max(H, n) + min(L, n)) / 2.C > midprice(20)

Oscillators

SignatureLevelDefinitionExample
stochrsi(x, n=14, m=14, k=3)LibraryStochastic RSI %K, 0-100: where rsi(x, n) sits in its own m-bar range, smoothed over k bars.stochrsi(C) < 20
stochrsid(x, n=14, m=14, k=3, d=3)LibraryStochastic RSI %D: the d-bar average of stochrsi().crossup(stochrsi(C), stochrsid(C))
mfi(n=14)LibraryMoney flow index, 0-100: a volume-weighted RSI of the typical price.mfi(14) < 20
cmo(x, n=14)LibraryChande momentum oscillator, -100 to 100: (sum of gains - sum of losses) / their total, over plain n-bar sums (Chande's definition).cmo(C, 14) > 50
tsi(x, long=25, short=13)LibraryTrue strength index, -100 to 100: double-smoothed momentum over double-smoothed absolute momentum.tsi(C) > 0
uo(fast=7, mid=14, slow=28)LibraryUltimate oscillator, 0-100: buying pressure over three windows, weighted 4:2:1.uo() < 30
dpo(x, n=20)LibraryDetrended price oscillator: x minus the sma(x, n) of n/2+1 bars ago (the non-centred form, so it never looks ahead).dpo(C, 20) > 0
ppo(x, fast=12, slow=26)LibraryPercentage price oscillator: (ema fast - ema slow) / ema slow * 100. A MACD you can compare across symbols.ppo(C) > 0
pposignal(x, fast=12, slow=26, signal=9)LibrarySignal line of ppo(): its ema over `signal` bars.crossup(ppo(C), pposignal(C))
ppohist(x, fast=12, slow=26, signal=9)Libraryppo() minus its signal line.ppohist(C) > 0
apo(x, fast=12, slow=26)LibraryAbsolute price oscillator: ema(x, fast) - ema(x, slow), in price.apo(C) > 0
kst(x)LibraryKnow Sure Thing (Pring): weighted sum of four smoothed rates of change (10/15/20/30).kst(C) > kstsignal(C)
kstsignal(x)LibrarySignal line of kst(): its 9-bar sma.crossup(kst(C), kstsignal(C))
trix(x, n=15)LibraryTRIX: 1-bar percent change of a triple-smoothed ema.trix(C, 15) > 0
ao()LibraryAwesome oscillator (Bill Williams): sma(hl2, 5) - sma(hl2, 34).ao() > 0
ac()LibraryAccelerator oscillator: ao() minus its 5-bar sma.ac() > 0
mom(x, n=10)LibraryMomentum: x - x n bars ago, in the units of x.mom(C, 10) > 0
rocr(x, n=10)LibraryRate of change as a ratio: x / x n bars ago (1.05 = up 5%).rocr(C, 20) > 1.05
bop()LibraryBalance of power, -1 to 1: (C - O) / (H - L). 0 on a bar with no range.sma(bop(), 14) > 0
aroonup(n=25)LibraryAroon up, 0-100: 100 when the n-bar high is today, falling as it ages.aroonup(25) > 70
aroondown(n=25)LibraryAroon down, 0-100: 100 when the n-bar low is today.aroondown(25) > 70
aroonosc(n=25)LibraryAroon oscillator, -100 to 100: aroonup - aroondown.aroonosc(25) > 50
fisher(n=9)LibraryEhlers Fisher transform of the bar's position in its n-bar range: sharp turning points.fisher(9) > 1.5
rvgi(n=10)LibraryRelative vigor index: smoothed (C - O) over smoothed (H - L), summed over n bars.rvgi(10) > rvgisignal(10)
rvgisignal(n=10)LibrarySignal line of rvgi(): its symmetric 4-bar average.crossup(rvgi(10), rvgisignal(10))
coppock(x, wma=10, long=14, short=11)LibraryCoppock curve: wma of the sum of two rates of change. A turn up from below zero is the classic long-term buy signal.coppock(C) > ref(coppock(C), -1)
crsi(x, rsi=3, streak=2, rank=100)LibraryConnors 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.crsi(C) < 10
smi(n=10, s1=3, s2=3)LibraryStochastic momentum index, -100 to 100: the close against the middle of its n-bar range, double-smoothed.smi(10) < -40
cfo(x, n=14)LibraryChande forecast oscillator: percent gap between x and its n-bar linear-regression value.cfo(C, 14) > 0
qstick(n=10)LibraryQstick: the n-bar average of (C - O). Positive = mostly green bodies.qstick(10) > 0
bullpower(n=13)LibraryElder bull power: H - ema(C, n).bullpower(13) > 0
bearpower(n=13)LibraryElder bear power: L - ema(C, n).bearpower(13) < 0
imi(n=14)LibraryIntraday momentum index, 0-100: an RSI built from bar bodies (C - O) instead of closes.imi(14) > 70
adxr(n=14)LibraryADX rating: (adx + adx n-1 bars ago) / 2. A slower trend-strength reading.adxr(14) > 25
dx(n=14)LibraryDirectional movement index: 100 * |DI+ - DI-| / (DI+ + DI-), the unsmoothed ADX.dx(14) > 30
plusdm(n=14)LibraryPlus directional movement, Wilder-smoothed (a running sum).plusdm(14) > minusdm(14)
minusdm(n=14)LibraryMinus directional movement, Wilder-smoothed.minusdm(14) > plusdm(14)
wavetrend(channel=10, avg=21)LibraryWaveTrend (LazyBear) WT1: a CCI-like channel index of hlc3, ema-smoothed. Below -60 is oversold.wavetrend() < -60
wavetrendsignal(channel=10, avg=21)LibraryWaveTrend WT2: the 4-bar sma of wavetrend().crossup(wavetrend(), wavetrendsignal())

Trend

SignatureLevelDefinitionExample
supertrend(n=10, mult=3)LibrarySupertrend line: an ATR band that trails below price in an uptrend and above it in a downtrend.C > supertrend(10, 3)
supertrenddir(n=10, mult=3)LibrarySupertrend direction: 1 in an uptrend, -1 in a downtrend.supertrenddir(10, 3) > 0
psar(step=0.02, max=0.2)LibraryParabolic SAR (Wilder): a stop that accelerates toward price. Price above it = long side.C > psar()
tenkan(n=9)LibraryIchimoku conversion line (tenkan-sen): the middle of the n-bar high-low range.tenkan() > kijun()
kijun(n=26)LibraryIchimoku base line (kijun-sen): the middle of the 26-bar range.C > kijun()
senkou_a(tenkan=9, kijun=26, shift=26)LibraryIchimoku leading span A as it stands under TODAY's bar: (tenkan + kijun) / 2 computed `shift` bars ago. No look-ahead.C > senkou_a()
senkou_b(n=52, shift=26)LibraryIchimoku leading span B under today's bar: the 52-bar midprice from `shift` bars ago.C > senkou_b()
cloud_top()LibraryTop of the Ichimoku cloud under today's bar: greatest(senkou_a(), senkou_b()).C > cloud_top()
cloud_bottom()LibraryBottom of the Ichimoku cloud under today's bar.C < cloud_bottom()
viplus(n=14)LibraryVortex indicator VI+: upward vortex movement over true range, n bars.viplus(14) > viminus(14)
viminus(n=14)LibraryVortex indicator VI-.viminus(14) > viplus(14)
linreg_slope(x, n=14)LibrarySlope of the n-bar least-squares line, in x units per bar.linreg_slope(C, 20) > 0
linreg_intercept(x, n=14)LibraryValue of the n-bar regression line at the window's FIRST bar.C > linreg_intercept(C, 20)
linreg_angle(x, n=14)LibraryAngle of the regression line in degrees: atan(slope). Scale-dependent, so compare it on the same symbol.linreg_angle(C, 20) > 0
linreg_r2(x, n=20)LibraryR-squared of the n-bar regression line, 0-1: how straight the trend is.linreg_r2(C, 50) > 0.8 and linreg_slope(C, 50) > 0
tsf(x, n=14)LibraryTime series forecast: the regression line extended one bar ahead.C > tsf(C, 14)
chop(n=14)LibraryChoppiness index, 0-100: high (over 61.8) in a sideways market, low (under 38.2) in a trend.chop(14) > 61.8
er(x, n=10)LibraryKaufman efficiency ratio, 0-1: net move over the sum of bar-to-bar moves. 1 = a straight line.er(C, 10) > 0.5
massindex(n=9, sum=25)LibraryMass index: range expansion. A 'reversal bulge' rises above 27 then drops below 26.5.massindex() > 27
vhf(x, n=28)LibraryVertical horizontal filter: n-bar range over the sum of bar moves. High = trending.vhf(C, 28) > 0.35

Volatility

SignatureLevelDefinitionExample
natr(n=14)LibraryNormalized ATR: atr(n) / C * 100. The same as atr_pct().natr(14) > 3
kcupper(n=20, mult=2, atr=10)LibraryUpper Keltner channel: ema(C, n) + mult * atr(atr).C > kcupper(20, 2)
kcmid(n=20)LibraryMiddle Keltner line: ema(C, n).C > kcmid(20)
kclower(n=20, mult=2, atr=10)LibraryLower Keltner channel: ema(C, n) - mult * atr(atr).C < kclower(20, 2)
dcupper(n=20)LibraryUpper Donchian channel: highest high of the last n bars, including today.H >= dcupper(20)
dclower(n=20)LibraryLower Donchian channel: lowest low of the last n bars, including today.L <= dclower(20)
dcmid(n=20)LibraryMiddle of the Donchian channel.C > dcmid(20)
dcwidth(n=20)LibraryDonchian width as a percent of its middle: how wide the n-bar range is.dcwidth(20) < 8
bbwidth(x, n=20, k=2)LibraryBollinger band width as a percent of the middle band: (upper - lower) / mid * 100.bbwidth(C, 20) < percentile(bbwidth(C, 20), 252, 10)
bbpct(x, n=20, k=2)LibraryBollinger %B: where x sits between the bands, 0 = lower band, 1 = upper band.bbpct(C, 20) > 1
chaikinvol(n=10, roc=10)LibraryChaikin volatility: percent change over `roc` bars of the ema of the high-low range.chaikinvol(10, 10) > 20
ulcer(x, n=14)LibraryUlcer index: root-mean-square of the percent drawdown from the n-bar high. Downside volatility only.ulcer(C, 14) < 3
hv(x, n=20)LibraryHistorical volatility: annualised sample standard deviation of n daily log returns, in percent.hv(C, 20) > hv(C, 100)
parkinson(n=20)LibraryParkinson volatility from the high-low range, annualised percent.parkinson(20) > 30
garmanklass(n=20)LibraryGarman-Klass volatility from open, high, low and close, annualised percent.garmanklass(20) > 30
rogerssatchell(n=20)LibraryRogers-Satchell volatility (drift-independent), annualised percent.rogerssatchell(20) > 30
yangzhang(n=20)LibraryYang-Zhang volatility: overnight, open-to-close and Rogers-Satchell parts combined, annualised percent. The most efficient OHLC estimator.yangzhang(20) > 30
range_pct()LibraryToday's high-low range as a percent of the close.range_pct() > 2 * adr(20)
adr(n=20)LibraryAverage daily range percent: the n-bar average of (H / L - 1) * 100.adr(20) > 4
squeeze(n=20, bb=2, kc=1.5)LibraryTTM-style squeeze: 1 while the Bollinger bands (k=bb) sit inside the Keltner channel (sma mid, kc * atr(n)).squeeze(20)
chandelier_long(n=22, mult=3)LibraryChandelier exit for longs: max(H, n) - mult * atr(n).C < chandelier_long(22, 3)
chandelier_short(n=22, mult=3)LibraryChandelier exit for shorts: min(L, n) + mult * atr(n).C > chandelier_short(22, 3)

Volume

SignatureLevelDefinitionExample
ad()LibraryAccumulation/distribution line: running total of volume weighted by where each bar closed in its range.ad() > sma(ad(), 20)
adosc(fast=3, slow=10)LibraryChaikin oscillator: ema(ad, fast) - ema(ad, slow).adosc() > 0
cmf(n=20)LibraryChaikin money flow, -1 to 1: sum of money-flow volume over sum of volume, n bars.cmf(20) > 0.1
force(n=13)LibraryElder force index: ema of (C - C[-1]) * V.force(13) > 0
eom(n=14)LibraryEase of movement: n-bar average of 10000 * (hl2 change) * (H - L) / V. Positive = price rises on light volume.eom(14) > 0
pvt()LibraryPrice volume trend: running total of V times each bar's fractional change.pvt() > sma(pvt(), 50)
nvi()LibraryNegative volume index (starts at 1000): moves only on days volume falls. Above its 255-day ema is the classic bull signal.nvi() > ema(nvi(), 255)
pvi()LibraryPositive volume index (starts at 1000): moves only on days volume rises.pvi() > ema(pvi(), 255)
rvwap(n=20)LibraryRolling VWAP over the last n bars, from the typical price. Works on daily bars.C > rvwap(20)
avwap(c)LibraryAnchored 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.C > avwap(crossup(C, sma(C, 50)))
wvwap()LibraryVWAP anchored to the first bar of the week.
Requires dated bars.
C > wvwap()
mvwap()LibraryVWAP anchored to the first bar of the month.
Requires dated bars.
C > mvwap()
qvwap()LibraryVWAP anchored to the first bar of the quarter.
Requires dated bars.
C > qvwap()
yvwap()LibraryVWAP anchored to the first bar of the year.
Requires dated bars.
C > yvwap()
volosc(fast=5, slow=10)LibraryVolume oscillator: percent gap between a fast and a slow ema of volume.volosc(5, 20) > 25
kvo(fast=34, slow=55)LibraryKlinger volume oscillator (simple form): ema(sv, fast) - ema(sv, slow), where sv is the volume signed by the direction of hlc3.kvo() > ema(kvo(), 13)
dollarvol(n=1)LibraryAverage dollar volume (C * V) over n bars.dollarvol(20) > 50000000
updownvol(n=20)LibraryUp/down volume ratio: volume on up closes over volume on down closes, n bars.updownvol(50) > 1.5
wad()LibraryWilliams accumulation/distribution: running total of the close's move against the true range edge (no volume).wad() > sma(wad(), 20)

Statistics

SignatureLevelDefinitionExample
variance(x, n=20)LibraryPopulation variance over the window (the square of std()).variance(roc(C, 1), 20) > 4
var(x, n=20)LibraryAnother name for variance().var(roc(C, 1), 20) > 4
skew(x, n=20)LibrarySample skewness of the window (bias-corrected). Negative = a long left tail.skew(roc(C, 1), 60) < -0.5
kurt(x, n=20)LibrarySample excess kurtosis of the window (bias-corrected). Above 0 = fatter tails than normal.kurt(roc(C, 1), 60) > 3
corr(x, y, n=20)LibraryPearson correlation of two series over n bars, -1 to 1.corr(C, V, 20) < 0
covar(x, y, n=20)LibrarySample covariance of two series over n bars.covar(roc(C, 1), roc(V, 1), 20) > 0
percentile(x, n=252, p=50)LibraryThe p-th percentile (0-100) of the last n values, linearly interpolated.atr_pct(14) < percentile(atr_pct(14), 252, 10)
mad(x, n=20)LibraryMean absolute deviation from the window mean.abs(C - sma(C, 20)) > 2 * mad(C, 20)
drawdown(x, n=0)LibraryPercent 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.drawdown(C) > 20
maxdrawdown(x, n=252)LibraryLargest peak-to-trough fall inside the last n bars, in percent (a positive number).maxdrawdown(C, 252) < 15
runup(x, n=252)LibraryLargest trough-to-peak rise inside the last n bars, in percent.runup(C, 20) > 25
barssincehigh(n=252)LibraryBars since the highest high of the last n bars (0 = today). Ties count the most recent.barssincehigh(252) <= 5
barssincelow(n=252)LibraryBars since the lowest low of the last n bars (0 = today).barssincelow(252) > 100
streak(c)LibraryHow many bars in a row the condition has held, ending today (0 = not today).streak(C > sma(C, 20)) >= 10
upstreak()LibraryConsecutive higher closes ending today.upstreak() >= 4
downstreak()LibraryConsecutive lower closes ending today.downstreak() >= 4
count(c, n=20)LibraryHow many of the last n bars the condition held.count(C > O, 10) >= 7
freq(c, n=20)LibraryPercent of the last n bars on which the condition held.freq(C > C[-1], 20) > 60
any(c, n=20)Library1 if the condition held on at least one of the last n bars.any(crossup(C, sma(C, 50)), 5)
all(c, n=20)Library1 if the condition held on every one of the last n bars.all(C > sma(C, 50), 20)
valuewhen(c, x, nth=0)LibraryThe value of x on the nth most recent bar where the condition held (nth=0: the latest, today included).C > valuewhen(crossup(C, sma(C, 50)), C)
cross(x, y)Library1 on the bar where x crosses y in either direction.cross(C, sma(C, 50))
crossover(x, y)Library1 on the bar where x crosses ABOVE y: the same as crossup().crossover(C, sma(C, 50))
crossunder(x, y)Library1 on the bar where x crosses BELOW y: the same as crossdown().crossunder(C, sma(C, 50))
rising(x, n=3)Library1 if x rose on each of the last n bars (n higher values in a row).rising(sma(C, 20), 5)
falling(x, n=3)Library1 if x fell on each of the last n bars.falling(sma(C, 20), 5)
change(x, n=1)Libraryx minus its value n bars ago.change(C, 1) > 0
normalize(x, n=20)LibraryWhere x sits in its own n-bar range, 0-100 (a stochastic of any series).normalize(rsi(C, 14), 100) > 90
cummax(x)LibraryRunning maximum of x over all bars loaded.C >= cummax(C)
cummin(x)LibraryRunning minimum of x over all bars loaded.L <= cummin(L)
autocorr(x, n=20, lag=1)LibraryCorrelation of x with itself `lag` bars earlier, over n bars. Positive on returns = momentum, negative = mean reversion.autocorr(roc(C, 1), 60) < -0.2

Returns and risk

SignatureLevelDefinitionExample
ret(n=1)LibrarySimple return of the close over n bars, in percent.ret(20) > 10
logret(n=1)LibraryLog return of the close over n bars, in percent (100 * ln(C / C[-n])).logret(5) < -5
cagr(n=252)LibraryCompound annual growth rate of the close over the last n bars, percent (252 bars = a year).cagr(756) > 15
sharpe(n=252)LibraryAnnualised Sharpe ratio of daily returns over n bars (risk-free rate 0).sharpe(252) > 1
sortino(n=252)LibraryAnnualised Sortino ratio: mean daily return over downside deviation (target 0).sortino(252) > 1.5
calmar(n=252)LibraryCalmar ratio: cagr(n) / maxdrawdown(C, n).calmar(756) > 1
hvar(n=252, p=95)LibraryHistorical value at risk: the daily loss, in percent, exceeded on only (100 - p)% of the last n days (a positive number).hvar(252, 95) > 3
cvar(n=252, p=95)LibraryConditional VaR (expected shortfall): the average daily loss, in percent, on the days at or beyond hvar(n, p).cvar(252, 95) > 4
winrate(n=20)LibraryPercent of the last n bars that closed higher than the bar before.winrate(20) > 60
omega(n=252)LibraryOmega ratio (threshold 0): total daily gains over total daily losses, n bars.omega(252) > 1.2
gainloss(n=20)LibraryAverage gain on up days over average loss on down days, n bars.gainloss(50) > 1.2

Calendar

SignatureLevelDefinitionExample
dayofweek()LibraryDay of the week: 1 = Monday ... 5 = Friday.
Requires dated bars.
dayofweek() == 1
dayofmonth()LibraryCalendar day of the month, 1-31.
Requires dated bars.
dayofmonth() <= 5
month()LibraryMonth, 1-12.
Requires dated bars.
month() == 12
quarter()LibraryQuarter, 1-4.
Requires dated bars.
quarter() == 4
year()LibraryCalendar year.
Requires dated bars.
year() >= 2026
dayofyear()LibraryDay of the year, 1-366.
Requires dated bars.
dayofyear() > 350
weekofyear()LibraryISO week number, 1-53.
Requires dated bars.
weekofyear() >= 52
days_to_month_end()LibraryWeekdays left in the month after today (0 on the last weekday of the month). Exchange holidays are not counted out.
Requires dated bars.
days_to_month_end() <= 2
is_month_end()Library1 on the last weekday of the month.
Requires dated bars.
is_month_end()
is_month_start()Library1 on the first bar of a new month.
Requires dated bars.
is_month_start()
is_week_start()Library1 on the first bar of a new week.
Requires dated bars.
is_week_start()
is_week_end()Library1 on the last weekday of the week (Friday).
Requires dated bars.
is_week_end()
is_quarter_end()Library1 on the last weekday of the quarter.
Requires dated bars.
is_quarter_end()
is_quarter_start()Library1 on the first bar of a new quarter.
Requires dated bars.
is_quarter_start()
is_year_end()Library1 on the last weekday of the year.
Requires dated bars.
is_year_end()
is_year_start()Library1 on the first bar of a new year.
Requires dated bars.
is_year_start()
tdom()LibraryTrading day of the month: 1 on the month's first bar, 2 on the second ... (not known for the first, partial month loaded).
Requires dated bars.
tdom() <= 3
is_opex()Library1 on the monthly options expiration day: the third Friday of the month.
Requires dated bars.
is_opex()
days_to_opex()LibraryWeekdays from today to the next monthly options expiration (0 on the day itself).
Requires dated bars.
days_to_opex() <= 3

Relative strength

SignatureLevelDefinitionExample
close_of(symbol)LibraryAnother 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.
Requires dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars.
C / close_of('SPY') > ref(C / close_of('SPY'), -20)
open_of(symbol)LibraryAnother 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.
Requires dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars.
C > O and open_of('SPY') > 0
high_of(symbol)LibraryAnother 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.
Requires dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars.
C > O and high_of('SPY') > 0
low_of(symbol)LibraryAnother 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.
Requires dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars.
C > O and low_of('SPY') > 0
volume_of(symbol)LibraryAnother 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.
Requires dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars.
volume_of('SPY') > 1.5 * sma(volume_of('SPY'), 20)
rs(n=63, symbol='SPY')LibraryRelative strength: percent the symbol out- or under-performed the benchmark over n bars, ((C / C[-n]) / (B / B[-n]) - 1) * 100.
Requires dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars.
rs(63) > 0
rsline(symbol='SPY')LibraryRelative-strength line: C / the benchmark's close. Compare it with its own past.
Requires dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars.
rsline() > max(ref(rsline(), -1), 252)
beta(n=60, symbol='SPY')LibraryBeta of daily returns against the benchmark over n bars: cov(r, rb) / var(rb).
Requires dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars.
beta(60) > 1.5
correl(n=60, symbol='SPY')LibraryCorrelation of daily returns with the benchmark over n bars, -1 to 1.
Requires dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars.
correl(60) < 0.3
alpha(n=252, symbol='SPY')LibraryAnnualised alpha against the benchmark over n bars, percent: (mean r - beta * mean rb) * 252 * 100.
Requires dated daily bars (the symbol's, or state.bars with dates); reads another symbol's daily bars.
alpha(252) > 5

Patterns

SignatureLevelDefinitionExample
doji()PatternBody at most 10% of the day's range.
Expands to(H > L) and (abs(C - O) <= 0.1 * (H - L))
doji()
dragonfly_doji()PatternDoji with a long lower wick and almost no upper wick.
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))
dragonfly_doji()
gravestone_doji()PatternDoji with a long upper wick and almost no lower wick.
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))
gravestone_doji()
spinning_top()PatternSmall body (10-30% of range) with wicks on both sides.
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))
spinning_top()
marubozu()PatternBody at least 90% of the range: almost no wicks.
Expands to(H > L) and (abs(C - O) >= 0.9 * (H - L))
marubozu()
hammer()PatternLong lower wick (2x the body), tiny upper wick, after a 5-bar decline.
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])
hammer()
hanging_man()PatternHammer shape after a 5-bar rise.
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])
hanging_man()
shooting_star()PatternLong upper wick (2x the body), tiny lower wick, after a 5-bar rise.
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])
shooting_star()
inverted_hammer()PatternShooting-star shape after a 5-bar decline.
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])
inverted_hammer()
engulfing_bull()PatternGreen body that engulfs yesterday's red body.
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]))
engulfing_bull()
engulfing_bear()PatternRed body that engulfs yesterday's green body.
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]))
engulfing_bear()
harami_bull()PatternSmall green body inside yesterday's red body.
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])
harami_bull()
harami_bear()PatternSmall red body inside yesterday's green body.
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])
harami_bear()
piercing_line()PatternOpens below yesterday's close, closes above the middle of its red body.
Expands to(C[-1] < O[-1]) and (C > O) and (O < C[-1]) and (C > (O[-1] + C[-1]) / 2) and (C < O[-1])
piercing_line()
dark_cloud()PatternOpens above yesterday's close, closes below the middle of its green body.
Expands to(C[-1] > O[-1]) and (C < O) and (O > C[-1]) and (C < (O[-1] + C[-1]) / 2) and (C > O[-1])
dark_cloud()
morning_star()PatternRed bar, small bar below it, then a green bar closing above the middle of the first.
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)
morning_star()
evening_star()PatternGreen bar, small bar above it, then a red bar closing below the middle of the first.
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)
evening_star()
three_white_soldiers()PatternThree green bars, each closing higher and opening inside the previous body.
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])
three_white_soldiers()
three_black_crows()PatternThree red bars, each closing lower and opening inside the previous body.
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])
three_black_crows()
tweezer_bottom()PatternRed then green with lows within 0.1%.
Expands to(C[-1] < O[-1]) and (C > O) and (abs(L - L[-1]) <= 0.001 * C[-1])
tweezer_bottom()
tweezer_top()PatternGreen then red with highs within 0.1%.
Expands to(C[-1] > O[-1]) and (C < O) and (abs(H - H[-1]) <= 0.001 * C[-1])
tweezer_top()
inside_bar()PatternToday's range inside yesterday's.
Expands to(H <= H[-1]) and (L >= L[-1])
inside_bar()
outside_bar()PatternToday's range beyond yesterday's high and low.
Expands to(H > H[-1]) and (L < L[-1])
outside_bar()
gap_up()PatternToday's whole range above yesterday's high (unfilled gap up).
Expands toL > H[-1]
gap_up()
gap_down()PatternToday's whole range below yesterday's low (unfilled gap down).
Expands toH < L[-1]
gap_down()

10. Errors

A rule that does not parse, calls an unknown function, passes the wrong number of arguments, puts an offset on anything but a price letter, or names an unknown fact MUST be rejected, not answered. Over HTTP, Ora returns 422 with one entry per problem, pointing at the question:

{
  "detail": [
    {
      "loc": [
        "body",
        "questions",
        "trend",
        "ask"
      ],
      "type": "value_error",
      "msg": "Unknown function `smaa`. Did you mean `sma`?"
    }
  ]
}

11. Conformance

LevelAn implementation must
CoreParse the grammar (section 3) with its precedence; evaluate prices, offsets and the core functions; produce beliefs exactly as section 6, to 4 decimal places.
LibraryCore, plus any library functions it offers, under these names and definitions. It MUST reject (not reinterpret) library names it does not offer.
PatternsCore, plus every pattern evaluated exactly as its expansion.
FactsCore, plus a fact group with these names, units, scales and point-in-time meaning.

Say which levels you implement, for example “Ora Rules v1: Core + Patterns”. Test vectors: the belief table in section 6 is normative; the rule library shows real answers on real symbols for comparison.

12. Versioning

This is Ora Rules v1 (1.0.0). Minor versions (1.1, 1.2) only add functions, facts or patterns; they never change what an existing rule means. A change to the meaning of an existing rule or to the belief formula is a new major version with its own URL. Each version stays published at /spec/v<major> and /spec/v<major>.json.

13. Implementing it

  • Start from /spec/v1.json: the grammar, belief constants and every function with its signature, definition and an example.
  • Check your beliefs against Ora's on the same bars: send your own bars to POST /v1/systemone (bring your own bars) and compare.
  • Agents can call Ora directly over MCP, and share any rule as a public link from the Playground.

Ora Rules v1 by KlickAnalytics (oramodel.com), licensed CC BY 4.0. Information, not investment advice.