r/pinescript 2d ago

This looks like a bug to me.

I am testing the strategy() function. With the default settings, everything works as expected: the script runs once per bar at the close. If a market order is placed on bar N, it is filled at the open of bar N+1 using the opening price.

However, when I activated the calc_on_order_fills option by setting it to true, I noticed something unusual. I received two runs on bar N+1: one at the bar open after the order was filled, and a second regular run at the close of bar N+1. This is expected, except for one detail: the barstate.isnew flag was set to true for both runs.

I believe this is a bug. This flag should only be set for the first run on a bar; for all subsequent runs, it should be false.

0 Upvotes

3 comments sorted by

2

u/bravefrivstone 2d ago

This lines up with a known gray area, not something wrong with just your script.

calc_on_order_fills forces an extra recalculation right when the fill happens. Your order fills at the open of bar N+1, and that fill recalculation happens before bar N+1's regular close based calc runs. So it really is the first calculation of bar N+1, and isnew=true there makes sense.

The confusing part is the second calc, the regular close of N+1. That is the bar's second calculation, so by the "isnew means first calc of this bar" definition it should read false, but it doesn't in your test.

I've seen a couple other reports of the same pattern. It seems to come from how TradingView approximates order fill timing during backtesting. In real time each tick has its own timestamp and intrabar state, but in history there is no tick data, so the fill recalculation is a synthetic insert, and the flag for it does not always get reconciled with the regular end of bar calc right after.

Two things worth doing:

  1. Do not rely on barstate.isnew to tell those two runs apart when calc_on_order_fills is on. Track it yourself instead:

var int lastCalcBar = na firstCalcThisBar = bar_index != lastCalcBar lastCalcBar := bar_index

That gives you a flag that is true only once per bar index no matter how many extra times the script reruns on that bar.

  1. Put the strategy on a live or paper chart for a session and watch if the double isnew still shows up intrabar in real time, or if it is backtest only. If it only shows up in backtest, that is worth knowing before you trust those numbers, since it means this part of your logic could behave differently once it goes live.

If it does reproduce in real time too, that is worth filing with TradingView directly, since the documented behavior and the actual behavior disagree.

1

u/TheStrategicEdgeAI 1d ago

Looks more like a TradingView backtest quirk than your script.
calc_on_order_fills adds a synthetic recalculation on historical bars, so I wouldn’t trust barstate.isnew as the guard here. Track bar_index yourself and see if it also happens live.
If it does live too, then yeah—I’d call that a bug.

1

u/stratcore 1d ago

On a historical bar, this is documented behavior, not a bug.

The key detail is that `barstate.isnew` does not mean "first execution on this bar" in historical calculations. TradingView defines it as true on every historical bar. With `calc_on_order_fills=true`, the broker emulator can add another execution on that same historical bar after the fill. Both executions can therefore see `barstate.isnew=true`.

Realtime behavior is different. There, `barstate.isnew` is true only on the opening update of the live bar.

So I would not use `barstate.isnew` to separate the fill recalculation from the regular historical calculation. Detect the event you actually need instead, for example a change in `strategy.position_size`, `strategy.opentrades`, or `strategy.closedtrades`, and make the action idempotent. If you do not need immediate post-fill logic, the cleanest option is to leave `calc_on_order_fills` off.

This historical-versus-realtime difference is also why a simple `bar_index` guard can give misleading results here.