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;
}
}
}