Skip to content

process_orders_on_close = true: a stop-limit entry placed at the bar close does not fill on that close #707

Closed

Description

@letiep97

Summary

With process_orders_on_close = true, an entry order created on a bar's close with both limit and stop equal to that close does not fill on the same closing tick. It stays pending and fills on a later bar. Market, limit-only and stop-only entries created the same way fill on the closing tick, as documented.

Entry call (on the order bar's close) Fill bar on PineForge
strategy.entry('Long', strategy.long) order bar
strategy.entry('Long', strategy.long, limit = close) order bar
strategy.entry('Long', strategy.long, stop = close) order bar
strategy.entry('Long', strategy.long, limit = close, stop = close) next bar, or later after a gap

With process_orders_on_close = false, all four fill on the next bar, so only the stop-limit case ignores the setting.

Versions

pineforge-engine v1.9.1 (fc0a2df), pineforge-codegen 1.9.1, Apple clang 17.0.0, macOS 15 arm64. Run on BTCUSDT 1h bars from 2024-01-01 to 2024-03-01 (1,441 bars), initial capital 1000, no commission or slippage, input timeframe = script timeframe = 1h.

Reproduction

//@version=6
strategy('repro', process_orders_on_close = true)
if bar_index % 50 == 0 and strategy.position_size == 0
    strategy.entry('Long', strategy.long, limit = close, stop = close)
if strategy.position_size > 0 and bar_index % 50 == 10
    strategy.close('Long')

Generated: strategy_entry(std::string("Long"), true, current_bar_.close, current_bar_.close, na<double>(), "", "", 0, -1);

27 trades. Every entry fills one bar after its order bar: the order created on bar 0 fills on bar 1, bar 50 on bar 51, bar 100 on bar 101. Two orders never fill before the next order bar. Bars of this series open at the previous close, so the delayed fill keeps the same price; when the next bar gaps away from the level, the order waits until the price comes back.

Control, limit = close only:

    strategy.entry('Long', strategy.long, limit = close)

Generated: strategy_entry(std::string("Long"), true, current_bar_.close, na<double>(), na<double>(), "", "", 0, -1);

29 trades, every entry fills on its order bar (bar 0, 50, 100, ...) at that bar's close. stop = close alone and a market entry give the same 29 trades on the order bars.

Expected

The entry fills on the order bar at its close, as in the control. With process_orders_on_close = true the broker emulator can fill orders created on a bar's close on that same closing tick (process_orders_on_close). On that tick the price equals both the stop and the limit level, so the stop triggers and the limit is marketable at the same price.

TradingView check (2026-11-02, BINANCE:BTCUSDT 1h, Deep Backtesting 2024-01-01 to 2024-03-01): the stop-limit entry and the limit-only control give the same 29 trades with the same entry times and prices, 50 bars apart (for example 2024-01-02 02:00 UTC at 45378.96 in both runs), so TradingView fills the stop-limit entry where it fills the limit-only one.

Workaround

When stop and limit are the same price, pass only limit (or only stop). We have not tested a workaround for a stop-limit entry with two different prices.

Activity

  1. luisleo526 commented on Nov 2, 2026

    @luisleo526
    Collaborator

    Thank you for the report and the script. We are reproducing it now: the stop-limit entry with process_orders_on_close = true, against TradingView's own fills, on v1.9.1 and on the current development build. You will get the triage result here by 2026-11-03 18:00 (Taipei), including which bar the order should fill on and how it will be fixed if it differs.

  2. luisleo526 commented on Nov 2, 2026

    @luisleo526
    Collaborator

    Confirmed. With your script on v1.9.1 (and on the current development build, identical result), every stop-limit entry with limit = close and stop = close under process_orders_on_close = true fills one bar after its order bar, at the same price, where TradingView fills it on the order bar's close; over your test range the engine makes 27 entries against TradingView's 29, with no entry time in common. This is a silent wrong result in a released version; a fix is in progress and the next release will not ship without it. We will post the fix here.

  3. added a commit that references this issue on Nov 5, 2026
    d129cc0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions