Pine Script strategy.risk Controls: Drawdown, Intraday Loss, Losing Days and Position Limits
A stop from strategy.exit() belongs to a trade or entry. A strategy.risk.* command applies across the strategy and can cancel pending orders, close positions, block further actions or reduce size after its condition is reached. This guide does not recommend a percentage threshold. It audits what each strategy-wide command enforces and how that enforcement changes the trade sample.
Who this guide is for: Pine developers adding global guardrails or investigating why orders disappear after a risk command triggers
Key points to understand first
- Design signal/sizing, per-trade exits and strategy-wide risk commands as three separate layers.
strategy.risk.*applies across the strategy regardless of source location and is not a per-execution toggle.- Drawdown, intraday loss, consecutive loss days, fill count, position size and allowed direction have different triggers and scopes.
- On timeframes above one day, an intraday command can operate per chart bar; never infer behavior from the name alone.
- A guardrail changes trade count, exit reason and the equity path, so export it as a distinct strategy version.
strategy.risk is a strategy-wide enforcement layer
- 01Signal & sizing
entry condition / qty / pyramidingWhen and how much to enter
- 02Per-trade exits
strategy.exit / strategy.closeHow a particular entry closes
- 03Strategy-wide guardrails
strategy.risk.*Global permission, caps and stops
- 1threshold reached
- 2cancel pending orders
- 3close or reduce where specified
- 4block further actions for the command scope
A risk command enforces behavior; it is not a performance metric
strategy.risk.max_drawdown() is not the variable that displays maximum drawdown. It is a command that cancels pending orders, closes an open position and stops further trade actions after its condition is reached. strategy.max_drawdown and report drawdown describe an observed result. Similar names do not mean the same job.
| Layer | Example | Action | Question |
|---|---|---|---|
| Measurement | strategy.max_drawdown / report DD | Measure the result | How far did equity fall? |
| Per-trade control | strategy.exit stop | Create an exit for selected entries | Where does this trade close? |
| Strategy-wide enforcement | strategy.risk.* | Constrain all strategy actions | When must the strategy stop acting? |
Separate a reported measurement from a command that changes later simulation behavior.
Choose commands by trigger and enforcement scope
| Command | Monitors | Typical enforcement | Boundary caveat |
|---|---|---|---|
| allow_entry_in | Permitted direction | An opposite entry can close instead of reverse | Confirm its scope on strategy.entry |
| max_cons_loss_days | Consecutive losing days | Cancel, close and stop further trade actions | Define the trading-day boundary |
| max_drawdown | DD amount or percent from maximum equity | Cancel, close and stop later trade actions | Percent is based on maximum equity |
| max_intraday_filled_orders | Filled orders in a trading day | Cancel, close and halt until session end | Above 1D it operates per chart bar |
| max_intraday_loss | Intraday loss amount or percent | Cancel, close and halt until session end | Above 1D it operates per chart bar |
| max_position_size | Position quantity after strategy.entry | Reduce entry quantity to the cap or suppress it | Do not assume identical effect on every order API |
Use the current reference manual for exact arguments, units and resumption behavior.
Commands can be combined. The first one to trigger changes orders and positions, which also changes the data observed by the others. Because the trade list may not identify the causal command on its own, retain input settings, version names, comments, alert messages or Pine logs.
Do not wrap a command in if and expect a temporary guard
Official TradingView guidance states that risk-management commands execute on every tick and order execution event and apply irrespective of their location in source. They cannot be deactivated for selected script executions. Treat their inputs as version-level settings rather than assuming a local conditional creates a time-limited guard.
The word intraday is timeframe-dependent. On a timeframe higher than one day, maximum intraday loss and filled-order count operate per chart bar. A weekly chart does not imply a hidden daily reset. Test the actual timeframe and symbol session.
| Order | Question | Evidence |
|---|---|---|
| 1 | Which bar/tick reached the threshold? | Equity, daily P/L or filled-order count |
| 2 | Which pending orders were cancelled? | Order IDs and attempted reissues |
| 3 | Was the position closed or quantity reduced? | Exit comment, price and qty |
| 4 | How long were later entries blocked? | Next true condition with no order |
| 5 | When did activity resume or end? | Session boundary, next bar and later trade list |
Complete the trace independently for each command.
Audit intersections with exits, pyramiding, margin and sessions
A strategy-wide command does not replace a per-trade exit. A bracket stop can fill first, or an intraday guard can close every open trade first. Exit reasons, holding time and MFE/MAE then change, so do not label ordinary and risk-enforced exits as one event class.
| Other layer | What can happen | Audit method |
|---|---|---|
| strategy.exit bracket | Per-trade SL/TP races the global guard | Separate exit IDs and comments |
| pyramiding | Multiple entries reach position or fill-count caps sooner | Record open trades and transactions |
| margin | A margin call reduces exposure before the risk threshold | Do not label it a guardrail trigger |
| session filter | Candidate entries and reset boundaries change | Freeze IANA timezone and session |
| process_orders_on_close | The enforced close fill timing can change | Separate creation and fill bars |
TV16 audits sizing, TV17 order states and TV19 session classification.
max_position_size() can reduce a strategy.entry() quantity so the resulting position stays below the cap. If even the minimum quantity cannot satisfy it, an entry can be suppressed. Do not assume it constrains strategy.order() in the same way; test each order API used by the script.
Expose educational thresholds and measure one difference at a time
This structural example declares several risk commands. Its defaults are fictional mechanics-test values, not safe or recommended limits. In practice, begin with a version containing one command. The moving-average cross exists only to generate test events.
//@version=6
strategy("Strategy risk guardrail audit", overlay = true, pyramiding = 3,
initial_capital = 100000, margin_long = 100, margin_short = 100)
float maxDrawdownInput = input.float(20.0, "Max drawdown (%)", minval = 0.1, maxval = 100)
float maxDayLossInput = input.float(4.0, "Max intraday loss (%)", minval = 0.1, maxval = 100)
int maxFillsInput = input.int(6, "Max intraday filled orders", minval = 1)
int maxLossDaysInput = input.int(3, "Max consecutive loss days", minval = 1)
float maxPositionInput = input.float(4.0, "Max position units", minval = 1)
strategy.risk.max_drawdown(maxDrawdownInput, strategy.percent_of_equity)
strategy.risk.max_intraday_loss(maxDayLossInput, strategy.percent_of_equity)
strategy.risk.max_intraday_filled_orders(maxFillsInput)
strategy.risk.max_cons_loss_days(maxLossDaysInput)
strategy.risk.max_position_size(maxPositionInput)
float fast = ta.ema(close, 20)
float slow = ta.ema(close, 50)
bool longSignal = ta.crossover(fast, slow)
bool shortSignal = ta.crossunder(fast, slow)
if longSignal
strategy.entry("Long", strategy.long, qty = 2)
if shortSignal
strategy.entry("Short", strategy.short, qty = 2)
plot(fast, "Fast", color.aqua)
plot(slow, "Slow", color.orange)
plot(strategy.equity, "Equity", display = display.data_window)
plot(strategy.position_size, "Position size", display = display.data_window)The values are fictional. Compare with otherwise identical baselines that remove one command at a time.
Give every input a unit and tooltip in production. Never mix cash and percentage limits in the version ledger. Plot observed metrics such as strategy.max_drawdown, equity and position size in a section separate from command settings.
Measure current and hypothetical drawdown from peak equity
Assuming current equity does not exceed peak equity, the calculator uses peak equity, current equity, a configured drawdown percentage and a hypothetical next loss. It returns current drawdown, projected drawdown and the current remaining amount before the configured level. It does not recommend a threshold or predict an emulator fill.
current DD amount = max(peak equity − current equity, 0)current DD % = current DD amount ÷ peak equity × 100projected DD % = max(peak − (current − next loss), 0) ÷ peak × 100current remaining amount = max(peak × guardrail % ÷ 100 − current DD amount, 0)Assumes current equity≤peak equity. The model does not reproduce open-profit updates, order fills, gaps, slippage or internal event ordering. If equity reaches zero or below while strategy.risk.max_drawdown uses strategy.percent_of_equity, pending orders are cancelled, open positions are closed and new orders remain disabled; do not interpret an arithmetic drawdown above 100% as usable capacity.- Save the baseline
Export Strategy Tester results without the risk command.
- Add one control
Rerun identical conditions with only one guard added.
- Find the divergence
Record the first bar, order or position where trade lists differ.
- Compare sample changes
Review count, exit reason, holding time, distribution and drawdown.
- Build the combined version
Combine only controls whose isolated effects are understood.
Bind a trigger ledger to baseline and guarded versions
- Are command, value and cash/percent unit stored in the version record?
- Did you trace pending orders, open positions and later entries around the trigger?
- Are risk exits, ordinary exits and margin calls distinct?
- Did you test timeframe and session boundaries, especially intraday rules above 1D?
- Did max_position_size act on the actual order API used?
- Was the guarded CSV exported with the same market, period, sizing and cost assumptions?
A lower drawdown is not proof that a command triggered, so keep a guardrail trigger ledger. For each command, store version ID, argument and unit, monitored basis, first trigger bar, pre-trigger value, cancelled orders, enforced closes, reduced quantity and later blocked entries, then bind the row to baseline and guarded CSV files.
Do not attribute a drawdown difference to the guard until the ledger explains the first trade-list divergence. Build a multi-command version only after each single-command trigger row is understood, and store risk exits, ordinary exits and margin calls as separate reasons.
Drawdown from peak equity
Assuming current equity≤peak equity, estimate current and post-loss drawdown from maximum equity. This is not a threshold recommendation.
Input premise: b≤a. currentDD=max(a−b,0)÷a×100; projectedDD=max(a−(b−d),0)÷a×100; remaining=max(a×c÷100−max(a−b,0),0). Remaining is the current amount measured from current drawdown. Excludes orders, open-profit paths, gaps, slippage and event ordering. Verify the terminal percent-of-equity risk behavior separately when equity≤0.
Frequently asked questions
Are strategy.risk.max_drawdown() and strategy.max_drawdown the same?
No. The first is an enforcement command that stops strategy actions after its condition. The second is an observed maximum-drawdown value from the simulation.
Can I put a risk command in if to enable it only at certain times?
Do not design it as a temporary toggle. Official guidance says risk commands apply across the strategy regardless of source location and cannot be deactivated for selected executions.
Does max_intraday_loss reset daily on a weekly chart?
On timeframes above one day, the rule can operate per chart bar. Test the exact timeframe and session rather than inferring behavior from the word intraday.
What happens if equity reaches zero or below under percent-of-equity max_drawdown?
When strategy.risk.max_drawdown() uses strategy.percent_of_equity and equity reaches zero or below, pending orders are cancelled, the open position is closed and no new orders can be placed afterward. A calculator result above 100% is arithmetic, not evidence that the strategy can continue trading.
Does strategy.risk.max_position_size() always reject an oversized entry?
Not always. It can reduce the effective order quantity to fit the cap. If even the instrument’s minimum tradable quantity would violate the cap, the order is not placed. Keep requested and effective quantity as separate trigger-ledger fields.
Primary sources and verification links
- TradingView Pine Script — StrategiesOfficial risk-management commands, margin and order interactions.
- TradingView Pine Script — Reference Manual v6Official signatures, types and effects for strategy.risk commands.
- TradingView Pine Script — Strategies FAQOfficial sizing, custom statistics and strategy behavior examples.
- TradingView Help — Strategy propertiesOfficial capital, sizing, margin and recalculation settings.
- TradingView Pine Script — Migration to v6Official version changes including v6 margin defaults.
Edited and published by: SG Group · Editorial approach: We prioritize official TradingView Help Center and Pine Script documentation, then use exchange, regulator and other primary materials for market and product context. Features, data, pricing and connection terms change, so verify the current interface and linked sources before use.
Important notice: This article provides general education about TradingView interfaces, charts, alerts, screeners, paper trading and Pine Script. It is not investment advice, a trading signal, a recommendation of any instrument, data source or broker, or a guarantee of future price or profit. Features, pricing, data, exchange coverage, notifications, order integrations and Pine Script behavior vary by plan, region, connection and date and may change. Before risking money, verify current TradingView documentation and the terms of the relevant data source and connected provider, then rehearse the workflow with fictional data or paper trading.

