From 8a7addafd7042646253f66ceb8ea4ba55d232253 Mon Sep 17 00:00:00 2001 From: cryptooda Date: Sun, 28 Dec 2025 20:59:11 +0700 Subject: [PATCH] Refactor signal generation in TradingService to process indicators individually, improving clarity and performance. Add new API types for refining indicators, including request and response structures. --- .../Trading/TradingService.cs | 64 +++++++++---------- .../src/generated/ManagingApiTypes.ts | 13 ++++ .../src/generated/ManagingApi.ts | 52 +++++++++++++++ .../src/generated/ManagingApiTypes.ts | 13 ++++ 4 files changed, 110 insertions(+), 32 deletions(-) diff --git a/src/Managing.Application/Trading/TradingService.cs b/src/Managing.Application/Trading/TradingService.cs index fdf1d49e..274db3dd 100644 --- a/src/Managing.Application/Trading/TradingService.cs +++ b/src/Managing.Application/Trading/TradingService.cs @@ -580,7 +580,7 @@ public class TradingService : ITradingService } /// - /// Generates signals for a date range using a rolling window approach. + /// Generates signals for a date range by running each indicator individually and collecting all signals. /// private List GenerateSignalsForDateRange( List candles, @@ -588,47 +588,47 @@ public class TradingService : ITradingService Dictionary preCalculatedIndicatorValues) { var allSignals = new List(); - var previousSignals = new Dictionary(); var lightScenario = LightScenario.FromScenario(scenario); - // Use rolling window approach similar to backtests - const int RollingWindowSize = 600; - var rollingWindowCandles = new List(RollingWindowSize); - - foreach (var candle in candles) + // Process each indicator individually to get all signals from each + foreach (var lightIndicator in lightScenario.Indicators) { - // Maintain rolling window - if (rollingWindowCandles.Count >= RollingWindowSize) - { - rollingWindowCandles.RemoveAt(0); - } - rollingWindowCandles.Add(candle); + // Build the indicator instance + var indicatorInstance = lightIndicator.ToInterface(); - // Only process if we have enough candles for indicators - if (rollingWindowCandles.Count < 2) + // Use pre-calculated indicator values if available + List indicatorSignals; + if (preCalculatedIndicatorValues != null && preCalculatedIndicatorValues.ContainsKey(lightIndicator.Type)) { - continue; + // Use pre-calculated values to avoid recalculating indicators + indicatorSignals = indicatorInstance.Run(candles, preCalculatedIndicatorValues[lightIndicator.Type]); + } + else + { + // Normal path: calculate indicators on the fly + indicatorSignals = indicatorInstance.Run(candles); } - // Generate signal for current position - var signal = TradingBox.GetSignal( - rollingWindowCandles, - lightScenario, - previousSignals, - scenario.LookbackPeriod, - preCalculatedIndicatorValues); - - if (signal != null) + // Add all signals from this indicator to the collection + if (indicatorSignals != null && indicatorSignals.Count > 0) { - // Check if this is a new signal (not already in our collection) - if (!previousSignals.ContainsKey(signal.Identifier)) - { - allSignals.Add(signal); - previousSignals[signal.Identifier] = signal; - } + // 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(); + + allSignals.AddRange(filteredSignals); } } - return allSignals.OrderBy(s => s.Date).ToList(); + // Remove duplicates based on identifier and sort by date + return allSignals + .GroupBy(s => s.Identifier) + .Select(g => g.First()) + .OrderBy(s => s.Date) + .ToList(); } } \ No newline at end of file diff --git a/src/Managing.Web3Proxy/src/generated/ManagingApiTypes.ts b/src/Managing.Web3Proxy/src/generated/ManagingApiTypes.ts index 6985348b..d269e8f3 100644 --- a/src/Managing.Web3Proxy/src/generated/ManagingApiTypes.ts +++ b/src/Managing.Web3Proxy/src/generated/ManagingApiTypes.ts @@ -1469,6 +1469,19 @@ export interface IndicatorRequestDto { requesterName: string; } +export interface RefineIndicatorsResponse { + indicatorsValues?: { [key in keyof typeof IndicatorType]?: IndicatorsResultBase; } | null; + signals?: LightSignal[] | null; +} + +export interface RefineIndicatorsRequest { + ticker: Ticker; + timeframe: Timeframe; + startDate: Date; + endDate: Date; + indicators: IndicatorRequest[]; +} + export interface LoginRequest { name: string; address: string; diff --git a/src/Managing.WebApp/src/generated/ManagingApi.ts b/src/Managing.WebApp/src/generated/ManagingApi.ts index 0db7d052..ed92fcdc 100644 --- a/src/Managing.WebApp/src/generated/ManagingApi.ts +++ b/src/Managing.WebApp/src/generated/ManagingApi.ts @@ -4137,6 +4137,45 @@ export class TradingClient extends AuthorizedApiBase { } return Promise.resolve(null as any); } + + trading_RefineIndicators(request: RefineIndicatorsRequest): Promise { + let url_ = this.baseUrl + "/Trading/RefineIndicators"; + url_ = url_.replace(/[?&]$/, ""); + + const content_ = JSON.stringify(request); + + let options_: RequestInit = { + body: content_, + method: "POST", + headers: { + "Content-Type": "application/json", + "Accept": "application/json" + } + }; + + return this.transformOptions(options_).then(transformedOptions_ => { + return this.http.fetch(url_, transformedOptions_); + }).then((_response: Response) => { + return this.processTrading_RefineIndicators(_response); + }); + } + + protected processTrading_RefineIndicators(response: Response): Promise { + const status = response.status; + let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; + if (status === 200) { + return response.text().then((_responseText) => { + let result200: any = null; + result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as RefineIndicatorsResponse; + return result200; + }); + } else if (status !== 200 && status !== 204) { + return response.text().then((_responseText) => { + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + }); + } + return Promise.resolve(null as any); + } } export class UserClient extends AuthorizedApiBase { @@ -5972,6 +6011,19 @@ export interface IndicatorRequestDto { requesterName: string; } +export interface RefineIndicatorsResponse { + indicatorsValues?: { [key in keyof typeof IndicatorType]?: IndicatorsResultBase; } | null; + signals?: LightSignal[] | null; +} + +export interface RefineIndicatorsRequest { + ticker: Ticker; + timeframe: Timeframe; + startDate: Date; + endDate: Date; + indicators: IndicatorRequest[]; +} + export interface LoginRequest { name: string; address: string; diff --git a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts index 6985348b..d269e8f3 100644 --- a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts +++ b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts @@ -1469,6 +1469,19 @@ export interface IndicatorRequestDto { requesterName: string; } +export interface RefineIndicatorsResponse { + indicatorsValues?: { [key in keyof typeof IndicatorType]?: IndicatorsResultBase; } | null; + signals?: LightSignal[] | null; +} + +export interface RefineIndicatorsRequest { + ticker: Ticker; + timeframe: Timeframe; + startDate: Date; + endDate: Date; + indicators: IndicatorRequest[]; +} + export interface LoginRequest { name: string; address: string;