TradingCharts

Strategies

A strategy is an indicator that also places orders. The runtime maintains a position, matches entries against exits, applies commission and slippage, and feeds a backtest report.

Declaration

strategy("My strategy", overlay=true,
         initial_capital           = 100_000,
         default_qty_type          = strategy.cash,
         default_qty_value          = 10_000,
         commission_type           = strategy.commission.percent,
         commission_value          = 0.05,
         slippage                   = 1,
         pyramiding                 = 1,
         process_orders_on_close    = false)
  • Every argument to strategy() is const. See Declaration statements for the full parameter list.
  • strategy() scripts can also use everything an indicator() can — plots, shapes, drawings, tables, alerts.

Placing orders

strategy.entry(id, direction, qty?, when?, limit?, stop?, oca_name?, oca_type?, alert_message?)

if ta.crossover(close, ta.sma(close, 20))
    strategy.entry("Long", strategy.long)

if ta.crossunder(close, ta.sma(close, 20))
    strategy.close("Long")
  • direction: strategy.long / strategy.short.
  • qty omitted → the declaration's default_qty_value/type apply.
  • limit=/stop= turn the entry into a working order that fills intrabar when the price touches the level.
  • oca_name= groups working orders one-cancels-all; oca_type= is strategy.oca.cancel, strategy.oca.reduce, or strategy.oca.none.
  • alert_message= is forwarded verbatim when the fill triggers an alert.

strategy.exit(id, from_entry, stop?, limit?, trail_points?, trail_price?, trail_offset?, profit?, loss?, qty?, qty_percent?, alert_message?)

Attach exit orders to an entry. All tick-count parameters (profit, loss, trail_points, trail_offset) are converted to price using the symbol's mintick.

strategy.exit("Long Exit", from_entry="Long",
              stop  = strategy.position_avg_price - 2 * ta.atr(14),
              limit = strategy.position_avg_price + 3 * ta.atr(14),
              trail_points = 10, trail_offset = 5)
  • stop/limit are prices.
  • profit/loss are ticks from entry.
  • trail_points/trail_offset are ticks. A trailing stop requires BOTH trail_points/trail_price and trail_offset; omitting the offset drops the trail (reference semantics).

strategy.close(id, when?, qty?, qty_percent?, alert_message?)

Market-closes the named entry.

strategy.close_all(alert_message?)

Market-closes every open position.

strategy.order(id, direction, qty?, when?, limit?, stop?, oca_name?, …)

Lower-level: directly places an order that nets FIFO against opposite open positions. Use strategy.order when strategy.entry's reversal + pyramiding semantics are not what you want.

strategy.cancel(id) / strategy.cancel_all()

Removes working orders.

Fill model

By default a strategy calculates once per bar and its market orders fill at the next bar's open; stop / limit / trailing orders are evaluated against each bar's OHLC. Five declaration flags refine this — all of them are enforced by the backtester and the live engine:

Flag Effect
calc_on_every_tick=true The forming live bar recalculates on every price update, so orders can be placed and filled intrabar. Historical bars still calculate once per bar — add calc_on_every_history_tick=true (ChartScript extension) to replay history tick-by-tick too, so the backtest matches live behaviour.
calc_on_order_fills=true After a fill, the script runs once more on the same bar. Use it when an exit must be placed from the updated strategy.position_size / strategy.position_avg_price without waiting for the next bar.
use_bar_magnifier=true Fills of working orders are resolved against the lower-timeframe bars inside each chart bar (the actual price path) instead of the assumed open→high→low→close path. The backtester uses the finest timeframe it has data for and falls back to coarser ones; the report's header shows the timeframe(s) used, the number of intrabars, and how many chart bars they covered. Capped at 200,000 intrabars per run.
fill_orders_on_standard_ohlc=true On non-standard charts (Heikin Ashi, Renko, Kagi, Line Break, Point & Figure, Range) orders fill at the underlying real OHLC prices rather than the synthetic bar's.
margin_long / margin_short Percent of a position's value that equity must cover (100 = cash only, 50 = 2× leverage). An entry that cannot be funded is rejected (counted in the report); when equity no longer covers the requirement a margin call liquidates enough of the position to restore it (shown as Margin call exits, with the smallest account size that would have avoided every call).

slippage (ticks) worsens every market and stop fill; commission_type / commission_value apply the commission model to each fill and to all strategy.* account values.

Order fill timing

Set by process_orders_on_close:

Setting Fill
process_orders_on_close = false (default) Market orders queue on the signal bar and fill at the next bar's open (platform default).
process_orders_on_close = true Market orders fill at the signal bar's close.

strategy.exit orders (stop / limit / trailing) are always evaluated intrabar against each bar's OHLC — or against lower-timeframe bars with use_bar_magnifier. With calc_on_every_tick, market orders on the live bar fill at the current price of the tick that placed them.

strategy.risk.*

Risk limits are enforced per bar by the engine; a breach liquidates and may halt the strategy for the rest of the run or the trading day.

strategy.risk.allow_entry_in(strategy.direction.long)
strategy.risk.max_drawdown(10, strategy.percent_of_equity)
strategy.risk.max_position_size(5)
strategy.risk.max_intraday_loss(1000, strategy.cash)
strategy.risk.max_cons_loss_days(3)
strategy.risk.max_intraday_filled_orders(10)
Rule Enforcement
allow_entry_in(direction) Blocks disallowed-direction entries. Reversal closes are still permitted.
max_position_size(n) Clamps entry size on every fill path.
max_drawdown(v, type) type=strategy.percent_of_equity → percent of peak equity; strategy.cash → points. Breach liquidates + halts.
max_intraday_loss(v, type) Day-scoped halt. Resumes next calendar day.
max_cons_loss_days(n) Scored at UTC day rollover; halts after n consecutive losing days.
max_intraday_filled_orders(n) Per-day cap on filled orders.

Both positional and reference named forms (value=, type=, count=, entry_direction=) are accepted.

Account values

strategy.* series expose the running account state (net of the declared commission model):

Name Meaning
strategy.position_size signed; positive = long, negative = short
strategy.position_avg_price average entry price (or na)
strategy.equity marked-to-market balance
strategy.openprofit unrealised P&L on the current position
strategy.netprofit cumulative realised P&L
strategy.max_drawdown / max_runup running peak-to-trough / trough-to-peak
strategy.wintrades / losstrades / eventrades counters
strategy.closedtrades / opentrades counts

Trade accessors (strategy.closedtrades.* / strategy.opentrades.*): entry_time, exit_time, entry_price, exit_price, profit, size, max_runup, max_drawdown.

Backtest report

Running "Backtest" from the editor or the Strategy Tester runs the strategy over the chart's symbol, timeframe and chart type (Heikin Ashi etc. are honoured) in an isolated worker and shows the report in the Account Manager (bottom panel) as a strategy account tab:

  • Overview — equity, drawdown and buy-and-hold curves over the tested range (deep histories are decimated to 6,000 points for drawing; metrics use every bar)
  • Performance — net / gross profit, profit factor, max drawdown and run-up, Sharpe and Sortino (from monthly returns), CAGR, buy-and-hold return, commission paid, margin calls and liquidated quantity, entries rejected for insufficient funds, smallest account size without a margin call
  • Trades analysis — counts, win rate, averages, largest win / loss, consecutive wins / losses, average bars held
  • List of trades — every trade with entry / exit, size, profit, run-up / drawdown and margin; exportable to XLSX with a column chooser
  • Settings — the effective declaration + overrides the run used (capital, sizing, slippage, commission, fill-model flags, magnifier tiers)

The Strategy Tester lets you re-run with a different period (default 5,000 bars, or a date window on deep history), initial capital and commission without editing the script; pyramiding and slippage can be overridden from Properties.

Alerts

See Alerts. alert_message= on orders rides the fill through to the alert channel; durable alert rules with trigger = order_fill POST the message to a webhook.

See also