DaveSkender

indicator-stream

@DaveSkender/indicator-stream
DaveSkender
1,166
265 forks
Updated 1/18/2026
View on GitHub

Implement StreamHub real-time indicators with O(1) performance. Use for ChainHub or QuoteProvider implementations. Covers provider selection, RollbackState patterns, performance anti-patterns, and comprehensive testing with StreamHubTestBase.

Installation

$skills install @DaveSkender/indicator-stream
Claude Code
Cursor
Copilot
Codex
Antigravity

Details

Path.github/skills/indicator-stream/SKILL.md
Branchv3
Scoped Name@DaveSkender/indicator-stream

Usage

After installing, this skill will be available to your AI coding assistant.

Verify installation:

skills list

Skill Instructions


name: indicator-stream description: Implement StreamHub real-time indicators with O(1) performance. Use for ChainHub or QuoteProvider implementations. Covers provider selection, RollbackState patterns, performance anti-patterns, and comprehensive testing with StreamHubTestBase.

StreamHub indicator development

Provider selection

Provider BaseInputOutputUse Case
ChainHub<IReusable, TResult>Single valueIReusableChainable indicators
ChainHub<IQuote, TResult>OHLCVIReusableQuote-driven, chainable output
QuoteProvider<IQuote, TResult>OHLCVIQuoteQuote-to-quote transformation

Performance requirements

Target: StreamHub ≤ 1.5x slower than Series

Anti-pattern 1: O(n²) recalculation (FORBIDDEN)

// WRONG - Rebuilds entire history on each tick
for (int k = 0; k <= i; k++) { subset.Add(cache[k]); }
var result = subset.ToIndicator();

Correct: O(1) incremental update

// CORRECT - Maintain state, update incrementally
_avgGain = ((_avgGain * (period - 1)) + gain) / period;

Anti-pattern 2: O(n) window scans

Use RollingWindowMax/Min utilities instead of linear scans for max/min operations.

RollbackState pattern

Override when maintaining stateful fields:

protected override void RollbackState(DateTime timestamp)
{
    _window.Clear();
    int targetIndex = ProviderCache.IndexGte(timestamp) - 1;
    int startIdx = Math.Max(0, targetIndex + 1 - LookbackPeriods);

    for (int p = startIdx; p <= targetIndex; p++)
        _window.Add(ProviderCache[p].Value);
}

Testing requirements

  1. Inherit from StreamHubTestBase
  2. Implement exactly ONE observer interface:
    • ITestChainObserver (most common)
    • ITestQuoteObserver (quote-only providers)
  3. Implement at most ONE provider interface: ITestChainProvider
  4. Comprehensive rollback validation (required):
    • Prefill warmup window before subscribing
    • Stream in-order including duplicates
    • Insert a late historical quote → verify recalculation
    • Remove a historical quote → verify recalculation
    • Compare results to Series with strict ordering

Required implementation

  • Source code: src/**/{IndicatorName}.StreamHub.cs file exists
    • Uses appropriate provider base (ChainHub or QuoteProvider)
    • Validates parameters in constructor; calls Reinitialize() as needed
    • Implements O(1) state updates; avoids O(n²) recalculation
    • Overrides RollbackState() when maintaining stateful fields
    • Overrides ToString() with concise hub name
  • Unit testing: tests/indicators/**/{IndicatorName}.StreamHub.Tests.cs exists
    • Inherits StreamHubTestBase with correct test interfaces
    • Comprehensive rollback validation present
    • Verifies Series parity

Examples

  • Chain: src/e-k/Ema/Ema.StreamHub.cs
  • Complex state: src/a-d/Adx/Adx.StreamHub.cs
  • Rolling window: src/a-d/Chandelier/Chandelier.StreamHub.cs

See references/ for detailed patterns:

  • provider-selection.md - Choosing the right provider base
  • rollback-patterns.md - RollbackState implementation examples
  • performance-patterns.md - O(1) optimization techniques

Last updated: December 31, 2025