Strategy anatomy
Every strategy the AI assembles is a structured object with a fixed set of fields. This page explains what each field means, how the platform uses it at runtime, and what the valid range is.
Understanding these fields is the fastest way to get from a first backtest to a well-calibrated live strategy.
Identity & classification
| Field | Type | What it means |
|---|---|---|
name | string | The strategy's display name, derived from the AI conversation |
asset | string | The instrument being traded - e.g. ETH/USDT, TCS_SHARE |
asset_class_id | string | equity_cash or crypto_spot |
timeframe | string | Candle interval: 1m, 5m, 15m, 1h, 4h, 1d |
tag | string | bullish, bearish, or neutral - the directional bias |
type | string | manual (single asset) or dynamic (multi-asset universe) |
Entry conditions
The entry block defines when the strategy opens a position. All conditions must be true simultaneously on a completed candle close.
| Field | Type | What it means |
|---|---|---|
entry_conditions | array | List of indicator + threshold rules that must all be true |
entry_logic | string | Always AND - every condition must hold |
entry_on | string | candle_close - positions open at the close of the triggering candle |
Example entry condition set:
RSI(14) above 60
EMA(9) above EMA(21)
Volume above 20-period average
All three must be true on the same candle close for a position to open.
Signal plan structure
Internally the AI constructs a SignalPlan that the execution engine evaluates:
interface SignalPlanEntry {
name: string; // indicator name, e.g. "RSI", "EMA"
params: Record<string, unknown>; // indicator parameters, e.g. { period: 14 }
signal_type: 'TRIGGER' | 'FILTER'; // TRIGGER fires the trade; FILTER must also pass
timeframe: string; // candle resolution for this indicator
}
interface SignalPlan {
entry: SignalPlanEntry[];
entry_condition: string; // logical expression, e.g. "A AND B"
exit: SignalPlanEntry[];
exit_condition: string;
signals_available: number; // total indicators available in the KB
signals_used: string[]; // which signals this strategy uses
kb_signals_available: number; // knowledge-base indicator count
kb_signals_used: string[]; // KB-sourced signals used
}
TRIGGER signals are the primary fire condition. FILTER signals must also be
true but alone cannot open a position. A typical strategy has 1-2 TRIGGERs and
0-2 FILTERs.
Exit conditions
The exit block controls when an open position is closed. The platform checks exit conditions on every candle close and on every price tick for stop-loss and take-profit.
| Field | Type | What it means |
|---|---|---|
exit_conditions | array | Indicator rules - if any is true, the position closes |
exit_logic | string | Always OR - any single condition triggers exit |
stop_loss_pct | float | Distance from entry price at which the hard stop fires, in % |
take_profit_pct | float | Fixed take-profit distance from entry, in % (if not trailing) |
trailing_tp | bool | Whether take-profit trails the price instead of being fixed |
trailing_tp_trail_pct | float | How far the TP trails behind peak price, in % |
trailing_tp_arms_at_pct | float | TP only activates after price has moved this far in your favour |
How trailing TP works:
When trailing_tp is true, the take-profit only arms after price has moved arms_at_pct in your direction. Once armed, it trails trail_pct below the peak price seen since entry. The position closes when price falls back to the trailing level.
Example: entry at 100, arms_at = 1.5%, trail = 0.8%.
- TP arms when price hits 101.5
- At that point the trail level is 100.69 (1.5% - 0.8% from entry)
- If price peaks at 102, the trail level rises to 101.18 (102 x (1 - 0.008))
- Position closes when price falls to 101.18
The stop_loss_pct level is evaluated on every price tick, not just candle closes. A candle can wick down to your stop and close above it - you will still be stopped out.
Multi-target take-profit (TP1 / TP2)
Instead of a single all-or-nothing exit, a strategy can take profit in stages - closing part of the position at a first target (TP1), more at a second (TP2), and so on. This locks in gains early while leaving a portion running for a larger move.
| Field | Type | What it means |
|---|---|---|
take_profit_targets | array | Ordered list of profit targets; each closes a slice of the position |
target.at_pct | float | Distance from entry, in %, at which this target triggers |
target.close_pct | float | Portion of the original position to close when this target hits |
Example - a two-stage exit:
| Target | Triggers at | Closes | Remaining |
|---|---|---|---|
| TP1 | +1.5% | 50% of the position | 50% |
| TP2 | +3.0% | remaining 50% | 0% |
- At +1.5%, half the position is sold - profit banked, risk on the trade halved.
- The rest runs to +3.0% (TP2) or until an exit condition / stop fires first.
Multi-target take-profit combines with the other exit mechanics: the stop_loss_pct
still protects the whole position, and trailing_tp can manage the final remaining slice
after the fixed targets are hit.
Between targets, the position remains subject to exit_conditions and the stop-loss. If
an exit condition fires before a target is reached, the entire remaining position
closes.
Sizing & risk
| Field | Type | What it means |
|---|---|---|
per_trade_risk_pct | float | Maximum % of allocated capital to risk on a single trade |
risk_reward_ratio | float | Target R:R - used to derive take-profit if not explicitly set |
max_position_size_pct | float | Maximum % of allocated capital in one position at a time |
How position size is calculated:
position_size = (capital_allocated × per_trade_risk_pct) / stop_loss_pct
If this would exceed max_position_size_pct, the position is capped at that percentage instead. This means the actual loss on a stop-out could be less than per_trade_risk_pct.
Trading window
| Field | Type | What it means |
|---|---|---|
trading_window_start | time | Earliest time a new position can be opened, in the venue's local time (e.g. 09:15) |
trading_window_end | time | Latest time a new position can be opened, in the venue's local time (e.g. 15:30) |
max_trades_per_day | int | Hard cap on new positions per calendar day |
max_open_positions | int | How many positions can be open at the same time |
continuous | bool | Continuous markets (crypto) - strategy runs 24/7, no window restriction |
09:15 means 09:15 where the market is, IST on NSE, ET on a US venue, London time on LSE. It is not your local time and it is not UTC.
That matters twice. A window is only meaningful inside the venue's session, so 09:15-15:30 describes a full Indian session and describes nothing at all on a US venue, whose regular session is 09:30-16:00 local. And the offset from UTC changes with daylight saving (Sydney's DST runs opposite to the northern hemisphere, and London, New York, Amsterdam and Madrid all change on different dates) which is why the window is stated in local time rather than converted.
Per-venue sessions are listed on Trading hours & holidays. A window outside its venue's session simply never opens.
An equity strategy defaults to its venue's regular session. Crypto strategies with continuous: true ignore the trading window for new entries but still respect max_trades_per_day.
Status fields
These are set by the platform, not the AI, and reflect the current state of the strategy.
| Field | Meaning |
|---|---|
status: draft | Created but not deployed |
status: paper | Running in the paper simulator |
status: live | Running against a real broker/exchange |
status: paused | Deployed but not accepting new entries |
status: stopped | Fully halted; no positions will open |
deployedPaper: true | Strategy has an active paper deployment |
deployedLive: true | Strategy has an active live deployment |
A strategy can be in both deployedPaper and deployedLive simultaneously - those are independent paths.