# Decision — KlickAnalytics > Typed answers about market data. You send a state and typed questions in one JSON > object; you get back structured answers your code can branch on. Rules are computed > arithmetically on the server; plain-English questions go to a model. Not advice, not > a forecast: a decision is a rule evaluated against history already on file. Endpoint: POST https://api.klickanalytics.com/decision/v1/run (/v1/decision/run is a permanent alias for the same thing) Auth: X-API-Key: (or Authorization: Bearer ) Docs: https://www.klickanalytics.com/decision (Documentation tab) Engine: kad-latest ## The one rule that trips writers up A request carries EITHER `decision_type` (a preset) OR `questions` (your own set). Both together is an error, not a merge. Unknown parameters are REJECTED, not ignored, so a typo fails loudly instead of silently using a default. ## Request ```json { "decision_type": "rsi_check", // OR "questions": {...} "symbol": "NVDA", // OR "state": {...} for questions "output": "simple", // "simple" (default) | "full" "bars": 400 // 30-2000, default 400 } ``` Request-level parameters (valid on both paths): `output`, `bars`. Body limit 8 KB. POST only. One decision per request. ## Response `status` is the first field of every response: "ok" or "error". An error is the same shape in every output mode: ```json {"status": "error", "error": {"code": "unknown_symbol", "message": "..."}} ``` output=simple -> status, decision, confidence (preset) status, answers{key: value} (question set; a choice collapses to its winner) output=full -> the above plus summary, checks, evidence/state, meta `provisional: true` means the latest bar is TODAY and the session has not closed — its volume is only the hours traded so far. `stale: true` means the last close is over a week old. Both survive into simple output. Neither is an error. ## CONFIDENCE IS MARGIN, NOT PROBABILITY Every number called a confidence, and every `noul` value, is how far a reading sits past the line it was compared with, rescaled to 0-1. It is NOT a probability that the answer is correct. It never reaches 1.00. Exactly 0.5 means "the numbers do not tell you". Do not present it to a user as a hit rate or an accuracy. ## Presets (decision_type) ### rsi_check Is this symbol overbought or oversold on Wilder RSI? Returns: overbought | oversold | neutral - symbol (symbol) REQUIRED — Ticker to evaluate. - period (int) default 14, range 2-200 — RSI lookback in sessions. - overbought (num) default 70, range 50-100 — Level at or above which the verdict is overbought. - oversold (num) default 30, range 0-50 — Level at or below which the verdict is oversold. ```json {"decision_type":"rsi_check","symbol":"NVDA","output":"simple"} ``` ### ma_cross Where does the fast moving average sit against the slow one, and did it just cross? Returns: golden_cross | death_cross | above | below - symbol (symbol) REQUIRED — Ticker to evaluate. - fast (int) default 50, range 2-300 — Fast simple moving average, in sessions. - slow (int) default 200, range 3-400 — Slow simple moving average, in sessions. Must exceed fast. ```json {"decision_type":"ma_cross","symbol":"NVDA","fast":50,"slow":200,"output":"simple"} ``` ### volume_spike Is the latest session trading unusually heavily against its own recent average? Returns: spike | elevated | normal | quiet - symbol (symbol) REQUIRED — Ticker to evaluate. - lookback (int) default 20, range 5-250 — Sessions in the comparison average (the latest bar is excluded from it). - threshold (num) default 2, range 1-20 — Multiple of the average that counts as a spike. ```json {"decision_type":"volume_spike","symbol":"NVDA","output":"simple"} ``` ### range_position Where is the last close inside its own high/low range over a window? Returns: at_high | upper | middle | lower | at_low - symbol (symbol) REQUIRED — Ticker to evaluate. - window (int) default 252, range 10-400 — Sessions in the range. 252 is about one year. ```json {"decision_type":"range_position","symbol":"NVDA","window":252,"output":"simple"} ``` ## Your own questions `questions` is an object keyed by YOUR names, max 32. Each question declares its type. State comes from `symbol` (computed from prices) or `state` (your own JSON object, no market data touched). The state is returned under output=full. ### yes_no — yes/no as a probability `{"type": "yes_no", "ask": "rsi > 70"}` -> `{"value": 0.09}` One number 0-1. There is no confidence field because the number IS the belief. `noul` is a permanent alias for this type; the answer echoes whichever word you used. ### choice — pick one, with the odds on every option `{"type": "choice", "options": {"up": "spread_pct > 2", "down": "spread_pct < -2", "flat": "else"}}` -> `{"pick": "up", "odds": {"up": 0.89, "flat": 0.06, "down": 0.05}}` Max 255 options. ONE option may use the rule "else": it takes whatever belief the others leave unclaimed. NEVER write "true" for a catch-all — it scores 0.95 and beats a decisively satisfied real option. This is the single most common mistake. ### score — a rung on a ladder, landing between rungs `{"type": "score", "of": "rsi", "from": 0, "to": 100, "rungs": 5}` -> `{"value": 3.226}` rungs 2-10. `from` > `to` inverts the ladder. Out-of-range values pin to the end and set `clamped: true`. The decimals are the point; do not round them away. ### rank — an ordering, with each option's strength `{"type": "rank", "options": {"a": "rsi > 60", "b": "spread_pct > 2"}}` -> `{"order": ["b","a"], "ranked": [{"name":"b","strength":0.93,"place":1}, …]}` No "else" in a rank: a catch-all has no place in an ordering. ### amount — a number straight from the state `{"type": "amount", "of": "rsi", "round": 2}` -> `{"value": 56.12}` Not a judgement. Use it to report the figure a judgement was read off, in the same call. It takes "of", never "instructions" — there is nothing for a model to decide. ### Asking in words instead of a rule Replace `ask` (or rules-as-options) with `instructions` and a model answers, in the same shape. Max 4 per request, ~10s each, they run sequentially. Rule questions are free and instant. Use a rule wherever one fits. Every answer carries `via`: "rules" | "model". One bad question does NOT sink the set: its entry carries an `error` string and every other question still answers. ## Pricing $1.00 per million PROCESSED TOKENS. Processed tokens are what it took to answer: processed = base + the answer + what an AI processed base = 2000 on daily bars | 8000 on intraday bars (1m-1h) answer = the bytes we send back, at 4 bytes to a token AI = the model's own tokens, prompt AND completion, on `instructions` questions The base is flat within an interval — a preset and twenty rule questions in one request cost the same. Intraday is higher because those bars are fetched live rather than read from our own database. The SIZE OF YOUR REQUEST does not change the charge; the size of the ANSWER does, which is the only reason `output: full` costs a little more than `simple` — roughly 200 extra tokens, or $0.0002. So: one daily preset call is about $0.002 and a thousand of them about $2.00; one intraday call about $0.008; one plain-English question about $0.0034. Ask twenty rule questions in ONE request rather than twenty requests — that is one base, not twenty. Prepaid, no invoice, no overdraft. A NEW ACCOUNT GETS A SMALL NUMBER OF FREE CALLS (see `free_calls` on /decision/v1/account); errors do not count against them. After that a top-up is required and the refusal is `free_calls_used`. A call refused before billable work is NOT CHARGED. Top-ups themselves are final and non-refundable. ## Rule language IT IS THE BACKTESTING LANGUAGE. Same grammar, same functions, same semantics as an entry condition in Backtest Quick. Do not invent a second dialect. ``` rule := or or := and ( "or" and )* and := not ( "and" not )* not := ( "not" | "!" ) not | cmp cmp := add ( OP add )* add := mul ( ("+" | "-") mul )* mul := pow ( ("*" | "/" | "%") pow )* pow := unary ( "^" unary )* unary := ("+" | "-") unary | primary primary := "(" or ")" | number | fn "(" args ")" [@tf] | O|H|L|C|V [ "[" offset "]" ] [@tf] | field OP := > >= < <= == != ``` `and` binds tighter than `or`. Combining is FUZZY: `and` takes the lower of its parts' beliefs, `or` the higher. `==` runs the other way from the rest — it is confident when the gap is SMALL. `true` scores 0.95 and `false` 0.05, never 1 and 0. AN UNKNOWN FUNCTION NAME IS A HARD ERROR naming the nearest match — never a rule that silently never fires. NaN propagates and every comparison against NaN is false, so an indicator that has not warmed up cannot trigger. min/max INCLUDE the current bar; highest/lowest EXCLUDE it. `C > max(C,20)` can never be true. `C > highest(C,20)` is the breakout you meant. ### Prices | call | meaning | | --- | --- | | `O H L C V` | Open, high, low, close, volume of the bar being evaluated. | | `C[-2]` | An earlier bar. The offset counts back; C[-1] is the previous close. | | `C@5m` | The same price on another timeframe. See Timeframes. | | `hl2()` | (H + L) / 2. | | `hlc3()` | (H + L + C) / 3 — the typical price. | | `ohlc4()` | (O + H + L + C) / 4. | | `tr()` | True range: the bar's range, extended to include an overnight gap. | ### Windows | call | meaning | | --- | --- | | `sma(x, n)` | Simple moving average. | | `avg(x, n)` | The same thing. mean() too — three spellings, one function, because people arrive from three different tools. | | `mean(x, n)` | See avg(). | | `sum(x, n)` | Rolling sum. | | `min(x, n)` | Lowest value, INCLUDING the current bar. | | `max(x, n)` | Highest value, INCLUDING the current bar — so C > max(C,20) can never be true. | | `highest(x, n)` | Highest of the n bars BEFORE this one. This is the breakout form. | | `lowest(x, n)` | Lowest of the n bars before this one. | | `std(x, n)` | Standard deviation over the window. | | `zscore(x, n)` | How many deviations the current value sits from its window mean. | | `median(x, n)` | Middle value of the window. | | `percentrank(x, n)` | Where the current value ranks among the n-1 before it, 0-100. | | `wma(x, n)` | Linearly weighted moving average. | | `ema(x, n)` | Exponential moving average, 2/(n+1). | | `rma(x, n)` | Wilder's smoothing, 1/n — what RSI and ATR are built from. | | `linreg(x, n)` | Least-squares fit over the window, read at the current bar. | | `roc(x, n)` | Percent change over n bars. | | `cum(x)` | Running total from the first bar. | | `highestbars(x, n)` | Bars since the window's high. 0 means it is happening now. | | `lowestbars(x, n)` | Bars since the window's low. | ### Indicators | call | meaning | | --- | --- | | `rsi(x, n)` | Relative strength, 0-100. rsi(C,14) is the usual one. | | `atr(n)` | Average true range, in price. | | `atr_pct(n)` | ATR as a percentage of price — comparable across symbols. | | `macd(x[,f,s,sig])` | MACD line. Defaults 12, 26, 9. | | `macdsignal(x[,…])` | The MACD signal line. | | `macdhist(x[,…])` | The MACD histogram: line minus signal. | | `bbupper(x,n[,up,dn])` | Upper Bollinger band. Default 2 deviations. | | `bbmid(x, n[,…])` | The middle Bollinger band — a simple average. | | `bblower(x, n[,…])` | Lower Bollinger band. | | `adx(n)` | Trend strength, 0-100. Direction-free: 25+ is usually called trending. | | `diplus(n) diminus(n)` | The directional movement components ADX is built from. | | `stochk(n[,d,slow])` | Stochastic %K. | | `stochd(n[,d,slow])` | Stochastic %D — the signal line of %K. | | `cci(n)` | Commodity channel index. | | `willr(n)` | Williams %R, -100 to 0. | | `obv()` | On-balance volume: a running total that adds the bar's volume on an up close and subtracts it on a down one. | ### Signals | call | meaning | | --- | --- | | `crossup(a, b)` | 1 on the bar where a crosses above b, 0 otherwise. | | `crossdown(a, b)` | 1 on the bar where a crosses below b. | | `barssince(cond)` | How many bars since a condition was last true. | ### Maths | call | meaning | | --- | --- | | `abs(x) sqrt(x) log(x) sign(x)` | The usual ones. log is natural. | | `iif(cond, a, b)` | a when the condition holds, b otherwise. | | `ref(x, -n)` | The value n bars ago, as a function instead of an offset. | | `between(x, a, b)` | 1 when x is inside the range, inclusive. | | `least(a, b) greatest(a, b)` | The smaller and larger of two series. | ### Intraday | call | meaning | | --- | --- | | `vwap()` | Volume-weighted average price, reset every session. | | `opening_range_high([m])` | Highest price of the first m minutes. Default 30. | | `opening_range_low([m])` | Lowest price of the opening range. | | `minutes_since_open()` | Minutes elapsed in the session. | | `time_of_day()` | The bar's clock time as HHMM, so 1030 is 10:30. | | `rvol(n)` | Relative volume: this bar against the average of the previous n. | | `gap()` | Percent from the previous close to this bar's open. | ### Recursion | call | meaning | | --- | --- | | `rec(seed, step)` | A series defined from its own previous value; `self` inside `step` is that previous value. Trailing stops and running extremes are made of this. | ### Timeframes | form | meaning | | --- | --- | | `1m 5m 15m 30m 1h 1d` | The intervals a request can run on. | | `C@1d` | A reference to a HIGHER timeframe. It sees the last bar that had already closed, so there is no look-ahead. | | `sma(C@5m, 20)` | Computed ON the 5-minute series, then carried across. A window cannot mix timeframes — compute each and compare them. | A request picks its interval with `"prices": {"interval": "5m", "bars": 300}`. Daily is the default and is read from our own database; intraday is fetched from the market data feed during the call, so ask for it only when the question needs it. A referenced `@tf` must be LONGER than the interval being run, and always reads the last bar that had already closed. ## State fields Every field carries a SCALE: the distance, in that field's own units, at which being past a line stops being a coin flip. That is what turns a comparison into a belief. A field is ABSENT when it is meaningless for that symbol (an ETF has no eps_surprise_pct; a recent listing has no sma_200). Naming an absent field is an error listing what exists — never a silent zero. Do not defend against this by defaulting to 0. SHORTCUTS, NOT A SECOND VOCABULARY. Each one below is the same thing as the expression in the third column — use either. Only the earnings fields have no equivalent, because the rule language only ever sees bars. | field | same as | meaning | | --- | --- | --- | | last_close | `C` | Latest adjusted close. | | sma_50 | `sma(C,50)` | 50-session simple moving average. | | sma_200 | `sma(C,200)` | 200-session simple moving average. | | spread_pct | `(sma(C,50) - sma(C,200)) / sma(C,200) * 100` | How far SMA50 sits above (+) or below (-) SMA200, in percent. | | rsi | `rsi(C,14)` | Wilder RSI over 14 sessions, 0-100. | | return_1d | `roc(C,1)` | Percent change over the last 1 session. | | return_5d | `roc(C,5)` | Percent change over the last 5 sessions. | | return_21d | `roc(C,21)` | Percent change over the last 21 sessions. | | volume | `V` | Latest session volume. | | avg_volume_20 | `ref(avg(V,20), -1)` | Mean volume of the 20 sessions before the latest. | | volume_multiple | `V / ref(avg(V,20), -1)` | Latest volume divided by that 20-session mean. 1 is typical. | | range_high | `max(H,252)` | Highest price over the last 252 sessions. | | range_low | `min(L,252)` | Lowest price over the last 252 sessions. | | range_position_pct | `(C - min(L,252)) / (max(H,252) - min(L,252)) * 100` | Where the close sits in that range. 0 is on the low, 100 on the high. | | pct_off_high | `(C - max(H,252)) / max(H,252) * 100` | Distance below the range high, in percent. 0 is a new high. | | days_to_earnings | — | Sessions from the latest bar to the next scheduled report. | | days_since_earnings | — | Calendar days from this bar back to the last reported quarter. | | eps_surprise_pct | — | Last quarter EPS against consensus, in percent. Positive is a beat. | | revenue_surprise_pct | — | Last quarter revenue against consensus, in percent. Positive is a beat. | (Read from the engine when this page was served. These named fields are always DAILY, whatever interval the request runs on — `rsi` is the daily `rsi(C,14)`. Write the function out to compute it on the interval you asked for. There is no point-in-time parameter: a run always reads the latest bars.) ## Errors - `bad_json` — 400 — body did not parse - `bad_request` — 400 — missing, unknown, wrong type, out of range, or contradictory parameter - `unknown_decision_type` — 400 — no such preset; the message lists the real ones - `unknown_symbol` — 400 — ticker not in the database - `no_data` — 400 — symbol exists, no usable daily history - `insufficient_history` — 400 — not enough bars for the lookback asked for; raise `bars` - `unauthorized` — 401 — no valid key - `free_calls_used` — 402 — the new-account free calls are gone; top up - `no_balance` — 402 — empty wallet and no free calls configured - `insufficient_funds` — 402 — balance ran out; the message gives the balance and the cost - `monthly_cap_reached` — 402 — the caller's own monthly limit, not an empty wallet - `forbidden` — 403 — key valid, account cannot use Decision - `method_not_allowed` — 405 — use POST ## Worked examples ### The smallest possible request [Start here] A preset, a ticker, and the shape you want back. Everything else has a default. "output" has one too — simple — but it is written out here and in every example below, because a field you never see is a field you never learn you can change. ```json {"output":"simple","decision_type":"rsi_check","symbol":"NVDA"} ``` ### The same call, with the working shown [Start here] Switch "output" to full and you get the checks, the raw numbers they were read off, and where the data came from. Use it while you are deciding whether to trust a threshold. ```json {"decision_type":"rsi_check","symbol":"NVDA","output":"full"} ``` ### Your first question of your own [Start here] Instead of a preset, one question you wrote. The answer is a probability: 0.93 means 93% yes, 0.5 means the numbers do not tell you. ```json {"output":"simple","symbol":"NVDA","questions":{"is_overbought":{"type":"yes_no","ask":"rsi > 70"}}} ``` ### Several questions in one call [Start here] One state is built, every question is asked against it. Cheaper and more consistent than three separate calls, which could each land on a different bar. ```json {"symbol":"MSFT","output":"full","questions":{"overbought":{"type":"yes_no","ask":"rsi > 70"},"oversold":{"type":"yes_no","ask":"rsi < 30"},"uptrend":{"type":"yes_no","ask":"sma_50 > sma_200"}}} ``` ### Moving average cross [Start here] Tells "just crossed" from "has been above for months" — the two are different trades and one verdict would hide it. ```json {"decision_type":"ma_cross","symbol":"AAPL","fast":50,"slow":200,"output":"full"} ``` ### Volume spike, with your own bar [Start here] threshold is the multiple of the recent average that counts as a spike. Lower it for a quiet name, raise it for one that gaps every week. ```json {"decision_type":"volume_spike","symbol":"TSLA","lookback":20,"threshold":1.5,"output":"full"} ``` ### Where in the year’s range [Start here] 252 sessions is about a year. Shrink the window to ask the same question about a quarter. ```json {"decision_type":"range_position","symbol":"AMZN","window":252,"output":"full"} ``` ### The old name still works [Yes/no] `noul` was the original word for this type and remains a permanent alias — it is in saved queries and in other people\u{2019}s code. The answer echoes whichever word you used, so nothing has to be rewritten. ```json {"output":"full","symbol":"NVDA","questions":{"old_word":{"type":"noul","ask":"rsi > 70"},"new_word":{"type":"yes_no","ask":"rsi > 70"}}} ``` ### A threshold, as a probability [Yes/no] Not a true/false. RSI at 84 against a line of 70 comes back near 0.95; RSI at 71 comes back near 0.55. The number carries how decisive the reading was. ```json {"symbol":"NVDA","output":"full","questions":{"hot":{"type":"yes_no","ask":"rsi > 70"}}} ``` ### Comparing two fields, not a constant [Yes/no] Either side of the comparison can be a state field. This is the whole of a golden-cross test, written in one line. ```json {"symbol":"GOOGL","output":"full","questions":{"trend_up":{"type":"yes_no","ask":"sma_50 > sma_200"}}} ``` ### Near the high [Yes/no] pct_off_high is 0 at a new high and negative below it, so "within 3% of the high" is a single comparison. ```json {"symbol":"META","output":"full","questions":{"near_high":{"type":"yes_no","ask":"pct_off_high > -3"}}} ``` ### Two things at once [Yes/no] and takes the LOWER of the two beliefs, so a pair where one half is marginal stays marginal. It is not rounded up to certainty by the confident half. ```json {"symbol":"NVDA","output":"full","questions":{"strong_uptrend":{"type":"yes_no","ask":"spread_pct > 2 and rsi > 55"}}} ``` ### Either of two things [Yes/no] or takes the HIGHER of the two beliefs. This asks whether the name is at an extreme in either direction. ```json {"symbol":"AMD","output":"full","questions":{"at_an_extreme":{"type":"yes_no","ask":"rsi > 70 or rsi < 30"}}} ``` ### Put three setups in order [Rank — an order] Scored exactly like a choice — every option is its own yes/no — but the answer keeps the whole ordering instead of collapsing to a winner. Use it when you are going to act on the top two, not just the top one. ```json {"output":"full","symbol":"NVDA","questions":{"setups":{"type":"rank","options":{"trend":"spread_pct > 2","momentum":"rsi > 60","near_high":"pct_off_high > -3","heavy":"volume_multiple > 1.5"}}}} ``` ### Rank, then read the strengths [Rank — an order] Under full output each place carries its belief, so a first place at 0.93 reads differently from a first place at 0.52. Under simple you get just the ordered names, which is usually what code wants. ```json {"output":"simple","symbol":"AAPL","questions":{"order":{"type":"rank","options":{"uptrend":"sma_50 > sma_200","stretched":"rsi > 65","cheap":"rsi < 40"}}}} ``` ### Why a rank has no "else" [Rank — an order] A catch-all has no meaningful position in an ordering, so it is refused rather than quietly placed somewhere in the middle of a list you are about to act on. Use a choice when you want a fallback. NOTE: this example is MEANT to fail — it demonstrates an error. ```json {"output":"full","symbol":"NVDA","questions":{"bad":{"type":"rank","options":{"up":"spread_pct > 2","other":"else"}}}} ``` ### Return the number, not a judgement [Amount — a number] An amount reads a field straight out of the state. No threshold, no belief. It is here so a set can report the figure its judgements were read off, in the same call and from the same bar. ```json {"output":"full","symbol":"NVDA","questions":{"rsi_now":{"type":"amount","of":"rsi","round":2},"last_close":{"type":"amount","of":"last_close","round":2},"overbought":{"type":"yes_no","ask":"rsi > 70"}}} ``` ### A judgement and its evidence together [Amount — a number] The pattern worth stealing: ask the question, and ask for the numbers behind it, in one request. Two calls would mean two states and an answer that might not match its own evidence. ```json {"output":"simple","symbol":"MSFT","questions":{"in_uptrend":{"type":"yes_no","ask":"sma_50 > sma_200"},"sma_50":{"type":"amount","of":"sma_50","round":2},"sma_200":{"type":"amount","of":"sma_200","round":2},"gap_pct":{"type":"amount","of":"spread_pct","round":2}}} ``` ### An amount cannot be asked in words [Amount — a number] There is nothing for a model to decide: the number is already in the state. Asking for it with "instructions" is refused rather than quietly costing you a model call for a lookup. NOTE: this example is MEANT to fail — it demonstrates an error. ```json {"output":"full","symbol":"NVDA","questions":{"bad":{"type":"amount","instructions":"What is the RSI?"}}} ``` ### Three regimes, with the odds [Choice — pick one] You get the winner AND every option’s share. "bullish" alone would hide that "flat" was one point behind it. ```json {"symbol":"NVDA","output":"full","questions":{"trend":{"type":"choice","options":{"bullish":"spread_pct > 2","bearish":"spread_pct < -2","flat":"else"}}}} ``` ### Why the catch-all must be "else" [Choice — pick one] Write the fallback as "true" and it scores 0.95 — beating a decisively satisfied real option and winning the answer. "else" takes only the belief the others leave unclaimed. Run both and compare. ```json {"symbol":"NVDA","output":"full","questions":{"right_way":{"type":"choice","options":{"bullish":"spread_pct > 2","other":"else"}},"wrong_way":{"type":"choice","options":{"bullish":"spread_pct > 2","other":"true"}}}} ``` ### Bucketing a number into named bands [Choice — pick one] A choice is a good way to turn one continuous field into labels your code can switch on. ```json {"symbol":"AAPL","output":"full","questions":{"rsi_zone":{"type":"choice","options":{"overbought":"rsi > 70","strong":"rsi > 55","weak":"rsi < 45","oversold":"rsi < 30","neutral":"else"}}}} ``` ### Which leg of the range [Choice — pick one] Options may use different fields from each other. Nothing requires a choice to be about one number. ```json {"symbol":"TSLA","output":"full","questions":{"location":{"type":"choice","options":{"at_the_high":"pct_off_high > -1","upper_half":"range_position_pct > 50","lower_half":"else"}}}} ``` ### RSI on a five-rung ladder [Score — a ladder] from and to are the ends of the ladder in the field’s own units. The answer lands between rungs — 3.226, not 3 — because that difference is information. ```json {"symbol":"NVDA","output":"full","questions":{"heat":{"type":"score","of":"rsi","from":0,"to":100,"rungs":5}}} ``` ### A ten-rung ladder for finer grain [Score — a ladder] Ladders run from 2 to 10 rungs. More rungs is not more accuracy — it is just a finer ruler over the same number. ```json {"symbol":"MSFT","output":"full","questions":{"in_range":{"type":"score","of":"range_position_pct","from":0,"to":100,"rungs":10}}} ``` ### A ladder that runs backwards [Score — a ladder] Put the larger number in from and the smaller in to, and the ladder inverts: heavy volume scores LOW. Useful when rung 1 should mean "best". ```json {"symbol":"TSLA","output":"full","questions":{"quietness":{"type":"score","of":"volume_multiple","from":3,"to":0,"rungs":5}}} ``` ### What "clamped" means [Score — a ladder] A value outside from..to pins to the end of the ladder and the answer says clamped:true. That flag is the difference between "at the top" and "off the top of your scale". ```json {"symbol":"NVDA","output":"full","questions":{"narrow_ladder":{"type":"score","of":"rsi","from":0,"to":20,"rungs":4}}} ``` ### The same question, two ways [Prices and indicators] A named field is shorthand: rsi IS the daily rsi(C,14). Write it out when you want to change the period, the source or the interval. The beliefs differ slightly because the written-out form measures its own scale from how this symbol actually moves, while the named field carries a declared one. ```json {"symbol":"NVDA","output":"full","questions":{"named":{"type":"yes_no","ask":"rsi > 70"},"written":{"type":"yes_no","ask":"rsi(C,14) > 70"},"shorter":{"type":"yes_no","ask":"rsi(C,7) > 70"}}} ``` ### O H L C V, and looking back a bar [Prices and indicators] The five prices of the bar being read, with an offset in brackets to reach earlier ones. C[-1] is the previous close; C[-2] the one before that. ```json {"symbol":"AAPL","output":"full","questions":{"up_day":{"type":"yes_no","ask":"C > O"},"higher_high":{"type":"yes_no","ask":"H > H[-1] and L > L[-1]"},"three_up":{"type":"yes_no","ask":"C > C[-1] and C[-1] > C[-2]"},"range":{"type":"amount","of":"H - L","round":2}}} ``` ### A breakout, written correctly [Prices and indicators] highest() EXCLUDES the current bar and max() includes it, which is the whole difference between a breakout and something that can never be true — C > max(C,20) is unsatisfiable because max already contains C. ```json {"symbol":"NVDA","output":"full","questions":{"breakout":{"type":"yes_no","ask":"C > highest(H, 20)"},"never_true":{"type":"yes_no","ask":"C > max(C, 20)"},"with_volume":{"type":"yes_no","ask":"C > highest(H, 20) and V > avg(V, 20) * 1.5"}}} ``` ### Indicators without naming a field [Prices and indicators] The whole library is available: moving averages, bands, ADX, stochastics, ATR. Anything the backtester can test, a decision can ask. ```json {"symbol":"MSFT","output":"full","questions":{"trending":{"type":"yes_no","ask":"adx(14) > 25 and diplus(14) > diminus(14)"},"stretched":{"type":"yes_no","ask":"C > bbupper(C, 20)"},"squeezed":{"type":"yes_no","ask":"bbupper(C,20) - bblower(C,20) < atr(14) * 3"},"volatility":{"type":"amount","of":"atr_pct(14)","round":2}}} ``` ### The bar a cross happens on [Prices and indicators] crossup() is 1 only on the bar where the cross prints, so it answers "did this just happen" rather than "is this true". barssince() turns that into "how long ago". ```json {"symbol":"NVDA","output":"full","questions":{"crossed_today":{"type":"yes_no","ask":"crossup(sma(C,50), sma(C,200))"},"above_now":{"type":"yes_no","ask":"sma(C,50) > sma(C,200)"},"bars_ago":{"type":"amount","of":"barssince(crossup(C, sma(C,50)))"}}} ``` ### Arithmetic between indicators [Prices and indicators] Either side of a comparison can be a whole expression, and "of" takes one too. Distance from the mean measured in ranges is comparable across symbols in a way a dollar figure is not. ```json {"symbol":"TSLA","output":"full","questions":{"stretched":{"type":"yes_no","ask":"(C - sma(C,20)) / atr(14) > 2"},"how_far":{"type":"amount","of":"(C - sma(C,20)) / atr(14)","round":2},"position":{"type":"score","of":"(C - lowest(L,20)) / (highest(H,20) - lowest(L,20)) * 100","from":0,"to":100,"rungs":5}}} ``` ### A series that remembers itself [Prices and indicators] rec(seed, step) is the one construct that can refer to its own previous value, through `self`. A trailing stop is exactly that: the greater of where it was and where the new one would be, so it never moves down. ```json {"symbol":"NVDA","output":"full","questions":{"trail_stop":{"type":"amount","of":"rec(L, greatest(self, C - 2 * atr(14)))","round":2},"high_water":{"type":"amount","of":"rec(C, iif(C > self, C, self))","round":2},"stop_hit":{"type":"yes_no","ask":"C < rec(L, greatest(self, C - 2 * atr(14)))"}}} ``` ### (fails as designed) A function that does not exist [Prices and indicators] An unknown name is a hard error naming the nearest match, never a rule that silently never fires. A typo that reads as "this never happens" is indistinguishable from a pattern that does not occur, which is the worst failure a rule engine has. NOTE: this example is MEANT to fail — it demonstrates an error. ```json {"symbol":"NVDA","questions":{"typo":{"type":"yes_no","ask":"smaa(C, 50) > 1"}}} ``` ### Running on five-minute bars [Intraday] The "prices" node says what a bar is for this request. Daily comes from our database; intraday is fetched from the market data feed at the moment of the call, which is why you have to ask for it. ```json {"symbol":"NVDA","prices":{"interval":"5m","bars":300},"output":"full","questions":{"above_vwap":{"type":"yes_no","ask":"C > vwap()"},"last":{"type":"amount","of":"C","round":2},"vwap":{"type":"amount","of":"vwap()","round":2},"elapsed":{"type":"amount","of":"minutes_since_open()"}}} ``` ### An opening-range break [Intraday] opening_range_high() is the highest price of the first half hour, reset every session. Pairing it with minutes_since_open() keeps the rule from firing during the range it is measuring. ```json {"symbol":"AAPL","prices":{"interval":"5m","bars":200},"output":"full","questions":{"broke_out":{"type":"yes_no","ask":"C > opening_range_high() and minutes_since_open() > 30"},"or_high":{"type":"amount","of":"opening_range_high()","round":2},"or_low":{"type":"amount","of":"opening_range_low()","round":2}}} ``` ### Fast chart, slow filter [Intraday] @1d reads the daily series from inside a five-minute rule. It sees the last daily bar that had already CLOSED, so a rule can never look at a price before it existed. ```json {"symbol":"NVDA","prices":{"interval":"5m","bars":250},"output":"full","questions":{"long_ok":{"type":"yes_no","ask":"C > sma(C,20) and C@1d > sma(C@1d,50)"},"trend":{"type":"choice","options":{"with_trend":"C > sma(C,20) and C@1d > sma(C@1d,50)","against":"C > sma(C,20) and C@1d < sma(C@1d,50)","flat":"else"}}}} ``` ### (fails as designed) Looking down a timeframe [Intraday] A referenced timeframe has to be LONGER than the one you are running on. Reading 5-minute bars from a daily rule would mean seeing inside a bar that has already closed, so it is refused rather than quietly approximated. NOTE: this example is MEANT to fail — it demonstrates an error. ```json {"symbol":"NVDA","questions":{"impossible":{"type":"yes_no","ask":"C > sma(C@5m, 20)"}}} ``` ### Every comparison operator [Writing rules] Six of them. "==" is confident when the gap is SMALL, which is the opposite direction from the others — being on the nose is the certain answer. ```json {"symbol":"AAPL","output":"full","questions":{"gt":{"type":"yes_no","ask":"rsi > 50"},"gte":{"type":"yes_no","ask":"rsi >= 50"},"lt":{"type":"yes_no","ask":"rsi < 50"},"eq":{"type":"yes_no","ask":"rsi == 50"},"ne":{"type":"yes_no","ask":"rsi != 50"}}} ``` ### and binds tighter than or [Writing rules] There are no parentheses. "a and b or c" reads as "(a and b) or c", the same as everywhere else. Split a set into several questions if you need other grouping. ```json {"symbol":"NVDA","output":"full","questions":{"setup":{"type":"yes_no","ask":"rsi > 55 and spread_pct > 2 or rsi > 80"}}} ``` ### true and false as literals [Writing rules] They score 0.95 and 0.05, never 1 and 0 — a threshold is a convention, and nothing here claims certainty about a convention. This is also exactly why a catch-all option needs "else" instead. ```json {"symbol":"NVDA","output":"full","questions":{"always":{"type":"yes_no","ask":"true"},"never":{"type":"yes_no","ask":"false"}}} ``` ### How the same gap reads on different fields [Writing rules] Each field declares a scale — the distance at which being past a line stops being a coin flip. Ten RSI points is decisive; ten percent on a 1-day return is enormous. That is why these two do not come back the same. ```json {"symbol":"NVDA","output":"full","questions":{"rsi_10_over":{"type":"yes_no","ask":"rsi > 45"},"ret_10_over":{"type":"yes_no","ask":"return_1d > -10"}}} ``` ### How the last quarter landed [Earnings] eps_surprise_pct and revenue_surprise_pct are the most recent REPORTED quarter against consensus. Positive is a beat. Both are absent for anything that does not report, so a rule naming them on an ETF fails loudly instead of reading zero. ```json {"output":"full","symbol":"NVDA","questions":{"beat_on_eps":{"type":"yes_no","ask":"eps_surprise_pct > 0"},"beat_on_revenue":{"type":"yes_no","ask":"revenue_surprise_pct > 0"},"clean_beat":{"type":"yes_no","ask":"eps_surprise_pct > 2 and revenue_surprise_pct > 1"}}} ``` ### Is a report coming up [Earnings] days_to_earnings counts from the latest bar to the next SCHEDULED report — so it is absent whenever the calendar has not published one yet, which is common weeks out. Ask about it and handle its absence; do not assume it is there. This example answers with an error on purpose whenever no date is published, which is most of the time. NOTE: this example is MEANT to fail — it demonstrates an error. ```json {"output":"full","symbol":"COST","questions":{"reports_within_a_week":{"type":"yes_no","ask":"days_to_earnings <= 7"},"event_risk":{"type":"score","of":"days_to_earnings","from":30,"to":0,"rungs":5}}} ``` ### Still in the drift window [Earnings] days_since_earnings is measured from the bar, not from today, so a decision read off an old close answers about that day. It is dropped entirely once the last report is over about fifteen months old — at that point the number means nothing. ```json {"output":"full","symbol":"AAPL","questions":{"fresh_print":{"type":"yes_no","ask":"days_since_earnings < 10"},"phase":{"type":"choice","options":{"just_reported":"days_since_earnings < 5","drift":"days_since_earnings < 30","quiet":"else"}}}} ``` ### A beat the tape did not buy [Earnings] The interesting earnings setups are disagreements. This one asks for a beat on both lines while momentum has stayed cool — three fields from two different feeds, in one rule. ```json {"output":"full","symbol":"NVDA","questions":{"unloved_beat":{"type":"yes_no","ask":"eps_surprise_pct > 2 and revenue_surprise_pct > 0 and rsi < 55"}}} ``` ### How much history a run reads [Data window] "bars" sets how many daily sessions are loaded — 400 by default, 30 to 2000 allowed. It is what every lookback is computed from, so it has to be at least as long as the longest one you ask for. ```json {"output":"full","decision_type":"rsi_check","symbol":"NVDA","bars":120} ``` ### Too little history for the question [Data window] Ask for a 200-session average off 120 bars and it says so rather than quietly averaging whatever it found. Raise "bars" or shorten the lookback. NOTE: this example is MEANT to fail — it demonstrates an error. ```json {"output":"full","decision_type":"ma_cross","symbol":"NVDA","fast":50,"slow":200,"bars":120} ``` ### A long window for a long lookback [Data window] Widen the window and a multi-year range becomes askable. 1250 sessions is about five years. The read is one bounded, symbol-scoped query either way. ```json {"output":"full","decision_type":"range_position","symbol":"AAPL","window":400,"bars":1250} ``` ### Every lookback a preset takes [Data window] Each preset has its own lookback parameter — period for RSI, fast and slow for the cross, lookback for volume, window for the range. "bars" bounds them all: it is the pool they are drawn from. ```json {"output":"full","symbol":"MSFT","bars":800,"questions":{"short_rsi":{"type":"yes_no","ask":"rsi > 60"},"long_trend":{"type":"yes_no","ask":"sma_50 > sma_200"},"in_range":{"type":"score","of":"range_position_pct","from":0,"to":100,"rungs":5}}} ``` ### Facts that have nothing to do with markets [Your own state] Send "state" instead of "symbol" and no price data is touched at all. The primitives work on any numbers you have. ```json {"output":"full","state":{"temperature":38.5,"age":72,"heart_rate":104},"questions":{"fever":{"type":"yes_no","ask":"temperature > 37.5"},"tachycardic":{"type":"yes_no","ask":"heart_rate > 100"}}} ``` ### Scoring a supplied number [Your own state] Ladders work the same on your own fields. You supply from and to, so you are declaring the units the engine does not know. ```json {"output":"full","state":{"order_value":4200,"days_late":6},"questions":{"urgency":{"type":"score","of":"days_late","from":0,"to":14,"rungs":5},"tier":{"type":"choice","options":{"enterprise":"order_value > 10000","mid":"order_value > 1000","small":"else"}}}} ``` ### Simple — one value per question [Output shape] The default, and what a program wants: one value per question, a choice collapsed to its winner. Leave "output" out entirely and you get this anyway. ```json {"output":"simple","symbol":"NVDA","questions":{"hot":{"type":"yes_no","ask":"rsi > 70"},"trend":{"type":"choice","options":{"up":"spread_pct > 0","down":"else"}}}} ``` ### Full — the same call with its working [Output shape] "full" adds the odds, the rule each answer used, the state it was read from and the meta. Run this and the one above back to back; only the one word differs. ```json {"symbol":"NVDA","output":"full","questions":{"hot":{"type":"yes_no","ask":"rsi > 70"},"trend":{"type":"choice","options":{"up":"spread_pct > 0","down":"else"}}}} ``` ### Morning triage on one name [Recipes] The four questions worth asking before you look at a chart: is it stretched, which way is it trending, how unusual is the volume, and where in its range is it. ```json {"symbol":"NVDA","output":"full","questions":{"stretched":{"type":"yes_no","ask":"rsi > 70 or rsi < 30"},"trend":{"type":"choice","options":{"up":"spread_pct > 2","down":"spread_pct < -2","sideways":"else"}},"volume_story":{"type":"score","of":"volume_multiple","from":0,"to":3,"rungs":5},"range_spot":{"type":"score","of":"range_position_pct","from":0,"to":100,"rungs":10}}} ``` ### Is this a breakout or a fade [Recipes] Near the high on heavy volume reads differently from near the high on nothing. Asking both and comparing is the point — neither answer means much alone. ```json {"symbol":"META","output":"full","questions":{"near_high":{"type":"yes_no","ask":"pct_off_high > -2"},"on_volume":{"type":"yes_no","ask":"volume_multiple > 1.5"},"confirmed":{"type":"yes_no","ask":"pct_off_high > -2 and volume_multiple > 1.5"}}} ``` ### A pullback inside an uptrend [Recipes] Trend intact, momentum cooled. Written as one rule so the answer is a single belief you can threshold on, rather than three you have to combine yourself. ```json {"symbol":"AAPL","output":"full","questions":{"pullback_in_uptrend":{"type":"yes_no","ask":"sma_50 > sma_200 and rsi < 45 and range_position_pct > 40"}}} ``` ### Size the position from the setup [Recipes] A ladder is a natural position sizer: feed the score straight into your own allocation, and "3.2 of 5" carries more than a yes. ```json {"symbol":"MSFT","output":"full","questions":{"conviction":{"type":"score","of":"spread_pct","from":-5,"to":10,"rungs":5},"crowded":{"type":"yes_no","ask":"rsi > 65"}}} ``` ### A typo in one question [When it goes wrong] One bad question does not sink the set. It reports its own error, names the fields that do exist, and every other answer still comes back. NOTE: this example is MEANT to fail — it demonstrates an error. ```json {"symbol":"NVDA","output":"full","questions":{"broken":{"type":"yes_no","ask":"rsii > 70"},"fine":{"type":"yes_no","ask":"rsi > 50"}}} ``` ### A parameter that does not exist [When it goes wrong] Unknown parameters are rejected rather than ignored. If you typed "symbl" you want to be told, not handed a confident answer about a default you never chose. NOTE: this example is MEANT to fail — it demonstrates an error. ```json {"output":"simple","decision_type":"rsi_check","symbol":"NVDA","perod":14} ``` ### Asking two ways at once [When it goes wrong] A preset and your own questions in one request is a contradiction, not a merge — there is no honest answer to "what is the verdict" when you defined the output. NOTE: this example is MEANT to fail — it demonstrates an error. ```json {"output":"simple","decision_type":"rsi_check","symbol":"NVDA","questions":{"hot":{"type":"yes_no","ask":"rsi > 70"}}} ``` ### A ticker that is not there [When it goes wrong] Unknown symbols fail loudly rather than returning an empty answer that looks like a real one. NOTE: this example is MEANT to fail — it demonstrates an error. ```json {"output":"simple","decision_type":"rsi_check","symbol":"NOTATICKER"} ``` ### Comparing something that is not a number [When it goes wrong] A text field cannot be on either side of ">". The error says so, and points you at "instructions" — which is the right tool for text. NOTE: this example is MEANT to fail — it demonstrates an error. ```json {"output":"simple","state":{"message":"still waiting"},"questions":{"bad":{"type":"yes_no","ask":"message > 1"}}} ``` ### Three readings, no judgement [Amount — a number] No threshold and no belief — just the figures a judgement would be read off. Useful as the second half of a set whose first half decides something. ```json {"symbol":"NVDA","output":"simple","questions":{"rsi":{"type":"amount","of":"rsi","round":1},"atr_pct":{"type":"amount","of":"atr_pct(14)","round":2},"off_high":{"type":"amount","of":"pct_off_high","round":2}}} ``` ### "and" takes the weaker half [Writing rules] Two questions that share a leg. The second adds a marginal RSI condition and the whole answer drops to it, because "and" returns the LOWER of the two beliefs rather than multiplying them. ```json {"symbol":"NVDA","output":"simple","questions":{"trend_up":{"type":"yes_no","ask":"C > sma_50 and sma_50 > sma_200"},"with_momentum":{"type":"yes_no","ask":"C > sma_50 and rsi > 50"}}} ``` ### Arithmetic on both sides [Writing rules] Either side of a comparison can be an expression. The first adds half an ATR of buffer so a wick is not a breakout; the third is not a comparison at all, just the stop distance. ```json {"symbol":"NVDA","bars":300,"output":"full","questions":{"breakout":{"type":"yes_no","ask":"C > highest(C,20) + 0.5 * atr(14)"},"wide_regime":{"type":"yes_no","ask":"atr(14) / C * 100 > 3"},"stop_distance":{"type":"amount","of":"2 * atr(14)","round":2}}} ``` ### Reading the bars before this one [Writing rules] C[-1] is yesterday and C[-2] the day before; ref(x,-n) does the same for anything that is not a bare price. Chaining them is how you ask about a sequence rather than a state. ```json {"symbol":"AAPL","bars":300,"output":"full","questions":{"three_up_days":{"type":"yes_no","ask":"C > C[-1] and C[-1] > C[-2] and C[-2] > C[-3]"},"day_return":{"type":"amount","of":"(C - C[-1]) / C[-1] * 100","round":2},"rsi_rising":{"type":"yes_no","ask":"rsi > ref(rsi, -1) and ref(rsi, -1) > ref(rsi, -2)"}}} ``` ### Twelve questions, one price history [Yes/no] The bill is the same as for one. A set costs one base whatever it holds, so twelve separate calls would cost twelve times this and could each land on a different bar. ```json {"symbol":"NVDA","bars":300,"output":"simple","questions":{"rsi_50":{"type":"yes_no","ask":"rsi > 50"},"rsi_60":{"type":"yes_no","ask":"rsi > 60"},"rsi_70":{"type":"yes_no","ask":"rsi > 70"},"above_50d":{"type":"yes_no","ask":"C > sma_50"},"above_200d":{"type":"yes_no","ask":"C > sma_200"},"trending":{"type":"yes_no","ask":"adx(14) > 25"},"heavy_volume":{"type":"yes_no","ask":"V > avg(V,20)"},"macd_up":{"type":"yes_no","ask":"macdhist(C) > 0"},"stoch_high":{"type":"yes_no","ask":"stochk(14) > 80"},"willr_high":{"type":"yes_no","ask":"willr(14) > -20"},"cci_high":{"type":"yes_no","ask":"cci(20) > 100"},"above_band":{"type":"yes_no","ask":"C > bbupper(C,20)"}}} ``` ### A window over an expression [Prices and indicators] avg() takes an expression, not just a field, so Bollinger width can be measured against its own fifty-bar average. That is a squeeze — narrow relative to this symbol rather than narrow in dollars. ```json {"symbol":"MSFT","bars":400,"output":"full","questions":{"squeeze":{"type":"yes_no","ask":"bbupper(C,20) - bblower(C,20) < 0.8 * avg(bbupper(C,20) - bblower(C,20), 50)"},"band_width_pct":{"type":"amount","of":"(bbupper(C,20) - bblower(C,20)) / C * 100","round":2}}} ``` ### Events, not states [Prices and indicators] crossup fires on the ONE bar where the lines cross, so compare it with 0.5 rather than 0 — against 0 a non-event scores 0.5, which reads as "it does not know". barssince turns a state into an age. ```json {"symbol":"JPM","bars":400,"output":"full","questions":{"golden_cross_today":{"type":"yes_no","ask":"crossup(sma(C,50), sma(C,200)) > 0.5"},"macd_just_crossed":{"type":"yes_no","ask":"crossup(macd(C), macdsignal(C)) > 0.5"},"bars_since_above_50d":{"type":"amount","of":"barssince(C > sma(C,50))","round":0},"bars_since_52w_high":{"type":"amount","of":"highestbars(C,252)","round":0}}} ``` ### Statistics instead of thresholds [Prices and indicators] A z-score and a percentile put today in the context of this symbol’s own history, which a fixed threshold cannot. "Volume over 2 sigma" means the same thing on every ticker; "volume over 50m" does not. ```json {"symbol":"AAPL","bars":400,"output":"full","questions":{"volume_zscore":{"type":"amount","of":"zscore(V, 60)","round":2},"unusual_volume":{"type":"yes_no","ask":"zscore(V, 60) > 2"},"return_percentile":{"type":"amount","of":"percentrank(roc(C,21), 252)","round":1},"trend_slope":{"type":"amount","of":"(linreg(C,60) - ref(linreg(C,60), -20)) / C * 100","round":2}}} ``` ### Two ladders from one state [Score — a ladder] A score lands BETWEEN rungs, which is the whole point: 4.3 out of 5 says "near the top of the range but not at it", and a forced 4 or 5 cannot. ```json {"symbol":"NVDA","output":"simple","questions":{"where_in_range":{"type":"score","of":"range_position_pct","from":0,"to":100,"rungs":5},"heat":{"type":"score","of":"rsi","from":30,"to":70,"rungs":10}}} ``` ### Which supports are actually holding [Rank — an order] A rank only tells you something when the options can separate. Five conditions that are each a single comparison spread out; five that are each an "and" of two often all bottom out at the floor and tie. ```json {"symbol":"NVDA","bars":300,"output":"full","questions":{"supports":{"type":"rank","options":{"above_50d":"C > sma_50","above_200d":"C > sma_200","momentum_up":"rsi > 50","heavy_volume":"V > avg(V,20)","near_52w_high":"range_position_pct > 80"}}}} ``` ### A stop that only ever ratchets up [Recipes] rec(seed, step) defines a series from its own previous value, which "self" refers to inside the step. A chandelier stop is exactly that: the greater of the current level and where it already was. ```json {"symbol":"MSFT","bars":400,"output":"full","questions":{"running_high":{"type":"amount","of":"rec(C, iif(C > self, C, self))","round":2},"chandelier_stop":{"type":"amount","of":"rec(C - 3 * atr(14), greatest(self, C - 3 * atr(14)))","round":2},"still_above_stop":{"type":"yes_no","ask":"C > rec(C - 3 * atr(14), greatest(self, C - 3 * atr(14)))"}}} ``` ### Position sizing, end to end [Recipes] Entry, stop, risk per share, share count and notional in one call, off one price history. Doing it in five calls would risk five different bars and five different ATRs. ```json {"symbol":"MSFT","bars":300,"output":"full","questions":{"entry":{"type":"amount","of":"C","round":2},"stop":{"type":"amount","of":"C - 2 * atr(14)","round":2},"risk_per_share":{"type":"amount","of":"2 * atr(14)","round":2},"shares_for_1000_risk":{"type":"amount","of":"1000 / (2 * atr(14))","round":0},"notional":{"type":"amount","of":"1000 / (2 * atr(14)) * C","round":0},"worth_the_risk":{"type":"yes_no","ask":"(highest(C,252) - C) / (2 * atr(14)) > 3 and C > sma(C,200)"}}} ``` ### A desk screen — every shape at once [Recipes] choice for the regime, score for conviction, rank for what is working, amount for the numbers. One state, one bill, and nothing that can disagree with itself. ```json {"symbol":"NVDA","bars":400,"output":"full","questions":{"regime":{"type":"choice","options":{"trending_up":"C > sma(C,50) and sma(C,50) > sma(C,200) and adx(14) > 25","trending_down":"C < sma(C,50) and sma(C,50) < sma(C,200) and adx(14) > 25","range_bound":"adx(14) < 20","transition":"else"}},"conviction":{"type":"score","of":"adx(14)","from":10,"to":40,"rungs":5},"what_is_working":{"type":"rank","options":{"trend":"C > sma(C,50) and adx(14) > 25","momentum":"rsi > 55 and roc(C,21) > 5","mean_reversion":"rsi < 40 and C < bblower(C,20)","breakout":"C > highest(C,20)","volume":"zscore(V,60) > 1.5"}},"extended":{"type":"yes_no","ask":"(C - sma(C,50)) / atr(14) > 2"},"atrs_to_high":{"type":"amount","of":"(highest(C,252) - C) / atr(14)","round":2}}} ``` ### VWAP, the opening range, and relative volume [Intraday] The three things an intraday trader looks at first. These bars are fetched live from the market data feed, so the call costs 8,000 processed tokens rather than 2,000. ```json {"symbol":"NVDA","prices":{"interval":"5m"},"output":"full","questions":{"above_vwap":{"type":"yes_no","ask":"C > vwap()"},"broke_opening_range":{"type":"yes_no","ask":"C > opening_range_high(30)"},"relative_volume":{"type":"amount","of":"rvol(20)","round":2},"minutes_in":{"type":"amount","of":"minutes_since_open()","round":0},"gap_pct":{"type":"amount","of":"gap()","round":2}}} ``` ### Daily bias, intraday trigger [Intraday] C@1d reads the last daily bar that had already CLOSED, so a rule that mixes timeframes cannot see into its own future. Named fields like rsi stay daily whatever the interval — write rsi(C,14) for the one you asked for. ```json {"symbol":"NVDA","prices":{"interval":"15m","bars":300},"output":"full","questions":{"daily_trend_up":{"type":"yes_no","ask":"C@1d > sma(C@1d, 50)"},"intraday_pullback":{"type":"yes_no","ask":"rsi(C,14) < 40"},"buy_the_dip":{"type":"yes_no","ask":"C@1d > sma(C@1d, 50) and rsi(C,14) < 40"},"intraday_vs_daily":{"type":"amount","of":"(C - C@1d) / C@1d * 100","round":3}}} ``` ### Overbought or oversold [Graded — intermediate] The two classic thresholds side by side, with the reading they came from. ```json {"symbol":"MSFT","bars":400,"output":"full","questions":{"overbought":{"type":"yes_no","ask":"rsi > 70"},"oversold":{"type":"yes_no","ask":"rsi < 30"},"rsi_now":{"type":"amount","of":"rsi","round":1}}} ``` ### Above both moving averages [Graded — intermediate] Three separate states, not one verdict — read them together. ```json {"symbol":"AAPL","bars":400,"output":"full","questions":{"above_50d":{"type":"yes_no","ask":"C > sma_50"},"above_200d":{"type":"yes_no","ask":"C > sma_200"},"golden_state":{"type":"yes_no","ask":"sma_50 > sma_200"}}} ``` ### How far above the 50-day [Graded — intermediate] The same distance in percent and in ATRs. The ATR one compares across symbols. ```json {"symbol":"AMZN","bars":400,"output":"full","questions":{"pct_above_50d":{"type":"amount","of":"(C - sma_50) / sma_50 * 100","round":2},"in_atrs":{"type":"amount","of":"(C - sma_50) / atr(14)","round":2}}} ``` ### Volume against its own average [Graded — intermediate] A multiple, not a raw count. 52m shares means nothing without the average. ```json {"symbol":"TSLA","bars":400,"output":"full","questions":{"heavy":{"type":"yes_no","ask":"V > 2 * avg(V,20)"},"multiple":{"type":"amount","of":"V / avg(V,20)","round":2}}} ``` ### Volatility as a percentage of price [Graded — intermediate] atr_pct is the comparable one; atr() is what you size a stop with. ```json {"symbol":"NVDA","bars":400,"output":"full","questions":{"atr_pct":{"type":"amount","of":"atr_pct(14)","round":2},"wide":{"type":"yes_no","ask":"atr_pct(14) > 3"},"atr_dollars":{"type":"amount","of":"atr(14)","round":2}}} ``` ### Where in the 52-week range [Graded — intermediate] range_position_pct is 0 at the low and 100 at the high. ```json {"symbol":"GOOGL","bars":400,"output":"full","questions":{"position_pct":{"type":"amount","of":"range_position_pct","round":1},"near_high":{"type":"yes_no","ask":"range_position_pct > 80"},"off_high_pct":{"type":"amount","of":"pct_off_high","round":2}}} ``` ### Position inside the Bollinger bands [Graded — intermediate] Turns two bands into one 0-100 number. ```json {"symbol":"META","bars":400,"output":"full","questions":{"above_upper":{"type":"yes_no","ask":"C > bbupper(C,20)"},"below_lower":{"type":"yes_no","ask":"C < bblower(C,20)"},"band_pos_pct":{"type":"amount","of":"(C - bblower(C,20)) / (bbupper(C,20) - bblower(C,20)) * 100","round":1}}} ``` ### MACD above or below its signal [Graded — intermediate] All three lines, so you can see how close the cross is. ```json {"symbol":"JPM","bars":400,"output":"full","questions":{"hist_positive":{"type":"yes_no","ask":"macdhist(C) > 0"},"hist":{"type":"amount","of":"macdhist(C)","round":4},"macd":{"type":"amount","of":"macd(C)","round":4},"signal":{"type":"amount","of":"macdsignal(C)","round":4}}} ``` ### Stochastic at an extreme [Graded — intermediate] %K and %D together — a %K over 80 with %D below it is a different bar. ```json {"symbol":"XOM","bars":400,"output":"full","questions":{"k":{"type":"amount","of":"stochk(14)","round":1},"d":{"type":"amount","of":"stochd(14)","round":1},"overbought":{"type":"yes_no","ask":"stochk(14) > 80"},"oversold":{"type":"yes_no","ask":"stochk(14) < 20"}}} ``` ### Williams %R [Graded — intermediate] Runs -100 to 0, so the comparisons look inverted until you expect it. ```json {"symbol":"MSFT","bars":400,"output":"full","questions":{"willr":{"type":"amount","of":"willr(14)","round":1},"stretched_up":{"type":"yes_no","ask":"willr(14) > -20"},"stretched_down":{"type":"yes_no","ask":"willr(14) < -80"}}} ``` ### Commodity channel index [Graded — intermediate] Unbounded, unlike RSI, so +100/-100 are conventions not limits. ```json {"symbol":"AAPL","bars":400,"output":"full","questions":{"cci":{"type":"amount","of":"cci(20)","round":1},"strong_up":{"type":"yes_no","ask":"cci(20) > 100"},"strong_down":{"type":"yes_no","ask":"cci(20) < -100"}}} ``` ### Is anything actually trending [Graded — intermediate] ADX is direction-free. Under 20 most trend rules are noise. ```json {"symbol":"NVDA","bars":400,"output":"full","questions":{"adx":{"type":"amount","of":"adx(14)","round":1},"trending":{"type":"yes_no","ask":"adx(14) > 25"},"directionless":{"type":"yes_no","ask":"adx(14) < 20"}}} ``` ### Which way the directional movement points [Graded — intermediate] The two components ADX is built from. ```json {"symbol":"AMZN","bars":400,"output":"full","questions":{"di_plus":{"type":"amount","of":"diplus(14)","round":1},"di_minus":{"type":"amount","of":"diminus(14)","round":1},"bulls_ahead":{"type":"yes_no","ask":"diplus(14) > diminus(14)"}}} ``` ### Rate of change over three horizons [Graded — intermediate] Week, month, quarter. Disagreement between them is the signal. ```json {"symbol":"GOOGL","bars":400,"output":"full","questions":{"roc_5":{"type":"amount","of":"roc(C,5)","round":2},"roc_21":{"type":"amount","of":"roc(C,21)","round":2},"roc_63":{"type":"amount","of":"roc(C,63)","round":2}}} ``` ### Above, below, or sitting on the 200-day [Graded — intermediate] A choice with a real else — the 5% band around the line. ```json {"symbol":"META","bars":400,"output":"full","questions":{"vs_200d":{"type":"choice","options":{"well_above":"C > sma(C,200) * 1.05","well_below":"C < sma(C,200) * 0.95","close_to_it":"else"}}}} ``` ### RSI on a ten-rung ladder [Graded — intermediate] A score lands between rungs, so 6.36 says more than 'above 50'. ```json {"symbol":"TSLA","bars":400,"output":"full","questions":{"heat":{"type":"score","of":"rsi","from":20,"to":80,"rungs":10},"rsi":{"type":"amount","of":"rsi","round":1}}} ``` ### The four levels that matter [Graded — intermediate] No judgement at all — just the numbers a judgement gets read off. ```json {"symbol":"NVDA","bars":400,"output":"full","questions":{"close":{"type":"amount","of":"C","round":2},"sma_50":{"type":"amount","of":"sma(C,50)","round":2},"sma_200":{"type":"amount","of":"sma(C,200)","round":2},"high_252":{"type":"amount","of":"highest(C,252)","round":2}}} ``` ### Three days of direction [Graded — intermediate] C[-1] and C[-2] are the previous bars. Chained with and. ```json {"symbol":"AAPL","bars":400,"output":"full","questions":{"up_today":{"type":"yes_no","ask":"C > C[-1]"},"three_up":{"type":"yes_no","ask":"C > C[-1] and C[-1] > C[-2] and C[-2] > C[-3]"},"day_return_pct":{"type":"amount","of":"(C - C[-1]) / C[-1] * 100","round":2}}} ``` ### The gap between the two averages [Graded — intermediate] Widening or narrowing matters more than the sign. ```json {"symbol":"MSFT","bars":400,"output":"full","questions":{"spread_pct":{"type":"amount","of":"(sma(C,50) - sma(C,200)) / sma(C,200) * 100","round":2},"widening":{"type":"yes_no","ask":"sma(C,50) - sma(C,200) > ref(sma(C,50) - sma(C,200), -10)"}}} ``` ### Is volume confirming the move [Graded — intermediate] Up on volume is a different bar from up on nothing. ```json {"symbol":"XOM","bars":400,"output":"full","questions":{"vol_5d_vs_60d":{"type":"amount","of":"avg(V,5) / avg(V,60)","round":2},"confirming":{"type":"yes_no","ask":"C > C[-1] and V > avg(V,20)"}}} ``` ### On-balance volume direction [Graded — intermediate] OBV is a running total, so only its CHANGE means anything. ```json {"symbol":"AMZN","bars":400,"output":"full","questions":{"obv_rising":{"type":"yes_no","ask":"obv() > ref(obv(), -10)"},"obv":{"type":"amount","of":"obv()","round":0}}} ``` ### Volatility-adjusted breakout [Graded — advanced] Half an ATR of buffer separates a break from a wick. ```json {"symbol":"NVDA","bars":400,"output":"full","questions":{"clean_break":{"type":"yes_no","ask":"C > highest(C,20) + 0.5 * atr(14)"},"any_break":{"type":"yes_no","ask":"C > highest(C,20)"},"buffer_atrs":{"type":"amount","of":"(C - highest(C,20)) / atr(14)","round":2}}} ``` ### Volume as a z-score, not a multiple [Graded — advanced] Z-scores are comparable across symbols; multiples are not. ```json {"symbol":"AAPL","bars":400,"output":"full","questions":{"z":{"type":"amount","of":"zscore(V, 60)","round":2},"unusual":{"type":"yes_no","ask":"zscore(V, 60) > 2"},"quiet":{"type":"yes_no","ask":"zscore(V, 60) < -1"}}} ``` ### Where this month's return ranks [Graded — advanced] percentrank puts today in the context of its own year. ```json {"symbol":"AMZN","bars":400,"output":"full","questions":{"pctile":{"type":"amount","of":"percentrank(roc(C,21), 252)","round":1},"top_decile":{"type":"yes_no","ask":"percentrank(roc(C,21), 252) > 90"}}} ``` ### Trend slope from a regression, not a cross [Graded — advanced] A moving-average cross is late; a slope is not. ```json {"symbol":"GOOGL","bars":400,"output":"full","questions":{"slope_pct":{"type":"amount","of":"(linreg(C,60) - ref(linreg(C,60), -20)) / C * 100","round":2},"rising":{"type":"yes_no","ask":"linreg(C,60) > ref(linreg(C,60), -20)"},"fit_now":{"type":"amount","of":"linreg(C,60)","round":2}}} ``` ### How long since it was above the 50-day [Graded — advanced] barssince turns a state into an age. ```json {"symbol":"META","bars":400,"output":"full","questions":{"bars_since":{"type":"amount","of":"barssince(C > sma(C,50))","round":0},"bars_since_rsi70":{"type":"amount","of":"barssince(rsi > 70)","round":0}}} ``` ### How long since the extremes [Graded — advanced] highestbars is 0 on the day the high is made. ```json {"symbol":"TSLA","bars":400,"output":"full","questions":{"bars_since_high":{"type":"amount","of":"highestbars(C,252)","round":0},"bars_since_low":{"type":"amount","of":"lowestbars(C,252)","round":0},"high_252":{"type":"amount","of":"highest(C,252)","round":2},"low_252":{"type":"amount","of":"lowest(C,252)","round":2}}} ``` ### Four consecutive closes [Graded — advanced] Chained offsets. Each `and` takes the weakest link. ```json {"symbol":"XOM","bars":400,"output":"full","questions":{"four_up":{"type":"yes_no","ask":"C > C[-1] and C[-1] > C[-2] and C[-2] > C[-3] and C[-3] > C[-4]"},"four_down":{"type":"yes_no","ask":"C < C[-1] and C[-1] < C[-2] and C[-2] < C[-3] and C[-3] < C[-4]"}}} ``` ### Momentum that is itself accelerating [Graded — advanced] ref(rsi,-1) reads the previous bar's RSI. ```json {"symbol":"NVDA","bars":400,"output":"full","questions":{"rsi_rising":{"type":"yes_no","ask":"rsi > ref(rsi, -1) and ref(rsi, -1) > ref(rsi, -2)"},"rsi_change_5d":{"type":"amount","of":"rsi - ref(rsi, -5)","round":2},"roc_accelerating":{"type":"yes_no","ask":"roc(C,5) > roc(C,21)"}}} ``` ### Intraday: is this bar busy [Graded — advanced] rvol compares this bar with the previous n, not with a daily average. ```json {"symbol":"TSLA","output":"full","questions":{"rvol":{"type":"amount","of":"rvol(20)","round":2},"busy":{"type":"yes_no","ask":"rvol(20) > 2"},"minutes_in":{"type":"amount","of":"minutes_since_open()","round":0},"clock":{"type":"amount","of":"time_of_day()","round":0}},"prices":{"interval":"5m"}} ``` ### How far intraday has run from the daily close [Graded — advanced] The clean way to measure an intraday move. ```json {"symbol":"MSFT","output":"full","questions":{"gap_from_daily_pct":{"type":"amount","of":"(C - C@1d) / C@1d * 100","round":3},"above_daily":{"type":"yes_no","ask":"C > C@1d"}},"prices":{"interval":"15m","bars":300}} ``` ### Distance to the high, measured in volatility [Graded — advanced] Percent flatters a quiet stock. ATRs do not. ```json {"symbol":"AMZN","bars":400,"output":"full","questions":{"atrs_to_high":{"type":"amount","of":"(highest(C,252) - C) / atr(14)","round":2},"within_two_atrs":{"type":"yes_no","ask":"(highest(C,252) - C) / atr(14) < 2"},"pct_to_high":{"type":"amount","of":"(highest(C,252) - C) / C * 100","round":2}}} ``` ### Is volatility itself unusual [Graded — advanced] Volatility has its own percentile, and it mean-reverts. ```json {"symbol":"GOOGL","bars":400,"output":"full","questions":{"atr_pct":{"type":"amount","of":"atr_pct(14)","round":2},"atr_pctile":{"type":"amount","of":"percentrank(atr_pct(14), 252)","round":1},"vol_expanding":{"type":"yes_no","ask":"atr(14) > avg(atr(14), 50)"}}} ``` ### A cross that also needs confirmation [Graded — advanced] Three versions of the same idea, each stricter. ```json {"symbol":"META","bars":400,"output":"full","questions":{"macd_up":{"type":"yes_no","ask":"macdhist(C) > 0"},"and_trending":{"type":"yes_no","ask":"macdhist(C) > 0 and adx(14) > 20"},"and_above_50d":{"type":"yes_no","ask":"macdhist(C) > 0 and adx(14) > 20 and C > sma(C,50)"}}} ``` ### Trend with volume behind it [Graded — advanced] Adding the volume leg is what separates them. ```json {"symbol":"JPM","bars":400,"output":"full","questions":{"trend":{"type":"yes_no","ask":"C > sma(C,50) and sma(C,50) > sma(C,200)"},"with_volume":{"type":"yes_no","ask":"C > sma(C,50) and sma(C,50) > sma(C,200) and avg(V,5) > avg(V,60)"},"vol_ratio":{"type":"amount","of":"avg(V,5) / avg(V,60)","round":2}}} ``` ### A mean-reversion setup, fully specified [Graded — advanced] Oversold inside an uptrend is a different trade from oversold. ```json {"symbol":"XOM","bars":400,"output":"full","questions":{"stretched_down":{"type":"yes_no","ask":"rsi < 35 and C < bblower(C,20)"},"but_still_in_uptrend":{"type":"yes_no","ask":"rsi < 35 and C < bblower(C,20) and C > sma(C,200)"},"atrs_below_20d":{"type":"amount","of":"(C - sma(C,20)) / atr(14)","round":2}}} ``` ### Price and volume disagreeing [Graded — advanced] The divergence question is the third one, built from the first two. ```json {"symbol":"AAPL","bars":400,"output":"full","questions":{"price_up_21d":{"type":"yes_no","ask":"roc(C,21) > 0"},"obv_up_21d":{"type":"yes_no","ask":"obv() > ref(obv(), -21)"},"divergence":{"type":"yes_no","ask":"roc(C,21) > 0 and obv() < ref(obv(), -21)"}}} ``` ### Four-way regime with a real catch-all [Graded — complex] Five options, one of them else. Never use `true` for that. ```json {"symbol":"AAPL","bars":400,"output":"full","questions":{"regime":{"type":"choice","options":{"markup":"C > sma(C,50) and sma(C,50) > sma(C,200) and adx(14) > 22","markdown":"C < sma(C,50) and sma(C,50) < sma(C,200) and adx(14) > 22","accumulation":"adx(14) < 18 and C > sma(C,200)","distribution":"adx(14) < 18 and C < sma(C,200)","in_between":"else"}},"adx":{"type":"amount","of":"adx(14)","round":1},"above_200d":{"type":"yes_no","ask":"C > sma(C,200)"}}} ``` ### Trade setup with its own risk and reward [Graded — complex] The verdict and the arithmetic that justifies it, together. ```json {"symbol":"AMZN","bars":400,"output":"full","questions":{"setup":{"type":"choice","options":{"long_continuation":"C > sma(C,50) and rsi > 55 and adx(14) > 20","long_pullback":"C > sma(C,200) and rsi < 45","no_trade":"else"}},"entry":{"type":"amount","of":"C","round":2},"stop":{"type":"amount","of":"C - 2 * atr(14)","round":2},"target":{"type":"amount","of":"highest(C,252)","round":2},"reward_risk":{"type":"amount","of":"(highest(C,252) - C) / (2 * atr(14))","round":2},"acceptable":{"type":"yes_no","ask":"(highest(C,252) - C) / (2 * atr(14)) > 2"}}} ``` ### Intraday execution plan [Graded — complex] Levels and a trigger, on five-minute bars. ```json {"symbol":"NVDA","output":"full","questions":{"above_vwap":{"type":"yes_no","ask":"C > vwap()"},"broke_or":{"type":"yes_no","ask":"C > opening_range_high(30)"},"busy":{"type":"yes_no","ask":"rvol(20) > 1.5"},"trigger":{"type":"choice","options":{"long_break":"C > opening_range_high(30) and C > vwap() and rvol(20) > 1.5","short_break":"C < opening_range_low(30) and C < vwap() and rvol(20) > 1.5","wait":"else"}},"or_high":{"type":"amount","of":"opening_range_high(30)","round":2},"or_low":{"type":"amount","of":"opening_range_low(30)","round":2},"vwap":{"type":"amount","of":"vwap()","round":2}},"prices":{"interval":"5m"}} ``` ### Trend quality scorecard [Graded — complex] Four ladders plus a rank — no single number hides the parts. ```json {"symbol":"GOOGL","bars":400,"output":"full","questions":{"structure":{"type":"score","of":"adx(14)","from":10,"to":45,"rungs":5},"participation":{"type":"score","of":"avg(V,5) / avg(V,60)","from":0.6,"to":1.8,"rungs":5},"extension":{"type":"score","of":"(C - sma(C,50)) / atr(14)","from":-2,"to":4,"rungs":5},"components":{"type":"rank","options":{"above_20d":"C > sma(C,20)","above_50d":"C > sma(C,50)","above_200d":"C > sma(C,200)","ma_stacked":"sma(C,20) > sma(C,50) and sma(C,50) > sma(C,200)","adx_ok":"adx(14) > 25"}}}} ``` ### Breakout readiness [Graded — complex] Coiled, quiet, and near the high. The trigger level is in the answer. ```json {"symbol":"META","bars":400,"output":"full","questions":{"squeezed":{"type":"yes_no","ask":"bbupper(C,20) - bblower(C,20) < 0.85 * avg(bbupper(C,20) - bblower(C,20), 50)"},"coiled_near_high":{"type":"yes_no","ask":"range_position_pct > 70 and bbupper(C,20) - bblower(C,20) < 0.85 * avg(bbupper(C,20) - bblower(C,20), 50)"},"dry_volume":{"type":"yes_no","ask":"avg(V,5) < avg(V,60)"},"ready":{"type":"yes_no","ask":"range_position_pct > 70 and avg(V,5) < avg(V,60) and adx(14) < 20"},"trigger_level":{"type":"amount","of":"highest(C,20)","round":2},"bars_since_high":{"type":"amount","of":"highestbars(C,60)","round":0}}} ``` ### Mean reversion or momentum, decided [Graded — complex] ADX picks which playbook applies before RSI is read. ```json {"symbol":"TSLA","bars":400,"output":"full","questions":{"which":{"type":"choice","options":{"buy_momentum":"adx(14) > 25 and rsi > 55 and C > sma(C,50)","fade_the_move":"adx(14) < 20 and rsi > 70","buy_the_dip":"adx(14) < 20 and rsi < 30 and C > sma(C,200)","nothing":"else"}},"adx":{"type":"amount","of":"adx(14)","round":1},"rsi":{"type":"amount","of":"rsi","round":1},"regime_is_trending":{"type":"yes_no","ask":"adx(14) > 25"}}} ``` ### Risk dashboard for a position you hold [Graded — complex] Ratcheting stop, drawdown, and an explicit exit condition. ```json {"symbol":"JPM","bars":400,"output":"full","questions":{"stop":{"type":"amount","of":"rec(C - 3 * atr(14), greatest(self, C - 3 * atr(14)))","round":2},"room_to_stop_pct":{"type":"amount","of":"(C - rec(C - 3 * atr(14), greatest(self, C - 3 * atr(14)))) / C * 100","round":2},"drawdown_from_high_pct":{"type":"amount","of":"(C - rec(C, iif(C > self, C, self))) / rec(C, iif(C > self, C, self)) * 100","round":2},"still_above_stop":{"type":"yes_no","ask":"C > rec(C - 3 * atr(14), greatest(self, C - 3 * atr(14)))"},"trend_intact":{"type":"yes_no","ask":"C > sma(C,50)"},"exit_signal":{"type":"yes_no","ask":"C < rec(C - 3 * atr(14), greatest(self, C - 3 * atr(14))) or C < sma(C,200)"}}} ``` ### Volatility regime drives the size [Graded — complex] Two sizes for two regimes, so you can see the difference. ```json {"symbol":"NVDA","bars":400,"output":"full","questions":{"atr_pct":{"type":"amount","of":"atr_pct(14)","round":2},"vol_regime":{"type":"choice","options":{"calm":"atr_pct(14) < 2","normal":"atr_pct(14) < 4","wild":"else"}},"shares_calm":{"type":"amount","of":"1000 / (1.5 * atr(14))","round":0},"shares_wild":{"type":"amount","of":"1000 / (3 * atr(14))","round":0},"vol_expanding":{"type":"yes_no","ask":"atr(14) > avg(atr(14), 50)"}}} ``` ### Six strategies, ranked against each other [Graded — complex] A rank only means something when the options can separate. ```json {"symbol":"MSFT","bars":400,"output":"full","questions":{"strategies":{"type":"rank","options":{"trend_follow":"C > sma(C,50) and sma(C,50) > sma(C,200) and adx(14) > 25","breakout":"C > highest(C,20) and V > avg(V,20)","pullback":"C > sma(C,200) and rsi < 45","mean_revert":"rsi < 35 and C < bblower(C,20)","momentum":"roc(C,21) > 5 and rsi > 55","squeeze":"bbupper(C,20) - bblower(C,20) < 0.8 * avg(bbupper(C,20) - bblower(C,20), 50)"}},"best_is_strong":{"type":"yes_no","ask":"C > sma(C,50) and sma(C,50) > sma(C,200) and adx(14) > 25"}}} ``` ### Squeeze, direction, then size [Graded — complex] Three decisions in the order you actually make them. ```json {"symbol":"AAPL","bars":400,"output":"full","questions":{"squeezed":{"type":"yes_no","ask":"bbupper(C,20) - bblower(C,20) < 0.85 * avg(bbupper(C,20) - bblower(C,20), 50)"},"lean":{"type":"choice","options":{"up":"C > sma(C,50) and macdhist(C) > 0","down":"C < sma(C,50) and macdhist(C) < 0","flat":"else"}},"trigger_up":{"type":"amount","of":"highest(C,20)","round":2},"trigger_down":{"type":"amount","of":"lowest(C,20)","round":2},"stop_atrs":{"type":"amount","of":"1.5 * atr(14)","round":2},"shares_1k":{"type":"amount","of":"1000 / (1.5 * atr(14))","round":0}}} ``` ### A plan that changes with the daily trend [Graded — complex] Higher timeframe sets the direction, lower one the entry. ```json {"symbol":"TSLA","output":"full","questions":{"daily_uptrend":{"type":"yes_no","ask":"C@1d > sma(C@1d, 50) and sma(C@1d, 50) > sma(C@1d, 200)"},"daily_rsi":{"type":"amount","of":"rsi(C@1d, 14)","round":1},"intraday_above_vwap":{"type":"yes_no","ask":"C > vwap()"},"aligned":{"type":"yes_no","ask":"C@1d > sma(C@1d, 50) and C > vwap()"},"plan":{"type":"choice","options":{"long_only":"C@1d > sma(C@1d, 50)","short_only":"C@1d < sma(C@1d, 50)","either":"else"}}},"prices":{"interval":"15m","bars":300}} ``` ### An earnings-aware setup [Graded — complex] The four earnings fields come from the earnings table, not from bars. ```json {"symbol":"AMZN","bars":400,"output":"full","questions":{"days_since_earnings":{"type":"amount","of":"days_since_earnings","round":0},"eps_surprise_pct":{"type":"amount","of":"eps_surprise_pct","round":2},"beat_and_trending":{"type":"yes_no","ask":"eps_surprise_pct > 0 and C > sma(C,50)"},"post_earnings_drift":{"type":"yes_no","ask":"eps_surprise_pct > 5 and days_since_earnings < 45 and C > sma(C,50)"},"revenue_beat":{"type":"yes_no","ask":"revenue_surprise_pct > 0"}}} ``` ### One conviction number from many parts [Graded — complex] Four ladders kept separate, with one strict all-or-nothing check. ```json {"symbol":"GOOGL","bars":400,"output":"full","questions":{"trend":{"type":"score","of":"(C - sma(C,200)) / atr(14)","from":-3,"to":6,"rungs":5},"momentum":{"type":"score","of":"rsi","from":30,"to":70,"rungs":5},"structure":{"type":"score","of":"adx(14)","from":10,"to":40,"rungs":5},"position":{"type":"score","of":"range_position_pct","from":0,"to":100,"rungs":5},"all_four_positive":{"type":"yes_no","ask":"C > sma(C,200) and rsi > 50 and adx(14) > 20 and range_position_pct > 50"}}} ``` ### How much the stop distance changes the size [Graded — complex] The same risk budget, three stops, three very different positions. ```json {"symbol":"META","bars":400,"output":"full","questions":{"atr":{"type":"amount","of":"atr(14)","round":2},"shares_1atr":{"type":"amount","of":"1000 / atr(14)","round":0},"shares_2atr":{"type":"amount","of":"1000 / (2 * atr(14))","round":0},"shares_3atr":{"type":"amount","of":"1000 / (3 * atr(14))","round":0},"notional_2atr":{"type":"amount","of":"1000 / (2 * atr(14)) * C","round":0},"tight_stop_too_big":{"type":"yes_no","ask":"1000 / atr(14) * C > 50000"}}} ``` ### Nested logic with real parentheses [Graded — complex] `a and (b or c)` needs them. `not` inverts a belief. ```json {"symbol":"JPM","bars":400,"output":"full","questions":{"either_trend":{"type":"yes_no","ask":"(C > sma(C,50) and adx(14) > 25) or (C > sma(C,200) and rsi > 60)"},"trend_and_not_extended":{"type":"yes_no","ask":"(C > sma(C,50) and sma(C,50) > sma(C,200)) and not ((C - sma(C,50)) / atr(14) > 3)"},"any_of_three":{"type":"yes_no","ask":"rsi > 70 or C > bbupper(C,20) or zscore(V,60) > 2"}}} ``` ### A portfolio view of one name [Graded — complex] Verdict, weights, conviction and size — what you would actually write down. ```json {"symbol":"AAPL","bars":400,"output":"full","questions":{"verdict":{"type":"choice","options":{"add":"C > sma(C,50) and rsi < 65 and adx(14) > 20","hold":"C > sma(C,200)","trim":"else"}},"weights":{"type":"rank","options":{"trend":"C > sma(C,200)","momentum":"roc(C,63) > 0","quality_of_trend":"adx(14) > 25","not_extended":"(C - sma(C,50)) / atr(14) < 2"}},"conviction":{"type":"score","of":"adx(14)","from":10,"to":40,"rungs":5},"entry":{"type":"amount","of":"C","round":2},"stop":{"type":"amount","of":"C - 2 * atr(14)","round":2},"shares_1k_risk":{"type":"amount","of":"1000 / (2 * atr(14))","round":0}}} ``` ### Everything the engine knows, in one call [Graded — complex] Eleven questions, one price history, one bill. ```json {"symbol":"XOM","bars":400,"output":"full","questions":{"price":{"type":"amount","of":"C","round":2},"rsi":{"type":"amount","of":"rsi","round":1},"adx":{"type":"amount","of":"adx(14)","round":1},"atr_pct":{"type":"amount","of":"atr_pct(14)","round":2},"range_pos":{"type":"amount","of":"range_position_pct","round":1},"vol_z":{"type":"amount","of":"zscore(V,60)","round":2},"macd_hist":{"type":"amount","of":"macdhist(C)","round":4},"bb_pos":{"type":"amount","of":"(C - bblower(C,20)) / (bbupper(C,20) - bblower(C,20)) * 100","round":1},"trend":{"type":"yes_no","ask":"C > sma(C,50) and sma(C,50) > sma(C,200)"},"regime":{"type":"choice","options":{"trending":"adx(14) > 25","quiet":"adx(14) < 18","mixed":"else"}},"quality":{"type":"score","of":"adx(14)","from":10,"to":40,"rungs":5}}} ``` ### A long-history question [Graded — complex] 800 bars. Longest lookback plus the 60-bar scale window. ```json {"symbol":"SPY","bars":800,"output":"full","questions":{"above_200d":{"type":"yes_no","ask":"C > sma(C,200)"},"drawdown_pct":{"type":"amount","of":"(C - highest(C,252)) / highest(C,252) * 100","round":2},"vol_pctile":{"type":"amount","of":"percentrank(atr_pct(14), 252)","round":1},"bars_since_high":{"type":"amount","of":"highestbars(C,252)","round":0},"regime":{"type":"choice","options":{"risk_on":"C > sma(C,200) and atr_pct(14) < 2","risk_off":"C < sma(C,200)","choppy":"else"}}}} ``` ## Checklist before you send 1. `decision_type` OR `questions`, never both. 2. Every parameter is one the preset actually takes — unknown ones are rejected. 3. A choice catch-all uses "else", not "true". 4. `bars` is at least as long as the longest lookback you asked for. 5. You are reading `status`, not assuming success. 6. You are not presenting confidence as a probability of being right.