using System;
using System.Collections.Generic;
using NT8.Core.Common.Models;
using NT8.Core.Intelligence;
using NT8.Core.Logging;
namespace NT8.Core.Sizing
{
///
/// Applies confluence grade and risk mode multipliers on top of base sizing output.
///
public class GradeBasedSizer
{
private readonly ILogger _logger;
private readonly GradeFilter _gradeFilter;
///
/// Creates a grade-based sizer.
///
/// Logger instance.
/// Grade filter instance.
public GradeBasedSizer(ILogger logger, GradeFilter gradeFilter)
{
if (logger == null)
throw new ArgumentNullException("logger");
if (gradeFilter == null)
throw new ArgumentNullException("gradeFilter");
_logger = logger;
_gradeFilter = gradeFilter;
}
///
/// Calculates final size from base sizing plus grade and mode adjustments.
///
/// Strategy intent.
/// Strategy context.
/// Confluence score with grade.
/// Current risk mode.
/// Base sizing configuration.
/// Base position sizer used to compute initial contracts.
/// Current risk mode configuration.
/// Final sizing result including grade/mode metadata.
public SizingResult CalculateGradeBasedSize(
StrategyIntent intent,
StrategyContext context,
ConfluenceScore confluenceScore,
RiskMode riskMode,
SizingConfig baseConfig,
IPositionSizer baseSizer,
RiskModeConfig modeConfig)
{
if (intent == null)
throw new ArgumentNullException("intent");
if (context == null)
throw new ArgumentNullException("context");
if (confluenceScore == null)
throw new ArgumentNullException("confluenceScore");
if (baseConfig == null)
throw new ArgumentNullException("baseConfig");
if (baseSizer == null)
throw new ArgumentNullException("baseSizer");
if (modeConfig == null)
throw new ArgumentNullException("modeConfig");
try
{
if (!_gradeFilter.ShouldAcceptTrade(confluenceScore.Grade, riskMode))
{
var reject = _gradeFilter.GetRejectionReason(confluenceScore.Grade, riskMode);
var rejectCalcs = new Dictionary();
rejectCalcs.Add("rejected", true);
rejectCalcs.Add("rejection_reason", reject);
rejectCalcs.Add("grade", confluenceScore.Grade.ToString());
rejectCalcs.Add("risk_mode", riskMode.ToString());
_logger.LogInformation("Grade-based sizing rejected trade: {0}", reject);
return new SizingResult(0, 0.0, baseConfig.Method, rejectCalcs);
}
var baseResult = baseSizer.CalculateSize(intent, context, baseConfig);
var gradeMultiplier = _gradeFilter.GetSizeMultiplier(confluenceScore.Grade, riskMode);
var modeMultiplier = modeConfig.SizeMultiplier;
var combinedMultiplier = CombineMultipliers(gradeMultiplier, modeMultiplier);
var adjustedContractsRaw = baseResult.Contracts * combinedMultiplier;
var adjustedContracts = ApplyConstraints(
(int)Math.Floor(adjustedContractsRaw),
baseConfig.MinContracts,
baseConfig.MaxContracts);
var riskPerContract = baseResult.Contracts > 0 ? baseResult.RiskAmount / baseResult.Contracts : 0.0;
var finalRisk = adjustedContracts * riskPerContract;
var calculations = new Dictionary();
calculations.Add("base_contracts", baseResult.Contracts);
calculations.Add("base_risk", baseResult.RiskAmount);
calculations.Add("grade", confluenceScore.Grade.ToString());
calculations.Add("risk_mode", riskMode.ToString());
calculations.Add("grade_multiplier", gradeMultiplier);
calculations.Add("mode_multiplier", modeMultiplier);
calculations.Add("combined_multiplier", combinedMultiplier);
calculations.Add("adjusted_contracts_raw", adjustedContractsRaw);
calculations.Add("adjusted_contracts", adjustedContracts);
calculations.Add("risk_per_contract", riskPerContract);
calculations.Add("final_risk", finalRisk);
return new SizingResult(adjustedContracts, finalRisk, baseResult.Method, calculations);
}
catch (Exception ex)
{
_logger.LogError("CalculateGradeBasedSize failed: {0}", ex.Message);
throw;
}
}
///
/// Combines grade and mode multipliers.
///
/// Grade-based multiplier.
/// Mode-based multiplier.
/// Combined multiplier.
public double CombineMultipliers(double gradeMultiplier, double modeMultiplier)
{
if (gradeMultiplier < 0.0)
throw new ArgumentException("gradeMultiplier must be non-negative", "gradeMultiplier");
if (modeMultiplier < 0.0)
throw new ArgumentException("modeMultiplier must be non-negative", "modeMultiplier");
return gradeMultiplier * modeMultiplier;
}
///
/// Applies min/max contract constraints.
///
/// Calculated contracts.
/// Minimum allowed contracts.
/// Maximum allowed contracts.
/// Constrained contracts.
public int ApplyConstraints(int calculatedSize, int min, int max)
{
if (min < 0)
throw new ArgumentException("min must be non-negative", "min");
if (max < min)
throw new ArgumentException("max must be greater than or equal to min", "max");
if (calculatedSize < min)
return min;
if (calculatedSize > max)
return max;
return calculatedSize;
}
}
}