Target: QuantConnect/Lean
Issue: QuantConnect/Lean#9440
PR: QuantConnect/Lean#9538
Field Lab: https://github.com/scarab-systems/scarab-field-lab
This field test targeted a continuous futures behavior gap in QuantConnect Lean.
The issue requested more control over continuous futures roll behavior, specifically around two related needs:
- configurable roll timing before expiry
- selected contract-month holding cycles
That is a specialized domain surface.
Continuous futures are not simply βpick the next contract when one expires.β
Trading systems often need to control when a continuous contract rolls, how many tradeable days before expiry the roll should occur, and whether the strategy should walk through every contract month or only selected contract cycles.
The visible issue was not a generic futures bug.
It was a mapping-policy boundary.
The diagnostic question was not:
How do we redesign continuous futures?
The better question was:
Where does Lean already decide which mapped contract should be active, and how can roll timing and contract-month selection be threaded through that existing path without breaking the rest of the futures system?
Field Lab record
The public case record for this field test is available in the Scarab Field Lab:
https://github.com/scarab-systems/scarab-field-lab
SDS result
This field test was recorded as a diagnostic-proof-and-repair case against QuantConnect Lean.
The useful result was a bounded repair lane.
The failure area touched several parts of Leanβs futures machinery:
- continuous futures subscription behavior
- historical data requests
- futures universe selection
- mapping-event generation
- mapped-symbol behavior
- contract expiry and last-trading-day data
- selected contract-month cycles
That is exactly the kind of issue where a patch can easily get too broad.
A repair could try to invent a separate roll calendar.
A repair could duplicate mapping data.
A repair could hard-code a specific futures cycle.
A repair could alter futures selection behavior in one path but forget history, subscriptions, or mapping events.
That would be dangerous.
The safer repair lane was to extend Leanβs existing continuous futures mapping behavior with a new mapping mode and thread the selected roll offset through the paths that already participate in continuous futures mapping.
Failure shape
The failure shape was a missing policy expression.
Lean already supports continuous futures mapping.
Lean already has futures map rows with LastTradingDay.
Lean already has subscription, history, universe, and mapping-event paths that participate in continuous futures behavior.
But the requested behavior needed a way to express:
roll this many tradeable days before expiry
and, separately:
walk through selected contract-month cycles instead of treating all contract months the same way
Without that, users who needed roll timing before expiry or selected month-cycle behavior had to work around the engine instead of expressing the policy through Leanβs continuous futures configuration.
That is not ideal for a trading engine.
The roll policy belongs inside the continuous futures mapping surface.
Boundary
The boundary here is:
continuous futures mapping policy versus downstream algorithm workaround
Lean does not need every algorithm to manually reconstruct futures roll timing.
It does not need users to duplicate mapping behavior outside the engine.
It does not need a separate ad hoc roll calendar when existing map data already carries the relevant last-trading-day information.
But Lean does own the continuous futures mapping policy surface.
That is the repair boundary.
The patch stays inside that boundary by adding a mapping mode that can express tradeable-day roll timing before expiry, then threading that offset through the Lean paths where continuous futures mapping is already resolved.
That matters because continuous futures behavior has to remain coherent.
Subscription behavior, historical requests, universe selection, and mapping events should not each invent their own interpretation of the active contract.
They need to agree.
That is the core boundary:
one mapping policy, consistently carried through the engine surfaces that depend on it
What changed
The PR adds a TradingDaysBeforeExpiry mapping mode.
That gives Lean a way to express roll timing as a tradeable-day offset before contract expiry.
The repair threads that roll offset through continuous futures behavior across the relevant paths, including:
- subscription handling
- history behavior
- universe behavior
- mapping-event behavior
- mapped-symbol selection
The PR also adds optional contract-month-cycle walking.
That lets the mapping behavior support selected holding cycles instead of forcing every use case through a single generic contract-month progression.
Importantly, the repair reuses existing LastTradingDay map rows.
That keeps the patch grounded in Leanβs existing futures mapping data instead of inventing a separate source of expiry truth.
That is the narrow shape of the repair:
- add an explicit mapping mode for tradeable-day roll timing
- carry the chosen roll offset through the existing continuous futures mapping paths
- support selected contract-month-cycle walking
- reuse existing LastTradingDay map data
- avoid a separate futures-roll subsystem
Why this was not a broad futures rewrite
This issue could easily tempt a broad patch.
Continuous futures touch a lot of engine behavior.
They affect subscriptions.
They affect history.
They affect symbol mapping.
They affect universe selection.
They affect how algorithms see continuity across expiring contracts.
But the issue did not require rewriting futures support.
It required a controlled way to express and apply a missing mapping policy.
That is why the repair is framed around mapping behavior rather than a new futures engine.
The existing system already has the concept of mapped contracts.
The existing map rows already include LastTradingDay.
The missing piece was a mapping mode and roll-offset path that could apply the requested behavior consistently.
That is the difference between a surgical engine patch and a sprawling feature rewrite.
Why contract-month cycles mattered
The selected contract-month cycle part is important.
Many futures strategies do not want to roll through every available contract month in a naive sequence.
They may need to hold selected cycles depending on the instrument, strategy, liquidity assumptions, or domain convention.
If the engine only supports a simpler month progression, users may be forced to work around the mapping behavior externally.
That creates drift between what the algorithm wants and what the engine believes the mapped contract is.
The repair adds optional contract-month-cycle walking so the mapping behavior can represent that strategy-level need inside the engineβs existing continuous futures path.
Again, the important part is not just adding an option.
The important part is keeping the option inside the mapping boundary where subscription, history, universe, and mapping-event behavior can agree.
Why the diagnostic result mattered
This case is useful because the repair sits in a high-stakes domain surface.
Financial-engine code has less tolerance for vague behavior.
A roll rule is not a cosmetic preference.
It changes what contract an algorithm believes it is trading or analyzing at a given time.
So the diagnostic posture had to keep the patch grounded around the actual engine contract:
- which contract is active
- when the mapping should roll
- which contract month should be selected
- which engine paths need to see the same mapped result
The patch was strongest when it stayed inside that ownership surface.
It did not try to redesign user strategy logic.
It did not invent a separate roll calendar.
It did not duplicate expiry truth.
It extended the existing continuous futures mapping behavior so the requested policy could be expressed directly.
That is the Scarab boundary:
when a platform already owns the mapping contract, repair the missing policy expression inside that contract instead of pushing the burden to downstream users
Validation
Local build validation passed with zero compile errors.
The field record notes that the PR is open and ready for review, with upstream CI left as the authoritative full test run.
That is the correct validation posture for this case.
For a large platform like Lean, local compile validation is meaningful, but upstream CI remains the final project-owned validation surface.
For exact validation status and public case details, see the Field Lab record:
https://github.com/scarab-systems/scarab-field-lab
Field test result
This was a bounded continuous futures mapping repair candidate for QuantConnect Lean.
The issue reduced to:
- users needed configurable continuous futures roll timing before expiry
- users also needed selected contract-month holding cycles
- Lean already owned the continuous futures mapping surface
- existing map rows already carried LastTradingDay
- the missing surface was a mapping mode and roll-offset path for tradeable-day roll timing
- the PR adds TradingDaysBeforeExpiry
- the PR threads the roll offset through subscription, history, universe, and mapping-event paths
- the PR adds optional contract-month-cycle walking
- the PR reuses existing LastTradingDay map rows
- local build validation passed with zero compile errors
- upstream CI remains the authoritative full validation run
That is the repair lane.
This patch does not claim to redesign Lean futures support.
It does not claim to replace algorithm strategy logic.
It does not claim to invent a separate futures-roll calendar.
It does not claim to change every futures mapping mode.
It adds a bounded mapping-policy path for tradeable-day roll timing and selected contract-month progression.
Public claim
The correct claim for this field test is:
Scarab/SDS helped drive a bounded repair candidate for QuantConnect/Lean#9440, where continuous futures needed configurable roll timing before expiry and selected contract-month holding cycles. The upstream PR adds a TradingDaysBeforeExpiry mapping mode, threads a tradeable-day roll offset through Leanβs continuous futures subscription, history, universe, and mapping-event paths, adds optional contract-month-cycle walking, and reuses existing LastTradingDay map rows instead of inventing a separate expiry source. Local build validation passed with zero compile errors, and the PR is open for upstream review with CI left as the authoritative full validation surface. This does not claim to redesign Lean futures support broadly; it repairs the continuous futures mapping-policy boundary where roll timing and selected contract cycles needed to be expressed.
Disclosure: This field report was prepared with AI-assisted editing from my own field-test notes, public issue and PR records, validation summary, and Field Lab record. The technical claims and final wording were reviewed before publication.
Top comments (0)