◆ New — Strategy Validator, Code Fixer & Prop Compliance Checker are live. Try one free →
Home / Learn / Pyramiding in Pine Script Strategies: How It Works and How It Distorts Backtests
Pine Script

Pyramiding in Pine Script Strategies: How It Works and How It Distorts Backtests

A precise guide to the pyramiding parameter in Pine Script v6 strategies: what it actually limits, how strategy.entry() and strategy.order() treat it differently, how adds interact with position sizing, average price, and exits, and why pyramided backtests systematically look better than the live trading they claim to describe. This is educational material only, not financial advice; pyramiding concentrates risk into your open position, and a backtest result is never a forecast of live performance.

What does the pyramiding setting actually do?

The pyramiding parameter of the strategy() declaration sets the maximum number of successive entries the strategy can make in the same direction via strategy.entry(). Its default is 0, which permits exactly one open entry per direction: while a long position is open, further strategy.entry(..., strategy.long) calls are silently ignored. Setting pyramiding = 3 allows up to three stacked entries — an initial position plus two adds — before additional same-direction entry calls are dropped.

Three boundary details matter, and most tutorials skip all of them. First, the limit is directional and positional, not historical: it counts currently open entries in the position, so after the position closes the counter resets. Second, an opposite-direction strategy.entry() call is never blocked by pyramiding — it reverses the position regardless, because reversal is entry behaviour, not adding behaviour. Third, and most important for correctness: the pyramiding limit is enforced only by `strategy.entry()`. The lower-level strategy.order() function ignores it completely, which is a genuine trap we cover in its own section below.

It also helps to be clear about what pyramiding is not. It is not a risk setting, a sizing rule, or a money-management feature. It is a cap on order acceptance. Everything that determines how dangerous a pyramided position is — the size of each add, the spacing between adds, where the stop sits relative to the blended average price — is your code's responsibility, and the tester will happily simulate arrangements that no live account should carry. Everything here is educational only; nothing in this article is a recommendation to trade, and trading carries a real risk of loss.

How do I enable pyramiding correctly in Pine v6?

Here is a complete, compilable v6 strategy that allows up to three stacked long entries, spaces each add by a volatility-scaled distance from the previous fill, and anchors one hard stop to the blended average price. The SMA-cross entry is only a vehicle; the pyramiding plumbing is the point.

//@version=6
// Educational only — validate before trading; not financial advice.
strategy("Pyramiding demo", overlay = true, pyramiding = 3,
     default_qty_type = strategy.fixed, default_qty_value = 1)

stopPts    = input.int(400, "Hard stop (points)", minval = 1)
addSpacing = input.float(1.0, "Add spacing (ATR multiples)", minval = 0.1)
maxAdds    = 3

atrVal  = ta.atr(14)
trendUp = close > ta.sma(close, 50)
baseSig = ta.crossover(close, ta.sma(close, 20))

// Initial entry on a confirmed signal only
if baseSig and trendUp and barstate.isconfirmed and strategy.position_size == 0
    strategy.entry("Long-1", strategy.long)

// Add only after price moves addSpacing ATRs beyond the last fill
lastFill = strategy.opentrades > 0 ?
     strategy.opentrades.entry_price(strategy.opentrades - 1) : na
if strategy.position_size > 0 and strategy.opentrades < maxAdds and
     barstate.isconfirmed and close > lastFill + addSpacing * atrVal
    strategy.entry("Long-" + str.tostring(strategy.opentrades + 1), strategy.long)

// One stop for the whole stacked position, anchored to average price
if strategy.position_size > 0
    strategy.exit("Exit",
         stop = strategy.position_avg_price - stopPts * syminfo.mintick)

Reading the load-bearing parts: each add gets a unique entry id (Long-1, Long-2, ...) built with str.tostring(), which keeps the trade list legible and lets you bind exits to specific adds later. strategy.opentrades counts currently open entries, so it doubles as both the add counter and the index for strategy.opentrades.entry_price(), which retrieves the most recent fill so adds are spaced from fills, not from signals. Signals are gated on barstate.isconfirmed so no add fires off a still-forming bar, and the declared stop input is genuinely wired into strategy.exit(stop = ...) — no dead risk inputs.

The bypass trap: strategy.order() ignores the pyramiding limit

The single most consequential mechanic in this topic: pyramiding constrains strategy.entry() only. The strategy.order() function is a raw order primitive — it does not check direction, does not reverse positions, and does not respect the pyramiding cap. A script that uses strategy.order() for adds can stack unbounded size while its author believes the declaration line is protecting them.

// ❌ WRONG — this framing looks safe but is not.
// strategy() says pyramiding = 2, yet strategy.order() ignores that cap:
// every confirmed signal adds ANOTHER unit, without limit.
if addSig and barstate.isconfirmed
    strategy.order("Add", strategy.long)

On a trending chart this bug is nearly invisible in the tester — the adds keep winning, the equity curve looks smooth, and nothing errors. The position size column in the List of Trades is the only tell. The fix is to route adds through strategy.entry() and, belt-and-braces, to check the open-trade count yourself:

//@version=6
// Educational only — validate before trading; not financial advice.
strategy("Capped adds", overlay = true, pyramiding = 2)

addSig = ta.crossover(close, ta.sma(close, 20))
if addSig and barstate.isconfirmed and strategy.opentrades < 2
    strategy.entry("Long-" + str.tostring(strategy.opentrades + 1), strategy.long)

The explicit strategy.opentrades < 2 guard is technically redundant when you only use strategy.entry() — the engine enforces the cap — but it makes the limit visible in the logic itself, survives a later refactor to strategy.order(), and costs nothing. Defensive redundancy on order-placement code is cheap insurance against the class of bug that only shows up as a margin call.

How pyramiding interacts with sizing, average price, and exits

Pyramiding does not just multiply your entries; it changes what several familiar built-ins mean, and exit logic written for single-entry strategies can quietly stop describing reality.

Average price moves with every add. strategy.position_avg_price is the volume-weighted average of all open fills. Each add into a rising market drags the average up toward current price, which means a stop expressed as a fixed distance below the average — like the one in our demo script — mechanically rises and tightens relative to the first entry as you add. That can be the intent, but you should know it is happening: after the third add, the original entry's downside cushion is not what the settings pane implies.

Percent-of-equity sizing compounds through adds. With default_qty_type = strategy.percent_of_equity, each add is sized off equity at the moment of the add — equity that includes the open profit of the earlier entries. In a trending backtest this creates a flattering feedback loop: winners finance bigger adds, which enlarge the winners. Live, the same loop runs in reverse during losing sequences. Fixed-quantity sizing (as in the demo) keeps adds interpretable while you study the mechanics.

Exits can target one add or all of them. By default, strategy.exit() with no from_entry argument applies to all open entries. To manage each rung separately, bind exits by id:

// Give the second add its own stop, independent of the first entry
strategy.exit("X2", from_entry = "Long-2",
     stop = strategy.position_avg_price - 200 * syminfo.mintick)

Per-entry exits are powerful but multiply the surface area for asymmetry bugs — a mirrored short side must invert every one of these levels. Audit that arithmetic explicitly; it is exactly the kind of slip a deterministic check catches and a smooth equity curve hides.

Why pyramided backtests flatter you — margin, drawdown, and the live gap

A pyramided strategy tested on the right stretch of history is one of the most reliable ways to produce an equity curve that oversells itself. The distortions are structural, not incidental.

Margin may not be simulated at all. Whether the broker emulator enforces margin depends on the margin_long and margin_short arguments of strategy(). When margin requirements are set to 0, the emulator skips margin checks entirely: a stacked position several times your equity sails through the backtest untouched, when a real broker would have issued a margin call or forcibly liquidated somewhere in the middle of your third add. Before trusting any pyramided result, check what your margin settings actually are and re-run the test with values that match your real account. If the equity curve changes shape, the original curve was describing capital you do not have.

Adds cluster your risk at the worst moment. By construction, a pyramided trend-following position is at maximum size precisely when the trend is most extended — which is also when reversals are sharpest. Losses on the full stack from the high are nonlinear relative to the single-entry version: the last add has the least cushion and the most size behind the average price. Per-trade statistics obscure this because the tester reports each add as its own trade; the position-level drawdown is what your account experiences.

Selection bias does the rest. Pyramiding parameters — number of adds, spacing, sizing per rung — are extra degrees of freedom, and every added degree of freedom makes it easier to fit the historical path rather than any repeatable behaviour. A pyramided backtest that survives realistic margin, position-level drawdown analysis, and out-of-sample data has earned some cautious attention. One that has not is a curve-fit with extra leverage. Either way: this is educational material, not financial advice — trading carries a real risk of loss, and no backtest is a forecast.

Key takeaways

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

Catch the bug that compiles.Run auditGet Pro