SoftTraderBackend/ServiceHandler/PriceSpikeEvaluator.cs
2026-03-20 12:38:18 -04:00

72 lines
1.8 KiB
C#

using System.Text.Json;
using Backend.DatabaseHandler;
using Backend.Interface;
namespace Backend.ServiceHander;
public sealed class PriceSpikeEvaluator : IAlertEvaluator
{
public string AlertType => "price_spike";
public Task<AlertEvaluation> EvaluateAsync(AlertRecord alert, MarketDataService marketData)
{
var parameters = JsonSerializer.Deserialize<PriceSpikeParameters>(alert.Parameters)
?? throw new InvalidOperationException($"Invalid parameters for alert {alert.Id}");
var snapshot = marketData.GetLiveQuote(alert.Symbol);
if (!snapshot.LastPrice.HasValue)
{
return Task.FromResult(new AlertEvaluation { Triggered = false });
}
double price = snapshot.LastPrice.Value;
var now = DateTime.UtcNow;
var state = JsonSerializer.Deserialize<PriceSpikeState>(alert.LastState)
?? new PriceSpikeState();
state.Prices ??= new List<PricePoint>();
state.Prices.Add(new PricePoint { Price = price, Timestamp = now });
var cutoff = now.AddMinutes(-parameters.WindowMinutes);
state.Prices.RemoveAll(p => p.Timestamp < cutoff);
bool triggered = false;
if (state.Prices.Count >= 2)
{
double oldest = state.Prices[0].Price;
if (oldest != 0)
{
double change = Math.Abs((price - oldest) / oldest * 100.0);
triggered = change >= parameters.PercentChange;
}
}
string updatedState = JsonSerializer.Serialize(state);
return Task.FromResult(new AlertEvaluation
{
Triggered = triggered,
UpdatedLastState = updatedState
});
}
private sealed class PriceSpikeParameters
{
public double PercentChange { get; set; }
public int WindowMinutes { get; set; } = 15;
}
private sealed class PriceSpikeState
{
public List<PricePoint>? Prices { get; set; }
}
private sealed class PricePoint
{
public double Price { get; set; }
public DateTime Timestamp { get; set; }
}
}