# Phrasing families

> The ways of saying a condition that the engine understands beyond a plain comparison: ranges, streaks, recency, relative volume and volatility-scaled moves.

Source: https://docs.texttoquant.com/reference/phrasings

---

Most conditions are a plain comparison: a value against a number or another series. But a lot of
real trading ideas are not shaped like that. They talk about **a window**, where price sits in its
yearly range, how long ago something happened, whether a state held for five bars, how big a move is
*relative to* current volatility.

Each of those is a **phrasing family**: a way of saying a condition that gets compiled into exact
maths on your bars. They are not a special mode or a separate syntax, write them in an ordinary
strategy sentence and combine them freely with everything else (AND/OR, direction, sessions,
higher-timeframe gates, stops and targets).

<Callout variant="tip" title="Copy the phrasing, change the numbers">
  Each example below is a working phrase. The wording in **bold** is what the parser keys on, keep
  it and swap the asset, the numbers and the window. Every family has a ready-to-run card in the
  [templates gallery](/templates) under **Windows & streaks**.
</Callout>

## Where a value sits in a window

| Say this | What the engine checks |
| --- | --- |
| price is in the **bottom 20% of its 52-week range** | `close ≤ lowest(low, N) + 0.20 × (highest(high, N) − lowest(low, N))` |
| price trades in the **top 10% of the 20-day range** | the same band, measured from the top |
| price is in the **lower quartile of its 100-bar range** | quartile / third / half / decile all work |
| price is **20% below its all-time high** | `close ≤ 0.80 × all-time high` |
| price is **within 5% of the 52-week high** | proximity to one extreme |

The window is read on **your** timeframe: a 52-week range on the 1d is 364 bars; a 6-month range on
the 4h is 1080.

<Callout variant="info" title="Range position is not distance from the high">
  "20% below the high" anchors on **one** extreme. "The bottom 20% of the range" anchors on the
  **whole** range: high *and* low. In an asymmetric range these are different conditions, and the
  parser keeps them apart. Say the one you mean.
</Callout>

## Where the bar closed inside itself

A close-strength (close-location) filter, for when a bar closing on its high means something
different from a bar closing on its low.

| Say this | What the engine checks |
| --- | --- |
| the candle **closes in the top 25% of its range** | `close ≥ low + 0.75 × (high − low)` |
| price **closes in the lower third of the bar** | `close ≤ low + 0.333 × (high − low)` |
| the bar **closes near its high** | the top quarter of the bar (the documented convention) |

The deciding difference from the family above is the window: **a bar/candle range** (or no window at
all) is this one; **an N-day / N-week / N-bar range** is range position.

## How long ago something happened

| Say this | What the engine checks |
| --- | --- |
| **at least 10 bars since** RSI was above 70 | `bars since (RSI > 70) ≥ 10` |
| **fewer than 5 bars since** the MACD crossed its signal | `bars since (cross) < 5` |
| **more than 20 bars since** price closed above the 200 EMA | `bars since (close > EMA200) > 20` |

<Callout variant="warning" title="This is the opposite of a live filter">
  "10 bars since RSI was above 70" does **not** mean RSI is above 70 now, it means it isn't, and
  hasn't been for a while. Use `at least / more than` for a cooldown and `fewer than / within` for
  freshness.
</Callout>

## A state or a direction that has to persist

| Say this | What the engine checks |
| --- | --- |
| RSI has been **above 50 for 5 consecutive bars** | the comparison holds on every one of the last 5 bars |
| price has **stayed above the 20 EMA for 3 bars in a row** | same, against a series |
| the 50 EMA has been **rising for 5 consecutive bars** | the EMA is higher than the bar before on each of those 5 steps |
| RSI has been **falling for 3 bars** | the mirror |

Compare with the *proportion* form, which is a different question: **60% of the last 20 bars** closed
above the 50 EMA counts bars in a state; **for 5 consecutive bars** requires all of them.

<Callout variant="info" title="Level vs slope">
  "the 10-bar ROC is **positive**" is a level (`ROC > 0`). "the 10-bar ROC is **rising**" is a
  direction. Adding "for 3 bars" to either one makes it persist; it does not turn one into the other.
</Callout>

## Volume, normalised

Raw volume thresholds don't travel between assets or across years. These do.

| Say this | What the engine checks |
| --- | --- |
| **relative volume is above 2** (or **RVOL > 2**) | `volume ≥ 2 × mean(volume, 20)`, twice normal participation |
| relative volume above 1.5 **over the last 50 bars** | the same, averaged over 50 bars |
| **dollar volume is above $10 million** | `close × volume ≥ 10,000,000` |
| **turnover** above $5M | same as dollar volume |
| volume is **twice its 20-bar average** | `volume ≥ 2 × mean(volume, 20)` |

<Callout variant="warning" title="RVOL is a multiple, never a unit count">
  "relative volume above 2" means twice the average, not two units, which would be true on every
  bar. If you want an absolute floor, say `volume is above 1,000,000`.
</Callout>

## A series at its own extreme

For any non-price series (RSI, ADX, ATR, volume, MACD) this adapts instead of waiting for a fixed
level a strong asset may never reach.

| Say this | What the engine checks |
| --- | --- |
| RSI is **at its lowest in 50 bars** | `RSI ≤ lowest(RSI, 50)` |
| volume is **the highest of the last 20 bars** | `volume ≥ highest(volume, 20)` |
| ADX is **at a 100-bar high** | `ADX ≥ highest(ADX, 100)` |

For **price** extremes, use the breakout wording instead: `price makes a new 20-day high`,
`price breaks above the 20-bar high`, which routes to the breakout/Donchian logic.

## Moves measured in volatility

The same percentage means something different in a calm month and a violent one. Measuring in ATR
keeps the rule honest across regimes.

| Say this | What the engine checks |
| --- | --- |
| price has **fallen more than 2 ATR in the last 5 bars** | `close − close[5] ≤ −2 × ATR(14)` |
| price has **risen more than 1.5 ATR(20) over the last 10 bars** | with an explicit ATR period |
| price has **moved more than 3 ATR over the last 10 bars** | undirected, both tails fire |
| price closes **more than 2 ATR above the previous close** | the single-bar form |
| price is **2 ATR below the 20 EMA** | an overextension band around an indicator |

## Intraday anchors: the opening range and today's levels

These read the CURRENT day, and reset at each new day.

| Say this | What the engine checks |
| --- | --- |
| price **breaks above the high of the first 30 minutes** | the first-N-bars range of the day, broken |
| price **breaks below the low of the first 4 bars** | the same range, downside |
| price is **above today's open** | `close > day_open` |
| price hits **the day's high** / **the low of the day** | the running extreme so far today |
| price is **down more than 3% on the day** | `close ≤ 0.97 × day_open` |

<Callout variant="info" title="Today is not yesterday, and the day is not a session">
  "Today's high" is the running extreme **so far today**; "yesterday's high" is a completed
  previous-day level. They are different conditions, and the parser keeps them apart. And if
  you name a **session**, "the New York session opening range", that anchors to the session
  open instead of the day open, which is a separate (also supported) level.
</Callout>

<Callout variant="warning" title="The opening range only exists once it is complete">
  Until the first N bars of the day have printed, the range is undefined and the condition is
  false. That is deliberate: a breakout judged against a range still being built would be
  reading bars the strategy has not traded through.
</Callout>

## Gaps, coils and bar shape

| Say this | What the engine checks |
| --- | --- |
| price **fills yesterday's gap** / **the gap closes** | price trades back through the previous day's close |
| the **last 10 bars have a range under 3%** | `highest(high,10) − lowest(low,10) ≤ 0.03 × close` |
| the **20-bar range is tighter than 5%** | the same, wider window |
| the **candle range is more than 1.5 ATR** | `(high − low) ≥ 1.5 × ATR(14)` |
| the **body is bigger than 1 ATR(20)** | `\|close − open\| ≥ 1 × ATR(20)` |
| price is **within 0.1% of a round 1000 level** | distance to the nearest multiple of 1000 |

<Callout variant="info" title="Gap fill is direction-agnostic on purpose">
  "Fills the gap" says nothing about which way the gap went, so the condition fires when price
  touches the previous close from either side. Say "gaps down 2%" separately if you want to
  detect the gap itself as well.
</Callout>

<Callout variant="warning" title="Round levels: state the increment, and keep the tolerance tight">
  "A round number" could mean 100, 1000 or 10000, the parser will ask rather than guess, so
  say which ("a round 1000 level", "multiples of 500"). And because the tolerance is a percent
  of price, it must be tighter than half the increment: at BTC prices, "within 1% of a round
  1000 level" is wider than the gap between levels, so every bar qualifies.
</Callout>

## Ranking a series against its own history

| Say this | What the engine checks |
| --- | --- |
| RSI is in the **bottom 10% of its last 100 readings** | percent rank of RSI over 100 bars ≤ 10 |
| ATR is in the **top 5% of the last 200 bars** | percent rank ≥ 95 |
| volatility is **in its bottom quartile** | the regime filter (no explicit window needed) |

The explicit "of its last N readings" form works **in exits too**, and composes inside larger
expressions, the shorter regime phrasing is an entry filter.

## How this asset moves against another one

These are relationships, not filters. "Only while BTC is above its 200 EMA" asks about BTC;
these ask how the two assets move **together**.

| Say this | What the engine checks |
| --- | --- |
| the **30-day correlation between ETH and BTC** drops below 0.5 | correlation of the two **return** series over 30 bars |
| the **correlation to BTC** is above 0.8 | the same, against the asset you are trading |
| ETH has **outperformed BTC over the last 30 days** | ETH's 30-bar growth ≥ BTC's 30-bar growth |
| it is **underperforming SPY** over 60 bars | the mirror |
| ETH **outperforms BTC by more than 5%** over 20 days | with an excess-return margin |
| the **ETH/BTC ratio is at a 50-bar high** | the pair ratio at its own rolling extreme |
| the **60-day beta to SPY** is above 1.5 | the OLS slope of this asset's returns on the benchmark's |

<Callout variant="info" title="Correlation and beta are measured on returns">
  Two assets that both trend up have a price correlation near 1 no matter how differently
  they behave, so a price-based answer would be meaningless. These conditions compare the
  **return** series, which is what the words mean to a trader.
</Callout>

<Callout variant="warning" title="Name the other asset">
  "Beats the market" and "correlated with everything" have no series behind them, so the
  parser will not invent one. Say the symbol: `outperformed BTC`, `beta to SPY`,
  `correlation between ETH and BTC`.
</Callout>

## Exits that depend on the trade, not on a price

Most exits are a level, a stop, a target, an indicator crossing. These four depend on how
the trade itself has behaved, which no price condition can express.

| Say this | What the engine does |
| --- | --- |
| **exit if the trade isn't profitable after 10 bars** | at bar 10 onward, close on the first bar that is not in profit |
| **give it 20 candles to work** then cut it | the same, in the other phrasing |
| **exit if it gives back half of the open profit** | close once the open profit falls to half its peak |
| **close it if it retraces 30% of the gain** | the same, at 30% |
| **exit on the first profitable close** | close on the first bar strictly beyond your entry |
| **hold for a maximum of 5 days** | a hard time cap, converted to bars for your timeframe |
| **exit when price closes below the lowest low of the last 5 bars** | a structure trail |

<Callout variant="info" title="A time box is not a time exit">
  "Exit after 10 bars" closes the trade no matter what, winners included. "Exit if it isn't
  profitable after 10 bars" spares the winner and cuts only the trade that failed to work.
  Both are supported; they are different strategies, so say the one you mean.
</Callout>

<Callout variant="info" title="Give-back is not a trailing stop">
  A trailing stop follows price by a fixed distance. A give-back is a fraction of the profit
  the trade has already shown, so it tightens automatically as the winner grows. It also
  arms only once the trade has genuinely been green, the peak is measured on closes, so a
  wick through your entry cannot turn it into a second stop-loss.
</Callout>

<Callout variant="warning" title="Say the time unit for a holding cap">
  The engine's clock is bars. "A maximum of 5 days" on a 4-hour chart becomes 30 bars, but
  only because you said *days*. If you write "5 bars", you get 5 bars.
</Callout>

## This week, this month, this year

The day anchors have week, month and year siblings. These are the **current** period's
running values, and they reset at each boundary.

| Say this | What the engine checks |
| --- | --- |
| price breaks above **this week's high** | the running high of the current week |
| price hits **the low of this month** | the running low of the current month |
| BTC is **down more than 5% this week** | `close ≤ 0.95 × the week's open` |
| it is **up 20% year to date** | `close ≥ 1.20 × the year's open` |
| price **makes a new weekly high** | this bar takes out the week's high so far |
| price is in the **bottom 20% of this week's range** | where in the week's range it is trading |

<Callout variant="warning" title="This week is not last week">
  "This week's high" is the running high of the week you are in; "last week's high" is a
  completed level from a different week. Both work, they are different conditions, and the
  parser keeps them apart on exactly that wording.
</Callout>

<Callout variant="info" title="Period-to-date resets; a rolling window does not">
  "Down 5% this week" is measured from the week's open and starts again every Monday.
  "Down 5% over the last 10 bars" slides forward every bar and never resets. Pick the one
  that matches how you think about the trade.
</Callout>

## Two timeframes in one condition

Saying "the daily RSI is above 50" moves that whole condition to the daily chart, which is
usually exactly right. These three are the cases where the two **sides** of the comparison
live on different timeframes, which a single higher-timeframe filter cannot express.

| Say this | What the engine checks |
| --- | --- |
| the **1h RSI is above the daily RSI** | this chart's RSI vs the daily RSI, in one condition |
| price is **more than 3% below the daily 200 EMA** | the *current* price against a daily level |
| price is **2 ATR above the weekly VWAP** | the same, measured in volatility |
| price is **above where it closed 3 daily bars ago** | this chart's price vs a daily close from three days back |

<Callout variant="info" title="A filter moves the whole condition; a distance does not">
  "Price is above the 200 EMA on the daily" asks whether the *daily* bar is above its daily
  EMA, a trend filter, and the right reading. "Price is 3% below the daily 200 EMA" asks
  how far the price is *right now* from that level. Adding the distance is what tells the
  parser you mean the second one.
</Callout>

<Callout variant="warning" title="Three daily bars ago is a value, not a period">
  "Where it closed 3 daily bars ago" is an earlier value of the same series. `RSI(3)` is a
  different indicator entirely. The parser keeps them apart, but the wording matters.
</Callout>

## Who is actually doing the buying

| Say this | What the engine checks |
| --- | --- |
| the **cumulative volume delta over the last 20 bars is positive** | each bar's volume signed by whether it closed up or down, summed |
| **CVD turns negative** over 50 bars | the same, other side |
| **up volume is more than twice down volume** over the last 20 bars | the two sides summed separately and compared |
| **dollar volume is in the top 10% of the last 100 bars** | participation ranked against its own history |
| the **bar range is in the top 5% of the last 200 bars** | the same, on range |
| bullish **OBV divergence**, bearish **volume divergence** | divergence measured on that series |

<Callout variant="info" title="Divergence is not RSI-only">
  Divergence works on **obv, volume, cci, mfi, roc, williams %r and stochastic** as well as
  rsi and macd. If you were told otherwise before, that guidance was out of date.
</Callout>

<Callout variant="warning" title="Bar-level CVD is a proxy">
  True cumulative volume delta needs tick data to know which side each trade hit. On bars,
  the standard approximation is each bar's volume signed by whether it closed up or down
  which is what these conditions use. It is a good proxy, not the real thing.
</Callout>

## Volatility and regime

These describe how the asset is *behaving* right now, rather than where its price is. They
are unit-free, so the same number means the same thing on every asset and in every year.

| Say this | What the engine checks |
| --- | --- |
| **20-day realized volatility is below 40%** | the standard deviation of returns, annualised, as a percentage |
| **historical volatility is above 80%** | the same (window defaults to 20) |
| the **20-bar efficiency ratio is above 0.6** | how much of the distance travelled actually went somewhere |
| a **3 standard deviation down move** | the size of the return against the asset's own recent spread |
| the move is **more than 3 sigma** | the same, either direction |
| the **20-day autocorrelation is negative** | whether yesterday's move tends to be given back |
| returns are **positively autocorrelated** over 30 days | the same, other side |

<Callout variant="info" title="Realized volatility is a percentage, ATR is a price">
  "Volatility below 40%" and "ATR below 40" are different conditions. ATR is measured in the
  asset's own currency, so 40 means something different for every asset and every year.
  Realized volatility is the annualised percentage every options desk quotes, write the
  **%** and you get the comparable one.
</Callout>

<Callout variant="info" title="Efficiency ratio vs ADX">
  Both answer "is this trending or chopping". The efficiency ratio is bounded between 0 and 1
  1.0 is a straight line, 0 is noise that ends where it started, so a threshold you pick
  on one asset transfers to another. ADX does not have that property.
</Callout>

<Callout variant="warning" title="Sigma near a band">
  **"2 standard deviations below VWAP"** is a *band* around VWAP, not the size of a move, the
  sigma points at a reference series. **"a 2 standard deviation down move"** is the size of the
  return. Both work; naming the reference series is what tells them apart.
</Callout>

## Candle geometry

Named candlestick patterns already work: **hammer, doji, engulfing, morning star, marubozu,
shooting star** and around sixty more. Name one and you get it. These phrasings are for when
you'd rather describe the *shape* than name the pattern.

| Say this | What the engine checks |
| --- | --- |
| a **long lower wick**, a **long tail** | the lower shadow is at least twice the body |
| the **upper wick is at least 60% of the range** | that shadow against the whole bar |
| the **lower wick is more than twice the body** | the multiple you state |
| the **body is more than 70% of the range** | how much of the bar the body fills |
| a **wide-bodied candle** | the same, at 70% |
| the **body is the largest of the last 10 bars** | against the ten bars *before* this one |
| an **inside bar**, an **outside bar**, an **outside reversal** | this bar's range against the previous bar's |
| **gaps up more than 2%**, **opens 1% above the previous close** | the open against the previous close |

<Callout variant="info" title="Describing a shape is not the same as naming a pattern">
  "A long lower wick" and "a hammer" select **different bars**. A hammer additionally requires
  a prior downtrend and a small upper wick, so naming the pattern narrows the strategy. Say
  whichever one you actually mean, both work.
</Callout>

<Callout variant="info" title="Outside bar vs engulfing">
  An **outside bar** takes out both extremes of the previous bar, it compares full ranges. A
  **bullish engulfing** compares bodies. Different conditions, both supported; say the one you
  want.
</Callout>

<Callout variant="warning" title="Gaps need a market that closes">
  A gap is the open against the previous close, so it only exists where trading stops
  overnight, **stocks**. On 24/7 crypto the tape is continuous and every bar opens where the
  last one closed, so there is nothing to measure. Ask for a gap on a crypto pair and the
  phrase is read the way a crypto trader means it: the bar **closed** that far above the
  previous close.
</Callout>

## Volatility, persistence and shape

| Say this | What the engine checks |
| --- | --- |
| **ATR is above 3% of price**, **ATR% below 1.5** | ATR divided by price, as a percentage |
| RSI has been above 50 **for the last 3 days** | every bar in those three days |
| price **stayed above the 200 EMA for two weeks** | the same, in weeks |
| **three consecutive narrowing bars**, a **coil** | each bar's range against the one before it |
| the range has **expanded two bars in a row** | the mirror |
| the 50 and 200 EMA are **converging**, **pulling apart** | the gap against its own value a few bars ago |

<Callout variant="warning" title="Write the % on an ATR filter">
  **"ATR above 3"** and **"ATR above 3% of price"** are different conditions. ATR is quoted in
  the asset's own currency, so a bare number is a *price*: on a $60,000 asset, "ATR above 3"
  is three dollars: true on every bar. It also drifts as price moves, so a threshold tuned at
  one price stops working at another. Say the percentage and the filter travels.
</Callout>

<Callout variant="info" title="Durations in days and weeks">
  A persistence filter can be written in calendar time: "for the last 3 days", "for two
  weeks", and the timeframe is accounted for: three days is **eighteen** bars on a 4h chart
  and **three** on a daily one. You don't need to do the conversion.
</Callout>

<Callout variant="info" title="Converging is not the same as close together">
  Two averages can be far apart and closing fast, or nearly touching and drifting apart
  opposite trades. **"Converging"** asks whether the gap is *shrinking*; ask for the spread if
  you want how far apart they are right now.
</Callout>

## Everyday conditions

| Say this | What the engine checks |
| --- | --- |
| the **third touch** of the 200 EMA, the **second retest** of VWAP | the level falls inside the bar, counted |
| only when the **daily candle is green** | the last completed daily close against its open |
| while the **4h candle is red** | the same, other side |
| price is **within 1 ATR of the 50 EMA** | the gap measured in ATR, not percent |
| **stop 1 ATR below entry**, **take profit at 3 ATR** | a stop sized to the asset's own volatility |

<Callout variant="warning" title="Say ATR and you get ATR">
  **"Stop 1 ATR below entry"** and **"1% stop"** are very different orders. Write the word
  **ATR** and the stop is sized to volatility; write **%** and it is a fixed percentage. You
  can mix them in one strategy, "3 ATR target and a 2% stop" keeps each one as written.
</Callout>

<Callout variant="info" title="Ordinals count">
  **"The third touch"** is not the same request as **"a touch"**, it fires on different bars
  and far less often. A touch means the level fell inside the bar's range, so a bar that
  merely closed nearby does not count.
</Callout>

<Callout variant="info" title="Higher-timeframe candles use the last completed bar">
  "Only when the daily candle is green" on a 1h chart reads the last **finished** daily
  candle. The day still forming is not used, because its colour is not knowable at the time
  the trade would be taken.
</Callout>

## The rest of the family list

These work the same way and are documented alongside the operators:

| Say this | What it means |
| --- | --- |
| price is **within 2% of the 200 EMA** | proximity to a level |
| the bar's **range is twice the average range** | range expansion |
| the **z-score is below −2** | standardised distance from the mean |
| the **50 EMA is more than 5% above the 200 EMA** | percent spread between two indicators |
| **RSI rose 20 points over the last 5 bars** | change of a series over a window |
| RSI is **2 standard deviations below its 20-bar mean** | a statistical band on any series |
| volatility is **in its bottom quartile** | percentile rank of a series vs its history |

## If a phrase doesn't work

Two things help more than rewording blindly:

1. **Check the parse.** The terminal shows the parsed conditions before you run, if a clause is
   missing or reads differently from what you meant, that's the signal.
2. **Use the wording above verbatim,** then change the numbers. The examples are the exact phrasings
   the parser is trained and tested on.

See also [operators & conditions](/docs/reference/operators) for the comparison vocabulary, and
[query syntax](/docs/reference/query-syntax) for the anatomy of a full strategy sentence.
