81 lines
3.4 KiB
C#
81 lines
3.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace FinlyticCore.Dtos.TechnicalAnalysis;
|
|
|
|
/// <summary>
|
|
/// Execution context supplied to pattern detectors and strategy evaluators containing multi-timeframe candles and indicators.
|
|
/// </summary>
|
|
public class TechnicalContext
|
|
{
|
|
public string Isin { get; init; } = string.Empty;
|
|
public string Symbol { get; init; } = string.Empty;
|
|
public string Timeframe { get; init; } = "15m";
|
|
public DateTime TimestampUtc { get; init; } = DateTime.UtcNow;
|
|
public decimal CurrentPrice { get; init; }
|
|
public decimal CurrentSpread { get; init; }
|
|
public bool IsSpreadVolatile { get; init; }
|
|
public decimal CurrentAtr { get; init; }
|
|
public MarketRegime Regime { get; init; } = MarketRegime.LowVolatilityRangebound;
|
|
|
|
/// <summary>
|
|
/// Multi-timeframe historical candles (e.g. "1m", "5m", "15m", "1h", "1d").
|
|
/// </summary>
|
|
public Dictionary<string, IReadOnlyList<CandleDto>> MultiTimeframeCandles { get; init; } = new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
/// <summary>
|
|
/// Pre-calculated mathematical indicator values for the primary timeframe.
|
|
/// </summary>
|
|
public Dictionary<string, decimal> Indicators { get; init; } = new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
/// <summary>
|
|
/// Per-run overrides for a strategy's tunable indicator parameters (e.g. <c>"MeanReversion.RsiOversold"</c>),
|
|
/// keyed by <c>"{StrategyKey}.{ParameterName}"</c> so a single context could in principle carry overrides
|
|
/// for more than one strategy without name collisions. Always empty for live scanning
|
|
/// (<c>TechnicalScoringEngine</c> never populates this - Rules.md §4: no silent behavior change to live
|
|
/// trade generation as a side effect of a backtesting feature); populated only by
|
|
/// <c>FinlyticSimulation.Engine.HistoricalReplayRunner</c> from <c>BacktestRequestDto.StrategyParameters</c>,
|
|
/// so per-asset/per-strategy tuning is opt-in and scoped to backtesting. See <see cref="GetParameter"/>.
|
|
/// </summary>
|
|
public Dictionary<string, decimal> ParameterOverrides { get; init; } = new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
/// <summary>
|
|
/// Resolves a tunable strategy parameter: the override in <see cref="ParameterOverrides"/> under
|
|
/// <c>"{strategyKey}.{parameterName}"</c> if present, otherwise <paramref name="defaultValue"/> (the
|
|
/// strategy's own hardcoded default, unchanged from before parametrization existed).
|
|
/// </summary>
|
|
public decimal GetParameter(string strategyKey, string parameterName, decimal defaultValue)
|
|
{
|
|
return ParameterOverrides.TryGetValue($"{strategyKey}.{parameterName}", out var v) ? v : defaultValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the candles for a specific timeframe (defaults to empty list if not found).
|
|
/// </summary>
|
|
public IReadOnlyList<CandleDto> GetCandles(string timeframe)
|
|
{
|
|
if (MultiTimeframeCandles.TryGetValue(timeframe, out var list))
|
|
{
|
|
return list;
|
|
}
|
|
return [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the primary timeframe candle sequence.
|
|
/// </summary>
|
|
public IReadOnlyList<CandleDto> PrimaryCandles => GetCandles(Timeframe);
|
|
|
|
/// <summary>
|
|
/// Gets a specific indicator value or null if not computed.
|
|
/// </summary>
|
|
public decimal? GetIndicator(string key)
|
|
{
|
|
if (Indicators.TryGetValue(key, out var val))
|
|
{
|
|
return val;
|
|
}
|
|
return null;
|
|
}
|
|
}
|