# Entry & exit logic

> How entries, exits, stops, targets, sizing, performance gates and custom series come together in a trade.

Source: https://docs.texttoquant.com/reference/entry-exit

---

A trade has two halves: the conditions that open it, and everything that closes it, exit conditions,
stops, targets and trailing logic. This page covers both, plus how you size the position and bring in
custom data.

## Entry & exit conditions

Entries and exits are built from indicator, price and pattern conditions. Combine them with `AND` /
`OR`, or run them in sequence with `then`.

```text
Buy when RSI crosses above 50 AND price breaks the swing high, for BTC 1D
```

```text
Buy BTC when the 12 EMA crosses above the 21 EMA, then price retests the 21 EMA, 4H last 90 days
```

```text
Buy BTC when RSI is higher than it was 5 bars ago, exit when it's lower, 4H last 90 days
```

<Callout variant="info" title="Sequential entries">
  The `then` in the second query makes the entry **sequential**: the EMA cross must come first and
  the retest must follow, in that order, not on the same bar. Each step waits up to 50 bars for the
  next one by default; add `within N bars` to set the window, or the sequence resets. See
  [combining conditions](/docs/reference/operators#combining-conditions).
</Callout>

## Stops & targets

| Control | Example |
| --- | --- |
| Stop loss | `SL 2%`, `SL 1.5 ATR`, `stop 1R`, at entry candle low |
| Take profit | `TP 5%`, `TP 3RR`, `take half at 2%` |
| Trailing | `trail by 1.5 ATR`, `trailing stop 3% (activate at 1%)` |
| Stop plan | `move to breakeven at 1R, then trail 1 ATR at 2R` |
| Partial in / out | `partial exit 50% at 1R, remainder at 2R` |
| Channel exit | `exit on a new 20-bar low`, close on a new N-bar high/low (Donchian-style) |

## Position sizing

| Mode | Example |
| --- | --- |
| Risk based | `risk 1% of equity per trade with a 2% stop` |
| % of equity | `use 10% of equity per trade` |
| Fixed units | `0.05 BTC per trade` |
| Fixed notional | `$5,000 per trade` |
| Leverage | `3× leverage` |

<RunInTerminal query="Buy BTC when RSI > 30, SL 2 ATR, TP 3RR, risk 1% per trade, 4H last 120 days" />

## Order execution

By default an entry fills **at the close of the signal bar** (see the
[execution model](/docs/concepts/execution-model)). You can also rest a **limit order**: it waits for
price to come to a level and fills on a later bar if it's touched, expiring after a set number of
bars.

| Mode | Example |
| --- | --- |
| Market (default) | fills at the signal bar's close |
| Limit | `place a limit buy at the 20 EMA, expire after 5 bars` |

<Callout variant="info" title="Two fill models, honestly">
  The engine models **market** and **limit** fills only. Phrasings that imply a next-bar-open or a
  stop-entry order resolve to the market fill, the docs don't promise an order type the engine
  doesn't run.
</Callout>

## Event windows

A single condition can require that something happened **recently**, not necessarily on this exact
bar: "X within the last N bars", optionally "at least / at most K times". This widens *when a
condition counts as met*: distinct from the sequential `then within N bars`, which orders two
separate steps.

```text
Buy BTC when RSI crossed above 30 within the last 10 bars, 4H last 90 days
```

## Performance gates

The strategy's own live performance is available as a condition, just like an indicator. Five
runtime metrics are computed bar by bar from the equity curve of the run itself, so a query can
gate its entries on how the strategy has been doing:

| Metric | Meaning |
| --- | --- |
| `rolling performance` | % return of the strategy over the last 30 bars |
| `rolling drawdown` | % of equity below its 30-bar peak (0 at highs, negative under water) |
| `rolling sharpe` / `rolling sortino` | Annualized risk-adjusted return over the last 30 bars |
| `equity` | Current account equity in $ |

```text
Buy BTC when RSI crosses above 50, but only trade if performance is above 0%, 4H last 180 days
```

```text
Buy when price breaks the swing high, only if drawdown is better than -10%, for ETH 1D
```

For hard cutoffs rather than bar-by-bar gates, use the risk guards: `stop trading if drawdown
exceeds 20%`, `stop after 3 consecutive losses`, `stop for the day after losing 5%`, `max 2 trades
per day`, `wait 5 bars between trades`.

<Callout variant="warning" title="Gates can lock themselves out">
  While a gate blocks entries the equity curve goes flat, and after 30 flat bars
  `rolling performance` settles at exactly 0. A strictly positive threshold (`above 0%`) then never re-arms
  and the strategy stays shut off for the rest of the run. Give the gate a way back in: use a
  slightly negative threshold (`above -2%`), or gate on drawdown, which re-arms as the window rolls
  past the old peak.
</Callout>

<Callout variant="info" title="What the metrics measure">
  Runtime metrics track the strategy's actual equity, including only trades that were really taken.
  The rolling window is fixed at 30 bars of the query's timeframe. They are not a per-signal track
  record: "only take this signal if it was profitable the last 3 times it fired" is a different
  concept and isn't part of a gate. To study a single condition's historical outcomes, use the
  probability scan in [analysis](/docs/reference/analysis).
</Callout>

## Custom indicators & @-tags

Bring your own series from CSV columns or Pine. Tag them with `@"Column"` and align the timeframe in
the UI. You can combine several custom series in one query.

```text
Long when @"fast" crosses above @"slow", SL 2% TP 3RR, for BTC 4H last 60 days
```

<Callout variant="warning" title="Strict mode">
  When strict mode is on, a custom query requires `@` tags, a data source, and a parsed custom
  condition, so a typo can't silently fall back to a built in indicator.
</Callout>
