diff --git a/src/Managing.Application/Trading/TradingService.cs b/src/Managing.Application/Trading/TradingService.cs
index 274db3dd..f89d21a3 100644
--- a/src/Managing.Application/Trading/TradingService.cs
+++ b/src/Managing.Application/Trading/TradingService.cs
@@ -580,7 +580,8 @@ public class TradingService : ITradingService
}
///
- /// Generates signals for a date range by running each indicator individually and collecting all signals.
+ /// Generates signals for a date range using a rolling window approach to capture signals throughout the entire range.
+ /// This is necessary because some indicators (like STC) only process recent candles, so we need to process incrementally.
///
private List GenerateSignalsForDateRange(
List candles,
@@ -588,47 +589,72 @@ public class TradingService : ITradingService
Dictionary preCalculatedIndicatorValues)
{
var allSignals = new List();
+ var seenSignalIdentifiers = new HashSet();
var lightScenario = LightScenario.FromScenario(scenario);
- // Process each indicator individually to get all signals from each
- foreach (var lightIndicator in lightScenario.Indicators)
+ // Use rolling window approach to process candles incrementally
+ // This ensures we capture signals throughout the entire date range, not just recent ones
+ const int RollingWindowSize = 600;
+ var rollingWindowCandles = new List(RollingWindowSize);
+
+ // Process each candle with a rolling window
+ foreach (var candle in candles)
{
- // Build the indicator instance
- var indicatorInstance = lightIndicator.ToInterface();
-
- // Use pre-calculated indicator values if available
- List indicatorSignals;
- if (preCalculatedIndicatorValues != null && preCalculatedIndicatorValues.ContainsKey(lightIndicator.Type))
+ // Maintain rolling window
+ if (rollingWindowCandles.Count >= RollingWindowSize)
{
- // Use pre-calculated values to avoid recalculating indicators
- indicatorSignals = indicatorInstance.Run(candles, preCalculatedIndicatorValues[lightIndicator.Type]);
+ rollingWindowCandles.RemoveAt(0);
}
- else
+ rollingWindowCandles.Add(candle);
+
+ // Only process if we have enough candles for indicators
+ if (rollingWindowCandles.Count < 2)
{
- // Normal path: calculate indicators on the fly
- indicatorSignals = indicatorInstance.Run(candles);
+ continue;
}
- // Add all signals from this indicator to the collection
- if (indicatorSignals != null && indicatorSignals.Count > 0)
+ // Process each indicator individually for this rolling window position
+ foreach (var lightIndicator in lightScenario.Indicators)
{
- // Filter signals to only include those within the candle date range
- var firstCandleDate = candles.First().Date;
- var lastCandleDate = candles.Last().Date;
-
- var filteredSignals = indicatorSignals
- .Where(s => s.Date >= firstCandleDate && s.Date <= lastCandleDate)
- .ToList();
+ // Build the indicator instance (create fresh instance for each indicator to avoid state issues)
+ var indicatorInstance = lightIndicator.ToInterface();
- allSignals.AddRange(filteredSignals);
+ // Use pre-calculated indicator values if available
+ List indicatorSignals;
+ if (preCalculatedIndicatorValues != null && preCalculatedIndicatorValues.ContainsKey(lightIndicator.Type))
+ {
+ // Use pre-calculated values to avoid recalculating indicators
+ indicatorSignals = indicatorInstance.Run(rollingWindowCandles, preCalculatedIndicatorValues[lightIndicator.Type]);
+ }
+ else
+ {
+ // Normal path: calculate indicators on the fly
+ indicatorSignals = indicatorInstance.Run(rollingWindowCandles);
+ }
+
+ // Add new signals from this indicator to the collection
+ if (indicatorSignals != null && indicatorSignals.Count > 0)
+ {
+ // Filter signals to only include those within the requested date range
+ var firstCandleDate = candles.First().Date;
+ var lastCandleDate = candles.Last().Date;
+
+ foreach (var signal in indicatorSignals)
+ {
+ // Only add signals within the date range and avoid duplicates
+ if (signal.Date >= firstCandleDate &&
+ signal.Date <= lastCandleDate &&
+ !seenSignalIdentifiers.Contains(signal.Identifier))
+ {
+ allSignals.Add(signal);
+ seenSignalIdentifiers.Add(signal.Identifier);
+ }
+ }
+ }
}
}
- // Remove duplicates based on identifier and sort by date
- return allSignals
- .GroupBy(s => s.Identifier)
- .Select(g => g.First())
- .OrderBy(s => s.Date)
- .ToList();
+ // Sort by date and return
+ return allSignals.OrderBy(s => s.Date).ToList();
}
}
\ No newline at end of file