Repository navigation
Create data subscriptions for changed user defined universe members - #9935
Conversation
Adding a new resolution to a security already in the user defined universe, or removing and re-adding it at a new resolution in the same time step, kept the same selected symbols. Universe.PerformSelection then returned Unchanged and ApplyUniverseSelection skipped existing members, so the new configurations never reached the data manager and the old feed kept running. ApplyUniverseSelection now syncs the selected members of a user defined universe before the unchanged check: it adds the requested configurations the data feed does not have yet and removes this universe's request for the ones it no longer requests. Other universes keep the current path. Fixes #9900.
The user defined universe now tracks the members that get a new configuration while they stay selected, a new resolution or a removal and re-addition in the same time step. ApplyUniverseSelection adds their requested subscriptions and drops the replaced ones, reusing the add path of new selections, instead of re-requesting every selected member on each selection, which would also re-create subscriptions that already finished. Co-Authored-By: Claude Opus 5.5 <[email protected]>
There was a problem hiding this comment.
The engine change looks right to me, but CI is red. The failing test still expects the old, buggy behavior, so it needs updating.
Failing check: Build & Test Lean fails on BacktestingResultHandlerTests.ResolutionSwitchingAlgorithmSamplesNotMisalignedAbsolute (Tests/Engine/Results/BacktestingResultHandlerTests.cs:461). The expected daily performance for 10/10 to 10/11 is 0.009923037, but the run gives 0.020927.
I ran ResolutionSwitchingAlgorithm on master and on this branch and compared the logs. It is the scenario from #9900: it removes SPY (Daily) in OnEndOfDay on 10/9, then calls AddEquity("SPY", Resolution.Minute) from OnSecuritiesChanged.
- master: no minute subscription is ever created. The daily feed keeps running. The liquidation fills at 10/10 16:00 on the daily bar, and the re-buy fills at 10/11 16:00 for $146.0354.
- this branch: at 10/9 20:00 the daily config is removed and
SPY Minute TradeBar/QuoteBarare added. The liquidation and the re-buy both fill at 10/10 09:31 ($144.6693 / $144.652).
So the new number comes from the fix itself, not from a regression. The test's own comment already says "On 2013-10-10, we switch from Daily Resolution to Minute", which never actually happened on master. Please:
- update the expected performance values, and any other values in that test that change, to what the fixed engine produces;
- update the comment table and the per-line comments (
// 10/9 - 10/10 <- RemoveSecurity liquidation ..., etc.) so they describe the minute feed taking over on 10/10; - check
SamplesNotMisalignedRelative("ResolutionSwitchingAlgorithm", false)still expresses what is intended. It passes locally, but its premise is the same misalignment.
Testing evidence: the --filter in the description leaves out QuantConnect.Tests.Engine.Results, which is why this was missed. Please run the full QuantConnect.Tests.dll suite, or at least add Engine.Results, and post the result.
Code: no other blocking findings.
- In
UserDefinedUniverse.Add, tracking members whose configs changed and havingTryTakeChangedMemberstake the replaced pending configs is a narrow, sensible approach. It runs under the existing lock, and the per-selection cost when nothing changed is a type check plus one lock. - Extracting
AddSubscriptionsremoves the duplication instead of copying it. CallingAddedSubscriptionRequestagain for configs that already exist is harmless, becauseInternalSubscriptionManageralready guards onalreadyInternal. - Regression, Research Regression, Syntax, Report Generator and Python venv checks pass. Benchmarks was still running when I checked.
This changes which data subscriptions user algorithms get at runtime when they add a resolution or re-add a symbol, so I'm marking it high risk.
ResolutionSwitchingAlgorithm removes SPY (Daily) at the end of 10/9 and re-adds it at Minute resolution. The minute subscription is now created, so the liquidation and the re-buy fill at the 10/10 open instead of on the daily bars, changing the 10/10 - 10/11 and 10/11 performance samples. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
Thanks. Pushed c2b5ebf, which updates
|
There was a problem hiding this comment.
My earlier request is addressed in c2b5ebf, and Build & Test Lean now passes.
ResolutionSwitchingAlgorithmSamplesNotMisalignedAbsolute: the expected 10/10 - 10/11 values (0.020927) and 10/11 values (0.00590684) match what I saw runningResolutionSwitchingAlgorithmon this branch: the liquidation and the re-buy both fill at the 10/10 09:31 open after SPY is re-added at Minute resolution. The benchmark expectations are unchanged. The frame table and per-line comments now describe the minute feed taking over, instead of the old misaligned daily fills.SamplesNotMisalignedRelative("ResolutionSwitchingAlgorithm", false): the reasoning for leaving it unchanged holds up. There's no 10/11 8PM equity point, and 10/10 - 10/11 still differs from the benchmark by more than the tolerance.- Testing: the full
QuantConnect.Tests.dllrun is now in the description. Its only failure is the timing-sensitiveIsolatorTests.WorksCorrectlyUsingWorker, which passes on a re-run. - Code: the engine and
UserDefinedUniversechanges haven't changed since my last review, and I have no further findings on them.
CI: Build & Test, Regression, Research Regression, Syntax, Report Generator and Python venv checks pass. Benchmarks is still running.
I'm keeping this high risk: it changes which data subscriptions user algorithms get at runtime when they add a resolution or re-add a symbol.
There was a problem hiding this comment.
No new commits since my last review at c2b5ebf. Every check now passes, Benchmarks included.
I went back over the engine change and found nothing new:
- Remove and re-add at a new resolution in one time step:
RemoveAndKeepTrackmoves the old configs to the pending removal list and clears the changed-member record. The re-added config isn't pending, so the symbol is recorded as changed.TryTakeChangedMembersthen hands back the old configs as replaced, andApplyUniverseSelectiondrops this universe's request for them before it adds the new ones. - Re-add at the same resolution: the config comes off the pending list and the member isn't marked as changed, so its running subscription is kept.
RemoveAndReaddAtSameResolutionInOneTimeStepKeepsDataSubscriptioncovers this. - Consistency:
RemoveMembergoes throughRemoveAndKeepTrack, which clears the changed-member record. A recorded symbol therefore can't outlive its membership, and replaced configs can't be dropped from the pending list without being removed from the data feed. - Cost: when nothing changed, a selection pays for a type check and one lock.
I'm keeping this high risk: it changes which data subscriptions user algorithms get at runtime when they add a resolution or re-add a symbol.
There was a problem hiding this comment.
No new commits since my last review at c2b5ebf, and every check passes.
Master has moved 12 commits ahead since this branch was cut. None of them touch the files this PR changes (UserDefinedUniverse, UniverseSelection, UniverseSelectionTests, BacktestingResultHandlerTests). GitHub reports the PR as mergeable with a clean merge state. The one change on master that affects the new regression algorithm is the sharding in RegressionTests.cs. It splits the discovered cases by index, so UserDefinedUniverseResolutionChangeRegressionAlgorithm is still picked up with no registration needed.
My earlier findings stand, and I have nothing new on the engine change.
I'm keeping this as risk:high because it changes which data subscriptions user algorithms get at runtime when they add a resolution or re-add a symbol.
Description
Adding a new resolution to a security that is already in the user defined universe (
AddEquity("SPY", Resolution.Tick)afterAddEquity("SPY", Resolution.Minute)), or removing it and re-adding it at another resolution in the same time step, recorded the new configuration onSecurity.Subscriptionsbut never created its data subscription: the algorithm kept the old bars and got no ticks.The member stays selected in both cases, so
Universe.PerformSelectionreturnsUnchangedandApplyUniverseSelectionreturns early. Even when it didn't, the "find new selections" loop skips existing members, and on the remove and re-add path the removal loop keeps the member, so its replaced minute feed stays.Changes:
UserDefinedUniverse.Add(SubscriptionDataConfig)records a symbol as a changed member when it adds a configuration for a symbol that is already a member. Re-adding a configuration that was pending removal (remove and re-add at the same resolution) just takes it off the pending list, since its subscription is still running.RemoveAndKeepTrackclears the record.UserDefinedUniverse.TryTakeChangedMembersreturns those members, plus the configurations pending removal that they replaced.ApplyUniverseSelectiontakes the changed members of a user defined universe before the unchanged check. It removes this universe's request for the replaced configurations, then adds the member's requested subscriptions through the same code new selections use. That code is now inAddSubscriptions, so it isn't duplicated.Only members whose configurations actually changed are touched. Re-requesting every selected member on each selection would add work to every user defined selection, including selector universes that fire at their resolution, and it would re-create subscriptions that already finished, for example when a security's data ends before the algorithm does.
This builds on #9930 by @Divyansh151005, cherry-picked with authorship kept: its unit tests and regression algorithm are reused as they are. The engine change is replaced by the targeted tracking described above.
Related Issue
Closes #9900
Motivation and Context
Switching a held symbol to tick resolution did nothing and gave no warning, in both backtesting and live trading.
Requires Documentation Change
No.
How Has This Been Tested?
UniverseSelectionTests.AddingResolutionToExistingUserDefinedSecurityCreatesDataSubscriptionandRemoveAndReaddAtNewResolutionInOneTimeStepReplacesDataSubscription(from Create data subscriptions for re-selected user defined members #9930): fail on master, pass with the change.New
UniverseSelectionTests.RemoveAndReaddAtSameResolutionInOneTimeStepKeepsDataSubscription: removing and re-adding at the same resolution keeps the existing subscriptions. Passes on master and with the change.UserDefinedUniverseResolutionChangeRegressionAlgorithm(from Create data subscriptions for re-selected user defined members #9930, AIG/BAC 2013-10-07): fails on master withExpected AIG ticks after adding the tick resolutionand passes with the change, with 307,906 data points.BacktestingResultHandlerTests.ResolutionSwitchingAlgorithmSamplesNotMisalignedAbsoluteis updated.ResolutionSwitchingAlgorithmremoves SPY (Daily) at the end of 10/9 and re-adds it at Minute resolution fromOnSecuritiesChanged, which is this exact scenario. Before, the minute subscription was never created: the liquidation filled at 10/10 16:00 on the daily bar and the re-buy at 10/11 16:00. Now the minute feed takes over and both fill at 10/10 09:31. The 10/10 - 10/11 performance changes from 0.009923 to 0.020927 and the 10/11 sample from 0.007854 to 0.005907. The benchmark values are unchanged.SamplesNotMisalignedRelative("ResolutionSwitchingAlgorithm", false)still holds: the equity series has no 10/11 8PM sample, and 10/10 - 10/11 still diverges from the benchmark by more than the 0.0005 tolerance.Full unit test suite (Linux, .NET 10):
The one failure is
IsolatorTests.WorksCorrectlyUsingWorker, a 0.1 second time limit test. It timed out under load and passes when re-run (IsolatorTests: 6/6 passed). The regression algorithms are covered by the separate regression check.Types of changes
Checklist:
bug-<issue#>-<description>orfeature-<issue#>-<description>🤖 Generated with Claude Code