◆ New — Strategy Validator, Code Fixer & Prop Compliance Checker are live. Try one free →
Home / Learn / Pine Script Strategy Properties Explained: Commission, Slippage, Initial Capital, and Fill Assumptions
Pine Script

Pine Script Strategy Properties Explained: Commission, Slippage, Initial Capital, and Fill Assumptions

A property-by-property guide to the strategy() declaration in Pine Script v6 — commission_type, commission_value, slippage, initial_capital, default_qty_type, margin, and the fill-timing switches — and how each one changes the numbers the Strategy Tester reports for the exact same entry logic. The defaults assume a zero-cost, frictionless market, so an untouched declaration almost always overstates results. This is educational material only, not financial advice; no property setting makes a strategy profitable, and trading carries a real risk of loss.

What do the strategy() properties like commission and slippage actually do?

The properties you pass to strategy() configure TradingView's broker emulator — the simulated broker that fills your orders during a backtest. commission_type and commission_value deduct a fee from every fill, slippage worsens the fill price of market and stop orders by a number of ticks, initial_capital sets the starting equity that every percentage metric is measured against, and default_qty_type / default_qty_value decide how large each position is. None of them touch your entry logic; they change what the same trades cost and how they are sized.

The part that matters for honesty: the defaults assume zero costs. commission_value defaults to 0, slippage defaults to 0, and orders fill at clean bar prices. A strategy declared as bare strategy("My strategy") is being tested in a market that does not exist — no spread, no fees, no slippage, perfect fills. For a strategy that trades a handful of times a year on a daily chart, the distortion may be small. For anything active on intraday charts, costs are frequently the difference between a green equity curve and a red one, because a fixed per-trade cost is charged against a shrinking per-trade edge as frequency rises.

So the working rule this article builds toward is simple: treat the cost properties as part of the strategy, not as optional settings. A backtest without commission and slippage is not an optimistic estimate; it is an answer to a different question. Everything below walks through the properties one group at a time — costs, fills, capital and sizing — and ends with a fully wired declaration you can adapt. All of it is educational only; correctly modelled costs make a backtest more honest, never profitable.

Commission: the three types and when each one fits

commission_type accepts three constants, and choosing the wrong one silently misprices every trade:

  • `strategy.commission.percent`commission_value is a percentage of the order's cash value. This fits percentage-fee venues: most crypto spot exchanges and many CFD brokers quote this way. Note the units: commission_value = 0.1 means 0.1%, not 10%.
  • `strategy.commission.cash_per_contract` — a fixed cash amount per contract/lot/share traded. This fits futures (fee per contract) and per-share stock commission schedules. It scales with position size.
  • `strategy.commission.cash_per_order` — a fixed cash amount per order, regardless of size. This fits flat-ticket brokers. It punishes small orders proportionally more, which is realistic for small accounts.

Two mechanics worth knowing. First, commission is charged on both sides — entry and exit each pay — so a round trip costs twice the per-fill figure. Second, commission interacts with position sizing: under strategy.percent_of_equity sizing, fees compound into every subsequent position size, so a high-frequency strategy decays faster than a flat mental estimate suggests.

//@version=6
// Educational only — validate before trading; not financial advice.
// ❌ WRONG: the frictionless default — zero commission, zero slippage.
strategy("Frictionless", overlay = true)
//@version=6
// Educational only — validate before trading; not financial advice.
// ✅ Fixed: costs declared as part of the strategy.
strategy("Costed", overlay = true,
     commission_type = strategy.commission.percent,
     commission_value = 0.05,
     slippage = 2)

The honest move is to test at your broker's actual schedule, then re-run at 1.5–2× that figure. If the equity curve flips sign under a modestly worse assumption, the result was never robust — it was a cost artefact.

Slippage and the fill-timing switches: where the emulator fills you

slippage is an integer number of ticks (one tick = syminfo.mintick) by which the emulator worsens your fill: market buys fill higher, market sells fill lower. It applies to market and stop orders only — limit orders are not slipped, because a limit order by definition fills at its price or better. That asymmetry is realistic, but it also means limit-heavy strategies keep an optimistic assumption that slippage cannot fix: in live trading a limit order can simply not fill while the backtest assumes it did. backtest_fill_limits_assumption partially addresses this by requiring price to exceed the limit level by N ticks before the fill is granted.

The timing switches decide when orders execute:

  • Default behaviour: an order placed on bar N fills at the open of bar N+1. This is the conservative, honest default — your code sees a completed bar before acting.
  • `process_orders_on_close = true` fills at the close of the signal bar instead. Sometimes appropriate for end-of-day execution models, but it removes the gap between signal and fill that live trading actually has.
  • `calc_on_every_tick = true` recalculates on live ticks — which creates a live-versus-backtest mismatch, because historical bars have no tick data.
  • `use_bar_magnifier = true` (higher-tier plans) fills intrabar orders using lower-timeframe data instead of assuming a path through the bar's OHLC. When stops and targets both fall inside one bar, the default emulator must guess which was hit first; the magnifier replaces part of that guess with data.

A useful discipline: change one switch at a time on the same strategy and diff the tester output. The size of the swing tells you how much of your "edge" was actually a fill assumption.

Initial capital, position sizing, and margin — the quiet distorters

initial_capital defaults to a large round figure (1,000,000 in the symbol currency by default), and that default distorts more than people expect. Every percentage metric — net profit %, max drawdown %, percent-of-equity sizing — is measured against it. A strategy trading one contract against a million of capital shows microscopic drawdown percentages that say nothing about how the strategy behaves at the account size you would actually run. Set initial_capital to a realistic figure first; several other numbers become meaningful only afterwards.

default_qty_type has three modes: strategy.fixed (a fixed number of contracts/shares — the default, at quantity 1), strategy.cash (a fixed cash amount per trade), and strategy.percent_of_equity (a percentage of current equity, which compounds wins and losses into subsequent sizing). Compounding sizing makes equity curves look smoother in good stretches and fall harder in bad ones; be conscious of which effect you are looking at when you compare runs.

currency converts results into a chosen account currency using daily FX rates — relevant when your account currency differs from the symbol's quote currency, otherwise leave it as the symbol default.

Finally, margin: margin_long and margin_short express what fraction of a position's value must be backed by equity. In Pine v6 these default to 100 — positions must be fully funded — and the emulator will force-liquidate positions the account can no longer support. This is a real behavioural change from older scripts (v5 defaulted to 0, disabling margin simulation entirely), and it is one of the common reasons a v5 strategy produces different results after migration to v6: trades that used to be silently carried at impossible sizes now get margin-called.

Realistic starting values by asset class — and a fully wired declaration

There is no universal correct number — fee schedules differ by broker, tier, and instrument — so treat these as illustrative shapes, not data: crypto spot venues usually fit strategy.commission.percent (taker fees are commonly quoted in basis points; check your exchange's published schedule). Futures fit cash_per_contract, with exchange plus broker fees per side. Stocks fit either cash_per_contract (per-share pricing) or cash_per_order (flat ticket). Spot forex is the awkward one: the main cost is the spread, which the emulator does not model directly since it fills at last-trade prices — the standard workaround is to express typical spread as slippage ticks, sized to the pair and session you trade.

Here is a declaration with every cost and sizing property wired, attached to a deliberately plain entry so the properties stay the subject:

//@version=6
// Educational only — validate before trading; not financial advice.
strategy("SMA cross with realistic costs", overlay = true,
     initial_capital = 10000,
     currency = currency.USD,
     commission_type = strategy.commission.percent,
     commission_value = 0.05,
     slippage = 2,
     default_qty_type = strategy.percent_of_equity,
     default_qty_value = 5)

fast = ta.sma(close, 20)
slow = ta.sma(close, 50)

longSig = ta.crossover(fast, slow)
flatSig = ta.crossunder(fast, slow)

if longSig and barstate.isconfirmed
    strategy.entry("Long", strategy.long)
if flatSig and barstate.isconfirmed
    strategy.close("Long")

plot(fast, "Fast SMA", color = color.teal)
plot(slow, "Slow SMA", color = color.orange)

Run your own strategy twice — once bare, once costed — and read the delta. That gap is not pessimism; it is the portion of the bare result that was never available to capture. ForexCodes' Audit flags declarations that leave commission and slippage at zero for exactly this reason. And the standing caveat applies: correctly modelled costs make a backtest more honest, but even an honest backtest is a description of the past, not a forecast. Trading carries a real risk of loss.

Key takeaways

Educational only — not financial advice. Trading involves substantial risk of loss.

Catch the bug that compiles.Run auditGet Pro