Refactor signal generation in TradingService to implement a rolling window approach, allowing for incremental processing of candles. This change enhances signal capture across the entire date range and prevents duplicate signals by tracking seen identifiers.
This commit is contained in:
@@ -580,7 +580,8 @@ public class TradingService : ITradingService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
private List<LightSignal> GenerateSignalsForDateRange(
|
||||
List<Candle> candles,
|
||||
@@ -588,12 +589,34 @@ public class TradingService : ITradingService
|
||||
Dictionary<IndicatorType, IndicatorsResultBase> preCalculatedIndicatorValues)
|
||||
{
|
||||
var allSignals = new List<LightSignal>();
|
||||
var seenSignalIdentifiers = new HashSet<string>();
|
||||
var lightScenario = LightScenario.FromScenario(scenario);
|
||||
|
||||
// Process each indicator individually to get all signals from each
|
||||
// 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<Candle>(RollingWindowSize);
|
||||
|
||||
// Process each candle with a rolling window
|
||||
foreach (var candle in candles)
|
||||
{
|
||||
// Maintain rolling window
|
||||
if (rollingWindowCandles.Count >= RollingWindowSize)
|
||||
{
|
||||
rollingWindowCandles.RemoveAt(0);
|
||||
}
|
||||
rollingWindowCandles.Add(candle);
|
||||
|
||||
// Only process if we have enough candles for indicators
|
||||
if (rollingWindowCandles.Count < 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Process each indicator individually for this rolling window position
|
||||
foreach (var lightIndicator in lightScenario.Indicators)
|
||||
{
|
||||
// Build the indicator instance
|
||||
// Build the indicator instance (create fresh instance for each indicator to avoid state issues)
|
||||
var indicatorInstance = lightIndicator.ToInterface();
|
||||
|
||||
// Use pre-calculated indicator values if available
|
||||
@@ -601,34 +624,37 @@ public class TradingService : ITradingService
|
||||
if (preCalculatedIndicatorValues != null && preCalculatedIndicatorValues.ContainsKey(lightIndicator.Type))
|
||||
{
|
||||
// Use pre-calculated values to avoid recalculating indicators
|
||||
indicatorSignals = indicatorInstance.Run(candles, preCalculatedIndicatorValues[lightIndicator.Type]);
|
||||
indicatorSignals = indicatorInstance.Run(rollingWindowCandles, preCalculatedIndicatorValues[lightIndicator.Type]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal path: calculate indicators on the fly
|
||||
indicatorSignals = indicatorInstance.Run(candles);
|
||||
indicatorSignals = indicatorInstance.Run(rollingWindowCandles);
|
||||
}
|
||||
|
||||
// Add all signals from this indicator to the collection
|
||||
// Add new signals from this indicator to the collection
|
||||
if (indicatorSignals != null && indicatorSignals.Count > 0)
|
||||
{
|
||||
// Filter signals to only include those within the candle date range
|
||||
// Filter signals to only include those within the requested date range
|
||||
var firstCandleDate = candles.First().Date;
|
||||
var lastCandleDate = candles.Last().Date;
|
||||
|
||||
var filteredSignals = indicatorSignals
|
||||
.Where(s => s.Date >= firstCandleDate && s.Date <= lastCandleDate)
|
||||
.ToList();
|
||||
|
||||
allSignals.AddRange(filteredSignals);
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user