using System; using System.Collections.Generic; using NT8.Core.Common.Models; namespace NT8.Core.Intelligence { /// /// Calculates one confluence factor from strategy and market context. /// public interface IFactorCalculator { /// /// Factor type produced by this calculator. /// FactorType Type { get; } /// /// Calculates the confluence factor. /// /// Current strategy intent. /// Current strategy context. /// Current bar data. /// Calculated confluence factor. ConfluenceFactor Calculate(StrategyIntent intent, StrategyContext context, BarData bar); } /// /// ORB setup quality calculator. /// public class OrbSetupFactorCalculator : IFactorCalculator { /// /// Gets the factor type. /// public FactorType Type { get { return FactorType.Setup; } } /// /// Calculates ORB setup validity score. /// /// Current strategy intent. /// Current strategy context. /// Current bar data. /// Setup confluence factor. public ConfluenceFactor Calculate(StrategyIntent intent, StrategyContext context, BarData bar) { if (intent == null) throw new ArgumentNullException("intent"); if (context == null) throw new ArgumentNullException("context"); if (bar == null) throw new ArgumentNullException("bar"); var score = 0.0; var details = new Dictionary(); var orbRange = bar.High - bar.Low; details.Add("orb_range", orbRange); if (orbRange >= 1.0) score += 0.3; var body = System.Math.Abs(bar.Close - bar.Open); details.Add("bar_body", body); if (body >= (orbRange * 0.5)) score += 0.2; var averageVolume = GetDouble(context.CustomData, "avg_volume", 1.0); details.Add("avg_volume", averageVolume); details.Add("current_volume", bar.Volume); if (averageVolume > 0.0 && bar.Volume > (long)(averageVolume * 1.1)) score += 0.2; var minutesFromSessionOpen = (bar.Time - context.Session.SessionStart).TotalMinutes; details.Add("minutes_from_open", minutesFromSessionOpen); if (minutesFromSessionOpen >= 0 && minutesFromSessionOpen <= 120) score += 0.3; if (score > 1.0) score = 1.0; return new ConfluenceFactor( FactorType.Setup, "ORB Setup Validity", score, 1.0, "ORB validity based on range, candle quality, volume, and timing", details); } private static double GetDouble(Dictionary data, string key, double defaultValue) { if (data == null || string.IsNullOrEmpty(key) || !data.ContainsKey(key) || data[key] == null) return defaultValue; var value = data[key]; if (value is double) return (double)value; if (value is float) return (double)(float)value; if (value is int) return (double)(int)value; if (value is long) return (double)(long)value; return defaultValue; } } /// /// Trend alignment calculator. /// public class TrendAlignmentFactorCalculator : IFactorCalculator { /// /// Gets the factor type. /// public FactorType Type { get { return FactorType.Trend; } } /// /// Calculates trend alignment score. /// /// Current strategy intent. /// Current strategy context. /// Current bar data. /// Trend confluence factor. public ConfluenceFactor Calculate(StrategyIntent intent, StrategyContext context, BarData bar) { if (intent == null) throw new ArgumentNullException("intent"); if (context == null) throw new ArgumentNullException("context"); if (bar == null) throw new ArgumentNullException("bar"); var details = new Dictionary(); var score = 0.0; var avwap = GetDouble(context.CustomData, "avwap", bar.Close); var avwapSlope = GetDouble(context.CustomData, "avwap_slope", 0.0); var trendConfirm = GetDouble(context.CustomData, "trend_confirm", 0.0); details.Add("avwap", avwap); details.Add("avwap_slope", avwapSlope); details.Add("trend_confirm", trendConfirm); var isLong = intent.Side == OrderSide.Buy; var priceAligned = isLong ? bar.Close > avwap : bar.Close < avwap; if (priceAligned) score += 0.4; var slopeAligned = isLong ? avwapSlope > 0.0 : avwapSlope < 0.0; if (slopeAligned) score += 0.3; if (trendConfirm > 0.5) score += 0.3; if (score > 1.0) score = 1.0; return new ConfluenceFactor( FactorType.Trend, "Trend Alignment", score, 1.0, "Trend alignment using AVWAP location, slope, and confirmation", details); } private static double GetDouble(Dictionary data, string key, double defaultValue) { if (data == null || string.IsNullOrEmpty(key) || !data.ContainsKey(key) || data[key] == null) return defaultValue; var value = data[key]; if (value is double) return (double)value; if (value is float) return (double)(float)value; if (value is int) return (double)(int)value; if (value is long) return (double)(long)value; return defaultValue; } } /// /// Volatility regime suitability calculator. /// public class VolatilityRegimeFactorCalculator : IFactorCalculator { /// /// Gets the factor type. /// public FactorType Type { get { return FactorType.Volatility; } } /// /// Calculates volatility regime score. /// /// Current strategy intent. /// Current strategy context. /// Current bar data. /// Volatility confluence factor. public ConfluenceFactor Calculate(StrategyIntent intent, StrategyContext context, BarData bar) { if (intent == null) throw new ArgumentNullException("intent"); if (context == null) throw new ArgumentNullException("context"); if (bar == null) throw new ArgumentNullException("bar"); var details = new Dictionary(); var currentAtr = GetDouble(context.CustomData, "current_atr", 1.0); var normalAtr = GetDouble(context.CustomData, "normal_atr", 1.0); details.Add("current_atr", currentAtr); details.Add("normal_atr", normalAtr); var ratio = normalAtr > 0.0 ? currentAtr / normalAtr : 1.0; details.Add("atr_ratio", ratio); var score = 0.3; if (ratio >= 0.8 && ratio <= 1.2) score = 1.0; else if (ratio < 0.8) score = 0.7; else if (ratio > 1.2 && ratio <= 1.5) score = 0.5; else if (ratio > 1.5) score = 0.3; return new ConfluenceFactor( FactorType.Volatility, "Volatility Regime", score, 1.0, "Volatility suitability from ATR ratio", details); } private static double GetDouble(Dictionary data, string key, double defaultValue) { if (data == null || string.IsNullOrEmpty(key) || !data.ContainsKey(key) || data[key] == null) return defaultValue; var value = data[key]; if (value is double) return (double)value; if (value is float) return (double)(float)value; if (value is int) return (double)(int)value; if (value is long) return (double)(long)value; return defaultValue; } } /// /// Session timing suitability calculator. /// public class TimeInSessionFactorCalculator : IFactorCalculator { /// /// Gets the factor type. /// public FactorType Type { get { return FactorType.Timing; } } /// /// Calculates session timing score. /// /// Current strategy intent. /// Current strategy context. /// Current bar data. /// Timing confluence factor. public ConfluenceFactor Calculate(StrategyIntent intent, StrategyContext context, BarData bar) { if (intent == null) throw new ArgumentNullException("intent"); if (context == null) throw new ArgumentNullException("context"); if (bar == null) throw new ArgumentNullException("bar"); var details = new Dictionary(); var t = bar.Time.TimeOfDay; details.Add("time_of_day", t); var score = 0.3; var open = new TimeSpan(9, 30, 0); var firstTwoHoursEnd = new TimeSpan(11, 30, 0); var middayEnd = new TimeSpan(14, 0, 0); var lastHourStart = new TimeSpan(15, 0, 0); var close = new TimeSpan(16, 0, 0); if (t >= open && t < firstTwoHoursEnd) score = 1.0; else if (t >= firstTwoHoursEnd && t < middayEnd) score = 0.6; else if (t >= lastHourStart && t < close) score = 0.8; else score = 0.3; return new ConfluenceFactor( FactorType.Timing, "Time In Session", score, 1.0, "Session timing suitability", details); } } /// /// Recent execution quality calculator. /// public class ExecutionQualityFactorCalculator : IFactorCalculator { /// /// Gets the factor type. /// public FactorType Type { get { return FactorType.ExecutionQuality; } } /// /// Calculates execution quality score from recent fills. /// /// Current strategy intent. /// Current strategy context. /// Current bar data. /// Execution quality factor. public ConfluenceFactor Calculate(StrategyIntent intent, StrategyContext context, BarData bar) { if (intent == null) throw new ArgumentNullException("intent"); if (context == null) throw new ArgumentNullException("context"); if (bar == null) throw new ArgumentNullException("bar"); var details = new Dictionary(); var quality = GetDouble(context.CustomData, "recent_execution_quality", 0.6); details.Add("recent_execution_quality", quality); var score = 0.6; if (quality >= 0.9) score = 1.0; else if (quality >= 0.75) score = 0.8; else if (quality >= 0.6) score = 0.6; else score = 0.4; return new ConfluenceFactor( FactorType.ExecutionQuality, "Recent Execution Quality", score, 1.0, "Recent execution quality suitability", details); } private static double GetDouble(Dictionary data, string key, double defaultValue) { if (data == null || string.IsNullOrEmpty(key) || !data.ContainsKey(key) || data[key] == null) return defaultValue; var value = data[key]; if (value is double) return (double)value; if (value is float) return (double)(float)value; if (value is int) return (double)(int)value; if (value is long) return (double)(long)value; return defaultValue; } } }