Skip to content

Fill triggered stop limit orders as limit orders on later bars - #9787

Merged
Martin-Molinero merged 3 commits into
QuantConnect:masterfrom
Martin-Molinero:bug-9773-stop-limit-post-trigger-fill
Sep 10, 2026
Merged

Fill triggered stop limit orders as limit orders on later bars#9787
Martin-Molinero merged 3 commits into
QuantConnect:masterfrom
Martin-Molinero:bug-9773-stop-limit-post-trigger-fill

Conversation

@Martin-Molinero

Copy link
Copy Markdown
Member

Description

Closes #9773

Once a stop limit order's stop has triggered on an earlier bar, every later bar is entirely post trigger, but the fill models kept applying the trigger bar's close based test, so a triggered stop limit never filled on an intra bar limit touch.

  • StopLimitOrder gets a nullable StopTriggeredTime in UTC, the end time of the data that triggered the stop. It is cloned, serialized as stopTriggeredTime (omitted when null) and deserialized, along with stopTriggered which was previously dropped. Missing fields deserialize to their defaults so existing payloads still load.
  • FillModel and EquityFillModel set the time when the stop triggers. On data that starts at or after the trigger time and ends after it, the limit leg fills under the resting limit rule, reusing the existing limit fill implementations (the equity one keeps the favorable gap open fill). The trigger bar keeps the close based test since its range may predate the trigger. Zero duration data at the exact trigger time, like a tick batch sharing the trigger tick time stamp or a live re-scan, stays on the close based rule.
  • OrderUpdateEvent carries the time, the backtesting brokerage emits it and the transaction handler applies it. Brokerages that only flip the flag get the current algorithm time, and the time is never cleared.

Related Issue

#9773

Motivation and Context

A live paper deployment had a buy stop limit triggered on the first bar, six later bars traded through the limit and none closed below it, so the order stayed submitted for hours. A plain limit order placed at the trigger would have filled.

Requires Documentation Change

StopLimitOrder.StopTriggeredTime is a new public property.

How Has This Been Tested?

  • New EquityFillModelTests covering fills at the limit and at the open on a favorable gap on the bar after the trigger, for buys and sells, and no fill from the trigger bar range even when re-scanned.
  • New ImmediateFillModelTests covering the base model for both directions, including the re-scan case.
  • OrderJsonConverterTests round trip the flag and time and cover payloads without them.
  • BrokerageTransactionHandlerTests cover applying the update with and without the time, and ignoring it when the algorithm is stopped.
  • StopLimitOrderRegressionAlgorithm (C# and Python) asserts the trigger time is set before the fill. UpdateOrderRegressionAlgorithm statistics changed because its stop limit orders now fill on the first later bar that trades through the limit. OrderTicketDemoAlgorithm, SplitEquityRegressionAlgorithm and StopLimitOrderRegressionAlgorithm only changed their order list hash from the new serialized field.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Refactor (non-breaking change which improves implementation)
  • Performance (non-breaking change which improves performance)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Non-functional change (xml comments/documentation/etc)

Checklist:

  • My code follows the code style of this project.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • My branch follows the naming convention bug-<issue#>-<description> or feature-<issue#>-<description>

🤖 Generated with Claude Code

https://claude.ai/code/session_013Zic3mz9ZogfCvhzoTXvDG

Martin-Molinero and others added 3 commits September 10, 2026 15:46
Track the time a stop limit order's stop was triggered, the end time of
the data that triggered it, and fill the limit leg like a resting limit
order on data that is entirely after it. The trigger bar keeps the
close based test since its range may predate the trigger. The time is
serialized, cloned and propagated through the order update event.

Closes QuantConnect#9773

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Zic3mz9ZogfCvhzoTXvDG
Brokerages that only flip the stop triggered flag get the algorithm time
as the trigger time, so the fill models can still tell later data apart.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Zic3mz9ZogfCvhzoTXvDG
@Martin-Molinero
Martin-Molinero merged commit 8ee075a into QuantConnect:master Sep 10, 2026
6 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant