New playbooks in your inbox
Hands-on tutorials for people who want to build with AI.

The Breakeven Stop-Loss Bug Claude Keeps Writing in Pine

Pine script breakeven stop loss triggering early? Claude gates it on a price touch, not a confirmed fill. The var bool tp1Hit pattern that fixes it for good.

From the youcanbuildthings catalog ▸ Build-tested 8 min read

Summary:

  1. Teaches the single most-cited Pine bug Claude generates: a breakeven stop that fires on a price touch instead of a confirmed fill.
  2. The fix is a var bool tp1Hit flag gated on strategy.closedtrades.exit_id, not on if high >= tp1Price.
  3. The wrong version backtests fine on some bars and bleeds the live account; the right version matches live behavior.
  4. Deliverable: the copy-paste fill-state pattern and the CLAUDE.md rule that stops Claude regenerating the bug.

A pine script breakeven stop loss triggering early is almost never a math error. It is a fill-versus-touch error, and Claude generates it about 80% of the time without a correction, because most public Pine on the topic is wrong the same way. Your stop moves to breakeven, then a normal pullback stops you out of a trade that should have run. The backtest looked fine. The live account bled.

This is the bug the entire r/TradingView “Frustrated with Claude” thread is about. The OP:

“Claude keeps giving me partial rewrites, adding unnecessary changes, or suggesting code that still doesn’t actually make the breakeven logic work.”

(r/TradingView, “Frustrated with Claude on coding tasks where the logic needs to be very exact.”)

The CH07 fill-versus-touch comparison: three r/TradingView trader quote cards on the left (the OP, Acesleychan, and LouZEverything), a WRONG block fires on touch not fill (if high greater-or-equal tp1Price then move stop to entry), and a RIGHT block gates on confirmed fill state (var bool tp1Hit checked against strategy.closedtrades.exit_id). Bottom callout: TOUCH does not equal FILL.

What actually causes the early breakeven?

The bug is that the code treats “price touched TP1” as “TP1 filled.” Those are not the same event, and TradingView’s broker emulator knows the difference even when your script does not.

Here is the wrong pattern Claude writes by default. It looks reasonable:

// PRICE TOUCH LOGIC (WRONG)
if high >= tp1Price
    // touch detected — not a confirmed fill
    stopPrice := entryPrice
else
    stopPrice := entryPrice * 0.99
strategy.exit("SL", from_entry="Long", stop=stopPrice)

The price wicks up to tp1Price intra-bar. high >= tp1Price goes true. The stop snaps to breakeven. Then the bar closes back below TP1 and the limit order never filled. Now you are carrying a breakeven stop on a position whose take-profit never triggered, and the next ordinary pullback stops you flat. A trader who posts as Acesleychan named the root cause in one line on that thread:

“are you tracking the fill or just the touch? That’s why your SL moves too early.”

Touch is not fill. Price can wick to your target and come straight back. Your stop should react to what actually filled, not what touched.

The fix: gate on confirmed fill state

The right pattern tracks a persistent boolean that flips only when the broker emulator confirms a TP1 fill:

//@version=6
var bool tp1Hit = false
entryPrice = strategy.position_avg_price

// Detect confirmed fill of the most recent closed trade
if strategy.closedtrades > 0
    lastExitId = strategy.closedtrades.exit_id(strategy.closedtrades - 1)
    if lastExitId == "TP1" and strategy.closedtrades.profit(strategy.closedtrades - 1) > 0
        tp1Hit := true   // TP1 was actually filled

// Dynamic stop: breakeven after TP1 fill, else initial risk
stopPrice := tp1Hit ? entryPrice : entryPrice * 0.99

// Exit order
strategy.exit("SL", from_entry="Long", stop=stopPrice)

Three things make this correct. var bool tp1Hit = false persists across bars; without var it resets every bar and the breakeven never activates. The strategy.closedtrades > 0 guard prevents an out-of-range lookup before the first trade closes. And the trigger lives entirely in strategy.closedtrades.exit_id(...) == "TP1", which only returns true after the broker emulator has run its fill logic on a closed bar. Before that, the original entryPrice * 0.99 stop holds. The breakeven cannot fire on a wick because a wick is not a closed trade.

LouZEverything, further down the same thread, named the recovery method when Claude keeps “improving” the code instead of fixing it:

“strip the strategy down to entry, TP1, fixed target, and stop only. Get the logic right first.”

That is the move. When Claude spirals, do not paste the whole 200-line script back. Strip to entry, TP1, stop. Get fill detection right. Then rebuild.

How to fix this in your own strategy

  1. Find the breakeven logic. Search the script for entryPrice or wherever the stop snaps to breakeven.
  2. Look at the condition. If it is a price comparison (if high >= tp1Price, if low <= ..., if close >= ...), it is gating on touch. That is the bug.
  3. Replace it with the strategy.closedtrades.exit_id check above. Name your take-profit order "TP1" so exit_id can match it.
  4. Add the var bool tp1Hit = false flag and the strategy.closedtrades > 0 guard.
  5. Reset the flag on a flat position so the next trade starts fresh:
if strategy.position_size == 0
    tp1Hit := false
  1. Backtest, then open Strategy Tester, List of Trades. Filter for trades that hit TP1 then exited at breakeven. The breakeven exit price should equal the entry price within slippage. If any breakeven exits land at a different price, the touch logic is still somewhere in the script.

Why the backtest hid it from you

This is the part that makes the bug expensive. TradingView’s broker emulator fills strategy orders only when chart data confirms the fill, so on bars where price closes through TP1 the backtest looks correct. On wick bars the touch logic fires a false breakeven the emulator partly absorbs. The result is a backtest that looks fine in aggregate and a live account where two of three winners get stopped at breakeven before the target. Same code. The emulator is more honest than the script; the live broker is exactly as honest as the emulator.

If you drive Claude Code, this is a one-line CLAUDE.md rule so the bug never comes back:

- All breakeven SL: gate on fill state via var bool tp1Hit checked against
  strategy.closedtrades.exit_id, never on a price touch (if high >= tp1Price).

What should you actually do?

  • If your stop moves to breakeven on wicks → replace every price-touch condition with the strategy.closedtrades.exit_id fill check. This is the whole fix.
  • If Claude keeps breaking other parts while “fixing” it → strip the strategy to entry, TP1, stop only. Get fill detection right in isolation, then rebuild.
  • If your backtest looks fine but live bleeds at breakeven → it is touch logic. The emulator hid it; the live broker will not.
  • If you use Claude Code on strategies → add the CLAUDE.md rule above on day one.

bottom_line

  • Touch is not fill. Build the breakeven trigger on a confirmed closed trade, never on a price comparison, every time.
  • The emulator is more honest than your script. If the live account disagrees with the backtest on breakeven exits, the script is reading a touch.
  • This single fix is the difference between a strategy that backtests beautifully and loses money and one whose live performance matches the test. It is the most-cited Pine bug Claude writes; fix it once in CLAUDE.md and it is gone for good.
Why trust this? Every youcanbuildthings guide is pulled from a build-tested book — code that ran in production before it was written down.

Frequently Asked Questions

Why does my Pine breakeven stop trigger before the take-profit?+

Your code checks a price touch (if high >= tp1Price) instead of a confirmed fill. Price wicks to TP1 intra-bar, your breakeven activates, the bar closes below TP1, and the broker emulator never actually filled the take-profit. The fix is to gate on fill state.

How do I detect a real TP1 fill in Pine Script?+

Check strategy.closedtrades and strategy.closedtrades.exit_id(strategy.closedtrades - 1) == "TP1". That return value means the broker emulator actually closed a trade against the TP1 order, not that price touched the level.

Why does my backtest look fine but the live account bleeds on this?+

TradingView's broker emulator distinguishes touch from fill correctly, so some backtest bars look right. A price-touch breakeven fires on wicks the live broker never filled, so live you get stopped at breakeven on trades that should have run.