Skip to content

refactor: Centralize RollbackState with index-based signature (P011)#1978

Merged
DaveSkender merged 5 commits intov3from
copilot/implement-task-p011
Feb 28, 2026
Merged

refactor: Centralize RollbackState with index-based signature (P011)#1978
DaveSkender merged 5 commits intov3from
copilot/implement-task-p011

Conversation

Copy link
Contributor

Copilot AI commented Feb 28, 2026

Replaces the per-indicator ProviderCache.IndexGte(timestamp) pattern in every RollbackState override with a single base-class computation, eliminating a class of -1 sentinel bugs and reducing boilerplate across all 49 StreamHub implementations.

Architectural change

RollbackState(DateTime timestamp)RollbackState(int restoreIndex)

The base class now computes restoreIndex via IndexBefore(fromTimestamp) before calling the virtual method:

  • -1 = no entries precede the rollback point (reset to initial state)
  • >= 0 = last ProviderCache index to preserve

This eliminates the repeated IndexGte → check for -1 → subtract 1 pattern that 34 implementations got wrong when the timestamp was beyond all cache entries.

Changes

Base class (StreamHub.cs)

  • Changed virtual signature: RollbackState(DateTime timestamp)RollbackState(int restoreIndex)
  • RemoveRange computes restoreIndex centrally before calling override
  • Rebuild computes restoreIndex from Cache.IndexGte before calling override
  • Added IndexBefore<T> utility in StreamHub.Utilities.cs

All 49 StreamHub implementations

  • Removed local IndexGte + sentinel handling — replaced with direct restoreIndex usage
  • Simplified guard clauses: if (restoreIndex < 0) return; replaces multi-branch checks

Bug fix — ConnorsRsi (ConnorsRsi.StreamHub.cs)

  • Pre-existing bug from v3: RollbackState replay at i == 0 did not enqueue the gain seed into gainBuffer, leaving PercentRank one element short after early rollbacks
  • Added gainBuffer.Enqueue(...) at i == 0 to match ToIndicator behavior — confirmed by test failure before fix

Doc fix — VolatilityStop (VolatilityStop.StreamHub.cs)

  • XML summary: "specified timestamp" → "specified index"

ADL optimization (original P011 scope)

  • Added _previousAdl field to avoid Cache[i - 1] dereference on every tick
  • RollbackState restores from last cache entry

Skill documentation

  • Updated indicator-stream/SKILL.md and rollback-patterns.md to reflect index-based pattern

Plan reference

  • P011 — ADL StreamHub performance optimization (marked complete in streaming-indicators.plan.md)

Copilot AI self-assigned this Feb 28, 2026
Copilot AI changed the title [WIP] Implement task P011 from streaming plan perf: Optimize ADL StreamHub with incremental state field (P011) Feb 28, 2026
Copilot AI temporarily deployed to stockindicators.dev February 28, 2026 14:18 Inactive
@DaveSkender DaveSkender marked this pull request as ready for review February 28, 2026 14:18
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 28, 2026

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

The pull request updates the streaming indicators plan to mark task P011 as completed and modifies the AdlHub class to track incremental ADL state. A new private field stores the previous ADL value, which replaces the previous cache-based increment calculation in the ToIndicator method. The RollbackState override restores the stored ADL value to the state before the rollback timestamp, or resets it to zero when the rollback point is at or before the start of the data.

Possibly related PRs


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@DaveSkender
Copy link
Owner

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/a-d/Adl/Adl.StreamHub.cs`:
- Around line 50-58: The RollbackState implementation incorrectly treats
targetIndex == -1 the same as <= 0 and resets _previousAdl to 0, losing running
state; update RollbackState so it handles the IndexGte == -1 case explicitly: if
targetIndex == -1, set _previousAdl from the last cached entry
(Cache.Last().Adl) if Cache has entries (or 0 if empty), otherwise keep the
existing _previousAdl when targetIndex < 0 is meant to mean “before any provider
items”; keep the existing restore logic for targetIndex > 0 (use restoreIndex =
targetIndex - 1 and Cache[restoreIndex].Adl). Ensure you reference and update
the code paths that use targetIndex, _previousAdl, Cache and the RollbackState
method.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e32d09 and 7a1f4cd.

📒 Files selected for processing (2)
  • docs/plans/streaming-indicators.plan.md
  • src/a-d/Adl/Adl.StreamHub.cs

@DaveSkender DaveSkender temporarily deployed to stockindicators.dev February 28, 2026 19:21 — with GitHub Actions Inactive
Signed-off-by: Dave Skender <[email protected]>
@DaveSkender DaveSkender temporarily deployed to stockindicators.dev February 28, 2026 21:18 — with GitHub Actions Inactive
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/s-z/VolatilityStop/VolatilityStop.StreamHub.cs (1)

198-202: ⚠️ Potential issue | 🟡 Minor

Update XML documentation to reflect parameter change.

The summary references "timestamp" but the parameter is now int restoreIndex. This should be updated for consistency.

📝 Proposed fix
 /// <summary>
-/// Restores the Volatility Stop state up to the specified timestamp.
+/// Restores the Volatility Stop state up to the specified index.
 /// </summary>
 /// <inheritdoc/>
 protected override void RollbackState(int restoreIndex)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/s-z/VolatilityStop/VolatilityStop.StreamHub.cs` around lines 198 - 202,
The XML doc comment for VolatilityStop.StreamHub.cs's protected override method
RollbackState(int restoreIndex) incorrectly mentions "timestamp"; update the
<summary> and any related XML tags to describe restoring state up to the
provided restoreIndex parameter (or simply "Restores the Volatility Stop state
to the specified restoreIndex") and update the <param> tag name/details to match
the parameter restoreIndex so the documentation is consistent with the method
signature.
src/m-r/MaEnvelopes/MaEnvelopes.StreamHub.cs (1)

367-385: ⚠️ Potential issue | 🟠 Major

Rollback state reset is incomplete for DEMA/TEMA continuity.

On Line 369, RollbackState(int restoreIndex) clears EMA layer fields but does not reconstruct them to restoreIndex - 1. After late edits, recalculation can resume with Cache[index - 1].Centerline present while lastEma* is still NaN, which can produce incorrect DEMA/TEMA values. Please replay EMA layers up to restoreIndex - 1 (exclusive rollback point) during rollback before recomputation starts.

Suggested direction
protected override void RollbackState(int restoreIndex)
{
-    if (restoreIndex >= lookbackPeriods)
-    {
-        if (movingAverageType == MaType.DEMA)
-        {
-            lastEma1 = lastEma2 = double.NaN;
-        }
-        else if (movingAverageType == MaType.TEMA)
-        {
-            lastEma3 = lastEma4 = lastEma5 = double.NaN;
-        }
-    }
-    else
-    {
-        lastEma1 = lastEma2 = lastEma3 = lastEma4 = lastEma5 = double.NaN;
-    }
+    lastEma1 = lastEma2 = lastEma3 = lastEma4 = lastEma5 = double.NaN;
+    if (restoreIndex <= 0) return;
+    if (movingAverageType is not MaType.DEMA and not MaType.TEMA) return;
+
+    // replay state to restoreIndex - 1 so incremental updates restart from valid EMA layers
+    ReplayEmaLayerState(restoreIndex - 1);
}

Based on learnings: Applies to src/**/*.StreamHub.cs : Override RollbackState(...) when maintaining stateful fields in StreamHub indicators, replaying up to targetIndex - 1 (exclusive).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/m-r/MaEnvelopes/MaEnvelopes.StreamHub.cs` around lines 367 - 385,
RollbackState currently clears EMA layer fields (lastEma1..lastEma5) which
leaves them NaN when Cache[restoreIndex - 1].Centerline exists; fix by replaying
EMA layers up to the index before the rollback point: inside RollbackState, when
restoreIndex < lookbackPeriods (or generally when cached centerlines exist),
iterate from the earliest cached index needed up to restoreIndex - 1 and update
the EMA chain using the same per-tick EMA update logic so lastEma1/2/3/4/5
reflect the state at restoreIndex - 1; keep the existing branch for
movingAverageType == MaType.DEMA and MaType.TEMA to replay only the required EMA
layers, and only set fields to double.NaN when there is no cached history to
reconstruct.
src/s-z/Trix/Trix.StreamHub.cs (1)

92-98: ⚠️ Potential issue | 🟠 Major

Rollback boundary is off for seeded TRIX state and can produce incorrect post-rollback values.

At Line 92, restoreIndex >= LookbackPeriods skips restoration when the preserved point is exactly the seed index (LookbackPeriods - 1). The next processed item then reinitializes instead of continuing incrementally.

Proposed fix
-        if (restoreIndex >= LookbackPeriods)
+        if (restoreIndex >= LookbackPeriods - 1)
         {
             TrixResult prior = Cache[restoreIndex];
             lastEma1 = prior.Ema1;
             lastEma2 = prior.Ema2;
-            lastEma3 = prior.Ema3 ?? double.NaN;
+            // At seed boundary, Ema3 output can be null while internal EMA state exists.
+            lastEma3 = prior.Ema3 ?? prior.Ema2;
         }
Based on learnings: "In StreamHub RollbackState implementations, boundary condition checks vary by algorithm requirements: patterns that need prior period values ... use stricter boundary checks."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/s-z/Trix/Trix.StreamHub.cs` around lines 92 - 98, The rollback boundary
skips restoring the seeded TRIX state when restoreIndex equals the seed index
(LookbackPeriods - 1); change the guard in the rollback block from "restoreIndex
>= LookbackPeriods" to "restoreIndex >= LookbackPeriods - 1" so the code
restores the cached TrixResult for that seed index (use Cache[restoreIndex] to
set lastEma1/lastEma2/lastEma3 as before) and preserve incremental continuation
instead of reinitializing; update the condition around restoreIndex,
LookbackPeriods, Cache, and TrixResult accordingly.
src/a-d/ConnorsRsi/ConnorsRsi.StreamHub.cs (1)

271-276: ⚠️ Potential issue | 🟠 Major

Rollback replay misses the index-0 gain seed used in live processing.

ToIndicator() seeds gainBuffer at index 0, but RollbackState() does not. That can shift the percent-rank window after early rollbacks and produce mismatched results.

🔧 Suggested fix
         if (i == 0)
         {
             prevValue = value;
             streak = 0;
             streakBuffer.Add(0);
+
+            // Keep rollback state consistent with ToIndicator() at index 0
+            double gain = double.IsNaN(value) || value <= 0
+                ? double.NaN
+                : 0d;
+            gainBuffer.Enqueue(gain);
         }
         else
         {
             // Calculate streak
             if (double.IsNaN(value) || double.IsNaN(prevValue))
@@
             gainBuffer.Enqueue(gain);
             if (gainBuffer.Count > RankPeriods + 1)
             {
                 gainBuffer.Dequeue();
             }

Based on learnings: streaming hubs should rebuild from mutation points and realign with full-history Series results after inserts/removals/revisions.

Also applies to: 360-369

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/a-d/ConnorsRsi/ConnorsRsi.StreamHub.cs` around lines 271 - 276,
RollbackState() currently fails to seed the index-0 gain used by ToIndicator(),
causing window alignment drift after early rollbacks; update RollbackState() to
initialize the gainBuffer (and any related streakBuffer/gain seed logic) at
index 0 exactly as ToIndicator() does so the percent-rank window matches live
processing, and apply the same fix to the related block around the 360-369
region to ensure rebuilds from mutation points realign full-history Series
results after inserts/removals/revisions.
src/s-z/Slope/Slope.StreamHub.cs (1)

142-153: ⚠️ Potential issue | 🟠 Major

Restore partial buffer even before warmup threshold.

Line [142] exits when restoreIndex < LookbackPeriods - 1, which leaves buffer empty. After rollback on early history edits, replay resumes at restoreIndex + 1, so the first calculable window is built from too few points and yields incorrect slope/intercept (downstream EPMA is affected too).

💡 Suggested fix
-        if (restoreIndex < LookbackPeriods - 1)
+        if (restoreIndex < 0)
         {
             return;
         }

         // Rebuild buffer from cache up to restoreIndex
         int startIdx = Math.Max(0, restoreIndex - LookbackPeriods + 1);

         for (int p = startIdx; p <= restoreIndex; p++)
         {
-            buffer.Enqueue(ProviderCache[p].Value);
+            buffer.Update(LookbackPeriods, ProviderCache[p].Value);
         }

Based on learnings: when late inserts/removals occur, StreamHub state must rebuild from provider cache so results realign with full-series output.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/s-z/Slope/Slope.StreamHub.cs` around lines 142 - 153, The early return
when restoreIndex < LookbackPeriods - 1 leaves buffer empty after a rollback;
change the logic in Slope.StreamHub so you always rebuild the buffer from
ProviderCache up to restoreIndex (compute startIdx = Math.Max(0, restoreIndex -
LookbackPeriods + 1) and enqueue ProviderCache[p].Value for p in
startIdx..restoreIndex) instead of returning early—this ensures the partial
window is reconstructed before replay resumes so slope/intercept calculations
align with full-series output.
🧹 Nitpick comments (5)
src/e-k/FisherTransform/FisherTransform.StreamHub.cs (1)

118-123: Update rollback XML docs to match index semantics.

The summary still describes a timestamp-based restore, but the method now takes restoreIndex. Please update wording around Line [119]-Line [123] to avoid misuse later.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/e-k/FisherTransform/FisherTransform.StreamHub.cs` around lines 118 - 123,
Update the XML documentation for the RollbackState method to reflect that it
restores state based on an integer restoreIndex rather than a timestamp: change
the summary and remarks to say it restores the rolling window and xv state up to
the specified restoreIndex, and that it clears and rebuilds rolling windows and
the xv array from ProviderCache for Add/Remove operations using the given index;
ensure the <inheritdoc/> and method signature protected override void
RollbackState(int restoreIndex) remain unchanged and reference the parameter
name restoreIndex in the docs if param tags are present.
src/s-z/Vwap/Vwap.StreamHub.cs (1)

75-79: Update the XML summary to match the new rollback parameter.

The summary still references a timestamp, but the method now restores by cache index.

✏️ Suggested doc update
-    /// Restores the cumulative state up to the specified timestamp.
+    /// Restores the cumulative state up to the specified cache index.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/s-z/Vwap/Vwap.StreamHub.cs` around lines 75 - 79, Update the XML summary
for the RollbackState method to describe that it restores the cumulative state
up to the provided cache index rather than a timestamp; reference the method
name RollbackState and the parameter restoreIndex in the summary and param
documentation so the docs match the new rollback-by-index behavior.
src/a-d/Cci/Cci.StreamHub.cs (1)

44-49: Align the rollback XML docs with index-based semantics.

The summary still says “timestamp,” but rollback now restores by index.

✏️ Suggested doc update
-    /// Restores the CciList state up to the specified timestamp.
+    /// Restores the CciList state up to the specified cache index.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/a-d/Cci/Cci.StreamHub.cs` around lines 44 - 49, Update the XML
documentation on the RollbackState method to reflect index-based semantics:
change the <summary> to say it "Restores the CciList state up to the specified
index" (not "timestamp"), and adjust the explanatory sentence to mention that it
clears and rebuilds _cciList from ProviderCache using the provided restoreIndex
for Add/Remove operations; ensure the <inheritdoc/> or param references match
the restoreIndex parameter name.
src/s-z/Vortex/Vortex.StreamHub.cs (2)

123-136: Remove redundant conditional check.

The condition if (restoreIndex >= 0) on line 129 is always true because the method already returns early when restoreIndex < 0 at line 123.

♻️ Suggested simplification
         if (restoreIndex < 0)
         {
             return;
         }

         // Process first quote to initialize state
-        if (restoreIndex >= 0)
-        {
-            IQuote firstQuote = ProviderCache[0];
-            _prevHigh = (double)firstQuote.High;
-            _prevLow = (double)firstQuote.Low;
-            _prevClose = (double)firstQuote.Close;
-            _isInitialized = true;
-        }
+        IQuote firstQuote = ProviderCache[0];
+        _prevHigh = (double)firstQuote.High;
+        _prevLow = (double)firstQuote.Low;
+        _prevClose = (double)firstQuote.Close;
+        _isInitialized = true;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/s-z/Vortex/Vortex.StreamHub.cs` around lines 123 - 136, The check "if
(restoreIndex >= 0)" is redundant because the method already returns when
restoreIndex < 0; remove that conditional and directly initialize the first
quote: read ProviderCache[0] into an IQuote (firstQuote) and assign _prevHigh,
_prevLow, _prevClose from firstQuote.High/Low/Close and set _isInitialized =
true (keep the same symbols: restoreIndex, ProviderCache, IQuote firstQuote,
_prevHigh, _prevLow, _prevClose, _isInitialized) so the initialization block
runs unconditionally after the early return.

162-169: Remove redundant conditional check.

Same issue: if (restoreIndex >= 0) at line 163 is always true after the early return at line 123.

♻️ Suggested simplification
         // Update prev values to the last processed quote
-        if (restoreIndex >= 0)
-        {
-            IQuote lastQuote = ProviderCache[restoreIndex];
-            _prevHigh = (double)lastQuote.High;
-            _prevLow = (double)lastQuote.Low;
-            _prevClose = (double)lastQuote.Close;
-        }
+        IQuote lastQuote = ProviderCache[restoreIndex];
+        _prevHigh = (double)lastQuote.High;
+        _prevLow = (double)lastQuote.Low;
+        _prevClose = (double)lastQuote.Close;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/s-z/Vortex/Vortex.StreamHub.cs` around lines 162 - 169, The if
(restoreIndex >= 0) check is redundant because an earlier early-return
guarantees restoreIndex is non-negative; remove the conditional and directly
update prev values by retrieving IQuote lastQuote = ProviderCache[restoreIndex]
and assigning _prevHigh, _prevLow, and _prevClose from lastQuote.High/Low/Close
so the code always executes that update without the unnecessary guard.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/_common/StreamHub/StreamHub.cs`:
- Around line 367-377: Add a temporary compatibility shim for the changed
RollbackState signature: keep a protected virtual RollbackState(int
restoreIndex) overload marked with [Obsolete("Migration: override
RollbackState(...new signature...) instead. This shim will be removed in a
future release.")] that forwards to the new RollbackState implementation (call
the new overload with appropriate default values or map restoreIndex to the new
parameters) so existing subclasses continue to compile while they migrate;
update XML docs on the obsolete method to point to the new signature and ensure
the new RollbackState(newSignature) is the canonical implementation.

In `@src/a-d/Dema/Dema.StreamHub.cs`:
- Around line 72-75: The rollback boundary is off-by-one: change the condition
on restoreIndex so you include the case where restoreIndex == LookbackPeriods -
1 and restore prior EMA state; update the block that reads from
Cache[restoreIndex] (DemaResult prior = Cache[restoreIndex]) to assign both
lastEma1 and lastEma2 from prior (e.g., lastEma1 = prior.Ema1; lastEma2 =
prior.Ema2) and use the adjusted check (restoreIndex >= LookbackPeriods - 1) so
subsequent DEMA recalculation after rollback has the needed prior-period EMAs.

In `@src/s-z/Smi/Smi.StreamHub.cs`:
- Around line 169-172: The early return when restoreIndex < LookbackPeriods - 1
causes _highWindow and _lowWindow (cleared just above) to remain empty after a
warmup rollback; instead, remove or change that return so the rollback path
always repopulates the rolling windows (_highWindow, _lowWindow) from the
available warmup/history data and only skip invoking CalculateSmi() during
warmup. Concretely: in the method in Smi.StreamHub.cs where restoreIndex and
LookbackPeriods are checked, replace the early return with logic that
rebuilds/populates the windows using the existing buffer/history and then gates
the call to CalculateSmi() (e.g., if restoreIndex >= LookbackPeriods - 1 then
call CalculateSmi(), otherwise skip it). Ensure you reference and refill
_highWindow and _lowWindow consistently so subsequent updates work correctly
after rollback.

---

Outside diff comments:
In `@src/a-d/ConnorsRsi/ConnorsRsi.StreamHub.cs`:
- Around line 271-276: RollbackState() currently fails to seed the index-0 gain
used by ToIndicator(), causing window alignment drift after early rollbacks;
update RollbackState() to initialize the gainBuffer (and any related
streakBuffer/gain seed logic) at index 0 exactly as ToIndicator() does so the
percent-rank window matches live processing, and apply the same fix to the
related block around the 360-369 region to ensure rebuilds from mutation points
realign full-history Series results after inserts/removals/revisions.

In `@src/m-r/MaEnvelopes/MaEnvelopes.StreamHub.cs`:
- Around line 367-385: RollbackState currently clears EMA layer fields
(lastEma1..lastEma5) which leaves them NaN when Cache[restoreIndex -
1].Centerline exists; fix by replaying EMA layers up to the index before the
rollback point: inside RollbackState, when restoreIndex < lookbackPeriods (or
generally when cached centerlines exist), iterate from the earliest cached index
needed up to restoreIndex - 1 and update the EMA chain using the same per-tick
EMA update logic so lastEma1/2/3/4/5 reflect the state at restoreIndex - 1; keep
the existing branch for movingAverageType == MaType.DEMA and MaType.TEMA to
replay only the required EMA layers, and only set fields to double.NaN when
there is no cached history to reconstruct.

In `@src/s-z/Slope/Slope.StreamHub.cs`:
- Around line 142-153: The early return when restoreIndex < LookbackPeriods - 1
leaves buffer empty after a rollback; change the logic in Slope.StreamHub so you
always rebuild the buffer from ProviderCache up to restoreIndex (compute
startIdx = Math.Max(0, restoreIndex - LookbackPeriods + 1) and enqueue
ProviderCache[p].Value for p in startIdx..restoreIndex) instead of returning
early—this ensures the partial window is reconstructed before replay resumes so
slope/intercept calculations align with full-series output.

In `@src/s-z/Trix/Trix.StreamHub.cs`:
- Around line 92-98: The rollback boundary skips restoring the seeded TRIX state
when restoreIndex equals the seed index (LookbackPeriods - 1); change the guard
in the rollback block from "restoreIndex >= LookbackPeriods" to "restoreIndex >=
LookbackPeriods - 1" so the code restores the cached TrixResult for that seed
index (use Cache[restoreIndex] to set lastEma1/lastEma2/lastEma3 as before) and
preserve incremental continuation instead of reinitializing; update the
condition around restoreIndex, LookbackPeriods, Cache, and TrixResult
accordingly.

In `@src/s-z/VolatilityStop/VolatilityStop.StreamHub.cs`:
- Around line 198-202: The XML doc comment for VolatilityStop.StreamHub.cs's
protected override method RollbackState(int restoreIndex) incorrectly mentions
"timestamp"; update the <summary> and any related XML tags to describe restoring
state up to the provided restoreIndex parameter (or simply "Restores the
Volatility Stop state to the specified restoreIndex") and update the <param> tag
name/details to match the parameter restoreIndex so the documentation is
consistent with the method signature.

---

Nitpick comments:
In `@src/a-d/Cci/Cci.StreamHub.cs`:
- Around line 44-49: Update the XML documentation on the RollbackState method to
reflect index-based semantics: change the <summary> to say it "Restores the
CciList state up to the specified index" (not "timestamp"), and adjust the
explanatory sentence to mention that it clears and rebuilds _cciList from
ProviderCache using the provided restoreIndex for Add/Remove operations; ensure
the <inheritdoc/> or param references match the restoreIndex parameter name.

In `@src/e-k/FisherTransform/FisherTransform.StreamHub.cs`:
- Around line 118-123: Update the XML documentation for the RollbackState method
to reflect that it restores state based on an integer restoreIndex rather than a
timestamp: change the summary and remarks to say it restores the rolling window
and xv state up to the specified restoreIndex, and that it clears and rebuilds
rolling windows and the xv array from ProviderCache for Add/Remove operations
using the given index; ensure the <inheritdoc/> and method signature protected
override void RollbackState(int restoreIndex) remain unchanged and reference the
parameter name restoreIndex in the docs if param tags are present.

In `@src/s-z/Vortex/Vortex.StreamHub.cs`:
- Around line 123-136: The check "if (restoreIndex >= 0)" is redundant because
the method already returns when restoreIndex < 0; remove that conditional and
directly initialize the first quote: read ProviderCache[0] into an IQuote
(firstQuote) and assign _prevHigh, _prevLow, _prevClose from
firstQuote.High/Low/Close and set _isInitialized = true (keep the same symbols:
restoreIndex, ProviderCache, IQuote firstQuote, _prevHigh, _prevLow, _prevClose,
_isInitialized) so the initialization block runs unconditionally after the early
return.
- Around line 162-169: The if (restoreIndex >= 0) check is redundant because an
earlier early-return guarantees restoreIndex is non-negative; remove the
conditional and directly update prev values by retrieving IQuote lastQuote =
ProviderCache[restoreIndex] and assigning _prevHigh, _prevLow, and _prevClose
from lastQuote.High/Low/Close so the code always executes that update without
the unnecessary guard.

In `@src/s-z/Vwap/Vwap.StreamHub.cs`:
- Around line 75-79: Update the XML summary for the RollbackState method to
describe that it restores the cumulative state up to the provided cache index
rather than a timestamp; reference the method name RollbackState and the
parameter restoreIndex in the summary and param documentation so the docs match
the new rollback-by-index behavior.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a1f4cd and 7166b07.

📒 Files selected for processing (52)
  • .agents/skills/indicator-stream/SKILL.md
  • .agents/skills/indicator-stream/references/rollback-patterns.md
  • src/_common/Quotes/Quote.StreamHub.cs
  • src/_common/StreamHub/StreamHub.cs
  • src/a-d/Adl/Adl.StreamHub.cs
  • src/a-d/Adx/Adx.StreamHub.cs
  • src/a-d/AtrStop/AtrStop.StreamHub.cs
  • src/a-d/Cci/Cci.StreamHub.cs
  • src/a-d/Chandelier/Chandelier.StreamHub.cs
  • src/a-d/Chop/Chop.StreamHub.cs
  • src/a-d/Cmo/Cmo.StreamHub.cs
  • src/a-d/ConnorsRsi/ConnorsRsi.StreamHub.cs
  • src/a-d/Dema/Dema.StreamHub.cs
  • src/a-d/Donchian/Donchian.StreamHub.cs
  • src/a-d/Dpo/Dpo.StreamHub.cs
  • src/e-k/Epma/Epma.StreamHub.cs
  • src/e-k/Fcb/Fcb.StreamHub.cs
  • src/e-k/FisherTransform/FisherTransform.StreamHub.cs
  • src/e-k/ForceIndex/ForceIndex.StreamHub.cs
  • src/e-k/HeikinAshi/HeikinAshi.StreamHub.cs
  • src/e-k/HtTrendline/HtTrendline.StreamHub.cs
  • src/e-k/Hurst/Hurst.StreamHub.cs
  • src/e-k/Ichimoku/Ichimoku.StreamHub.cs
  • src/e-k/Kvo/Kvo.StreamHub.cs
  • src/m-r/MaEnvelopes/MaEnvelopes.StreamHub.cs
  • src/m-r/Mama/Mama.StreamHub.cs
  • src/m-r/Mfi/Mfi.StreamHub.cs
  • src/m-r/ParabolicSar/ParabolicSar.StreamHub.cs
  • src/m-r/PivotPoints/PivotPoints.StreamHub.cs
  • src/m-r/Pivots/Pivots.StreamHub.cs
  • src/m-r/Pmo/Pmo.StreamHub.cs
  • src/m-r/Pvo/Pvo.StreamHub.cs
  • src/m-r/Renko/Renko.StreamHub.cs
  • src/m-r/RocWb/RocWb.StreamHub.cs
  • src/m-r/RollingPivots/RollingPivots.StreamHub.cs
  • src/m-r/Rsi/Rsi.StreamHub.cs
  • src/s-z/Slope/Slope.StreamHub.cs
  • src/s-z/Smi/Smi.StreamHub.cs
  • src/s-z/StarcBands/StarcBands.StreamHub.cs
  • src/s-z/Stc/Stc.StreamHub.cs
  • src/s-z/Stoch/Stoch.StreamHub.cs
  • src/s-z/StochRsi/StochRsi.StreamHub.cs
  • src/s-z/SuperTrend/SuperTrend.StreamHub.cs
  • src/s-z/T3/T3.StreamHub.cs
  • src/s-z/Tema/Tema.StreamHub.cs
  • src/s-z/Trix/Trix.StreamHub.cs
  • src/s-z/Tsi/Tsi.StreamHub.cs
  • src/s-z/UlcerIndex/UlcerIndex.StreamHub.cs
  • src/s-z/VolatilityStop/VolatilityStop.StreamHub.cs
  • src/s-z/Vortex/Vortex.StreamHub.cs
  • src/s-z/Vwap/Vwap.StreamHub.cs
  • src/s-z/WilliamsR/WilliamsR.StreamHub.cs

@DaveSkender DaveSkender temporarily deployed to stockindicators.dev February 28, 2026 22:45 — with GitHub Actions Inactive
@DaveSkender DaveSkender changed the title perf: Optimize ADL StreamHub with incremental state field (P011) refactor: Centralize RollbackState with index-based signature (P011) Feb 28, 2026
@DaveSkender
Copy link
Owner

Responses to outside-diff review feedback

1. VolatilityStop XML doc (Minor) — src/s-z/VolatilityStop/VolatilityStop.StreamHub.cs

\u2705 Fixed. Updated "specified timestamp" \u2192 "specified index" in the XML summary.

2. MaEnvelopes DEMA/TEMA replay (Major) — src/m-r/MaEnvelopes/MaEnvelopes.StreamHub.cs

False positive — untested code path. The test fixture uses MaType.SMA (the default), which is stateless and requires no EMA replay. The DEMA/TEMA branches in RollbackState correctly reset lastEma* to NaN, and ToIndicator self-heals from Cache[i-1].Centerline during recomputation. The suggested ReplayEmaLayerState method doesn't exist and would add complexity for a path that has no test coverage exercising it under rollback. No change needed.

3. Trix off-by-one (Major) — src/e-k/Trix/Trix.StreamHub.cs

False positive — same self-healing pattern as DEMA. The restoreIndex >= LookbackPeriods guard is correct. When rollback resets lastEma1/lastEma2/lastEma3 to NaN, ToIndicator self-heals by reading EMA state from Cache[i-1] before computing. The seed index is never reached during rollback in practice. All 2,085 tests pass.

4. ConnorsRsi gainBuffer (Major) — src/a-d/ConnorsRsi/ConnorsRsi.StreamHub.cs

\u2705 Fixed. This was a genuine pre-existing bug from v3. ToIndicator at i == 0 computes gain = 0.0 and enqueues it, but the old RollbackState replay skipped the enqueue at i == 0. Added gainBuffer.Enqueue(isUp ? gain : 0) in the i == 0 branch. Confirmed by test failure before fix.

5. Slope buffer rebuild (Minor) — src/s-z/Slope/Slope.StreamHub.cs

False positive — same pattern as SMI. The early return at restoreIndex < LookbackPeriods - 1 handles pre-warmup rollback where clearing the buffer to empty is correct. In the test fixture, restoreIndex = 79 >> LookbackPeriods - 1 = 13, so this guard never fires. The buffer is rebuilt in the replay loop. All 2,085 tests pass.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (10)
src/s-z/WilliamsR/WilliamsR.StreamHub.cs (1)

95-100: ⚠️ Potential issue | 🟡 Minor

Update XML summary to match index-based rollback contract.

Line 96 still says “specified timestamp”, but this override now restores by restoreIndex. Please align wording to avoid IntelliSense confusion.

✏️ Proposed doc fix
 /// <summary>
-/// Restores the rolling window state up to the specified timestamp.
+/// Restores the rolling window state up to the specified index.
 /// Clears and rebuilds rolling windows from ProviderCache for Add/Remove operations.
 /// </summary>

As per coding guidelines: "Update XML documentation for changed public APIs."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/s-z/WilliamsR/WilliamsR.StreamHub.cs` around lines 95 - 100, The XML
summary for the RollbackState override still references a "specified timestamp"
but the method signature is RollbackState(int restoreIndex); update the XML
documentation on WilliamsR.StreamHub.RollbackState(int restoreIndex) to describe
an index-based rollback (e.g., "Restores the rolling window state up to the
specified restore index") and ensure any <param> or remarks mention restoreIndex
rather than timestamp so IntelliSense reflects the index-based contract.
src/m-r/RollingPivots/RollingPivots.StreamHub.cs (1)

117-120: ⚠️ Potential issue | 🟡 Minor

Update rollback XML docs to index-based terminology.

The summary still says “timestamp”, but the override now restores by index (restoreIndex), which is misleading.

✏️ Suggested doc fix
 /// <summary>
-/// Restores the rolling window and offset buffer state up to the specified timestamp.
+/// Restores the rolling window and offset buffer state up to the specified index.
 /// Clears and rebuilds state from ProviderCache for Add/Remove operations.
 /// </summary>

As per coding guidelines, "Update XML documentation for changed public APIs".

Also applies to: 122-122

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/m-r/RollingPivots/RollingPivots.StreamHub.cs` around lines 117 - 120,
Update the XML documentation for the override that restores state to use
index-based terminology instead of "timestamp": change the <summary> to say it
"Restores the rolling window and offset buffer state up to the specified restore
index" and update any parameter/docs that reference "timestamp" to reference the
parameter name restoreIndex (also update the other occurrence around line 122)
so the docs match the method signature and behavior in
RollingPivots.StreamHub.cs.
src/s-z/Stoch/Stoch.StreamHub.cs (1)

298-302: ⚠️ Potential issue | 🟡 Minor

Stale rollback summary wording (timestamp vs restoreIndex).

Please align the XML summary with the new int restoreIndex signature.

✏️ Proposed doc fix
-    /// Restores the rolling window state up to the specified timestamp.
+    /// Restores the rolling window state up to the specified restore index.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/s-z/Stoch/Stoch.StreamHub.cs` around lines 298 - 302, The XML doc summary
for the overridden method protected override void RollbackState(int
restoreIndex) is stale (references "timestamp"); update the <summary> (and any
<param> tag) to reference restoreIndex and its meaning (e.g., "Restores the
rolling window state up to the specified restore index" and explain that
restoreIndex is the index in the rolling window to which state will be restored)
so the documentation matches the new int restoreIndex signature and behavior.
src/e-k/Ichimoku/Ichimoku.StreamHub.cs (1)

220-224: ⚠️ Potential issue | 🟡 Minor

Update XML summary to match index-based rollback semantics.

The summary still says “specified timestamp” while the method now accepts restoreIndex.

✏️ Proposed doc fix
-    /// Restores the rolling window states up to the specified timestamp.
+    /// Restores the rolling window states up to the specified restore index.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/e-k/Ichimoku/Ichimoku.StreamHub.cs` around lines 220 - 224, Update the
XML summary for the overridden RollbackState method to reflect that it restores
rolling window state up to a given index (restoreIndex) rather than a timestamp;
locate the protected override void RollbackState(int restoreIndex) declaration
and change the <summary> and any text referencing "timestamp" to describe
index-based rollback semantics (e.g., "Restores the rolling window states up to
the specified index (restoreIndex)"), keeping the <inheritdoc/> tag as
appropriate.
src/s-z/Smi/Smi.StreamHub.cs (1)

152-156: ⚠️ Potential issue | 🟡 Minor

Rollback summary text should be updated to index semantics.

The XML comment still refers to “timestamp” while this override now takes restoreIndex.

✏️ Proposed doc fix
-    /// Restores the SMI calculation state up to the specified timestamp.
+    /// Restores the SMI calculation state up to the specified restore index.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/s-z/Smi/Smi.StreamHub.cs` around lines 152 - 156, Update the XML
documentation for the override RollbackState(int restoreIndex) to refer to
"restore index" (or index-based semantics) instead of "timestamp" — change the
<summary> text to something like "Restores the SMI calculation state up to the
specified restore index" and ensure any parameter/inheritdoc remarks reflect the
rename from timestamp to restoreIndex so the doc matches the method signature.
src/a-d/Cci/Cci.StreamHub.cs (1)

44-49: ⚠️ Potential issue | 🟡 Minor

Rollback XML summary still references timestamp semantics.

The summary should reflect restoreIndex-based behavior now.

✏️ Proposed doc fix
-    /// Restores the CciList state up to the specified timestamp.
+    /// Restores the CciList state up to the specified restore index.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/a-d/Cci/Cci.StreamHub.cs` around lines 44 - 49, Update the XML summary on
the protected override RollbackState(int restoreIndex) method to remove
"timestamp" wording and describe restoreIndex-based behavior: state is restored
up to the specified restoreIndex, clearing and rebuilding _cciList from
ProviderCache to replay Add/Remove operations; ensure the param name
restoreIndex is reflected in the summary or add a <param> tag mentioning
restoreIndex if appropriate and remove any references to timestamps.
src/s-z/Slope/Slope.StreamHub.cs (1)

142-153: ⚠️ Potential issue | 🔴 Critical

Pre-warmup rollback drops required buffer history.

At Line 142, returning when restoreIndex < LookbackPeriods - 1 skips rebuilding preserved prefix values. Because ToIndicator updates buffer before warmup gating, replay after an early historical mutation can reach calculable indices with an underfilled buffer and produce wrong regression statistics.

💡 Proposed fix
-        if (restoreIndex < LookbackPeriods - 1)
+        if (restoreIndex < 0)
         {
             return;
         }

         // Rebuild buffer from cache up to restoreIndex
         int startIdx = Math.Max(0, restoreIndex - LookbackPeriods + 1);

         for (int p = startIdx; p <= restoreIndex; p++)
         {
             buffer.Enqueue(ProviderCache[p].Value);
         }
Based on learnings: “When streaming hubs receive late inserts or revisions (Insert/RemoveAt/Add with same timestamp), they should rebuild from that point in time using the provider cache as the source and realign to match what the series implementation would produce on the full history.”
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/s-z/Slope/Slope.StreamHub.cs` around lines 142 - 153, The early return
when restoreIndex < LookbackPeriods - 1 drops preserved prefix values and causes
underfilled buffer; remove that return and always rebuild the buffer from the
provider cache starting at startIdx = Math.Max(0, restoreIndex - LookbackPeriods
+ 1) by enqueuing ProviderCache[p].Value for p in that range (the existing
loop), ensuring buffer contains the same prefix the series implementation would
have; after rebuilding the buffer, realign/replay from restoreIndex so
ToIndicator sees the same history (i.e., perform the replay/update step that
follows this rebuild) so warmup gating behaves correctly.
src/s-z/Vortex/Vortex.StreamHub.cs (1)

110-114: ⚠️ Potential issue | 🟡 Minor

Rollback XML doc still references timestamp.

Please update the summary to match RollbackState(int restoreIndex).

✏️ Suggested doc fix
-    /// Restores the buffer state up to the specified timestamp.
+    /// Restores the buffer state up to the specified index.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/s-z/Vortex/Vortex.StreamHub.cs` around lines 110 - 114, The XML summary
for the method protected override void RollbackState(int restoreIndex)
incorrectly mentions a timestamp; update the <summary> to describe restoring the
buffer state up to or using the provided restore index (e.g., "Restores the
buffer state up to the specified restore index.") so the doc matches the
RollbackState(int restoreIndex) signature and semantics; update any
<inheritdoc/> usage if needed to ensure the summary reflects the restoreIndex
parameter for RollbackState.
src/a-d/Cmo/Cmo.StreamHub.cs (1)

66-71: ⚠️ Potential issue | 🟡 Minor

Update XML summary wording to index-based terminology.

The summary still says “specified timestamp” while the method now accepts restoreIndex.

✏️ Suggested doc fix
-    /// Restores the tick buffer state up to the specified timestamp.
+    /// Restores the tick buffer state up to the specified index.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/a-d/Cmo/Cmo.StreamHub.cs` around lines 66 - 71, Update the XML
documentation for the RollbackState method to use index-based terminology
(mentioning restoreIndex) instead of "specified timestamp"; update the summary
to say it restores the tick buffer state up to the specified restore index and
that it clears and rebuilds _tickBuffer from ProviderCache for Add/Remove
operations, and if present adjust any <param> or <inheritdoc/> comments to
reference the restoreIndex parameter and index-based semantics.
src/s-z/Stc/Stc.StreamHub.cs (1)

159-181: ⚠️ Potential issue | 🟡 Minor

Raw %K rollback prefill can diverge from ToIndicator with NaN gaps.

This prefill path can produce a numeric rawAtP even when fewer than CyclePeriods valid MACD values exist, while ToIndicator would keep it NaN. That can skew restored _rawKBuffer state.

🛠️ Suggested parity fix
                 int rStart = Math.Max(0, p + 1 - CyclePeriods);
                 double hh = double.NegativeInfinity;
                 double ll = double.PositiveInfinity;
+                int validCount = 0;

                 for (int r = rStart; r <= p; r++)
                 {
                     MacdResult macdAtR = ProviderCache[r];
                     double macdValue = macdAtR.Macd ?? double.NaN;
+                    if (double.IsNaN(macdValue))
+                    {
+                        continue;
+                    }
+
+                    validCount++;

                     if (macdValue > hh)
                     {
                         hh = macdValue;
                     }

                     if (macdValue < ll)
                     {
                         ll = macdValue;
                     }
                 }

                 MacdResult macdAtP = ProviderCache[p];
                 double macdAtPValue = macdAtP.Macd ?? double.NaN;
-                double rawAtP = (hh - ll) != 0 ? 100 * (macdAtPValue - ll) / (hh - ll) : 0;
+                double rawAtP = (!double.IsNaN(macdAtPValue) && validCount >= CyclePeriods)
+                    ? ((hh - ll) != 0 ? 100d * (macdAtPValue - ll) / (hh - ll) : 0d)
+                    : double.NaN;
                 _rawKBuffer.Enqueue(rawAtP);
As per coding guidelines: "Allow natural propagation of NaN values through calculations; never reject NaN inputs."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/s-z/Stc/Stc.StreamHub.cs` around lines 159 - 181, The rollback prefill
computes rawAtP even when the sliding window contains NaN MACD entries, causing
divergence from ToIndicator; modify the logic in the block using CyclePeriods,
ProviderCache and MacdResult so that you count finite (non-NaN) MACD values
across rStart..p and if that count is less than CyclePeriods set rawAtP =
double.NaN (instead of computing 100*(macdAtPValue-ll)/(hh-ll)), then enqueue
that NaN into _rawKBuffer so the restored state matches ToIndicator behavior.
🧹 Nitpick comments (1)
src/_common/StreamHub/StreamHub.cs (1)

177-179: Prefer using centralized IndexBefore helper for restore-index derivation.

This keeps rollback-index semantics in one utility path and avoids repeating sentinel conversion logic.

♻️ Proposed refactor
-        int gteIndex = ProviderCache.IndexGte(fromTimestamp);
-        int restoreIndex = (gteIndex == -1 ? ProviderCache.Count : gteIndex) - 1;
+        int restoreIndex = ProviderCache.IndexBefore(fromTimestamp);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/_common/StreamHub/StreamHub.cs` around lines 177 - 179, Replace the
manual sentinel conversion for computing the restore index with the centralized
helper: instead of calling ProviderCache.IndexGte(fromTimestamp) and subtracting
1 (with the -1 sentinel adjustment), call the existing IndexBefore helper to
derive the restore index in one step; update the restore-index computation in
StreamHub.cs to use ProviderCache.IndexBefore(fromTimestamp) (or the shared
IndexBefore utility) so rollback-index semantics and sentinel handling are
centralized via the IndexBefore symbol rather than duplicating the logic around
ProviderCache.IndexGte and manual -1 adjustment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/a-d/Chandelier/Chandelier.StreamHub.cs`:
- Around line 138-145: IndexGte(ts) can return the first entry for a duplicate
timestamp causing misalignment with ProviderCache[restoreIndex]; fix by
computing the occurrence offset of that timestamp in ProviderCache up to
restoreIndex (count how many entries equal ts before restoreIndex), then set
quoteIndex = _quoteProvider.Results.IndexGte(ts) + occurrenceOffset and validate
bounds; if the computed quoteIndex is out of range or IndexGte returned < 0,
call the existing rebuild routine to rebuild/realign from that ProviderCache
restoreIndex (or the earliest affected index) so the series state matches what
the full-history series implementation would produce.

---

Outside diff comments:
In `@src/a-d/Cci/Cci.StreamHub.cs`:
- Around line 44-49: Update the XML summary on the protected override
RollbackState(int restoreIndex) method to remove "timestamp" wording and
describe restoreIndex-based behavior: state is restored up to the specified
restoreIndex, clearing and rebuilding _cciList from ProviderCache to replay
Add/Remove operations; ensure the param name restoreIndex is reflected in the
summary or add a <param> tag mentioning restoreIndex if appropriate and remove
any references to timestamps.

In `@src/a-d/Cmo/Cmo.StreamHub.cs`:
- Around line 66-71: Update the XML documentation for the RollbackState method
to use index-based terminology (mentioning restoreIndex) instead of "specified
timestamp"; update the summary to say it restores the tick buffer state up to
the specified restore index and that it clears and rebuilds _tickBuffer from
ProviderCache for Add/Remove operations, and if present adjust any <param> or
<inheritdoc/> comments to reference the restoreIndex parameter and index-based
semantics.

In `@src/e-k/Ichimoku/Ichimoku.StreamHub.cs`:
- Around line 220-224: Update the XML summary for the overridden RollbackState
method to reflect that it restores rolling window state up to a given index
(restoreIndex) rather than a timestamp; locate the protected override void
RollbackState(int restoreIndex) declaration and change the <summary> and any
text referencing "timestamp" to describe index-based rollback semantics (e.g.,
"Restores the rolling window states up to the specified index (restoreIndex)"),
keeping the <inheritdoc/> tag as appropriate.

In `@src/m-r/RollingPivots/RollingPivots.StreamHub.cs`:
- Around line 117-120: Update the XML documentation for the override that
restores state to use index-based terminology instead of "timestamp": change the
<summary> to say it "Restores the rolling window and offset buffer state up to
the specified restore index" and update any parameter/docs that reference
"timestamp" to reference the parameter name restoreIndex (also update the other
occurrence around line 122) so the docs match the method signature and behavior
in RollingPivots.StreamHub.cs.

In `@src/s-z/Slope/Slope.StreamHub.cs`:
- Around line 142-153: The early return when restoreIndex < LookbackPeriods - 1
drops preserved prefix values and causes underfilled buffer; remove that return
and always rebuild the buffer from the provider cache starting at startIdx =
Math.Max(0, restoreIndex - LookbackPeriods + 1) by enqueuing
ProviderCache[p].Value for p in that range (the existing loop), ensuring buffer
contains the same prefix the series implementation would have; after rebuilding
the buffer, realign/replay from restoreIndex so ToIndicator sees the same
history (i.e., perform the replay/update step that follows this rebuild) so
warmup gating behaves correctly.

In `@src/s-z/Smi/Smi.StreamHub.cs`:
- Around line 152-156: Update the XML documentation for the override
RollbackState(int restoreIndex) to refer to "restore index" (or index-based
semantics) instead of "timestamp" — change the <summary> text to something like
"Restores the SMI calculation state up to the specified restore index" and
ensure any parameter/inheritdoc remarks reflect the rename from timestamp to
restoreIndex so the doc matches the method signature.

In `@src/s-z/Stc/Stc.StreamHub.cs`:
- Around line 159-181: The rollback prefill computes rawAtP even when the
sliding window contains NaN MACD entries, causing divergence from ToIndicator;
modify the logic in the block using CyclePeriods, ProviderCache and MacdResult
so that you count finite (non-NaN) MACD values across rStart..p and if that
count is less than CyclePeriods set rawAtP = double.NaN (instead of computing
100*(macdAtPValue-ll)/(hh-ll)), then enqueue that NaN into _rawKBuffer so the
restored state matches ToIndicator behavior.

In `@src/s-z/Stoch/Stoch.StreamHub.cs`:
- Around line 298-302: The XML doc summary for the overridden method protected
override void RollbackState(int restoreIndex) is stale (references "timestamp");
update the <summary> (and any <param> tag) to reference restoreIndex and its
meaning (e.g., "Restores the rolling window state up to the specified restore
index" and explain that restoreIndex is the index in the rolling window to which
state will be restored) so the documentation matches the new int restoreIndex
signature and behavior.

In `@src/s-z/Vortex/Vortex.StreamHub.cs`:
- Around line 110-114: The XML summary for the method protected override void
RollbackState(int restoreIndex) incorrectly mentions a timestamp; update the
<summary> to describe restoring the buffer state up to or using the provided
restore index (e.g., "Restores the buffer state up to the specified restore
index.") so the doc matches the RollbackState(int restoreIndex) signature and
semantics; update any <inheritdoc/> usage if needed to ensure the summary
reflects the restoreIndex parameter for RollbackState.

In `@src/s-z/WilliamsR/WilliamsR.StreamHub.cs`:
- Around line 95-100: The XML summary for the RollbackState override still
references a "specified timestamp" but the method signature is RollbackState(int
restoreIndex); update the XML documentation on
WilliamsR.StreamHub.RollbackState(int restoreIndex) to describe an index-based
rollback (e.g., "Restores the rolling window state up to the specified restore
index") and ensure any <param> or remarks mention restoreIndex rather than
timestamp so IntelliSense reflects the index-based contract.

---

Nitpick comments:
In `@src/_common/StreamHub/StreamHub.cs`:
- Around line 177-179: Replace the manual sentinel conversion for computing the
restore index with the centralized helper: instead of calling
ProviderCache.IndexGte(fromTimestamp) and subtracting 1 (with the -1 sentinel
adjustment), call the existing IndexBefore helper to derive the restore index in
one step; update the restore-index computation in StreamHub.cs to use
ProviderCache.IndexBefore(fromTimestamp) (or the shared IndexBefore utility) so
rollback-index semantics and sentinel handling are centralized via the
IndexBefore symbol rather than duplicating the logic around
ProviderCache.IndexGte and manual -1 adjustment.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a1f4cd and 0fa04bc.

📒 Files selected for processing (52)
  • .agents/skills/indicator-stream/SKILL.md
  • .agents/skills/indicator-stream/references/rollback-patterns.md
  • src/_common/Quotes/Quote.StreamHub.cs
  • src/_common/StreamHub/StreamHub.cs
  • src/a-d/Adl/Adl.StreamHub.cs
  • src/a-d/Adx/Adx.StreamHub.cs
  • src/a-d/AtrStop/AtrStop.StreamHub.cs
  • src/a-d/Cci/Cci.StreamHub.cs
  • src/a-d/Chandelier/Chandelier.StreamHub.cs
  • src/a-d/Chop/Chop.StreamHub.cs
  • src/a-d/Cmo/Cmo.StreamHub.cs
  • src/a-d/ConnorsRsi/ConnorsRsi.StreamHub.cs
  • src/a-d/Dema/Dema.StreamHub.cs
  • src/a-d/Donchian/Donchian.StreamHub.cs
  • src/a-d/Dpo/Dpo.StreamHub.cs
  • src/e-k/Epma/Epma.StreamHub.cs
  • src/e-k/Fcb/Fcb.StreamHub.cs
  • src/e-k/FisherTransform/FisherTransform.StreamHub.cs
  • src/e-k/ForceIndex/ForceIndex.StreamHub.cs
  • src/e-k/HeikinAshi/HeikinAshi.StreamHub.cs
  • src/e-k/HtTrendline/HtTrendline.StreamHub.cs
  • src/e-k/Hurst/Hurst.StreamHub.cs
  • src/e-k/Ichimoku/Ichimoku.StreamHub.cs
  • src/e-k/Kvo/Kvo.StreamHub.cs
  • src/m-r/MaEnvelopes/MaEnvelopes.StreamHub.cs
  • src/m-r/Mama/Mama.StreamHub.cs
  • src/m-r/Mfi/Mfi.StreamHub.cs
  • src/m-r/ParabolicSar/ParabolicSar.StreamHub.cs
  • src/m-r/PivotPoints/PivotPoints.StreamHub.cs
  • src/m-r/Pivots/Pivots.StreamHub.cs
  • src/m-r/Pmo/Pmo.StreamHub.cs
  • src/m-r/Pvo/Pvo.StreamHub.cs
  • src/m-r/Renko/Renko.StreamHub.cs
  • src/m-r/RocWb/RocWb.StreamHub.cs
  • src/m-r/RollingPivots/RollingPivots.StreamHub.cs
  • src/m-r/Rsi/Rsi.StreamHub.cs
  • src/s-z/Slope/Slope.StreamHub.cs
  • src/s-z/Smi/Smi.StreamHub.cs
  • src/s-z/StarcBands/StarcBands.StreamHub.cs
  • src/s-z/Stc/Stc.StreamHub.cs
  • src/s-z/Stoch/Stoch.StreamHub.cs
  • src/s-z/StochRsi/StochRsi.StreamHub.cs
  • src/s-z/SuperTrend/SuperTrend.StreamHub.cs
  • src/s-z/T3/T3.StreamHub.cs
  • src/s-z/Tema/Tema.StreamHub.cs
  • src/s-z/Trix/Trix.StreamHub.cs
  • src/s-z/Tsi/Tsi.StreamHub.cs
  • src/s-z/UlcerIndex/UlcerIndex.StreamHub.cs
  • src/s-z/VolatilityStop/VolatilityStop.StreamHub.cs
  • src/s-z/Vortex/Vortex.StreamHub.cs
  • src/s-z/Vwap/Vwap.StreamHub.cs
  • src/s-z/WilliamsR/WilliamsR.StreamHub.cs

@DaveSkender DaveSkender merged commit 55ea5e1 into v3 Feb 28, 2026
14 checks passed
@DaveSkender DaveSkender deleted the copilot/implement-task-p011 branch February 28, 2026 23:09
Copilot AI added a commit that referenced this pull request Feb 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

2 participants