Skip to content

Improve consolidator registration ergonomics: quote-to-trade bar adaptation, registration-time period validation, calendar periods in RegisterIndicator - #9675

Open
jhonabreul wants to merge 4 commits into
QuantConnect:masterfrom
jhonabreul:feature-consolidator-registration-ergonomics
Open

jhonabreul wants to merge 4 commits into
QuantConnect:masterfrom
jhonabreul:feature-consolidator-registration-ergonomics

Conversation

@jhonabreul

@jhonabreul jhonabreul commented Aug 11, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Consolidator registration on quote-only feeds (forex, cfd) and with mismatched periods fails late or needlessly. The engine can adapt type mismatches, validate periods at registration, accept calendar periods in RegisterIndicator, and reject null symbols clearly.

The change:

  • SubscriptionManager.AddConsolidator adapts trade bar consolidators to quote-only feeds through a new QuoteBarToTradeBarAdapter.
  • Each QuoteBar is collapsed into an internal QuoteDerivedTradeBar: mid-point prices, and reading or writing Volume throws InvalidOperationException.
  • Volume indicators on quote-only feeds therefore fail at the first bar with a clear message, instead of computing on zero volume.
  • TradeBarConsolidator keeps its consolidated bar volume-less when fed collapsed bars.
  • QCAlgorithm.RegisterIndicator<T> does the same collapse at the indicator layer (e.g. self.mass(symbol, resolution=Resolution.HOUR) on a cfd).
  • Adapters derive from ConsolidatorAdapterBase, which equals the consolidator it wraps and shares its hash code, so users remove the consolidator they hold.
  • ConsolidatorIdentityComparer backs SubscriptionDataConfig.Consolidators and the scan dictionary, so lookups match from either side (ConcurrentSet now accepts a comparer).
  • Fixed time span periods are validated at registration, on the user's consolidator before it is wrapped: a 5-minute consolidator on a daily subscription fails in Initialize.
  • RegisterIndicator/ResolveConsolidator accept calendar rules (Calendar.WEEKLY, Calendar.MONTHLY, custom), in C# and Python.
  • Clearer rejections: the type mismatch error suggests Consolidate(), and a null symbol (e.g. Future.Mapped before the first mapping) throws ArgumentNullException explaining the likely cause.
  • BaseData.IsFillForward setter is now protected so the collapsed bar can override Clone(bool) without touching volume.

Important

Known limits of the adapter identity

  • adapter.Equals(wrapped) is true, but wrapped.Equals(adapter) is false.
  • == / ReferenceEquals between the adapter and the wrapped consolidator stay false.
  • The comparer covers the collections where identity matters (SubscriptionDataConfig.Consolidators and the scan dictionary).

Related Issue

N/A

Motivation and Context

The engine has all the information to adapt or fail fast at registration, and the late period error can kill a backtest months of simulated time in.

Requires Documentation Change

register_indicator docs can mention calendar periods. The quote-to-trade adaptation is transparent, except that volume on the collapsed bars throws.

How Has This Been Tested?

  • ConsolidatorAutoAdaptationRegressionAlgorithm (C# + Python): trade bar consolidator and MassIndex on EURUSD receive collapsed bars, and the bars throw on volume.
  • RegisterIndicatorWithCalendarRegressionAlgorithm (C# + Python): weekly RSI registered with Calendar.WEEKLY updates when the week boundary is crossed.
  • ConsolidatorPeriodValidationRegressionAlgorithm (C# + Python): 10-second consolidator on minute data is rejected in Initialize.
  • QuoteDerivedTradeBarTests: mid-point collapse, volume get/set throw, clones keep the type, consolidation keeps bars volume-less.
  • SubscriptionManagerTests: adapter data flow and removal, Python adapted consolidator add/remove, adapter equality, comparer matching both ways, period validation cases, null-symbol message, mismatch suggestion.
  • AlgorithmIndicatorsTests: calendar registration in C# and Python, MassIndex on a quote-only feed updates, OnBalanceVolume on a quote-only feed throws on the first bar.
  • PythonIndicatorTests: updated messages.
  • Suites: affected fixtures 470 passed, 0 failed; the six regression algorithm cases passed.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • Refactor (non-breaking change which improves implementation)
  • Performance (non-breaking change which improves performance. Please add associated performance test and results)
  • New feature (non-breaking change which adds functionality)
  • 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>

- Adapt trade bar consolidators and indicators to quote-only feeds
  (forex, cfd) by collapsing quote bars into mid-point trade bars with
  zero volume (new QuoteBarToTradeBarAdapter), instead of rejecting the
  registration with a type mismatch error
- Validate fixed time span consolidator periods against the subscription
  period at registration time, mirroring the check
  PeriodCountConsolidatorBase performs when the first data point
  arrives, so invalid setups fail in Initialize instead of mid-backtest
- Accept calendar periods (Calendar.WEEKLY/MONTHLY, custom functions) in
  RegisterIndicator and ResolveConsolidator, in C# overloads and in the
  Python register_indicator path, like Consolidate already does
- Throw a clear ArgumentNullException for null symbols (e.g.
  Future.Mapped before the first mapping) instead of an NRE, and suggest
  Consolidate()/self.consolidate() in the type mismatch error
…or adapters the identity of the consolidator they wrap

- QuoteDerivedTradeBar: trade bars collapsed from quote bars throw on Volume access instead of reporting zero
- TradeBarConsolidator keeps consolidated bars volume-less when fed collapsed bars
- ConsolidatorAdapterBase: adapters equal their wrapped consolidator and share its hash code
- ConsolidatorIdentityComparer backs the registered consolidator collections so lookups work from either side
- Period validation runs on the user's consolidator before it is wrapped
@jhonabreul jhonabreul reopened this Oct 13, 2026
@jhonabreul
jhonabreul marked this pull request as ready for review October 13, 2026 19:21
…y the registration paths

- QuoteDerivedTradeBar reads the quote bar mid-point prices directly instead of going through Collapse()
- Drop the per-registration trace log from SubscriptionManager.AddConsolidator
- Python RegisterIndicator tries the calendar conversion before the throwing timedelta cast
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant