Overall Statistics
#region imports
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Globalization;
    using System.Drawing;
    using QuantConnect;
    using QuantConnect.Algorithm.Framework;
    using QuantConnect.Algorithm.Framework.Selection;
    using QuantConnect.Algorithm.Framework.Alphas;
    using QuantConnect.Algorithm.Framework.Portfolio;
    using QuantConnect.Algorithm.Framework.Portfolio.SignalExports;
    using QuantConnect.Algorithm.Framework.Execution;
    using QuantConnect.Algorithm.Framework.Risk;
    using QuantConnect.Algorithm.Selection;
    using QuantConnect.Api;
    using QuantConnect.Parameters;
    using QuantConnect.Benchmarks;
    using QuantConnect.Brokerages;
    using QuantConnect.Commands;
    using QuantConnect.Configuration;
    using QuantConnect.Util;
    using QuantConnect.Interfaces;
    using QuantConnect.Algorithm;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Auxiliary;
    using QuantConnect.Data.Consolidators;
    using QuantConnect.Data.Custom;
    using QuantConnect.Data.Custom.IconicTypes;
    using QuantConnect.DataSource;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.Shortable;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Notifications;
    using QuantConnect.Orders;
    using QuantConnect.Orders.Fees;
    using QuantConnect.Orders.Fills;
    using QuantConnect.Orders.OptionExercise;
    using QuantConnect.Orders.Slippage;
    using QuantConnect.Orders.TimeInForces;
    using QuantConnect.Python;
    using QuantConnect.Scheduling;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Equity;
    using QuantConnect.Securities.Future;
    using QuantConnect.Securities.Option;
    using QuantConnect.Securities.Positions;
    using QuantConnect.Securities.Forex;
    using QuantConnect.Securities.Crypto;
    using QuantConnect.Securities.CryptoFuture;
    using QuantConnect.Securities.IndexOption;
    using QuantConnect.Securities.Interfaces;
    using QuantConnect.Securities.Volatility;
    using QuantConnect.Storage;
    using QuantConnect.Statistics;
    using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
    using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
    using Calendar = QuantConnect.Data.Consolidators.Calendar;
#endregion

namespace CoreAlgo.Architecture.Core.Attributes
{
    /// <summary>
    /// Attribute to mark properties as strategy parameters
    /// </summary>
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
    public class StrategyParameterAttribute : Attribute
    {
        /// <summary>
        /// Gets or sets the parameter name for QC GetParameter()
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Gets or sets the parameter description
        /// </summary>
        public string Description { get; set; }

        /// <summary>
        /// Gets or sets the default value
        /// </summary>
        public object DefaultValue { get; set; }

        /// <summary>
        /// Gets or sets whether the parameter is required
        /// </summary>
        public bool IsRequired { get; set; }

        /// <summary>
        /// Gets or sets the minimum value (for numeric types)
        /// </summary>
        public object MinValue { get; set; }

        /// <summary>
        /// Gets or sets the maximum value (for numeric types)
        /// </summary>
        public object MaxValue { get; set; }

        /// <summary>
        /// Gets or sets the parameter group for organization
        /// </summary>
        public string Group { get; set; }

        /// <summary>
        /// Gets or sets the display order
        /// </summary>
        public int Order { get; set; }

        /// <summary>
        /// Creates a new instance of StrategyParameterAttribute
        /// </summary>
        public StrategyParameterAttribute()
        {
        }

        /// <summary>
        /// Creates a new instance of StrategyParameterAttribute with name and default value
        /// </summary>
        public StrategyParameterAttribute(string name, object defaultValue = null)
        {
            Name = name;
            DefaultValue = defaultValue;
        }
    }
}
using System.Collections.Generic;

namespace CoreAlgo.Architecture.Core.Configuration
{
    /// <summary>
    /// Embedded configuration constants that are guaranteed to sync with QuantConnect
    /// </summary>
    public static class EmbeddedConfiguration
    {
        /// <summary>
        /// Development environment configuration as JSON string
        /// </summary>
        public const string DevelopmentConfig = @"{
  ""environment"": ""development"",
  ""algorithm"": {
    ""startDate"": ""2023-12-25"",
    ""endDate"": ""2024-12-25"",
    ""startingCash"": 100000,
    ""dataNormalizationMode"": ""Raw""
  },
  ""strategies"": {
    ""default"": {
      ""maxActivePositions"": 1,
      ""maxOpenPositions"": 2,
      ""targetPremiumPct"": 0.01,
      ""scheduleStartTime"": ""09:30:00"",
      ""scheduleStopTime"": ""16:00:00"",
      ""scheduleFrequency"": ""00:05:00""
    },
    ""SPXic"": {
      ""ticker"": ""SPX"",
      ""maxActivePositions"": 10,
      ""dte"": 0,
      ""putWingSize"": 10,
      ""callWingSize"": 10,
      ""minPremium"": 0.9,
      ""maxPremium"": 1.2
    }
  },
  ""riskManagement"": {
    ""maxDrawdownPercent"": 0.2,
    ""maxLeverageRatio"": 2.0,
    ""stopLossPercent"": 0.05
  },
  ""logging"": {
    ""level"": ""Debug"",
    ""enableFileLogging"": true,
    ""logFilePath"": ""logs/corealgo-dev.log""
  },
  ""backtesting"": {
    ""slippageModel"": ""ConstantSlippage"",
    ""feeModel"": ""InteractiveBrokersFeeModel"",
    ""fillModel"": ""ImmediateFillModel""
  }
}";

        /// <summary>
        /// Production environment configuration as JSON string
        /// </summary>
        public const string ProductionConfig = @"{
  ""environment"": ""production"",
  ""algorithm"": {
    ""startDate"": ""2024-01-01"",
    ""endDate"": ""2024-12-31"",
    ""startingCash"": 1000000,
    ""dataNormalizationMode"": ""Adjusted""
  },
  ""strategies"": {
    ""default"": {
      ""maxActivePositions"": 5,
      ""maxOpenPositions"": 10,
      ""targetPremiumPct"": 0.015,
      ""scheduleStartTime"": ""09:45:00"",
      ""scheduleStopTime"": ""15:30:00"",
      ""scheduleFrequency"": ""00:15:00""
    },
    ""SPXic"": {
      ""ticker"": ""SPX"",
      ""maxActivePositions"": 20,
      ""dte"": 0,
      ""putWingSize"": 15,
      ""callWingSize"": 15,
      ""minPremium"": 0.8,
      ""maxPremium"": 1.5
    }
  },
  ""riskManagement"": {
    ""maxDrawdownPercent"": 0.15,
    ""maxLeverageRatio"": 1.5,
    ""stopLossPercent"": 0.03
  },
  ""logging"": {
    ""level"": ""Information"",
    ""enableFileLogging"": true,
    ""logFilePath"": ""logs/corealgo-prod.log""
  },
  ""backtesting"": {
    ""slippageModel"": ""AlphaStreamsSlippage"",
    ""feeModel"": ""AlphaStreamsFeeModel"", 
    ""fillModel"": ""PartialFillModel""
  },
  ""notifications"": {
    ""email"": {
      ""enabled"": true,
      ""recipients"": [""trading@example.com""],
      ""sendOnError"": true,
      ""sendOnTrade"": true
    }
  }
}";

        /// <summary>
        /// Gets configuration by environment name
        /// </summary>
        /// <param name="environment">Environment name (development, production)</param>
        /// <returns>JSON configuration string</returns>
        public static string GetConfiguration(string environment)
        {
            return environment?.ToLowerInvariant() switch
            {
                "development" or "dev" => DevelopmentConfig,
                "production" or "prod" => ProductionConfig,
                _ => DevelopmentConfig // Default to development
            };
        }

        /// <summary>
        /// Gets all available environment names
        /// </summary>
        /// <returns>List of environment names</returns>
        public static List<string> GetAvailableEnvironments()
        {
            return new List<string> { "development", "production" };
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Data;
using QuantConnect.Securities;
using QuantConnect.Securities.Option;
using CoreAlgo.Architecture.Core.Models;
namespace CoreAlgo.Architecture.Core.Configuration
{
    public class EntryRestrictions
    {
        private readonly StrategyConfig _config;
        private readonly QCAlgorithm _algorithm;
        public EntryRestrictions(StrategyConfig config, QCAlgorithm algorithm)
        {
            _config = config ?? throw new ArgumentNullException(nameof(config));
            _algorithm = algorithm ?? throw new ArgumentNullException(nameof(algorithm));
        }
        public bool CanEnterPosition(Symbol symbol, Slice slice, out string reason)
        {
            reason = string.Empty;
            var shouldLog = _config.LogEntryRestrictions;
            if (shouldLog)
            {
                _algorithm.Debug($"[DEBUG EntryRestrictions] Checking restrictions at {slice.Time}");
            }
            var withinHours = IsWithinTradingHours(slice.Time);
            if (shouldLog)
            {
                _algorithm.Debug($"  [1/5] Trading Hours: {withinHours} | Current: {slice.Time.TimeOfDay} | Window: {_config.TradingStartTime}-{_config.TradingEndTime}");
            }
            if (!withinHours)
            {
                reason = $"Outside trading hours ({_config.TradingStartTime}-{_config.TradingEndTime})";
                return false;
            }
            var hasCapacity = true;
            var currentPositionCount = 0;
            if (_config.MaxPositions > 0)
            {
                hasCapacity = HasCapacityForNewPosition();
                currentPositionCount = _algorithm.Portfolio.Where(kvp => kvp.Value.Invested).Count();
                if (shouldLog)
                {
                    _algorithm.Debug($"  [2/5] Max Positions: {hasCapacity} | Current: {currentPositionCount}/{_config.MaxPositions}");
                }
                if (!hasCapacity)
                {
                    reason = $"Max positions reached ({_config.MaxPositions})";
                    return false;
                }
            }
            else if (shouldLog)
            {
                _algorithm.Debug($"  [2/5] Max Positions: DISABLED (MaxPositions={_config.MaxPositions})");
            }
            if (_config.EnableOverlapPrevention && _config.MaxPositionsPerUnderlying > 0)
            {
                var existingCount = CountExistingPositions(symbol);
                var limit = _config.MaxPositionsPerUnderlying;
                if (shouldLog)
                {
                    _algorithm.Debug($"  [3/5] Overlap Prevention: Enabled | Existing: {existingCount}/{limit} | Mode={_config.OverlapPreventionMode}");
                }
                if (existingCount >= limit)
                {
                    var mode = (_config.OverlapPreventionMode ?? "Block").ToUpperInvariant();
                    var msg = $"Underlying limit reached ({existingCount}/{limit}) for {symbol.Underlying ?? symbol}";
                    if (mode == "BLOCK")
                    {
                        reason = msg;
                        return false;
                    }
                    else if (mode == "WARN")
                    {
                        _algorithm.Debug($"[ENTRY WARN] {msg}");
                    }
                    else // LOG mode
                    {
                        _algorithm.Debug($"[ENTRY LOG] {msg}");
                    }
                }
            }
            else if (shouldLog)
            {
                _algorithm.Debug($"  [3/5] Overlap Prevention: DISABLED - allowing multiple entries");
            }
            var hasSufficientCapital = HasSufficientCapital();
            if (shouldLog)
            {
                var portfolioValue = _algorithm.Portfolio.TotalPortfolioValue;
                var requiredCapital = portfolioValue * _config.AllocationPerPosition;
                var availableCash = _algorithm.Portfolio.Cash;
                _algorithm.Debug($"  [4/5] Capital: {hasSufficientCapital} | Available: ${availableCash:F2} | Required: ${requiredCapital:F2} (Allocation: {_config.AllocationPerPosition:P0})");
            }
            if (!hasSufficientCapital)
            {
                reason = "Insufficient capital for new position";
                return false;
            }
            if (_config.MinImpliedVolatility > 0)
            {
                var meetsVol = MeetsVolatilityRequirement(symbol, slice);
                if (shouldLog)
                {
                    _algorithm.Debug($"  [5/5] Volatility: {meetsVol} | Min IV: {_config.MinImpliedVolatility:P0}");
                }
                if (!meetsVol)
                {
                    reason = $"Implied volatility below minimum ({_config.MinImpliedVolatility:P0})";
                    return false;
                }
            }
            else if (shouldLog)
            {
                _algorithm.Debug($"  [5/5] Volatility: DISABLED (MinIV={_config.MinImpliedVolatility:P0})");
            }
            if (shouldLog)
            {
                _algorithm.Debug($"[DEBUG EntryRestrictions] ✓ ALL CHECKS PASSED");
            }
            return true;
        }
        public bool CanEnterOptionPosition(dynamic contract, out string reason)
        {
            reason = string.Empty;
            if (contract.Greeks == null)
            {
                reason = "Greeks not available for option contract";
                return false;
            }
            var delta = Math.Abs(contract.Greeks.Delta);
            if (delta < _config.EntryDeltaMin || delta > _config.EntryDeltaMax)
            {
                reason = $"Delta {delta:F2} outside range [{_config.EntryDeltaMin:F2}-{_config.EntryDeltaMax:F2}]";
                return false;
            }
            return true;
        }
        private bool IsWithinTradingHours(DateTime currentTime)
        {
            var timeOfDay = currentTime.TimeOfDay;
            if (_config.TradingStartTime == TimeSpan.Zero && _config.TradingEndTime == TimeSpan.Zero)
            {
                return true;
            }
            if (_config.TradingEndTime < _config.TradingStartTime)
            {
                return timeOfDay >= _config.TradingStartTime || timeOfDay <= _config.TradingEndTime;
            }
            return timeOfDay >= _config.TradingStartTime && timeOfDay <= _config.TradingEndTime;
        }
        private bool HasCapacityForNewPosition()
        {
            var currentPositions = _algorithm.Portfolio
                .Where(kvp => kvp.Value.Invested)
                .Count();
            return currentPositions < _config.MaxPositions;
        }
        private int CountExistingPositions(Symbol symbol)
        {
            if (symbol.SecurityType == SecurityType.Option)
            {
                var underlying = symbol.Underlying;
                return _algorithm.Portfolio.Count(kvp =>
                    kvp.Value.Invested &&
                    (kvp.Key == underlying || (kvp.Key.SecurityType == SecurityType.Option && kvp.Key.Underlying == underlying)));
            }
            return _algorithm.Portfolio.Count(kvp => kvp.Key == symbol && kvp.Value.Invested);
        }
        private bool HasSufficientCapital()
        {
            var portfolioValue = _algorithm.Portfolio.TotalPortfolioValue;
            var requiredCapital = portfolioValue * _config.AllocationPerPosition;
            var availableCash = _algorithm.Portfolio.Cash;
            return availableCash >= requiredCapital;
        }
        private bool MeetsVolatilityRequirement(Symbol symbol, Slice slice)
        {
            return true;
        }
        public Dictionary<string, object> GetRestrictionStatus()
        {
            var currentTime = _algorithm.Time;
            var activePositions = _algorithm.Portfolio.Where(kvp => kvp.Value.Invested).Count();
            var portfolioValue = _algorithm.Portfolio.TotalPortfolioValue;
            var availableCash = _algorithm.Portfolio.Cash;
            return new Dictionary<string, object>
            {
                ["CurrentTime"] = currentTime,
                ["TradingHoursActive"] = IsWithinTradingHours(currentTime),
                ["ActivePositions"] = activePositions,
                ["MaxPositions"] = _config.MaxPositions,
                ["AvailableSlots"] = _config.MaxPositions - activePositions,
                ["PortfolioValue"] = portfolioValue,
                ["AvailableCash"] = availableCash,
                ["AllocationPerPosition"] = _config.AllocationPerPosition,
                ["RequiredCapitalPerPosition"] = portfolioValue * _config.AllocationPerPosition
            };
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Data;
using QuantConnect.Securities;
using QuantConnect.Securities.Option;
using CoreAlgo.Architecture.Core.Models;
namespace CoreAlgo.Architecture.Core.Configuration
{
    public class ExitRestrictions
    {
        private readonly StrategyConfig _config;
        private readonly QCAlgorithm _algorithm;
        private readonly Dictionary<Symbol, DateTime> _positionEntryTimes;
        public ExitRestrictions(StrategyConfig config, QCAlgorithm algorithm)
        {
            _config = config ?? throw new ArgumentNullException(nameof(config));
            _algorithm = algorithm ?? throw new ArgumentNullException(nameof(algorithm));
            _positionEntryTimes = new Dictionary<Symbol, DateTime>();
        }
        public void RecordPositionEntry(Symbol symbol, DateTime entryTime)
        {
            _positionEntryTimes[symbol] = entryTime;
        }
        public bool ShouldExitPosition(Symbol symbol, Slice slice, out string reason)
        {
            reason = string.Empty;
            var holding = _algorithm.Portfolio[symbol];
            if (!holding.Invested)
            {
                reason = "No position to exit";
                return false;
            }
            if (IsProfitTargetReached(holding))
            {
                reason = $"Profit target reached ({holding.UnrealizedProfitPercent:P2} >= {_config.ProfitTarget:P2})";
                return true;
            }
            if (IsStopLossTriggered(holding))
            {
                reason = $"Stop loss triggered ({holding.UnrealizedProfitPercent:P2} <= {_config.StopLoss:P2})";
                return true;
            }
            if (IsMaxTimeReached(symbol, slice.Time))
            {
                var daysInTrade = GetDaysInTrade(symbol, slice.Time);
                reason = $"Max time in trade reached ({daysInTrade:F1} days >= {_config.MaxDaysInTrade} days)";
                return true;
            }
            if (!IsWithinTradingHours(slice.Time))
            {
            }
            return false;
        }
        public bool ShouldExitOptionPosition(Symbol symbol, dynamic contract, out string reason)
        {
            reason = string.Empty;
            if (ShouldExitPosition(symbol, _algorithm.CurrentSlice, out reason))
            {
                return true;
            }
            if (contract.Greeks != null)
            {
                var delta = Math.Abs(contract.Greeks.Delta);
                if (delta <= _config.ExitDelta)
                {
                    reason = $"Delta exit triggered ({delta:F3} <= {_config.ExitDelta:F3})";
                    return true;
                }
            }
            var daysToExpiry = (contract.Expiry - _algorithm.Time).TotalDays;
            if (daysToExpiry <= 1) // Exit if expiring tomorrow
            {
                reason = $"Near expiration ({daysToExpiry:F1} days)";
                return true;
            }
            return false;
        }
        private bool IsProfitTargetReached(SecurityHolding holding)
        {
            if (_config.ProfitTarget == 0) return false;
            return holding.UnrealizedProfitPercent >= _config.ProfitTarget;
        }
        private bool IsStopLossTriggered(SecurityHolding holding)
        {
            if (_config.StopLoss == 0) return false;
            return holding.UnrealizedProfitPercent <= _config.StopLoss;
        }
        private bool IsMaxTimeReached(Symbol symbol, DateTime currentTime)
        {
            if (!_positionEntryTimes.ContainsKey(symbol))
                return false;
            var daysInTrade = (currentTime - _positionEntryTimes[symbol]).TotalDays;
            return daysInTrade >= _config.MaxDaysInTrade;
        }
        private double GetDaysInTrade(Symbol symbol, DateTime currentTime)
        {
            if (!_positionEntryTimes.ContainsKey(symbol))
                return 0;
            return (currentTime - _positionEntryTimes[symbol]).TotalDays;
        }
        private bool IsWithinTradingHours(DateTime currentTime)
        {
            var timeOfDay = currentTime.TimeOfDay;
            return timeOfDay >= _config.TradingStartTime && timeOfDay <= _config.TradingEndTime;
        }
        public double GetExitUrgency(Symbol symbol)
        {
            var holding = _algorithm.Portfolio[symbol];
            if (!holding.Invested)
                return 0;
            var urgency = 0.0;
            if (_config.StopLoss != 0 && holding.UnrealizedProfitPercent <= _config.StopLoss)
            {
                urgency = 1.0;
            }
            else if (_config.ProfitTarget != 0 && holding.UnrealizedProfitPercent >= _config.ProfitTarget)
            {
                urgency = 0.8;
            }
            else if (_positionEntryTimes.ContainsKey(symbol))
            {
                var daysInTrade = GetDaysInTrade(symbol, _algorithm.Time);
                var timeUrgency = Math.Min(daysInTrade / _config.MaxDaysInTrade, 1.0);
                urgency = Math.Max(urgency, timeUrgency * 0.6);
            }
            return urgency;
        }
        public List<PositionExitStatus> GetAllPositionExitStatus()
        {
            var results = new List<PositionExitStatus>();
            foreach (var kvp in _algorithm.Portfolio.Where(p => p.Value.Invested))
            {
                var symbol = kvp.Key;
                var holding = kvp.Value;
                var shouldExit = ShouldExitPosition(symbol, _algorithm.CurrentSlice, out var reason);
                var urgency = GetExitUrgency(symbol);
                var daysHeld = _positionEntryTimes.ContainsKey(symbol)
                    ? GetDaysInTrade(symbol, _algorithm.Time)
                    : 0;
                results.Add(new PositionExitStatus
                {
                    Symbol = symbol,
                    UnrealizedProfitPercent = holding.UnrealizedProfitPercent,
                    DaysHeld = daysHeld,
                    ShouldExit = shouldExit,
                    ExitReason = reason,
                    ExitUrgency = urgency
                });
            }
            return results.OrderByDescending(r => r.ExitUrgency).ToList();
        }
        public void ClearPositionEntry(Symbol symbol)
        {
            _positionEntryTimes.Remove(symbol);
        }
    }
    public class PositionExitStatus
    {
        public Symbol Symbol { get; set; }
        public decimal UnrealizedProfitPercent { get; set; }
        public double DaysHeld { get; set; }
        public bool ShouldExit { get; set; }
        public string ExitReason { get; set; }
        public double ExitUrgency { get; set; }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Orders;
using QuantConnect.Scheduling;
namespace CoreAlgo.Architecture.Core.Execution
{
    public class ComboOrderTracker
    {
        public List<OrderTicket> ComboTickets { get; }
        public List<Leg> Legs { get; }
        public decimal CurrentNetPrice { get; private set; }
        public ComboQuote LastQuote { get; private set; }
        public OrderDirection ComboDirection { get; }
        public SmartPricingMode PricingMode { get; }
        public int AttemptNumber { get; private set; }
        public DateTime StartTime { get; }
        public ScheduledEvent ScheduledEvent { get; set; }
        public bool IsCompletelyFilled => ComboTickets.All(ticket =>
            ticket.Status == OrderStatus.Filled);
        public bool HasPartialFills => ComboTickets.Any(ticket =>
            ticket.Status == OrderStatus.PartiallyFilled ||
            (ticket.Status == OrderStatus.Filled && ticket.QuantityFilled != ticket.Quantity));
        public bool IsActive => ComboTickets.Any(ticket =>
            ticket.Status == OrderStatus.Submitted ||
            ticket.Status == OrderStatus.PartiallyFilled);
        public OrderTicket PrimaryTicket => ComboTickets.FirstOrDefault();
        public int PrimaryOrderId => PrimaryTicket?.OrderId ?? -1;
        public ComboOrderTracker(List<OrderTicket> comboTickets, List<Leg> legs, ComboQuote initialQuote,
            OrderDirection comboDirection, SmartPricingMode pricingMode, decimal initialNetPrice)
        {
            ComboTickets = new List<OrderTicket>(comboTickets ?? throw new ArgumentNullException(nameof(comboTickets)));
            Legs = new List<Leg>(legs ?? throw new ArgumentNullException(nameof(legs)));
            LastQuote = initialQuote ?? throw new ArgumentNullException(nameof(initialQuote));
            ComboDirection = comboDirection;
            PricingMode = pricingMode;
            CurrentNetPrice = initialNetPrice;
            AttemptNumber = 1;
            StartTime = DateTime.UtcNow;
            if (ComboTickets.Count == 0)
                throw new ArgumentException("Combo tickets cannot be empty", nameof(comboTickets));
            if (Legs.Count == 0)
                throw new ArgumentException("Legs cannot be empty", nameof(legs));
        }
        public void UpdateNetPrice(decimal newNetPrice, ComboQuote newQuote)
        {
            CurrentNetPrice = newNetPrice;
            LastQuote = newQuote ?? throw new ArgumentNullException(nameof(newQuote));
            AttemptNumber++;
        }
        public void UpdatePartialFill(OrderEvent orderEvent)
        {
        }
        public string GetSummary()
        {
            var status = IsCompletelyFilled ? "FILLED" :
                        HasPartialFills ? "PARTIAL" :
                        IsActive ? "ACTIVE" : "INACTIVE";
            var runtime = DateTime.UtcNow - StartTime;
            return $"Combo Order {PrimaryOrderId}: {Legs.Count} legs, " +
                   $"NetPrice=${CurrentNetPrice:F2}, Attempt={AttemptNumber}, " +
                   $"Status={status}, Runtime={runtime.TotalSeconds:F0}s, " +
                   $"Mode={PricingMode}";
        }
        public string GetLegStatus()
        {
            var legStatuses = ComboTickets.Select((ticket, index) =>
                $"Leg{index + 1}[{ticket.OrderId}]: {ticket.Status} " +
                $"({ticket.QuantityFilled}/{ticket.Quantity})");
            return string.Join(", ", legStatuses);
        }
        public TimeSpan GetRuntime()
        {
            return DateTime.UtcNow - StartTime;
        }
        public bool ShouldContinuePricing(int maxAttempts, TimeSpan maxRuntime)
        {
            if (!IsActive)
                return false;
            if (AttemptNumber >= maxAttempts)
                return false;
            if (GetRuntime() >= maxRuntime)
                return false;
            return true;
        }
    }
}
using System;
using System.Collections.Generic;
using QuantConnect.Orders;
namespace CoreAlgo.Architecture.Core.Execution
{
    public class ComboPricingEngine
    {
        private readonly SmartPricingMode _mode;
        private readonly decimal _maxNetSpreadWidth;
        public ComboPricingEngine(SmartPricingMode mode, decimal maxNetSpreadWidth = 5.0m)
        {
            _mode = mode;
            _maxNetSpreadWidth = maxNetSpreadWidth;
        }
        public decimal? CalculateInitialComboPrice(List<Leg> legs, ComboQuote comboQuote, OrderDirection orderDirection)
        {
            if (legs == null || legs.Count == 0 || comboQuote == null || !comboQuote.IsValid)
                return null;
            if (comboQuote.NetSpread > _maxNetSpreadWidth)
                return null;
            return comboQuote.NetMid;
        }
        public decimal? CalculateNextComboPrice(decimal currentNetPrice, ComboQuote comboQuote,
            OrderDirection orderDirection, int attemptNumber)
        {
            if (comboQuote == null || !comboQuote.IsValid)
                return null;
            var maxAttempts = GetMaxAttempts();
            if (attemptNumber >= maxAttempts)
                return null;
            var progressionRatio = CalculateProgressionRatio(attemptNumber, maxAttempts);
            decimal targetPrice;
            decimal startPrice = comboQuote.NetMid;
            if (orderDirection == OrderDirection.Buy)
            {
                targetPrice = comboQuote.NetAsk;
            }
            else
            {
                targetPrice = comboQuote.NetBid;
            }
            var nextPrice = startPrice + (targetPrice - startPrice) * progressionRatio;
            if (orderDirection == OrderDirection.Buy)
            {
                nextPrice = Math.Max(nextPrice, currentNetPrice);
                nextPrice = Math.Min(nextPrice, targetPrice);
            }
            else
            {
                nextPrice = Math.Min(nextPrice, currentNetPrice);
                nextPrice = Math.Max(nextPrice, targetPrice);
            }
            return Math.Round(nextPrice, 2);
        }
        public TimeSpan GetPricingInterval()
        {
            return _mode switch
            {
                SmartPricingMode.Fast => TimeSpan.FromSeconds(5),      // 3 steps over 15 seconds
                SmartPricingMode.Normal => TimeSpan.FromSeconds(10),   // 4 steps over 40 seconds
                SmartPricingMode.Patient => TimeSpan.FromSeconds(20),  // 5 steps over 100 seconds
                _ => TimeSpan.FromSeconds(10)
            };
        }
        public int GetMaxAttempts()
        {
            return _mode switch
            {
                SmartPricingMode.Fast => 3,
                SmartPricingMode.Normal => 4,
                SmartPricingMode.Patient => 5,
                _ => 4
            };
        }
        public bool ShouldAttemptComboPricing(ComboQuote comboQuote, OrderDirection orderDirection)
        {
            if (comboQuote == null || !comboQuote.IsValid)
                return false;
            if (comboQuote.NetSpread > _maxNetSpreadWidth)
                return false;
            if (comboQuote.NetSpread < 0.10m)
                return false;
            return true;
        }
        public static OrderDirection DetermineComboDirection(List<Leg> legs)
        {
            if (legs == null || legs.Count == 0)
                return OrderDirection.Buy; // Default
            int longLegs = 0;
            int shortLegs = 0;
            foreach (var leg in legs)
            {
                if (leg.Quantity > 0)
                    longLegs += Math.Abs(leg.Quantity);
                else if (leg.Quantity < 0)
                    shortLegs += Math.Abs(leg.Quantity);
            }
            return shortLegs > longLegs ? OrderDirection.Sell : OrderDirection.Buy;
        }
        private decimal CalculateProgressionRatio(int attemptNumber, int maxAttempts)
        {
            if (attemptNumber <= 0 || maxAttempts <= 1)
                return 0m;
            var ratio = (decimal)attemptNumber / maxAttempts;
            return Math.Min(ratio, 0.90m);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace CoreAlgo.Architecture.Core.Execution
{
    public class ComboQuote
    {
        public decimal NetBid { get; }
        public decimal NetAsk { get; }
        public decimal NetMid => (NetBid + NetAsk) / 2;
        public decimal NetSpread => NetAsk - NetBid;
        public Dictionary<Symbol, Quote> LegQuotes { get; }
        public DateTime Timestamp { get; }
        public bool IsValid => LegQuotes.Values.All(q => q.Bid > 0 && q.Ask > q.Bid) && NetSpread >= 0;
        public ComboQuote(List<Leg> legs, Dictionary<Symbol, Quote> legQuotes, DateTime timestamp)
        {
            if (legs == null || legs.Count == 0)
                throw new ArgumentException("Legs cannot be null or empty", nameof(legs));
            if (legQuotes == null)
                throw new ArgumentNullException(nameof(legQuotes));
            LegQuotes = new Dictionary<Symbol, Quote>(legQuotes);
            Timestamp = timestamp;
            decimal netBid = 0;
            decimal netAsk = 0;
            foreach (var leg in legs)
            {
                if (!legQuotes.TryGetValue(leg.Symbol, out var quote))
                {
                    throw new ArgumentException($"Missing quote for leg symbol {leg.Symbol}", nameof(legQuotes));
                }
                if (leg.Quantity > 0)
                {
                    netAsk += quote.Ask * Math.Abs(leg.Quantity);
                    netBid += quote.Bid * Math.Abs(leg.Quantity);
                }
                else if (leg.Quantity < 0)
                {
                    netBid -= quote.Ask * Math.Abs(leg.Quantity);  // We receive when selling (positive for us)
                    netAsk -= quote.Bid * Math.Abs(leg.Quantity);  // We pay when selling (less negative = better)
                }
            }
            NetBid = netBid;
            NetAsk = netAsk;
        }
        public static ComboQuote FromSecurities(List<Leg> legs, SecurityManager securities)
        {
            if (legs == null || legs.Count == 0)
                return null;
            try
            {
                var legQuotes = new Dictionary<Symbol, Quote>();
                var timestamp = DateTime.UtcNow;
                foreach (var leg in legs)
                {
                    var security = securities[leg.Symbol];
                    var quote = GetCurrentQuote(security);
                    if (quote == null)
                        return null; // Missing quote for any leg invalidates the entire combo quote
                    legQuotes[leg.Symbol] = quote;
                }
                return new ComboQuote(legs, legQuotes, timestamp);
            }
            catch
            {
                return null;
            }
        }
        private static Quote GetCurrentQuote(Security security)
        {
            try
            {
                var quoteBar = security.Cache.GetData<QuantConnect.Data.Market.QuoteBar>();
                if (quoteBar != null && quoteBar.Bid.Close > 0 && quoteBar.Ask.Close > 0)
                {
                    return new Quote(quoteBar.Bid.Close, quoteBar.Ask.Close);
                }
                var price = security.Price;
                if (price > 0)
                {
                    var estimatedSpread = price * 0.005m;
                    return new Quote(price - estimatedSpread / 2, price + estimatedSpread / 2);
                }
                return null;
            }
            catch
            {
                return null;
            }
        }
        public override string ToString()
        {
            return $"ComboQuote: NetBid={NetBid:F2}, NetAsk={NetAsk:F2}, NetMid={NetMid:F2}, NetSpread={NetSpread:F2}, Valid={IsValid}";
        }
        public bool IsStale(TimeSpan maxAge)
        {
            return DateTime.UtcNow - Timestamp > maxAge;
        }
    }
}
using System;

namespace CoreAlgo.Architecture.Core.Execution
{
    /// <summary>
    /// Fast SmartPricing strategy: 3 steps over 15 seconds (5-second intervals)
    /// 
    /// This aggressive approach is suitable for high-volume trading or when quick fills
    /// are more important than optimal pricing. Moves quickly toward market prices.
    /// 
    /// Timing: Step every 5 seconds for up to 15 seconds total
    /// Progression: Mid → 50% → 80% → Ask/Bid
    /// </summary>
    public class FastPricingStrategy : PricingStrategy
    {
        /// <summary>
        /// Fast pricing mode identifier
        /// </summary>
        public override SmartPricingMode Mode => SmartPricingMode.Fast;

        /// <summary>
        /// 3 progressive pricing steps (fewer steps for faster execution)
        /// </summary>
        protected override int StepCount => 3;

        /// <summary>
        /// 5-second intervals between steps (faster progression)
        /// </summary>
        protected override TimeSpan StepInterval => TimeSpan.FromSeconds(5);

        /// <summary>
        /// Aggressive pricing calculation for Fast mode with rapid progression
        /// </summary>
        public override decimal? CalculateNextPrice(decimal currentPrice, Quote quote, OrderDirection orderDirection, int attemptNumber)
        {
            if (quote == null)
                throw new ArgumentNullException(nameof(quote));

            if (attemptNumber > StepCount)
                return null;

            // Fast progression: aggressive moves toward market
            decimal targetPrice;
            var halfSpread = quote.Spread / 2;

            switch (attemptNumber)
            {
                case 1:
                    // Start at mid-spread (already set in initial order)
                    return null;

                case 2:
                    // Move 50% toward market price (more aggressive than Normal)
                    targetPrice = orderDirection == OrderDirection.Buy
                        ? quote.Price + (halfSpread * 0.50m)
                        : quote.Price - (halfSpread * 0.50m);
                    break;

                case 3:
                    // Move 80% toward market price
                    targetPrice = orderDirection == OrderDirection.Buy
                        ? quote.Price + (halfSpread * 0.80m)
                        : quote.Price - (halfSpread * 0.80m);
                    break;

                default:
                    // Final attempt: go to market (ask/bid)
                    targetPrice = orderDirection == OrderDirection.Buy ? quote.Ask : quote.Bid;
                    break;
            }

            // Ensure we don't exceed market boundaries
            if (orderDirection == OrderDirection.Buy)
            {
                targetPrice = Math.Min(targetPrice, quote.Ask);
            }
            else
            {
                targetPrice = Math.Max(targetPrice, quote.Bid);
            }

            // Fast mode uses smaller minimum change threshold for quicker updates
            var priceChange = Math.Abs(targetPrice - currentPrice);
            var minChange = quote.Spread * 0.03m; // Minimum 3% of spread movement

            return priceChange >= minChange ? targetPrice : null;
        }

        /// <summary>
        /// Fast mode should be more selective about when to use SmartPricing
        /// since it moves quickly toward market prices
        /// </summary>
        public override bool ShouldAttemptPricing(Quote quote, OrderDirection orderDirection)
        {
            if (!base.ShouldAttemptPricing(quote, orderDirection))
                return false;

            // Fast mode works best with reasonable spreads where quick progression makes sense
            // Minimum spread of $0.15 for options (higher than Normal)
            if (quote.Spread < 0.15m)
                return false;

            // Maximum spread of 3% of mid-price (more restrictive than Normal)
            if (quote.Spread > quote.Price * 0.03m)
                return false;

            // Don't use fast mode for very low-priced options (under $1)
            if (quote.Price < 1.0m)
                return false;

            return true;
        }
    }
}
using System;
using System.Collections.Generic;
using QuantConnect.Orders;
using QuantConnect.Data.Market;
namespace CoreAlgo.Architecture.Core.Execution
{
    public interface ISmartPricingEngine
    {
        SmartPricingMode Mode { get; }
        decimal CalculateInitialPrice(Quote quote, OrderDirection orderDirection);
        decimal? CalculateNextPrice(decimal currentPrice, Quote quote, OrderDirection orderDirection, int attemptNumber);
        TimeSpan GetPricingInterval();
        int GetMaxAttempts();
        bool ShouldAttemptPricing(Quote quote, OrderDirection orderDirection);
        decimal? CalculateInitialComboPrice(List<Leg> legs, ComboQuote comboQuote, OrderDirection orderDirection);
        decimal? CalculateNextComboPrice(decimal currentNetPrice, ComboQuote comboQuote, OrderDirection orderDirection, int attemptNumber);
        bool ShouldAttemptComboPricing(ComboQuote comboQuote, OrderDirection orderDirection);
    }
    public enum OrderDirection
    {
        Buy,
        Sell
    }
    public enum SmartPricingMode
    {
        Off,
        Normal,
        Fast,
        Patient
    }
}
using System;

namespace CoreAlgo.Architecture.Core.Execution
{
    /// <summary>
    /// Normal SmartPricing strategy: 4 steps over 40 seconds (10-second intervals)
    /// 
    /// This is the default balanced approach that provides good execution improvement
    /// without being too aggressive or too patient.
    /// 
    /// Timing: Step every 10 seconds for up to 40 seconds total
    /// Progression: Mid → 25% → 50% → 75% → Ask/Bid
    /// </summary>
    public class NormalPricingStrategy : PricingStrategy
    {
        /// <summary>
        /// Normal pricing mode identifier
        /// </summary>
        public override SmartPricingMode Mode => SmartPricingMode.Normal;

        /// <summary>
        /// 4 progressive pricing steps
        /// </summary>
        protected override int StepCount => 4;

        /// <summary>
        /// 10-second intervals between steps
        /// </summary>
        protected override TimeSpan StepInterval => TimeSpan.FromSeconds(10);

        /// <summary>
        /// Enhanced pricing calculation for Normal mode with optimized progression
        /// </summary>
        public override decimal? CalculateNextPrice(decimal currentPrice, Quote quote, OrderDirection orderDirection, int attemptNumber)
        {
            if (quote == null)
                throw new ArgumentNullException(nameof(quote));

            if (attemptNumber > StepCount)
                return null;

            // Normal progression: more conservative steps toward market
            decimal targetPrice;
            var halfSpread = quote.Spread / 2;

            switch (attemptNumber)
            {
                case 1:
                    // Start at mid-spread (already set in initial order)
                    return null;

                case 2:
                    // Move 25% toward market price
                    targetPrice = orderDirection == OrderDirection.Buy
                        ? quote.Price + (halfSpread * 0.25m)
                        : quote.Price - (halfSpread * 0.25m);
                    break;

                case 3:
                    // Move 50% toward market price
                    targetPrice = orderDirection == OrderDirection.Buy
                        ? quote.Price + (halfSpread * 0.50m)
                        : quote.Price - (halfSpread * 0.50m);
                    break;

                case 4:
                    // Move 75% toward market price
                    targetPrice = orderDirection == OrderDirection.Buy
                        ? quote.Price + (halfSpread * 0.75m)
                        : quote.Price - (halfSpread * 0.75m);
                    break;

                default:
                    // Final attempt: go to market (ask/bid)
                    targetPrice = orderDirection == OrderDirection.Buy ? quote.Ask : quote.Bid;
                    break;
            }

            // Ensure we don't exceed market boundaries
            if (orderDirection == OrderDirection.Buy)
            {
                targetPrice = Math.Min(targetPrice, quote.Ask);
            }
            else
            {
                targetPrice = Math.Max(targetPrice, quote.Bid);
            }

            // Only update if meaningful price change
            var priceChange = Math.Abs(targetPrice - currentPrice);
            var minChange = quote.Spread * 0.05m; // Minimum 5% of spread movement

            return priceChange >= minChange ? targetPrice : null;
        }

        /// <summary>
        /// Normal mode should attempt pricing for most reasonable market conditions
        /// </summary>
        public override bool ShouldAttemptPricing(Quote quote, OrderDirection orderDirection)
        {
            if (!base.ShouldAttemptPricing(quote, orderDirection))
                return false;

            // Normal mode is suitable for most options with reasonable spreads
            // Minimum spread of $0.10 for options
            if (quote.Spread < 0.10m)
                return false;

            // Maximum spread of 5% of mid-price
            if (quote.Spread > quote.Price * 0.05m)
                return false;

            return true;
        }
    }
}
using System;

namespace CoreAlgo.Architecture.Core.Execution
{
    /// <summary>
    /// Patient SmartPricing strategy: 5 steps over 100 seconds (20-second intervals)
    /// 
    /// This conservative approach is suitable for less liquid options or when getting
    /// the best possible price is more important than speed. Takes time to find optimal fills.
    /// 
    /// Timing: Step every 20 seconds for up to 100 seconds total
    /// Progression: Mid → 15% → 30% → 50% → 70% → Ask/Bid
    /// </summary>
    public class PatientPricingStrategy : PricingStrategy
    {
        /// <summary>
        /// Patient pricing mode identifier
        /// </summary>
        public override SmartPricingMode Mode => SmartPricingMode.Patient;

        /// <summary>
        /// 5 progressive pricing steps (more steps for patient execution)
        /// </summary>
        protected override int StepCount => 5;

        /// <summary>
        /// 20-second intervals between steps (slower progression)
        /// </summary>
        protected override TimeSpan StepInterval => TimeSpan.FromSeconds(20);

        /// <summary>
        /// Conservative pricing calculation for Patient mode with gradual progression
        /// </summary>
        public override decimal? CalculateNextPrice(decimal currentPrice, Quote quote, OrderDirection orderDirection, int attemptNumber)
        {
            if (quote == null)
                throw new ArgumentNullException(nameof(quote));

            if (attemptNumber > StepCount)
                return null;

            // Patient progression: gradual moves toward market
            decimal targetPrice;
            var halfSpread = quote.Spread / 2;

            switch (attemptNumber)
            {
                case 1:
                    // Start at mid-spread (already set in initial order)
                    return null;

                case 2:
                    // Move 15% toward market price (very conservative)
                    targetPrice = orderDirection == OrderDirection.Buy
                        ? quote.Price + (halfSpread * 0.15m)
                        : quote.Price - (halfSpread * 0.15m);
                    break;

                case 3:
                    // Move 30% toward market price
                    targetPrice = orderDirection == OrderDirection.Buy
                        ? quote.Price + (halfSpread * 0.30m)
                        : quote.Price - (halfSpread * 0.30m);
                    break;

                case 4:
                    // Move 50% toward market price
                    targetPrice = orderDirection == OrderDirection.Buy
                        ? quote.Price + (halfSpread * 0.50m)
                        : quote.Price - (halfSpread * 0.50m);
                    break;

                case 5:
                    // Move 70% toward market price
                    targetPrice = orderDirection == OrderDirection.Buy
                        ? quote.Price + (halfSpread * 0.70m)
                        : quote.Price - (halfSpread * 0.70m);
                    break;

                default:
                    // Final attempt: go to market (ask/bid)
                    targetPrice = orderDirection == OrderDirection.Buy ? quote.Ask : quote.Bid;
                    break;
            }

            // Ensure we don't exceed market boundaries
            if (orderDirection == OrderDirection.Buy)
            {
                targetPrice = Math.Min(targetPrice, quote.Ask);
            }
            else
            {
                targetPrice = Math.Max(targetPrice, quote.Bid);
            }

            // Patient mode uses higher minimum change threshold for more meaningful updates
            var priceChange = Math.Abs(targetPrice - currentPrice);
            var minChange = quote.Spread * 0.08m; // Minimum 8% of spread movement

            return priceChange >= minChange ? targetPrice : null;
        }

        /// <summary>
        /// Patient mode should work with wider spreads and less liquid options
        /// </summary>
        public override bool ShouldAttemptPricing(Quote quote, OrderDirection orderDirection)
        {
            if (!base.ShouldAttemptPricing(quote, orderDirection))
                return false;

            // Patient mode is designed for wider spreads where gradual progression helps
            // Minimum spread of $0.20 for options
            if (quote.Spread < 0.20m)
                return false;

            // Maximum spread of 8% of mid-price (more tolerant than other modes)
            if (quote.Spread > quote.Price * 0.08m)
                return false;

            // Patient mode works well for higher-priced options where small improvements matter
            // No minimum price restriction (unlike Fast mode)

            return true;
        }

    }
}
using System;
using System.Collections.Generic;
using QuantConnect.Orders;
namespace CoreAlgo.Architecture.Core.Execution
{
    public abstract class PricingStrategy : ISmartPricingEngine
    {
        public abstract SmartPricingMode Mode { get; }
        protected abstract int StepCount { get; }
        protected abstract TimeSpan StepInterval { get; }
        public virtual decimal CalculateInitialPrice(Quote quote, OrderDirection orderDirection)
        {
            if (quote == null)
                throw new ArgumentNullException(nameof(quote));
            return quote.Price;
        }
        public virtual decimal? CalculateNextPrice(decimal currentPrice, Quote quote, OrderDirection orderDirection, int attemptNumber)
        {
            if (quote == null)
                throw new ArgumentNullException(nameof(quote));
            if (attemptNumber > StepCount)
                return null; // No more steps available
            var progressionPct = (decimal)attemptNumber / StepCount;
            decimal targetPrice;
            if (orderDirection == OrderDirection.Buy)
            {
                targetPrice = quote.Price + (quote.Spread / 2 * progressionPct);
                targetPrice = Math.Min(targetPrice, quote.Ask);
            }
            else
            {
                targetPrice = quote.Price - (quote.Spread / 2 * progressionPct);
                targetPrice = Math.Max(targetPrice, quote.Bid);
            }
            var priceChange = Math.Abs(targetPrice - currentPrice);
            var minChange = quote.Spread * 0.1m; // Minimum 10% of spread movement
            return priceChange >= minChange ? targetPrice : null;
        }
        public virtual TimeSpan GetPricingInterval()
        {
            return StepInterval;
        }
        public virtual int GetMaxAttempts()
        {
            return StepCount;
        }
        public virtual bool ShouldAttemptPricing(Quote quote, OrderDirection orderDirection)
        {
            if (quote == null)
                return false;
            if (quote.Spread < 0.05m)
                return false;
            if (quote.Spread > quote.Price * 0.10m)
                return false;
            return true;
        }
        public virtual decimal? CalculateInitialComboPrice(List<Leg> legs, ComboQuote comboQuote, OrderDirection orderDirection)
        {
            if (legs == null || legs.Count == 0 || comboQuote == null || !comboQuote.IsValid)
                return null;
            var comboPricingEngine = new ComboPricingEngine(Mode, 5.0m); // Default max spread width
            return comboPricingEngine.CalculateInitialComboPrice(legs, comboQuote, orderDirection);
        }
        public virtual decimal? CalculateNextComboPrice(decimal currentNetPrice, ComboQuote comboQuote, OrderDirection orderDirection, int attemptNumber)
        {
            if (comboQuote == null || !comboQuote.IsValid)
                return null;
            var comboPricingEngine = new ComboPricingEngine(Mode, 5.0m); // Default max spread width
            return comboPricingEngine.CalculateNextComboPrice(currentNetPrice, comboQuote, orderDirection, attemptNumber);
        }
        public virtual bool ShouldAttemptComboPricing(ComboQuote comboQuote, OrderDirection orderDirection)
        {
            if (comboQuote == null || !comboQuote.IsValid)
                return false;
            var comboPricingEngine = new ComboPricingEngine(Mode, 5.0m); // Default max spread width
            return comboPricingEngine.ShouldAttemptComboPricing(comboQuote, orderDirection);
        }
        protected decimal CalculatePriceImprovement(Quote quote, OrderDirection orderDirection, int attemptNumber)
        {
            var aggressivePrice = orderDirection == OrderDirection.Buy ? quote.Ask : quote.Bid;
            var midPrice = quote.Price;
            var maxImprovement = Math.Abs(aggressivePrice - midPrice);
            var progressionPct = (decimal)(attemptNumber - 1) / StepCount;
            var currentImprovement = maxImprovement * (1 - progressionPct);
            return currentImprovement;
        }
    }
}
using System;
using QuantConnect.Orders;
using QuantConnect.Scheduling;
namespace CoreAlgo.Architecture.Core.Execution
{
    public class SmartOrderTracker
    {
        public OrderTicket OrderTicket { get; }
        public OrderDirection OrderDirection { get; }
        public SmartPricingMode PricingMode { get; }
        public decimal CurrentPrice { get; set; }
        public int AttemptNumber { get; set; }
        public DateTime StartTime { get; }
        public Quote InitialQuote { get; }
        public Quote CurrentQuote { get; private set; }
        public ScheduledEvent ScheduledEvent { get; set; }
        public decimal FilledQuantity { get; private set; }
        public decimal RemainingQuantity => Math.Abs(OrderTicket.Quantity) - FilledQuantity;
        public bool IsPartiallyFilled => FilledQuantity > 0 && FilledQuantity < Math.Abs(OrderTicket.Quantity);
        public PricingAttempt[] PriceHistory { get; private set; }
        public SmartOrderTracker(OrderTicket orderTicket, Quote initialQuote, OrderDirection direction, SmartPricingMode mode, decimal initialPrice)
        {
            OrderTicket = orderTicket ?? throw new ArgumentNullException(nameof(orderTicket));
            InitialQuote = initialQuote ?? throw new ArgumentNullException(nameof(initialQuote));
            OrderDirection = direction;
            PricingMode = mode;
            CurrentPrice = initialPrice;
            CurrentQuote = initialQuote;
            AttemptNumber = 1;
            StartTime = DateTime.UtcNow;
            FilledQuantity = 0;
            PriceHistory = new PricingAttempt[GetMaxAttempts(mode)];
            PriceHistory[0] = new PricingAttempt(1, CurrentPrice, initialQuote, StartTime);
        }
        public void UpdatePrice(decimal newPrice, Quote newQuote)
        {
            CurrentPrice = newPrice;
            CurrentQuote = newQuote;
            AttemptNumber++;
            if (AttemptNumber <= PriceHistory.Length)
            {
                PriceHistory[AttemptNumber - 1] = new PricingAttempt(AttemptNumber, newPrice, newQuote, DateTime.UtcNow);
            }
        }
        public void UpdatePartialFill(OrderEvent orderEvent)
        {
            if (orderEvent.Status == OrderStatus.PartiallyFilled)
            {
                FilledQuantity += Math.Abs(orderEvent.FillQuantity);
            }
        }
        public SmartPricingMetrics GetMetrics()
        {
            var elapsed = DateTime.UtcNow - StartTime;
            var initialSpread = InitialQuote.Spread;
            var currentSpread = CurrentQuote?.Spread ?? initialSpread;
            var aggressivePrice = OrderDirection == OrderDirection.Buy ? InitialQuote.Ask : InitialQuote.Bid;
            var priceImprovement = OrderDirection == OrderDirection.Buy ?
                aggressivePrice - CurrentPrice : CurrentPrice - aggressivePrice;
            return new SmartPricingMetrics
            {
                OrderId = OrderTicket.OrderId,
                Symbol = OrderTicket.Symbol.Value,
                Direction = OrderDirection,
                Mode = PricingMode,
                AttemptNumber = AttemptNumber,
                TotalAttempts = GetMaxAttempts(PricingMode),
                ElapsedTime = elapsed,
                InitialPrice = PriceHistory[0].Price,
                CurrentPrice = CurrentPrice,
                PriceImprovement = priceImprovement,
                InitialSpread = initialSpread,
                CurrentSpread = currentSpread,
                FilledQuantity = FilledQuantity,
                RemainingQuantity = RemainingQuantity,
                IsPartiallyFilled = IsPartiallyFilled,
                IsCompleted = OrderTicket.Status == OrderStatus.Filled || OrderTicket.Status == OrderStatus.Canceled
            };
        }
        private static decimal GetLimitPrice(OrderTicket orderTicket)
        {
            return 0; // Will be overridden by actual limit price from the order
        }
        private static int GetMaxAttempts(SmartPricingMode mode)
        {
            return mode switch
            {
                SmartPricingMode.Fast => 3,
                SmartPricingMode.Normal => 4,
                SmartPricingMode.Patient => 5,
                _ => 1
            };
        }
    }
    public class PricingAttempt
    {
        public int AttemptNumber { get; }
        public decimal Price { get; }
        public Quote MarketQuote { get; }
        public DateTime Timestamp { get; }
        public PricingAttempt(int attemptNumber, decimal price, Quote marketQuote, DateTime timestamp)
        {
            AttemptNumber = attemptNumber;
            Price = price;
            MarketQuote = marketQuote;
            Timestamp = timestamp;
        }
    }
    public class SmartPricingMetrics
    {
        public int OrderId { get; set; }
        public string Symbol { get; set; }
        public OrderDirection Direction { get; set; }
        public SmartPricingMode Mode { get; set; }
        public int AttemptNumber { get; set; }
        public int TotalAttempts { get; set; }
        public TimeSpan ElapsedTime { get; set; }
        public decimal InitialPrice { get; set; }
        public decimal CurrentPrice { get; set; }
        public decimal PriceImprovement { get; set; }
        public decimal InitialSpread { get; set; }
        public decimal CurrentSpread { get; set; }
        public decimal FilledQuantity { get; set; }
        public decimal RemainingQuantity { get; set; }
        public bool IsPartiallyFilled { get; set; }
        public bool IsCompleted { get; set; }
    }
}
using System;

namespace CoreAlgo.Architecture.Core.Execution
{
    /// <summary>
    /// Factory for creating SmartPricing engines based on configuration
    /// </summary>
    public static class SmartPricingEngineFactory
    {
        /// <summary>
        /// Creates a SmartPricing engine for the specified mode
        /// </summary>
        /// <param name="mode">The pricing mode to use</param>
        /// <returns>Configured pricing engine instance</returns>
        public static ISmartPricingEngine Create(SmartPricingMode mode)
        {
            return mode switch
            {
                SmartPricingMode.Fast => new FastPricingStrategy(),
                SmartPricingMode.Normal => new NormalPricingStrategy(),
                SmartPricingMode.Patient => new PatientPricingStrategy(),
                SmartPricingMode.Off => throw new InvalidOperationException("Cannot create engine for SmartPricingMode.Off"),
                _ => throw new ArgumentException($"Unknown SmartPricing mode: {mode}", nameof(mode))
            };
        }

        /// <summary>
        /// Creates a SmartPricing engine from a string configuration value
        /// </summary>
        /// <param name="modeString">String representation of the mode ("Normal", "Fast", "Patient", "Off")</param>
        /// <returns>Configured pricing engine instance</returns>
        public static ISmartPricingEngine Create(string modeString)
        {
            if (string.IsNullOrWhiteSpace(modeString))
                return Create(SmartPricingMode.Normal); // Default mode

            var mode = ParseMode(modeString);
            return Create(mode);
        }

        /// <summary>
        /// Parses a string into a SmartPricingMode enum value
        /// </summary>
        /// <param name="modeString">String to parse</param>
        /// <returns>Parsed SmartPricingMode</returns>
        public static SmartPricingMode ParseMode(string modeString)
        {
            if (string.IsNullOrWhiteSpace(modeString))
                return SmartPricingMode.Normal;

            return modeString.ToUpperInvariant() switch
            {
                "FAST" => SmartPricingMode.Fast,
                "NORMAL" => SmartPricingMode.Normal,
                "PATIENT" => SmartPricingMode.Patient,
                "OFF" => SmartPricingMode.Off,
                "DISABLED" => SmartPricingMode.Off,
                "FALSE" => SmartPricingMode.Off,
                _ => SmartPricingMode.Normal // Default fallback
            };
        }

        /// <summary>
        /// Gets a descriptive summary of a pricing mode
        /// </summary>
        /// <param name="mode">The pricing mode</param>
        /// <returns>Human-readable description</returns>
        public static string GetModeDescription(SmartPricingMode mode)
        {
            return mode switch
            {
                SmartPricingMode.Off => "Disabled - uses standard execution",
                SmartPricingMode.Fast => "Fast - 3 steps over 15 seconds (aggressive)",
                SmartPricingMode.Normal => "Normal - 4 steps over 40 seconds (balanced)",
                SmartPricingMode.Patient => "Patient - 5 steps over 100 seconds (conservative)",
                _ => "Unknown mode"
            };
        }

        /// <summary>
        /// Validates if a mode string is supported
        /// </summary>
        /// <param name="modeString">String to validate</param>
        /// <returns>True if the mode is supported</returns>
        public static bool IsValidMode(string modeString)
        {
            if (string.IsNullOrWhiteSpace(modeString))
                return true; // Default is valid

            var upper = modeString.ToUpperInvariant();
            return upper == "FAST" || upper == "NORMAL" || upper == "PATIENT" || 
                   upper == "OFF" || upper == "DISABLED" || upper == "FALSE";
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Algorithm.Framework.Execution;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Orders;
using QuantConnect.Scheduling;
using QuantConnect.Securities;
using CoreAlgo.Architecture.Core.Interfaces;
namespace CoreAlgo.Architecture.Core.Execution
{
    public class SmartPricingExecutionModel : ExecutionModel
    {
        private readonly IAlgorithmContext _context;
        private readonly ISmartPricingEngine _pricingEngine;
        private readonly Dictionary<int, SmartOrderTracker> _activeOrders;
        private readonly HashSet<ScheduledEvent> _scheduledEvents;
        public SmartPricingExecutionModel(IAlgorithmContext context, ISmartPricingEngine pricingEngine)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
            _pricingEngine = pricingEngine ?? throw new ArgumentNullException(nameof(pricingEngine));
            _activeOrders = new Dictionary<int, SmartOrderTracker>();
            _scheduledEvents = new HashSet<ScheduledEvent>();
        }
        public override void Execute(QCAlgorithm algorithm, IPortfolioTarget[] targets)
        {
            try
            {
                if (_pricingEngine.Mode == SmartPricingMode.Off)
                {
                    base.Execute(algorithm, targets);
                    return;
                }
                _context.Logger.Debug($"SmartPricing: Processing {targets.Length} portfolio targets in {_pricingEngine.Mode} mode");
                foreach (var target in targets)
                {
                    var security = algorithm.Securities[target.Symbol];
                    var quantity = target.Quantity - security.Holdings.Quantity;
                    if (quantity == 0)
                        continue;
                    var quote = GetCurrentQuote(security);
                    if (quote == null || !_pricingEngine.ShouldAttemptPricing(quote, quantity > 0 ? OrderDirection.Buy : OrderDirection.Sell))
                    {
                        algorithm.MarketOrder(target.Symbol, quantity, tag: "SmartPricing:Fallback", asynchronous: true);
                        continue;
                    }
                    var orderDirection = quantity > 0 ? OrderDirection.Buy : OrderDirection.Sell;
                    var initialPrice = _pricingEngine.CalculateInitialPrice(quote, orderDirection);
                    var orderTicket = algorithm.LimitOrder(target.Symbol, quantity, initialPrice,
                        tag: $"SmartPricing:{_pricingEngine.Mode}:Initial", asynchronous: true);
                    if (orderTicket != null)
                    {
                        var tracker = new SmartOrderTracker(orderTicket, quote, orderDirection, _pricingEngine.Mode, initialPrice);
                        _activeOrders[orderTicket.OrderId] = tracker;
                        ScheduleNextPricingUpdate(algorithm, tracker);
                        _context.Logger.Debug($"SmartPricing: Started {orderDirection} order {orderTicket.OrderId} at ${initialPrice:F2} " +
                                                        $"(Mid: ${quote.Price:F2}, Spread: ${quote.Ask - quote.Bid:F2})");
                    }
                }
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"SmartPricing execution error: {ex.Message}");
                base.Execute(algorithm, targets);
            }
        }
        public override void OnOrderEvent(QCAlgorithm algorithm, OrderEvent orderEvent)
        {
            if (!_activeOrders.TryGetValue(orderEvent.OrderId, out var tracker))
                return;
            switch (orderEvent.Status)
            {
                case OrderStatus.Filled:
                    _context.Logger.Debug($"SmartPricing: Order {orderEvent.OrderId} filled at ${orderEvent.FillPrice:F2} " +
                                                    $"after {tracker.AttemptNumber} attempts");
                    CleanupOrder(tracker);
                    break;
                case OrderStatus.PartiallyFilled:
                    _context.Logger.Debug($"SmartPricing: Order {orderEvent.OrderId} partially filled " +
                                                    $"({orderEvent.FillQuantity}/{tracker.OrderTicket.Quantity})");
                    tracker.UpdatePartialFill(orderEvent);
                    break;
                case OrderStatus.Canceled:
                case OrderStatus.Invalid:
                    _context.Logger.Warning($"SmartPricing: Order {orderEvent.OrderId} {orderEvent.Status}");
                    CleanupOrder(tracker);
                    break;
            }
        }
        private void ScheduleNextPricingUpdate(QCAlgorithm algorithm, SmartOrderTracker tracker)
        {
            var interval = _pricingEngine.GetPricingInterval();
            var updateTime = algorithm.Time.Add(interval);
            var scheduledEvent = algorithm.Schedule.On(algorithm.DateRules.On(updateTime.Date),
                algorithm.TimeRules.At(updateTime.Hour, updateTime.Minute, updateTime.Second),
                () => UpdateOrderPrice(algorithm, tracker));
            _scheduledEvents.Add(scheduledEvent);
            tracker.ScheduledEvent = scheduledEvent;
        }
        private void UpdateOrderPrice(QCAlgorithm algorithm, SmartOrderTracker tracker)
        {
            try
            {
                if (tracker.ScheduledEvent != null)
                {
                    _scheduledEvents.Remove(tracker.ScheduledEvent);
                    tracker.ScheduledEvent = null;
                }
                if (!_activeOrders.ContainsKey(tracker.OrderTicket.OrderId) ||
                    tracker.OrderTicket.Status == OrderStatus.Filled)
                {
                    return;
                }
                var security = algorithm.Securities[tracker.OrderTicket.Symbol];
                var currentQuote = GetCurrentQuote(security);
                if (currentQuote == null)
                {
                    _context.Logger.Warning($"SmartPricing: No quote available for {tracker.OrderTicket.Symbol}, canceling order {tracker.OrderTicket.OrderId}");
                    tracker.OrderTicket.Cancel("No quote available");
                    CleanupOrder(tracker);
                    return;
                }
                var nextPrice = _pricingEngine.CalculateNextPrice(
                    tracker.CurrentPrice, currentQuote, tracker.OrderDirection, tracker.AttemptNumber + 1);
                if (nextPrice.HasValue)
                {
                    var updateResult = tracker.OrderTicket.Update(new UpdateOrderFields { LimitPrice = nextPrice.Value });
                    if (updateResult.IsSuccess)
                    {
                        tracker.UpdatePrice(nextPrice.Value, currentQuote);
                        _context.Logger.Debug($"SmartPricing: Updated order {tracker.OrderTicket.OrderId} " +
                                                        $"to ${nextPrice.Value:F2} (attempt {tracker.AttemptNumber})");
                        if (tracker.AttemptNumber < _pricingEngine.GetMaxAttempts())
                        {
                            ScheduleNextPricingUpdate(algorithm, tracker);
                        }
                        else
                        {
                            _context.Logger.Debug($"SmartPricing: Order {tracker.OrderTicket.OrderId} reached max attempts, keeping final price");
                        }
                    }
                    else
                    {
                        _context.Logger.Warning($"SmartPricing: Failed to update order {tracker.OrderTicket.OrderId}: {updateResult.ErrorMessage}");
                    }
                }
                else
                {
                    _context.Logger.Debug($"SmartPricing: No more price improvements for order {tracker.OrderTicket.OrderId}");
                }
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"SmartPricing: Error updating order {tracker.OrderTicket.OrderId}: {ex.Message}");
            }
        }
        private Quote GetCurrentQuote(Security security)
        {
            try
            {
                var quoteBar = security.Cache.GetData<QuoteBar>();
                if (quoteBar != null && quoteBar.Bid.Close > 0 && quoteBar.Ask.Close > 0)
                {
                    return new Quote(quoteBar.Bid.Close, quoteBar.Ask.Close);
                }
                var price = security.Price;
                if (price > 0)
                {
                    var estimatedSpread = price * 0.005m;
                    return new Quote(price - estimatedSpread / 2, price + estimatedSpread / 2);
                }
                return null;
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"SmartPricing: Error getting quote for {security.Symbol}: {ex.Message}");
                return null;
            }
        }
        private void CleanupOrder(SmartOrderTracker tracker)
        {
            _activeOrders.Remove(tracker.OrderTicket.OrderId);
            if (tracker.ScheduledEvent != null)
            {
                _scheduledEvents.Remove(tracker.ScheduledEvent);
            }
        }
    }
    public class Quote
    {
        public decimal Bid { get; }
        public decimal Ask { get; }
        public decimal Price => (Bid + Ask) / 2;
        public decimal Spread => Ask - Bid;
        public Quote(decimal bid, decimal ask)
        {
            Bid = bid;
            Ask = ask;
        }
    }
}
using System;
using System.Collections.Generic;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace CoreAlgo.Architecture.Core.Helpers
{
    public static class AssignmentHandler
    {
        private static readonly HashSet<string> _processedAssignments = new HashSet<string>();
        public static void HandleAssignment(QCAlgorithm algorithm, OrderEvent assignmentEvent)
        {
            try
            {
                algorithm.Log($"ASSIGNMENT DETECTED: {assignmentEvent.Symbol} at {algorithm.Time}");
                var assignmentKey = $"{assignmentEvent.Symbol}_{assignmentEvent.OrderId}_{algorithm.Time}";
                if (_processedAssignments.Contains(assignmentKey))
                {
                    algorithm.Log($"ASSIGNMENT ALREADY PROCESSED: {assignmentEvent.Symbol} (skipping duplicate)");
                    return;
                }
                _processedAssignments.Add(assignmentKey);
                var assignedSymbol = assignmentEvent.Symbol;
                if (IsCashSettledIndexOption(assignedSymbol))
                {
                    algorithm.Log($"INDEX OPTION ASSIGNMENT: {assignedSymbol} (Cash-settled - no equity position created)");
                    HandleCashSettledAssignment(algorithm, assignmentEvent);
                    return;
                }
                var underlyingSymbol = GetUnderlyingSymbol(assignedSymbol);
                if (underlyingSymbol == null)
                {
                    algorithm.Error($"Could not determine underlying symbol for assignment: {assignedSymbol}");
                    return;
                }
                if (!algorithm.Securities.ContainsKey(assignedSymbol))
                {
                    algorithm.Error($"Option contract not in securities: {assignedSymbol}");
                    return;
                }
                var optionSec = algorithm.Securities[assignedSymbol];
                var contractMultiplier = optionSec.SymbolProperties.ContractMultiplier;
                var contracts = Math.Abs(assignmentEvent.FillQuantity);
                int sharesToFlatten;
                if (algorithm.Portfolio.ContainsKey(underlyingSymbol) &&
                    algorithm.Portfolio[underlyingSymbol].Quantity != 0)
                {
                    sharesToFlatten = -(int)algorithm.Portfolio[underlyingSymbol].Quantity;
                    algorithm.Log($"Using portfolio holdings: {underlyingSymbol.Value} has {algorithm.Portfolio[underlyingSymbol].Quantity} shares");
                }
                else
                {
                    var optionRight = assignedSymbol.ID.OptionRight;
                    if (optionRight == OptionRight.Call)
                    {
                        sharesToFlatten = (int)(contracts * contractMultiplier);
                    }
                    else // OptionRight.Put
                    {
                        sharesToFlatten = -(int)(contracts * contractMultiplier);
                    }
                    algorithm.Log($"Using computed position for {optionRight}: {contracts} contracts → {sharesToFlatten} shares");
                }
                algorithm.Log($"Assignment Details: {underlyingSymbol.Value} - Contracts: {contracts}, " +
                             $"Multiplier: {contractMultiplier}, Shares to flatten: {sharesToFlatten}");
                var orderTicket = algorithm.MarketOrder(underlyingSymbol, sharesToFlatten, tag: "Assignment Auto-Liquidation", asynchronous: true);
                algorithm.Log($"LIQUIDATING ASSIGNMENT: {underlyingSymbol.Value} - " +
                             $"Quantity: {sharesToFlatten}, Order ID: {orderTicket.OrderId}");
                LogAssignmentImpact(algorithm, assignmentEvent, underlyingSymbol, contractMultiplier);
            }
            catch (Exception ex)
            {
                algorithm.Error($"Error handling assignment: {ex.Message}");
                algorithm.Error($"Stack trace: {ex.StackTrace}");
            }
        }
        private static bool IsCashSettledIndexOption(Symbol symbol)
        {
            if (symbol?.SecurityType == SecurityType.IndexOption)
            {
                return true;
            }
            var symbolStr = symbol?.Value?.ToUpperInvariant() ?? "";
            if (symbolStr.StartsWith("SPXW") || symbolStr.StartsWith("SPX"))
                return true;
            if (symbolStr.StartsWith("VIX") || symbolStr.StartsWith("NDX") || symbolStr.StartsWith("RUT"))
                return true;
            return false;
        }
        private static Symbol GetUnderlyingSymbol(Symbol optionSymbol)
        {
            if (optionSymbol.SecurityType == SecurityType.Option || optionSymbol.SecurityType == SecurityType.IndexOption)
            {
                return optionSymbol.Underlying;
            }
            if (optionSymbol.SecurityType == SecurityType.Equity || optionSymbol.SecurityType == SecurityType.Index)
            {
                return optionSymbol;
            }
            return null;
        }
        private static void LogAssignmentImpact(QCAlgorithm algorithm, OrderEvent assignmentEvent,
            Symbol underlyingSymbol, decimal contractMultiplier)
        {
            try
            {
                var holding = algorithm.Portfolio[underlyingSymbol];
                decimal assignmentValue;
                if (holding.Invested && holding.HoldingsValue != 0)
                {
                    assignmentValue = Math.Abs(holding.HoldingsValue);
                }
                else
                {
                    var contracts = Math.Abs(assignmentEvent.FillQuantity);
                    var underlyingPrice = algorithm.Securities.ContainsKey(underlyingSymbol)
                        ? algorithm.Securities[underlyingSymbol].Price
                        : assignmentEvent.FillPrice;
                    assignmentValue = contracts * contractMultiplier * underlyingPrice;
                }
                var portfolioImpact = algorithm.Portfolio.TotalPortfolioValue > 0
                    ? assignmentValue / algorithm.Portfolio.TotalPortfolioValue * 100
                    : 0;
                algorithm.Log($"ASSIGNMENT IMPACT: {underlyingSymbol.Value} - " +
                             $"Value: ${assignmentValue:F0}, Portfolio Impact: {portfolioImpact:F1}%, " +
                             $"Free Margin: ${algorithm.Portfolio.MarginRemaining:F0}");
            }
            catch (Exception ex)
            {
                algorithm.Debug($"Error logging assignment impact: {ex.Message}");
            }
        }
        private static void HandleCashSettledAssignment(QCAlgorithm algorithm, OrderEvent assignmentEvent)
        {
            try
            {
                var optionSymbol = assignmentEvent.Symbol;
                var underlyingSymbol = GetUnderlyingSymbol(optionSymbol);
                if (underlyingSymbol == null)
                {
                    algorithm.Error($"Could not determine underlying symbol for cash-settled assignment: {optionSymbol}");
                    return;
                }
                var underlyingPrice = algorithm.Securities.ContainsKey(underlyingSymbol)
                    ? algorithm.Securities[underlyingSymbol].Price
                    : 0m;
                algorithm.Log($"CASH SETTLEMENT: {optionSymbol} based on {underlyingSymbol.Value} price: ${underlyingPrice:F2}");
                algorithm.Log($"Settlement Value: ${assignmentEvent.FillPrice:F2} per contract");
                algorithm.Log($"Total Settlement: ${assignmentEvent.FillPrice * Math.Abs(assignmentEvent.FillQuantity):F2}");
                LogCashSettledAssignmentImpact(algorithm, assignmentEvent, underlyingSymbol, underlyingPrice);
            }
            catch (Exception ex)
            {
                algorithm.Error($"Error handling cash-settled assignment: {ex.Message}");
            }
        }
        private static void LogCashSettledAssignmentImpact(QCAlgorithm algorithm, OrderEvent assignmentEvent,
            Symbol underlyingSymbol, decimal underlyingPrice)
        {
            try
            {
                var settlementValue = Math.Abs(assignmentEvent.FillPrice * assignmentEvent.FillQuantity);
                var portfolioImpact = algorithm.Portfolio.TotalPortfolioValue > 0
                    ? settlementValue / algorithm.Portfolio.TotalPortfolioValue * 100
                    : 0;
                algorithm.Log($"ASSIGNMENT IMPACT: {underlyingSymbol.Value} - " +
                             $"Value: ${settlementValue:F0}, Portfolio Impact: {portfolioImpact:F1}%, " +
                             $"Free Margin: ${algorithm.Portfolio.MarginRemaining:F0}");
            }
            catch (Exception ex)
            {
                algorithm.Debug($"Error logging cash-settled assignment impact: {ex.Message}");
            }
        }
        public static void MonitorUnexpectedEquityPositions(QCAlgorithm algorithm)
        {
            try
            {
                var unexpectedEquities = 0;
                foreach (var holding in algorithm.Portfolio.Values)
                {
                    if (holding.Invested &&
                        holding.Symbol.SecurityType == SecurityType.Equity &&
                        holding.Quantity != 0)
                    {
                        unexpectedEquities++;
                        algorithm.Log($"WARNING: UNEXPECTED EQUITY POSITION: {holding.Symbol.Value} - " +
                                     $"Qty: {holding.Quantity}, Value: ${holding.HoldingsValue:F0}");
                    }
                }
                if (unexpectedEquities > 0)
                {
                    algorithm.Log($"ALERT: Found {unexpectedEquities} unexpected equity positions - " +
                                 $"Check assignment handling logic");
                }
            }
            catch (Exception ex)
            {
                algorithm.Debug($"Error monitoring equity positions: {ex.Message}");
            }
        }
    }
}
using System;
using QuantConnect;
using QuantConnect.Indicators;

namespace CoreAlgo.Architecture.Core.Helpers
{
    /// <summary>
    /// BX-Trender indicator for consolidated weekly or monthly bars.
    /// Formula: RSI(EMA(close,5) - EMA(close,20), 5) - 50
    /// Colors: Lime (positive+rising), DarkGreen (positive+falling),
    ///         LightRed (negative+rising), DarkRed (negative+falling)
    /// </summary>
    public class BXTrender
    {
        private readonly ExponentialMovingAverage _emaShort;
        private readonly ExponentialMovingAverage _emaLong;
        private readonly RelativeStrengthIndex _rsiOfDiff;

        private decimal _prevValue = decimal.MinValue;
        private string _prevColorState = "NA";

        public string ColorState { get; private set; } = "NA";
        public bool IsBullish => ColorState == "Lime" || ColorState == "LightRed";
        public bool JustTurnedDarkGreen { get; private set; }
        public decimal Value { get; private set; }
        public bool IsReady => _rsiOfDiff.IsReady;

        public BXTrender(int emaShort = 5, int emaLong = 20, int rsiPeriod = 5)
        {
            _emaShort = new ExponentialMovingAverage(emaShort);
            _emaLong = new ExponentialMovingAverage(emaLong);
            _rsiOfDiff = new RelativeStrengthIndex(rsiPeriod, MovingAverageType.Wilders);
        }

        /// <summary>
        /// Feed a consolidated bar's close price. Call once per weekly/monthly bar.
        /// </summary>
        public void Update(DateTime time, decimal close)
        {
            // Update EMAs with the close price
            _emaShort.Update(time, close);
            _emaLong.Update(time, close);

            if (!_emaShort.IsReady || !_emaLong.IsReady)
                return;

            // Compute EMA diff and feed to RSI
            var diff = _emaShort.Current.Value - _emaLong.Current.Value;
            _rsiOfDiff.Update(time, diff);

            if (!_rsiOfDiff.IsReady)
                return;

            // shortTermXtrender = RSI - 50
            Value = _rsiOfDiff.Current.Value - 50m;

            // Determine color state
            _prevColorState = ColorState;
            var isPositive = Value > 0;
            var isRising = _prevValue != decimal.MinValue && Value > _prevValue;

            if (isPositive && isRising)
                ColorState = "Lime";
            else if (isPositive && !isRising)
                ColorState = "DarkGreen";
            else if (!isPositive && isRising)
                ColorState = "LightRed";
            else
                ColorState = "DarkRed";

            // Transition detection: just turned DarkGreen this bar
            // Guard: _prevValue != MinValue ensures we don't fire on the very first ready bar
            JustTurnedDarkGreen = _prevValue != decimal.MinValue
                && ColorState == "DarkGreen"
                && _prevColorState != "DarkGreen";

            _prevValue = Value;
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
namespace CoreAlgo.Architecture.Core.Helpers
{
    public class FairValueBand
    {
        private readonly int _period;
        private readonly bool _computePivotBands;
        private readonly int _pivotLookback;
        private readonly Queue<decimal> _ohlc4Window;
        private readonly List<decimal> _highRatios = new List<decimal>();
        private readonly List<decimal> _lowRatios = new List<decimal>();
        private const int MaxDeviationSamples = 1000;
        private readonly Queue<decimal> _ohlcSpreadWindow;
        private readonly List<decimal> _pivotUpRatios = new List<decimal>();
        private readonly List<decimal> _pivotDownRatios = new List<decimal>();
        private const int MaxPivotSamples = 2000;
        private int _updateCount;
        public decimal FairPrice { get; private set; }
        public decimal UpperBand { get; private set; }
        public decimal LowerBand { get; private set; }
        public int DirSwitch { get; private set; }
        public decimal PivotBandUp { get; private set; }
        public decimal PivotBandDown { get; private set; }
        public bool IsReady => _updateCount >= _period && _highRatios.Count > 0;
        public FairValueBand(int period, bool computePivotBands = false, int pivotLookback = 5)
        {
            _period = period;
            _computePivotBands = computePivotBands;
            _pivotLookback = pivotLookback;
            _ohlc4Window = new Queue<decimal>(period + 1);
            if (_computePivotBands)
            {
                _ohlcSpreadWindow = new Queue<decimal>(2 * pivotLookback + 2);
            }
        }
        public void Update(DateTime time, decimal open, decimal high, decimal low, decimal close)
        {
            _updateCount++;
            var ohlc4 = (open + high + low + close) / 4m;
            _ohlc4Window.Enqueue(ohlc4);
            if (_ohlc4Window.Count > _period)
                _ohlc4Window.Dequeue();
            if (_ohlc4Window.Count < _period)
                return;
            FairPrice = _ohlc4Window.Sum() / _period;
            if (low < FairPrice && high > FairPrice)
            {
                if (_highRatios.Count >= MaxDeviationSamples)
                    _highRatios.RemoveAt(0);
                _highRatios.Add(high / FairPrice);
                if (_lowRatios.Count >= MaxDeviationSamples)
                    _lowRatios.RemoveAt(0);
                _lowRatios.Add(low / FairPrice);
            }
            if (_highRatios.Count > 0 && _lowRatios.Count > 0)
            {
                UpperBand = FairPrice * Median(_highRatios);
                LowerBand = FairPrice * Median(_lowRatios);
            }
            if (high < LowerBand)
                DirSwitch = -1;  // Red — price below lower band
            else if (low > UpperBand)
                DirSwitch = 1;   // Green — price above upper band
            if (_computePivotBands && FairPrice > 0)
            {
                var ohlcSpread = ohlc4 / FairPrice;
                _ohlcSpreadWindow.Enqueue(ohlcSpread);
                var windowSize = 2 * _pivotLookback + 1;
                if (_ohlcSpreadWindow.Count > windowSize)
                    _ohlcSpreadWindow.Dequeue();
                if (_ohlcSpreadWindow.Count == windowSize)
                {
                    var values = _ohlcSpreadWindow.ToArray();
                    var centerIdx = _pivotLookback;
                    var centerValue = values[centerIdx];
                    decimal? pivotHigh = null;
                    bool isPivotHigh = true;
                    for (int i = 0; i < values.Length; i++)
                    {
                        if (i != centerIdx && values[i] >= centerValue)
                        {
                            isPivotHigh = false;
                            break;
                        }
                    }
                    if (isPivotHigh) pivotHigh = centerValue;
                    decimal? pivotLow = null;
                    bool isPivotLow = true;
                    for (int i = 0; i < values.Length; i++)
                    {
                        if (i != centerIdx && values[i] <= centerValue)
                        {
                            isPivotLow = false;
                            break;
                        }
                    }
                    if (isPivotLow) pivotLow = centerValue;
                    if (UpperBand > 0 && low > UpperBand)
                    {
                        if (pivotHigh.HasValue)
                        {
                            if (_pivotUpRatios.Count >= MaxPivotSamples)
                                _pivotUpRatios.RemoveAt(0);
                            _pivotUpRatios.Add(pivotHigh.Value);
                        }
                    }
                    else if (LowerBand > 0 && high < LowerBand)
                    {
                        if (pivotLow.HasValue)
                        {
                            if (_pivotDownRatios.Count >= MaxPivotSamples)
                                _pivotDownRatios.RemoveAt(0);
                            _pivotDownRatios.Add(pivotLow.Value);
                        }
                    }
                }
                if (_pivotUpRatios.Count > 0)
                    PivotBandUp = FairPrice * Median(_pivotUpRatios);
                if (_pivotDownRatios.Count > 0)
                    PivotBandDown = FairPrice * Median(_pivotDownRatios);
            }
        }
        private static decimal Median(List<decimal> values)
        {
            var sorted = values.OrderBy(v => v).ToArray();
            int mid = sorted.Length / 2;
            return sorted.Length % 2 == 0
                ? (sorted[mid - 1] + sorted[mid]) / 2m
                : sorted[mid];
        }
    }
}
using System;

namespace CoreAlgo.Architecture.Core.Helpers
{
	/// <summary>
	/// Shared time-parsing utilities used by multiple strategy templates
	/// </summary>
	public static class TimeHelper
	{
		/// <summary>
		/// Parse an "HH:mm" string and combine with a date to produce a DateTime
		/// </summary>
		public static bool TryParseTime(string hhmm, DateTime date, out DateTime resolved)
		{
			resolved = default;
			if (string.IsNullOrWhiteSpace(hhmm)) return false;
			var parts = hhmm.Split(':');
			if (parts.Length < 2) return false;
			if (!int.TryParse(parts[0], out var h)) return false;
			if (!int.TryParse(parts[1], out var m)) return false;
			if (h < 0 || h > 23 || m < 0 || m > 59) return false;
			resolved = date.AddHours(h).AddMinutes(m);
			return true;
		}
	}
}
using System;
using System.Linq;
using System.Text;
namespace CoreAlgo.Architecture.Core.Helpers
{
    public class TradeTag
    {
        public string Strategy { get; set; } = string.Empty;
        public string Type { get; set; } = string.Empty;
        public string Details { get; set; } = string.Empty;
        public string OptionalInfo { get; set; } = string.Empty;
        public string LegInfo { get; set; } = string.Empty;
        public static string Build(string strategy, string type, string details = "", string optionalInfo = "", string legInfo = "")
        {
            var tag = new TradeTag
            {
                Strategy = strategy ?? string.Empty,
                Type = type ?? string.Empty,
                Details = details ?? string.Empty,
                OptionalInfo = optionalInfo ?? string.Empty,
                LegInfo = legInfo ?? string.Empty
            };
            return tag.ToString();
        }
        public static TradeTag Parse(string tag)
        {
            if (string.IsNullOrEmpty(tag))
            {
                return new TradeTag();
            }
            var parts = tag.Split('_');
            if (parts.Length >= 2)
            {
                var tagObj = new TradeTag
                {
                    Strategy = parts[0],
                    Type = parts[1]
                };
                if (parts.Length >= 3)
                {
                    tagObj.Details = parts[2];
                }
                if (parts.Length >= 4)
                {
                    tagObj.OptionalInfo = parts[3];
                }
                if (parts.Length == 2 && parts[1].Contains(" "))
                {
                    var typeAndDetails = parts[1].Split(new[] { ' ' }, 2);
                    tagObj.Type = typeAndDetails[0];
                    if (typeAndDetails.Length > 1)
                    {
                        tagObj.Details = typeAndDetails[1];
                    }
                }
                else if (parts.Length == 1 && tag.Contains(" "))
                {
                    var allParts = tag.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    if (allParts.Length >= 1)
                    {
                        tagObj.Strategy = allParts[0];
                    }
                    if (allParts.Length >= 2)
                    {
                        tagObj.Type = allParts[1];
                    }
                    if (allParts.Length >= 3)
                    {
                        tagObj.Details = string.Join(" ", allParts.Skip(2));
                    }
                }
                return tagObj;
            }
            return new TradeTag
            {
                Details = tag
            };
        }
        public bool Matches(string strategy, string type)
        {
            return string.Equals(Strategy, strategy, StringComparison.OrdinalIgnoreCase) &&
                   string.Equals(Type, type, StringComparison.OrdinalIgnoreCase);
        }
        public bool IsEntry()
        {
            return string.Equals(Type, "Entry", StringComparison.OrdinalIgnoreCase);
        }
        public bool IsExit()
        {
            return string.Equals(Type, "Exit", StringComparison.OrdinalIgnoreCase) ||
                   string.Equals(Type, "Close", StringComparison.OrdinalIgnoreCase) ||
                   Type.Contains("Close", StringComparison.OrdinalIgnoreCase);
        }
        public bool IsStopLoss()
        {
            return string.Equals(Type, "StopLoss", StringComparison.OrdinalIgnoreCase) ||
                   Type.Contains("SL", StringComparison.OrdinalIgnoreCase);
        }
        public bool IsTakeProfit()
        {
            return string.Equals(Type, "TakeProfit", StringComparison.OrdinalIgnoreCase) ||
                   Type.Contains("TP", StringComparison.OrdinalIgnoreCase);
        }
        public bool IsScaleIn()
        {
            return string.Equals(Type, "ScaleIn", StringComparison.OrdinalIgnoreCase);
        }
        public bool IsEod()
        {
            return !string.IsNullOrEmpty(OptionalInfo) &&
                   OptionalInfo.Contains("EOD", StringComparison.OrdinalIgnoreCase);
        }
        public bool TryGetParentEid(out int parentEid)
        {
            parentEid = 0;
            if (string.IsNullOrEmpty(Details))
            {
                return false;
            }
            if (Details.StartsWith("E", StringComparison.OrdinalIgnoreCase) && Details.Length > 1)
            {
                return int.TryParse(Details.Substring(1), out parentEid);
            }
            return false;
        }
        public override string ToString()
        {
            var sb = new StringBuilder();
            if (!string.IsNullOrEmpty(Strategy))
            {
                sb.Append(Strategy);
            }
            if (!string.IsNullOrEmpty(Type))
            {
                if (sb.Length > 0) sb.Append('_');
                sb.Append(Type);
            }
            if (!string.IsNullOrEmpty(Details))
            {
                if (sb.Length > 0) sb.Append('_');
                sb.Append(Details);
            }
            if (!string.IsNullOrEmpty(OptionalInfo))
            {
                if (sb.Length > 0) sb.Append('_');
                sb.Append(OptionalInfo);
            }
            if (!string.IsNullOrEmpty(LegInfo))
            {
                if (sb.Length > 0) sb.Append('_');
                sb.Append(LegInfo);
            }
            return sb.Length > 0 ? sb.ToString() : "Unknown";
        }
        public static bool MatchesPattern(string tag, string strategy, string type)
        {
            if (string.IsNullOrEmpty(tag))
            {
                return false;
            }
            var parsed = Parse(tag);
            return parsed.Matches(strategy, type);
        }
    }
}
using System;

namespace CoreAlgo.Architecture.Core.Helpers
{
    /// <summary>
    /// Helper methods for volatility calculations including forward volatility.
    /// </summary>
    public static class VolatilityMath
    {
        /// <summary>
        /// Computes forward volatility between two maturities using the forward variance identity.
        /// 
        /// Formula:
        ///   T1 = nearDte / 365, T2 = farDte / 365
        ///   TV1 = nearIv^2 * T1, TV2 = farIv^2 * T2
        ///   ForwardVar = (TV2 - TV1) / (T2 - T1)
        ///   ForwardVol = sqrt(ForwardVar)
        /// 
        /// Returns false if inputs are invalid (T2 &lt;= T1, negative forward variance, etc.)
        /// </summary>
        /// <param name="nearIv">Near maturity implied volatility (as decimal, e.g., 0.45 for 45%)</param>
        /// <param name="nearDte">Near maturity days to expiration</param>
        /// <param name="farIv">Far maturity implied volatility (as decimal, e.g., 0.35 for 35%)</param>
        /// <param name="farDte">Far maturity days to expiration</param>
        /// <param name="forwardVol">Output: computed forward volatility (annualized, as decimal)</param>
        /// <returns>True if computation succeeded, false if inputs are invalid</returns>
        public static bool TryComputeForwardVolatility(
            decimal nearIv, 
            int nearDte, 
            decimal farIv, 
            int farDte, 
            out decimal forwardVol)
        {
            forwardVol = 0m;

            // Convert to years
            var T1 = nearDte / 365m;
            var T2 = farDte / 365m;

            // Validate: T2 must be > T1
            if (T2 <= T1)
                return false;

            // Compute total variances
            var tv1 = nearIv * nearIv * T1;
            var tv2 = farIv * farIv * T2;

            // Compute forward variance
            var fwdVar = (tv2 - tv1) / (T2 - T1);

            // Check for negative or zero forward variance
            if (fwdVar <= 0m)
                return false;

            // Compute forward volatility
            forwardVol = (decimal)Math.Sqrt((double)fwdVar);
            return true;
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Securities;
using CoreAlgo.Architecture.Core.Interfaces;
namespace CoreAlgo.Architecture.Core.Implementations
{
    public class CollateralValidationRule : IPositionOverlapRule
    {
        private readonly IAlgorithmContext _context;
        private readonly ISmartLogger _logger;
        public string RuleName => "CollateralValidationRule";
        public string Description => "Ensures sufficient collateral for short option positions";
        public bool IsEnabled { get; set; } = true;
        public CollateralValidationRule(IAlgorithmContext context)
        {
            _context = context;
            _logger = context.Logger;
        }
        public ValidationResult Validate(
            Symbol proposedSymbol,
            decimal quantity,
            IEnumerable<KeyValuePair<Symbol, SecurityHolding>> existingPositions,
            string strategyTag = "")
        {
            if (!IsEnabled)
                return ValidationResult.Success();
            try
            {
                if (proposedSymbol.SecurityType != SecurityType.Option)
                    return ValidationResult.Success();
                if (quantity >= 0)
                    return ValidationResult.Success();
                if (IsMultiLegStrategy(strategyTag))
                {
                    _logger.Debug($"[{RuleName}] Skipping collateral validation for multi-leg strategy: {strategyTag}");
                    return ValidationResult.Success();
                }
                var underlying = proposedSymbol.Underlying;
                var optionRight = proposedSymbol.ID.OptionRight;
                var contractMultiplier = 100; // Standard option contract size
                if (optionRight == OptionRight.Call)
                {
                    return ValidateCallCollateral(underlying, quantity, existingPositions, contractMultiplier);
                }
                else if (optionRight == OptionRight.Put)
                {
                    return ValidatePutCollateral(proposedSymbol, quantity, existingPositions, contractMultiplier);
                }
                return ValidationResult.Success();
            }
            catch (System.Exception ex)
            {
                _logger.Error($"[{RuleName}] Error validating collateral: {ex.Message}");
                return ValidationResult.Error($"Collateral validation error: {ex.Message}");
            }
        }
        private ValidationResult ValidateCallCollateral(
            Symbol underlying,
            decimal proposedQuantity,
            IEnumerable<KeyValuePair<Symbol, SecurityHolding>> existingPositions,
            int contractMultiplier)
        {
            try
            {
                var stockHolding = _context.Algorithm.Portfolio[underlying];
                var availableShares = stockHolding?.Quantity ?? 0;
                var existingShortCalls = existingPositions
                    .Where(p => p.Key.SecurityType == SecurityType.Option &&
                               p.Key.Underlying == underlying &&
                               p.Key.ID.OptionRight == OptionRight.Call &&
                               p.Value.Quantity < 0)
                    .Sum(p => -p.Value.Quantity); // Convert to positive for counting
                var totalShortCalls = existingShortCalls + (-proposedQuantity);
                var sharesRequired = totalShortCalls * contractMultiplier;
                _logger.Debug($"[{RuleName}] Call validation: " +
                             $"Available shares: {availableShares}, " +
                             $"Required: {sharesRequired}, " +
                             $"Existing short calls: {existingShortCalls}, " +
                             $"Proposed: {-proposedQuantity}");
                if (sharesRequired > availableShares)
                {
                    return ValidationResult.Blocked(
                        $"Insufficient shares for covered call: " +
                        $"Need {sharesRequired} shares, have {availableShares}. " +
                        $"Total short calls would be {totalShortCalls}.");
                }
                return ValidationResult.Success();
            }
            catch (System.Exception ex)
            {
                _logger.Warning($"[{RuleName}] Error in call validation: {ex.Message}");
                return ValidationResult.Success();
            }
        }
        private ValidationResult ValidatePutCollateral(
            Symbol proposedSymbol,
            decimal proposedQuantity,
            IEnumerable<KeyValuePair<Symbol, SecurityHolding>> existingPositions,
            int contractMultiplier)
        {
            try
            {
                var underlying = proposedSymbol.Underlying;
                var strike = proposedSymbol.ID.StrikePrice;
                var availableCash = _context.Algorithm.Portfolio.Cash;
                var cashRequiredForNewPut = Math.Abs(proposedQuantity) * strike * contractMultiplier;
                var existingCashCommitment = existingPositions
                    .Where(p => p.Key.SecurityType == SecurityType.Option &&
                               p.Key.Underlying == underlying &&
                               p.Key.ID.OptionRight == OptionRight.Put &&
                               p.Value.Quantity < 0)
                    .Sum(p => Math.Abs(p.Value.Quantity) * p.Key.ID.StrikePrice * contractMultiplier);
                var totalCashRequired = existingCashCommitment + cashRequiredForNewPut;
                _logger.Debug($"[{RuleName}] Put validation: " +
                             $"Available cash: ${availableCash:F2}, " +
                             $"Total required: ${totalCashRequired:F2}, " +
                             $"Existing commitment: ${existingCashCommitment:F2}, " +
                             $"New requirement: ${cashRequiredForNewPut:F2}");
                if (totalCashRequired > availableCash)
                {
                    return ValidationResult.Blocked(
                        $"Insufficient cash for cash-secured put: " +
                        $"Need ${totalCashRequired:F2}, have ${availableCash:F2}. " +
                        $"Put strike: ${strike}, quantity: {Math.Abs(proposedQuantity)}");
                }
                return ValidationResult.Success();
            }
            catch (System.Exception ex)
            {
                _logger.Warning($"[{RuleName}] Error in put validation: {ex.Message}");
                return ValidationResult.Success();
            }
        }
        private decimal GetBuyingPowerReduction(Symbol underlying,
            IEnumerable<KeyValuePair<Symbol, SecurityHolding>> positions)
        {
            try
            {
                return positions
                    .Where(p => GetPositionUnderlying(p.Key) == underlying)
                    .Sum(p => Math.Abs(p.Value.HoldingsValue));
            }
            catch
            {
                return 0m;
            }
        }
        private Symbol GetPositionUnderlying(Symbol symbol)
        {
            return symbol.SecurityType == SecurityType.Option ? symbol.Underlying : symbol;
        }
        private bool IsMultiLegStrategy(string strategyTag)
        {
            if (!string.IsNullOrEmpty(strategyTag))
            {
                var tag = strategyTag.ToUpperInvariant();
                if (tag.Contains("COMBO") || tag.Contains("MULTI") || tag.Contains("SPREAD"))
                    return true;
            }
            try
            {
                var algorithm = _context.Algorithm;
                if (algorithm != null)
                {
                    var comboLegCount = algorithm.GetParameter("ComboOrderLegCount");
                    if (int.TryParse(comboLegCount, out var legCount) && legCount > 1)
                    {
                        return true;
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.Debug($"[MULTI-LEG DETECTION] Could not read ComboOrderLegCount: {ex.Message}");
            }
            return false;
        }
    }
}
namespace CoreAlgo.Architecture.Core.Implementations
{
    /// <summary>
    /// Log levels for centralized logging with level checking
    /// Matches CentralAlgorithm pattern: ERROR=0, WARNING=1, INFO=2, DEBUG=3, TRACE=4
    /// </summary>
    public enum LogLevel
    {
        /// <summary>
        /// Error messages - highest priority, always logged
        /// </summary>
        Error = 0,
        
        /// <summary>
        /// Warning messages - important issues that don't stop execution
        /// </summary>
        Warning = 1,
        
        /// <summary>
        /// Information messages - general algorithm flow and status
        /// </summary>
        Info = 2,
        
        /// <summary>
        /// Debug messages - detailed execution information
        /// </summary>
        Debug = 3,
        
        /// <summary>
        /// Trace messages - highest granularity tracing, lowest priority
        /// </summary>
        Trace = 4
    }
}
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Concurrent;
    using System.Linq;
    using System.Globalization;
    using System.Drawing;
    using System.Text.RegularExpressions;
    using System.Security.Cryptography;
    using System.Text;
    using QuantConnect;
    using QuantConnect.Algorithm.Framework;
    using QuantConnect.Algorithm.Framework.Selection;
    using QuantConnect.Algorithm.Framework.Alphas;
    using QuantConnect.Algorithm.Framework.Portfolio;
    using QuantConnect.Algorithm.Framework.Portfolio.SignalExports;
    using QuantConnect.Algorithm.Framework.Execution;
    using QuantConnect.Algorithm.Framework.Risk;
    using QuantConnect.Algorithm.Selection;
    using QuantConnect.Api;
    using QuantConnect.Parameters;
    using QuantConnect.Benchmarks;
    using QuantConnect.Brokerages;
    using QuantConnect.Commands;
    using QuantConnect.Configuration;
    using QuantConnect.Util;
    using QuantConnect.Interfaces;
    using QuantConnect.Algorithm;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Auxiliary;
    using QuantConnect.Data.Consolidators;
    using QuantConnect.Data.Custom;
    using QuantConnect.Data.Custom.IconicTypes;
    using QuantConnect.DataSource;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.Shortable;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Notifications;
    using QuantConnect.Orders;
    using QuantConnect.Orders.Fees;
    using QuantConnect.Orders.Fills;
    using QuantConnect.Orders.OptionExercise;
    using QuantConnect.Orders.Slippage;
    using QuantConnect.Orders.TimeInForces;
    using QuantConnect.Python;
    using QuantConnect.Scheduling;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Equity;
    using QuantConnect.Securities.Future;
    using QuantConnect.Securities.Option;
    using QuantConnect.Securities.Positions;
    using QuantConnect.Securities.Forex;
    using QuantConnect.Securities.Crypto;
    using QuantConnect.Securities.CryptoFuture;
    using QuantConnect.Securities.IndexOption;
    using QuantConnect.Securities.Interfaces;
    using QuantConnect.Securities.Volatility;
    using QuantConnect.Storage;
    using QuantConnect.Statistics;
    using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
    using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
    using Calendar = QuantConnect.Data.Consolidators.Calendar;
    using CoreAlgo.Architecture.Core.Interfaces;
    using CoreAlgo.Architecture.Core.Services;
namespace CoreAlgo.Architecture.Core.Implementations
{
    public enum HighlightKind
    {
        Entry,
        TakeProfit,
        StopLoss,
        TrailingStop,
        PartialFill,
        Cancel
    }
    public static class SmartLoggerStore
    {
        public static readonly ConcurrentDictionary<string, List<SmartLogMessage>> DailyMessages =
            new ConcurrentDictionary<string, List<SmartLogMessage>>();
        public static readonly ConcurrentDictionary<string, MessageGroup> MessageGroups =
            new ConcurrentDictionary<string, MessageGroup>();
        public static DateTime? LastProcessedDay = null;
        public static readonly object ProcessLock = new object();
        public static readonly ConcurrentDictionary<string, Dictionary<string, string>> HighlightContexts =
            new ConcurrentDictionary<string, Dictionary<string, string>>();
        public static readonly ConcurrentQueue<SmartLogMessage> Highlights = new ConcurrentQueue<SmartLogMessage>();
        private static int _immediateHighlightCount = 0;
        private static DateTime _lastImmediateResetDay = DateTime.MinValue;
        private static readonly object _immediateCounterLock = new object();
        private const int MaxMetricsPerScope = 12;
        private const int MaxMetricChars = 80;
        private static readonly ConcurrentDictionary<string, (DateTime lastSeen, int count)> MessageThrottle =
            new ConcurrentDictionary<string, (DateTime, int)>();
        private static readonly TimeSpan ThrottleWindow = TimeSpan.FromMinutes(1);
        private const int MaxMessagesPerWindow = 50;
        public static (int dailyMessages, int messageGroups) GetCollectionCounts()
        {
            return (DailyMessages.Count, MessageGroups.Count);
        }
        public static bool ShouldThrottleMessage(string baseHash, DateTime currentTime)
        {
            var throttleKey = $"throttle_{baseHash}";
            var info = MessageThrottle.GetOrAdd(throttleKey, _ => (currentTime, 0));
            if (currentTime - info.lastSeen > ThrottleWindow)
            {
                MessageThrottle.TryUpdate(throttleKey, (currentTime, 1), info);
                return false;
            }
            var newCount = info.count + 1;
            MessageThrottle.TryUpdate(throttleKey, (currentTime, newCount), info);
            return newCount > MaxMessagesPerWindow;
        }
        private static void CleanOldThrottleEntries(DateTime currentDay)
        {
            var cutoff = currentDay.AddDays(-1); // Keep entries from yesterday and today only
            var keysToRemove = new List<string>();
            foreach (var kvp in MessageThrottle)
            {
                if (kvp.Value.lastSeen.Date < cutoff.Date)
                {
                    keysToRemove.Add(kvp.Key);
                }
            }
            foreach (var key in keysToRemove)
            {
                MessageThrottle.TryRemove(key, out _);
            }
        }
        public static void MergeMetrics(string scopeKey, Dictionary<string, string> metrics)
        {
            if (string.IsNullOrEmpty(scopeKey) || metrics == null || metrics.Count == 0)
                return;
            var context = HighlightContexts.GetOrAdd(scopeKey, _ => new Dictionary<string, string>());
            lock (context)
            {
                foreach (var kvp in metrics)
                {
                    if (context.Count >= MaxMetricsPerScope)
                        break;
                    var value = kvp.Value;
                    if (value.Length > MaxMetricChars)
                        value = value.Substring(0, MaxMetricChars - 3) + "...";
                    context[kvp.Key] = value;
                }
            }
        }
        public static bool TryGetAndClearMetrics(string scopeKey, out Dictionary<string, string> metrics)
        {
            metrics = null;
            if (string.IsNullOrEmpty(scopeKey))
                return false;
            if (HighlightContexts.TryRemove(scopeKey, out var context))
            {
                lock (context)
                {
                    metrics = new Dictionary<string, string>(context);
                }
                return metrics.Count > 0;
            }
            return false;
        }
        public static void ClearHighlightMetrics(string scopeKey)
        {
            if (!string.IsNullOrEmpty(scopeKey))
            {
                HighlightContexts.TryRemove(scopeKey, out _);
            }
        }
        public static void AddHighlight(SmartLogMessage message)
        {
            Highlights.Enqueue(message);
        }
        public static bool ShouldPrintImmediateHighlight(DateTime currentTime, int maxImmediate)
        {
            lock (_immediateCounterLock)
            {
                if (currentTime.Date > _lastImmediateResetDay.Date)
                {
                    _immediateHighlightCount = 0;
                    _lastImmediateResetDay = currentTime.Date;
                }
                if (_immediateHighlightCount < maxImmediate)
                {
                    _immediateHighlightCount++;
                    return true;
                }
                return false;
            }
        }
        public static List<SmartLogMessage> TryDequeueAllHighlights()
        {
            var result = new List<SmartLogMessage>();
            while (Highlights.TryDequeue(out var msg))
            {
                result.Add(msg);
            }
            return result;
        }
        public static void ProcessDailyLogs(QCAlgorithm algorithm, TradeSummary summary = null, int? maxHighlightsPerDayOverride = null)
        {
            if (algorithm.LiveMode)
                return;
            lock (ProcessLock)
            {
                var currentDay = algorithm.Time.Date;
                if (LastProcessedDay.HasValue && LastProcessedDay.Value == currentDay)
                {
                    return; // First OnEndOfDay() call processes, subsequent calls are ignored
                }
                algorithm.Log($"=== DEBUG: Collection counts - Daily: {DailyMessages.Count}, Groups: {MessageGroups.Count}, Highlights: {Highlights.Count} ===");
                algorithm.Log("=====================================");
                algorithm.Log($"Daily Summary - {currentDay:yyyy-MM-dd}");
                algorithm.Log("=====================================");
                if (summary != null)
                {
                    algorithm.Log("");
                    algorithm.Log("TRADE METRICS");
                    algorithm.Log($"Total: {summary.TotalTrades} | Working: {summary.WorkingCount} | Open: {summary.OpenCount} | Closed: {summary.ClosedCount}");
                    algorithm.Log($"P&L: ${summary.TotalPnL:F2}");
                    if (summary.OpenCount > 0)
                    {
                        algorithm.Log($"*** WARNING: {summary.OpenCount} trades still open at EOD ***");
                    }
                }
                var maxHighlightsPerDay = maxHighlightsPerDayOverride ??
                    (int.TryParse(algorithm.GetParameter("MaxHighlightsPerDay", "200"), out var parsedMax) ? parsedMax : 200);
                var highlights = TryDequeueAllHighlights();
                if (highlights.Any())
                {
                    algorithm.Log("");
                    algorithm.Log($"TRADE EVENTS ({highlights.Count} total)");
                    var sortedHighlights = highlights.OrderBy(h => h.Timestamp).ToList();
                    var displayCount = Math.Min(sortedHighlights.Count, maxHighlightsPerDay);
                    for (int i = 0; i < displayCount; i++)
                    {
                        var h = sortedHighlights[i];
                        algorithm.Log(h.Message);
                    }
                    if (sortedHighlights.Count > maxHighlightsPerDay)
                    {
                        algorithm.Log($"... and {sortedHighlights.Count - maxHighlightsPerDay} more");
                    }
                    var entryCount = highlights.Count(h => h.Message.Contains("==> ENTRY"));
                    var tpCount = highlights.Count(h => h.Message.Contains("<== TP"));
                    var slCount = highlights.Count(h => h.Message.Contains("!! SL"));
                    var tsCount = highlights.Count(h => h.Message.Contains("~~> TS"));
                    algorithm.Log($"Counts: Entry={entryCount}, TP={tpCount}, SL={slCount}, TS={tsCount}");
                }
                if (!DailyMessages.Any() && !MessageGroups.Any())
                {
                    if (highlights.Any())
                    {
                        algorithm.Log("");
                    }
                    algorithm.Log("BATCHED MESSAGES");
                    algorithm.Log("*** NO SMART MESSAGES WERE COLLECTED ***");
                }
                else
                {
                    algorithm.Log("");
                    algorithm.Log("BATCHED MESSAGES");
                    algorithm.Log($"Found {DailyMessages.Count} daily message groups and {MessageGroups.Count} statistical groups to process");
                    foreach (var kvp in DailyMessages)
                    {
                        var messages = kvp.Value;
                        if (messages.Count == 0) continue;
                        lock (messages)
                        {
                            var firstMsg = messages[0];
                            if (messages.Count > 1)
                            {
                                algorithm.Log($"{firstMsg.Timestamp:HH:mm:ss} {firstMsg.Level} -> " +
                                            $"{firstMsg.ClassName}.{firstMsg.FunctionName}: {firstMsg.Message} " +
                                            $"(repeated {messages.Count} times)");
                            }
                            else
                            {
                                algorithm.Log($"{firstMsg.Timestamp:HH:mm:ss} {firstMsg.Level} -> " +
                                            $"{firstMsg.ClassName}.{firstMsg.FunctionName}: {firstMsg.Message}");
                            }
                        }
                    }
                    foreach (var kvp in MessageGroups)
                    {
                        var group = kvp.Value;
                        algorithm.Log(group.GetSummary());
                    }
                }
                algorithm.Log("");
                algorithm.Log("=====================================");
                DailyMessages.Clear();
                MessageGroups.Clear();
                CleanOldThrottleEntries(currentDay);
                HighlightContexts.Clear();
                LastProcessedDay = currentDay;
            }
        }
    }
    public class SmartLogMessage
    {
        public string Level { get; set; }
        public string ClassName { get; set; }
        public string FunctionName { get; set; }
        public string Message { get; set; }
        public DateTime Timestamp { get; set; }
        private string _hash;
        private string _baseHash;
        public SmartLogMessage(string level, string className, string functionName, string message, DateTime timestamp)
        {
            Level = level;
            ClassName = className;
            FunctionName = functionName;
            Message = message;
            Timestamp = timestamp;
        }
        public string Hash
        {
            get
            {
                if (_hash == null)
                {
                    var content = $"{Level}|{ClassName}|{FunctionName}|{Message}";
                    _hash = ComputeHash(content);
                }
                return _hash;
            }
        }
        public string BaseHash
        {
            get
            {
                if (_baseHash == null)
                {
                    try
                    {
                        var template = Regex.Replace(Message, @"[-+]?[0-9,]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?", "NUM");
                        template = Regex.Replace(template, @"\s+", " ");
                        template = template.ToLowerInvariant();
                        template = Regex.Replace(template, @"(onendofday called for symbol\s+)(.*)", "$1SYMBOL_PLACEHOLDER");
                        template = Regex.Replace(template, @"(strategy state:\s+)(.*)", "$1STATE_PLACEHOLDER");
                        template = Regex.Replace(template, @"(strategy name:\s+)(.*)", "$1NAME_PLACEHOLDER");
                        template = Regex.Replace(template, @"(symbol\s+)([A-Z]{2,5})", "$1SYMBOL_PLACEHOLDER");
                        template = Regex.Replace(template, @"[:\[\]{}()]", "");
                        var content = $"{Level.ToLowerInvariant()}|{ClassName.ToLowerInvariant()}|{FunctionName.ToLowerInvariant()}|{template}";
                        _baseHash = ComputeHash(content);
                    }
                    catch
                    {
                        var content = $"{Level}|{ClassName}|{FunctionName}";
                        _baseHash = ComputeHash(content);
                    }
                }
                return _baseHash;
            }
        }
        public double? ExtractValue()
        {
            try
            {
                var matches = Regex.Matches(Message, @"[-+]?[0-9,]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?");
                if (matches.Count > 0)
                {
                    var lastMatch = matches[matches.Count - 1].Value.Replace(",", "");
                    if (double.TryParse(lastMatch, out double value))
                        return value;
                }
            }
            catch
            {
            }
            return null;
        }
        private static string ComputeHash(string input)
        {
            using (var md5 = MD5.Create())
            {
                var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(input));
                return Convert.ToHexString(hash).ToLowerInvariant();
            }
        }
    }
    public class MessageGroup
    {
        public string BaseHash { get; private set; }
        public List<SmartLogMessage> Messages { get; private set; }
        public DateTime FirstTime { get; private set; }
        public DateTime LastTime { get; private set; }
        public string Level { get; private set; }
        public string ClassName { get; private set; }
        public string FunctionName { get; private set; }
        public string BaseMessage { get; private set; }
        public MessageGroup(SmartLogMessage firstMessage)
        {
            BaseHash = firstMessage.BaseHash;
            Messages = new List<SmartLogMessage> { firstMessage };
            FirstTime = firstMessage.Timestamp;
            LastTime = firstMessage.Timestamp;
            Level = firstMessage.Level;
            ClassName = firstMessage.ClassName;
            FunctionName = firstMessage.FunctionName;
            BaseMessage = firstMessage.Message;
        }
        public bool TryAddMessage(SmartLogMessage message)
        {
            if (message.BaseHash != BaseHash)
                return false;
            Messages.Add(message);
            LastTime = message.Timestamp;
            return true;
        }
        public string GetSummary()
        {
            if (Messages.Count == 1)
                return FormatMessage(Messages[0]);
            var sortedMessages = Messages.OrderBy(m => m.Timestamp).ToList();
            var values = new List<double>();
            var timestamps = new List<DateTime>();
            foreach (var msg in sortedMessages)
            {
                var value = msg.ExtractValue();
                if (value.HasValue)
                {
                    values.Add(value.Value);
                    timestamps.Add(msg.Timestamp);
                }
            }
            if (!values.Any())
                return FormatMessage(Messages[0]);
            var mean = values.Average();
            var min = values.Min();
            var max = values.Max();
            var count = values.Count;
            var timeRange = $"{FirstTime:HH:mm:ss}-{LastTime:HH:mm:ss}";
            var sections = new List<string>
            {
                $"{timeRange} {Level} -> {ClassName}.{FunctionName}: {BaseMessage}",
                $"    Stats: mean={mean:F2}, min={min:F2}, max={max:F2}"
            };
            if (count >= 2)
            {
                var trend = GetTrendAnalysis(values, timestamps);
                if (!string.IsNullOrEmpty(trend))
                    sections.Add($"    Trend:{trend}");
            }
            if (count >= 3)
            {
                var distribution = GetDistributionAnalysis(values);
                if (!string.IsNullOrEmpty(distribution))
                    sections.Add($"    {distribution}");
            }
            sections.Add($"    Samples: {count}");
            return string.Join("\n", sections);
        }
        private string GetTrendAnalysis(List<double> values, List<DateTime> timestamps)
        {
            if (values.Count < 2) return "";
            try
            {
                var keyChanges = new List<(DateTime time, double value)>();
                for (int i = 1; i < values.Count - 1; i++)
                {
                    if ((values[i] > values[i-1] && values[i] > values[i+1]) ||
                        (values[i] < values[i-1] && values[i] < values[i+1]))
                    {
                        keyChanges.Add((timestamps[i], values[i]));
                    }
                }
                if (keyChanges.Count > 4)
                {
                    keyChanges = keyChanges.OrderByDescending(x => Math.Abs(x.value - values[0])).Take(4).ToList();
                    keyChanges = keyChanges.OrderBy(x => x.time).ToList();
                }
                if (!keyChanges.Any()) return "";
                var changes = keyChanges.Select(x =>
                    $"{(x.value > values[0] ? "↑" : "↓")}{x.value:F2}({x.time:HH:mm})").ToList();
                return " " + string.Join(" ", changes);
            }
            catch
            {
                return "";
            }
        }
        private string GetDistributionAnalysis(List<double> values)
        {
            if (values.Count < 3) return "";
            try
            {
                var minVal = values.Min();
                var maxVal = values.Max();
                if (Math.Abs(minVal - maxVal) < 0.001)
                    return $"Distribution: [constant={minVal:F2}]";
                var binSize = (maxVal - minVal) / 3;
                var bins = new int[3];
                foreach (var value in values)
                {
                    var binIdx = (int)((value - minVal) / binSize);
                    if (binIdx == 3) binIdx = 2; // Handle edge case for max value
                    bins[binIdx]++;
                }
                var distParts = new List<string> { "Distribution:" };
                for (int i = 0; i < 3; i++)
                {
                    if (bins[i] > 0)
                    {
                        var binStart = minVal + i * binSize;
                        var binEnd = minVal + (i + 1) * binSize;
                        distParts.Add($"[{binStart:F1}-{binEnd:F1}: {bins[i]}]");
                    }
                }
                return string.Join(" ", distParts);
            }
            catch
            {
                return "";
            }
        }
        private static string FormatMessage(SmartLogMessage msg)
        {
            return $"{msg.Timestamp:HH:mm:ss} {msg.Level} -> {msg.ClassName}.{msg.FunctionName}: {msg.Message}";
        }
    }
    public class QCLogger<T> : ILogger<T>, ISmartLogger
    {
        private readonly QCAlgorithm _algorithm;
        private readonly string _categoryName;
        private readonly int _currentLogLevel;
        private readonly bool _verboseMode;
        public QCLogger(QCAlgorithm algorithm, int logLevel = 3, bool verboseMode = false)
        {
            _algorithm = algorithm ?? throw new ArgumentNullException(nameof(algorithm));
            _categoryName = typeof(T).Name;
            _currentLogLevel = logLevel;
            _verboseMode = algorithm.LiveMode || verboseMode;
        }
        public void LogInformation(string message, params object[] args)
        {
            var formattedMessage = FormatMessage(message, args);
            if (_verboseMode)
            {
                var realTimeMessage = FormatRealTimeMessage(formattedMessage, _categoryName, GetCallingFunctionName(), LogLevel.Info);
                _algorithm.Log(realTimeMessage);
                return;
            }
            StoreSmartMessage("INFO", formattedMessage);
        }
        public void LogDebug(string message, params object[] args)
        {
            var formattedMessage = FormatMessage(message, args);
            if (_verboseMode)
            {
                var realTimeMessage = FormatRealTimeMessage(formattedMessage, _categoryName, GetCallingFunctionName(), LogLevel.Debug);
                _algorithm.Log(realTimeMessage);
                return;
            }
            StoreSmartMessage("DEBUG", formattedMessage);
        }
        public void LogTrace(string message, params object[] args)
        {
            var formattedMessage = FormatMessage(message, args);
            if (_verboseMode)
            {
                var realTimeMessage = FormatRealTimeMessage(formattedMessage, _categoryName, GetCallingFunctionName(), LogLevel.Trace);
                _algorithm.Log(realTimeMessage);
                return;
            }
            StoreSmartMessage("TRACE", formattedMessage);
        }
        public void LogWarning(string message, params object[] args)
        {
            var formattedMessage = FormatMessage(message, args);
            if (_verboseMode)
            {
                var realTimeMessage = FormatRealTimeMessage(formattedMessage, _categoryName, GetCallingFunctionName(), LogLevel.Warning);
                _algorithm.Log(realTimeMessage);
                return;
            }
            StoreSmartMessage("WARN", formattedMessage);
        }
        public void LogError(string message, params object[] args)
        {
            var formattedMessage = FormatMessage(message, args);
            if (_verboseMode)
            {
                var realTimeMessage = FormatRealTimeMessage(formattedMessage, _categoryName, GetCallingFunctionName(), LogLevel.Error);
                _algorithm.Log(realTimeMessage);
                return;
            }
            StoreSmartMessage("ERROR", formattedMessage);
        }
        public void LogError(Exception exception, string message, params object[] args)
        {
            var formattedMessage = FormatMessage(message, args);
            var fullMessage = $"{formattedMessage} - Exception: {exception.Message}";
            if (_verboseMode)
            {
                var realTimeMessage = FormatRealTimeMessage(fullMessage, _categoryName, GetCallingFunctionName(), LogLevel.Error);
                _algorithm.Log(realTimeMessage);
                var stackTraceMessage = FormatRealTimeMessage($"StackTrace: {exception.StackTrace}", _categoryName, GetCallingFunctionName(), LogLevel.Error);
                _algorithm.Log(stackTraceMessage);
                return;
            }
            StoreSmartMessage("ERROR", fullMessage);
            StoreSmartMessage("ERROR", $"StackTrace: {exception.StackTrace}");
        }
        public void LogMessage(string message, LogLevel level = LogLevel.Debug)
        {
            if ((int)level > _currentLogLevel)
                return;
            if (_verboseMode)
            {
                var realTimeMessage = FormatRealTimeMessage(message, _categoryName, GetCallingFunctionName(), level);
                _algorithm.Log(realTimeMessage);
                return;
            }
            switch (level)
            {
                case LogLevel.Error:
                    LogError(message);
                    break;
                case LogLevel.Warning:
                    LogWarning(message);
                    break;
                case LogLevel.Info:
                    LogInformation(message);
                    break;
                case LogLevel.Debug:
                    LogDebug(message);
                    break;
                case LogLevel.Trace:
                    LogTrace(message);
                    break;
                default:
                    LogDebug(message);
                    break;
            }
        }
        public void Error(string message) => LogMessage(message, LogLevel.Error);
        public void Warning(string message) => LogMessage(message, LogLevel.Warning);
        public void Info(string message) => LogMessage(message, LogLevel.Info);
        public void Debug(string message) => LogMessage(message, LogLevel.Debug);
        public void Trace(string message) => LogMessage(message, LogLevel.Trace);
        public string BuildScope(Symbol symbol, int? tradeId = null)
        {
            return $"{symbol.Value}:{tradeId?.ToString() ?? "_"}";
        }
        public void AddHighlightMetric(string scopeKey, string key, object value, int? decimalPlaces = null)
        {
            if (string.IsNullOrEmpty(scopeKey) || string.IsNullOrEmpty(key))
                return;
            var formattedValue = FormatMetricValue(value, decimalPlaces);
            var metrics = new Dictionary<string, string> { { key, formattedValue } };
            SmartLoggerStore.MergeMetrics(scopeKey, metrics);
        }
        public void AddHighlightMetrics(string scopeKey, IDictionary<string, object> metrics, int? decimalPlaces = null)
        {
            if (string.IsNullOrEmpty(scopeKey) || metrics == null || metrics.Count == 0)
                return;
            var formatted = new Dictionary<string, string>();
            foreach (var kvp in metrics)
            {
                formatted[kvp.Key] = FormatMetricValue(kvp.Value, decimalPlaces);
            }
            SmartLoggerStore.MergeMetrics(scopeKey, formatted);
        }
        public void ClearHighlightMetrics(string scopeKey)
        {
            SmartLoggerStore.ClearHighlightMetrics(scopeKey);
        }
        public void HighlightTradeEvent(string className, string methodName, HighlightKind kind, string oneLine, string scopeKey = null)
        {
            var highlightEnabled = !bool.TryParse(_algorithm.GetParameter("HighlightTrades", "true"), out var hlEnabled) || hlEnabled;
            if (!highlightEnabled)
                return;
            var immediateEnabled = !bool.TryParse(_algorithm.GetParameter("HighlightImmediate", "true"), out var immEnabled) || immEnabled;
            var maxImmediate = int.TryParse(_algorithm.GetParameter("MaxHighlightImmediate", "80"), out var parsedImm) ? parsedImm : 80;
            var includeMetrics = !bool.TryParse(_algorithm.GetParameter("HighlightIncludeStrategyMetrics", "true"), out var inclMetrics) || inclMetrics;
            var finalLine = oneLine;
            if (includeMetrics && !string.IsNullOrEmpty(scopeKey) &&
                SmartLoggerStore.TryGetAndClearMetrics(scopeKey, out var metrics))
            {
                var suffix = CoreAlgo.Architecture.Core.Services.TradeHighlightFormatter.FormatMetricsSuffix(metrics);
                if (!string.IsNullOrEmpty(suffix))
                {
                    finalLine = $"{oneLine} {suffix}";
                }
            }
            var msg = new SmartLogMessage("INFO", className, methodName, finalLine, _algorithm.Time);
            if (immediateEnabled && !_verboseMode)
            {
                var shouldPrintImmediate = SmartLoggerStore.ShouldPrintImmediateHighlight(_algorithm.Time, maxImmediate);
                if (shouldPrintImmediate)
                {
                    var realTimeMessage = FormatRealTimeMessage(finalLine, className, methodName, LogLevel.Info);
                    _algorithm.Log(realTimeMessage);
                }
            }
            SmartLoggerStore.AddHighlight(msg);
        }
        private string FormatMetricValue(object value, int? decimalPlaces)
        {
            if (value == null)
                return "null";
            if (value is decimal d)
                return d.ToString(decimalPlaces.HasValue ? $"F{decimalPlaces.Value}" : "F2");
            if (value is double dbl)
                return dbl.ToString(decimalPlaces.HasValue ? $"F{decimalPlaces.Value}" : "F2");
            if (value is float f)
                return f.ToString(decimalPlaces.HasValue ? $"F{decimalPlaces.Value}" : "F2");
            if (value is int || value is long || value is short)
                return value.ToString();
            if (value is bool b)
                return b ? "T" : "F";
            var str = value.ToString();
            if (str.Length > 20)
                str = str.Substring(0, 17) + "...";
            return str;
        }
        public void LogWithContext(string message, string className, string methodName, LogLevel level = LogLevel.Info)
        {
            if ((int)level > _currentLogLevel)
                return;
            if (string.IsNullOrEmpty(className) || className == "SimpleBaseStrategy")
            {
                var (dynClass, dynMethod) = GetCallerContextDynamic();
                if (dynClass != "Unknown")
                {
                    className = dynClass;
                    methodName = dynMethod;
                }
            }
            if (_verboseMode)
            {
                var formattedMessage = FormatRealTimeMessage(message, className, methodName, level);
                _algorithm.Log(formattedMessage);
                return;
            }
            bool shouldOutputImmediate = level <= LogLevel.Warning || // ERROR/WARNING always immediate
                                        (_verboseMode && level == LogLevel.Info);
            if (shouldOutputImmediate)
            {
                var formattedMessage = FormatRealTimeMessage(message, className, methodName, level);
                _algorithm.Log(formattedMessage);
            }
            if (!shouldOutputImmediate || level == LogLevel.Debug || level == LogLevel.Trace)
            {
                StoreSmartMessageWithContext(level.ToString().ToUpper(), message, className, methodName);
            }
        }
        private void StoreSmartMessage(string level, string message)
        {
            if (_verboseMode)
                return;
            var functionName = GetCallingFunctionName();
            var smartMessage = new SmartLogMessage(level, _categoryName, functionName, message, _algorithm.Time);
            if ((level == "DEBUG" || level == "TRACE") && SmartLoggerStore.ShouldThrottleMessage(smartMessage.BaseHash, _algorithm.Time))
            {
                return; // Skip this message due to throttling
            }
            var numericValue = smartMessage.ExtractValue();
            if (numericValue.HasValue)
            {
                var existingGroup = SmartLoggerStore.MessageGroups.GetOrAdd(smartMessage.BaseHash,
                    _ => new MessageGroup(smartMessage));
                if (existingGroup.BaseHash == smartMessage.BaseHash)
                {
                    if (existingGroup.TryAddMessage(smartMessage))
                    {
                        return; // Successfully added to group
                    }
                }
            }
            var messageList = SmartLoggerStore.DailyMessages.GetOrAdd(smartMessage.Hash, _ => new List<SmartLogMessage>());
            lock (messageList)
            {
                messageList.Add(smartMessage);
            }
        }
        private void StoreSmartMessageWithContext(string level, string message, string className, string methodName)
        {
            if (_verboseMode)
                return;
            var smartMessage = new SmartLogMessage(level, className, methodName, message, _algorithm.Time);
            if ((level == "DEBUG" || level == "TRACE") && SmartLoggerStore.ShouldThrottleMessage(smartMessage.BaseHash, _algorithm.Time))
            {
                return; // Skip this message due to throttling
            }
            var numericValue = smartMessage.ExtractValue();
            if (numericValue.HasValue)
            {
                var existingGroup = SmartLoggerStore.MessageGroups.GetOrAdd(smartMessage.BaseHash,
                    _ => new MessageGroup(smartMessage));
                if (existingGroup.BaseHash == smartMessage.BaseHash)
                {
                    if (existingGroup.TryAddMessage(smartMessage))
                    {
                        return; // Successfully added to group
                    }
                }
            }
            var messageList = SmartLoggerStore.DailyMessages.GetOrAdd(smartMessage.Hash, _ => new List<SmartLogMessage>());
            lock (messageList)
            {
                messageList.Add(smartMessage);
            }
        }
        private string FormatRealTimeMessage(string message, string className, string methodName, LogLevel level)
        {
            var timestamp = _algorithm.Time.ToString("HH:mm:ss");
            var realTime = DateTime.Now.ToString("HH:mm:ss.fff");
            if (_verboseMode)
            {
                return $"{timestamp} [{className}.{methodName}] {level.ToString().ToUpper()}: {message}";
            }
            else
            {
                return $"{timestamp} (Real: {realTime}) [{className}.{methodName}] {level.ToString().ToUpper()}: {message}";
            }
        }
        private static string GetCallingFunctionName()
        {
            try
            {
                var stackTrace = new System.Diagnostics.StackTrace();
                var frame = stackTrace.GetFrame(3);
                return frame?.GetMethod()?.Name ?? "Unknown";
            }
            catch
            {
                return "Unknown";
            }
        }
        private static (string className, string methodName) GetCallerContext()
        {
            try
            {
                var stackTrace = new System.Diagnostics.StackTrace();
                for (int i = 1; i < Math.Min(stackTrace.FrameCount, 10); i++)
                {
                    var frame = stackTrace.GetFrame(i);
                    var method = frame?.GetMethod();
                    if (method == null) continue;
                    var declaringType = method.DeclaringType;
                    if (declaringType == null) continue;
                    var typeName = declaringType.Name;
                    var methodName = method.Name;
                    if (typeName.Contains("Logger") ||
                        typeName.Contains("QCLogger") ||
                        typeName.Contains("<") ||  // Skip compiler-generated
                        methodName.StartsWith("Smart") ||  // Skip SmartLog wrappers
                        methodName == "StoreSmartMessage" ||
                        methodName == "StoreSmartMessageWithContext" ||
                        methodName == "LogMessage" ||
                        methodName == "LogWithContext")
                    {
                        continue;
                    }
                    if (typeName == "SimpleBaseStrategy")
                    {
                        continue;
                    }
                    return (typeName, methodName);
                }
                return ("Unknown", "Unknown");
            }
            catch
            {
                return ("Unknown", "Unknown");
            }
        }
        private static (string className, string methodName) GetCallerContextDynamic()
        {
            try
            {
                var stackTrace = new System.Diagnostics.StackTrace(true);
                for (int i = 2; i < Math.Min(stackTrace.FrameCount, 15); i++)
                {
                    var frame = stackTrace.GetFrame(i);
                    var method = frame?.GetMethod();
                    if (method == null) continue;
                    var declaringType = method.DeclaringType;
                    if (declaringType == null) continue;
                    var typeName = declaringType.Name;
                    var methodName = method.Name;
                    if (IsWrapperMethod(typeName, methodName))
                        continue;
                    if (typeName.EndsWith("Template") && !typeName.Contains("<"))
                        return (typeName, methodName);
                    if (typeName != "SimpleBaseStrategy" &&
                        !typeName.Contains("Logger") &&
                        !typeName.Contains("Algorithm") &&
                        !typeName.Contains("System"))
                    {
                        return (typeName, methodName);
                    }
                }
                return ("Unknown", "Unknown");
            }
            catch
            {
                return ("Unknown", "Unknown");
            }
        }
        private static bool IsWrapperMethod(string typeName, string methodName)
        {
            return typeName.Contains("Logger") ||
                   typeName.Contains("QCLogger") ||
                   typeName.Contains("<") ||  // Skip compiler-generated
                   methodName.StartsWith("Smart") ||  // Skip SmartLog wrappers
                   methodName == "StoreSmartMessage" ||
                   methodName == "StoreSmartMessageWithContext" ||
                   methodName == "LogMessage" ||
                   methodName == "LogWithContext" ||
                   methodName == "GetCallerContext" ||
                   methodName == "GetCallerContextDynamic" ||
                   typeName == "RuntimeMethodHandle" ||
                   typeName == "RuntimeType";
        }
        private string FormatMessage(string message, object[] args)
        {
            string formatted;
            try
            {
                if (args != null && args.Length > 0)
                {
                    var formattedMessage = message;
                    for (int i = 0; i < args.Length; i++)
                    {
                        formattedMessage = formattedMessage.Replace($"{{{args[i]?.GetType()?.Name ?? "arg"}}}", $"{{{i}}}");
                    }
                    formatted = string.Format(formattedMessage, args);
                }
                else
                {
                    formatted = message;
                }
            }
            catch
            {
                formatted = message;
                if (args != null && args.Length > 0)
                {
                    formatted += " [" + string.Join(", ", args) + "]";
                }
            }
            return formatted;
        }
    }
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Data;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities;
using QuantConnect.Securities.Option;
using CoreAlgo.Architecture.Core.Models;
using CoreAlgo.Architecture.Core.Services;
using CoreAlgo.Architecture.Core.Helpers;
using CoreAlgo.Architecture.Core.Configuration;
using CoreAlgo.Architecture.QC.Helpers;
namespace CoreAlgo.Architecture.Core.Implementations
{
    public partial class SimpleBaseStrategy
    {
        protected void Log(string message) => Logger.Info(message);
        protected void Error(string message) => Logger.Error(message);
        protected Security AddEquity(string ticker, Resolution resolution = Resolution.Minute) => Algorithm.AddEquity(ticker, resolution);
        protected Security AddOption(string underlying, Resolution resolution = Resolution.Minute) => Algorithm.AddOption(underlying, resolution);
        protected void SetStartDate(DateTime date) => Algorithm.SetStartDate(date);
        protected void SetEndDate(DateTime date) => Algorithm.SetEndDate(date);
        protected void SetCash(decimal cash) => Algorithm.SetCash(cash);
        protected void Debug(string message) => Logger.Debug(message);
        protected T GetConfigParameter<T>(string key, T defaultValue = default(T))
        {
            try
            {
                var stringValue = Algorithm.GetParameter(key);
                if (string.IsNullOrEmpty(stringValue))
                    return defaultValue;
                return (T)Convert.ChangeType(stringValue, typeof(T));
            }
            catch (Exception)
            {
                return defaultValue;
            }
        }
        protected void SmartLog(string message,
            [CallerMemberName] string memberName = "",
            [CallerFilePath] string sourceFilePath = "")
        {
            var className = ExtractClassNameFromPath(sourceFilePath);
            if (string.IsNullOrEmpty(className))
                className = this.GetType().Name;
            Logger.LogWithContext(message, className, memberName, LogLevel.Info);
        }
        protected void SmartWarn(string message,
            [CallerMemberName] string memberName = "",
            [CallerFilePath] string sourceFilePath = "")
        {
            var className = ExtractClassNameFromPath(sourceFilePath);
            if (string.IsNullOrEmpty(className))
                className = this.GetType().Name;
            Logger.LogWithContext(message, className, memberName, LogLevel.Warning);
        }
        protected void SmartDebug(string message,
            [CallerMemberName] string memberName = "",
            [CallerFilePath] string sourceFilePath = "")
        {
            var className = ExtractClassNameFromPath(sourceFilePath);
            if (string.IsNullOrEmpty(className))
                className = this.GetType().Name;
            Logger.LogWithContext(message, className, memberName, LogLevel.Debug);
        }
        protected void SmartTrace(string message,
            [CallerMemberName] string memberName = "",
            [CallerFilePath] string sourceFilePath = "")
        {
            var className = ExtractClassNameFromPath(sourceFilePath);
            if (string.IsNullOrEmpty(className))
                className = this.GetType().Name;
            Logger.LogWithContext(message, className, memberName, LogLevel.Trace);
        }
        protected void SmartError(string message,
            [CallerMemberName] string memberName = "",
            [CallerFilePath] string sourceFilePath = "")
        {
            var className = ExtractClassNameFromPath(sourceFilePath);
            if (string.IsNullOrEmpty(className))
                className = this.GetType().Name;
            Logger.LogWithContext(message, className, memberName, LogLevel.Error);
        }
        private string ExtractClassNameFromPath(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
                return "";
            try
            {
                var lastSlash = Math.Max(filePath.LastIndexOf('/'), filePath.LastIndexOf('\\'));
                var fileName = lastSlash >= 0 ? filePath.Substring(lastSlash + 1) : filePath;
                if (fileName.EndsWith(".cs"))
                    fileName = fileName.Substring(0, fileName.Length - 3);
                return fileName;
            }
            catch (Exception)
            {
                return this.GetType().Name;
            }
        }
        protected (Symbol underlying, Symbol options) SetupAssetWithOptions(string symbol, Resolution resolution = Resolution.Minute)
        {
            var security = AssetManager.AddAsset(this, symbol, resolution);
            var optionsSymbol = AssetManager.AddOptionsChain(this, security, resolution);
            return (security.Symbol, optionsSymbol);
        }
        protected void SetupStandardOptionFilter(Symbol optionsSymbol, int strikeRange, int minDTE, int maxDTE,
            bool callsOnly = false, bool putsOnly = false)
        {
            SmartLog($"[DEBUG] SETTING UP OPTION FILTER for {optionsSymbol}");
            SmartLog($"   Filter Parameters:");
            SmartLog($"   - strikeRange: {strikeRange} (±{strikeRange} strikes from ATM)");
            SmartLog($"   - minDTE: {minDTE} days");
            SmartLog($"   - maxDTE: {maxDTE} days");
            SmartLog($"   - callsOnly: {callsOnly}");
            SmartLog($"   - putsOnly: {putsOnly}");
            if (!Algorithm.Securities.ContainsKey(optionsSymbol))
            {
                SmartLog($"[ERROR] CRITICAL: optionsSymbol {optionsSymbol} NOT found in Securities collection!");
                SmartLog($"   Available securities count: {Algorithm.Securities.Count}");
                SmartLog($"   Sample securities: {string.Join(", ", Algorithm.Securities.Keys.Take(5))}");
                return;
            }
            var option = Algorithm.Securities[optionsSymbol] as Option;
            if (option == null)
            {
                SmartLog($"[ERROR] CRITICAL: Security {optionsSymbol} is not an Option type!");
                SmartLog($"   Actual type: {Algorithm.Securities[optionsSymbol].Type}");
                SmartLog($"   Security details: {Algorithm.Securities[optionsSymbol]}");
                return;
            }
            SmartLog($"[SUCCESS] Option security found: {option}");
            SmartLog($"   Option type: {option.Type}");
            SmartLog($"   Option resolution: {option.Subscriptions.GetHighestResolution()}");
            try
            {
                var distinctResolutions = option.Subscriptions
                    .Select(s => s.Resolution)
                    .Distinct()
                    .OrderBy(r => r)
                    .ToList();
                SmartLog($"   All subscription resolutions: {string.Join(", ", distinctResolutions)}");
                SmartWarn($"IMPORTANT: For canonical option universes, GetHighestResolution() and subscription lists may show 'Daily' due to the OptionUniverse subscription.");
                SmartWarn($"Intraday readiness should be determined by actual presence of contracts in slice.OptionChains during runtime, not the canonical security's subscription summary.");
            }
            catch { }
            SmartLog($"   Option exchange: {option.Exchange}");
            try
            {
                if (callsOnly)
                {
                    SmartLog($"[TARGET] Applying CALLS ONLY filter");
                    option.SetFilter(filter => filter
                        .CallsOnly()
                        .Strikes(-strikeRange, strikeRange)
                        .Expiration(minDTE, maxDTE)
                        .IncludeWeeklys());
                }
                else if (putsOnly)
                {
                    SmartLog($"[TARGET] Applying PUTS ONLY filter");
                    option.SetFilter(filter => filter
                        .PutsOnly()
                        .Strikes(-strikeRange, strikeRange)
                        .Expiration(minDTE, maxDTE)
                        .IncludeWeeklys());
                }
                else
                {
                    SmartLog($"[TARGET] Applying BOTH CALLS AND PUTS filter");
                    option.SetFilter(filter => filter
                        .Strikes(-strikeRange, strikeRange)
                        .Expiration(minDTE, maxDTE)
                        .IncludeWeeklys());
                }
                SmartLog($"[SUCCESS] Option filter applied successfully for {optionsSymbol}");
                SmartLog($"   Filter should include strikes: {-strikeRange} to +{strikeRange} from ATM");
                SmartLog($"   Filter should include DTE: {minDTE} to {maxDTE} days");
                SmartLog($"   Option is now ready to receive data in slice.OptionChains");
            }
            catch (Exception ex)
            {
                SmartLog($"[ERROR] EXCEPTION applying option filter: {ex.Message}");
                SmartLog($"   Exception type: {ex.GetType().Name}");
                SmartLog($"   Stack trace: {ex.StackTrace}");
            }
        }
        protected (Symbol underlying, Symbol options) SetupOptionsForSymbol(string symbol, int strikeRange = 10,
            int minDTE = 3, int maxDTE = 30, bool callsOnly = false, bool putsOnly = false, Resolution resolution = Resolution.Minute)
        {
            var (underlying, options) = SetupAssetWithOptions(symbol, resolution);
            SetupStandardOptionFilter(options, strikeRange, minDTE, maxDTE, callsOnly, putsOnly);
            return (underlying, options);
        }
        private void OnError(Exception exception, ErrorSeverity severity, bool canContinue, string context)
        {
            ErrorOccurred?.Invoke(this, new StrategyErrorEventArgs(exception, severity, canContinue, context));
        }
        protected virtual void OnPreExecuteAlways(Slice slice)
        {
        }
        protected void SetupAsyncUniverse(Resolution resolution = Resolution.Minute,
            bool enableAsync = true, bool extendedHours = false)
        {
            UniverseOptimizer.SetupOptimizedUniverse(Algorithm, resolution, enableAsync, extendedHours);
        }
        protected Dictionary<Symbol, List<decimal>> BatchFetchHistory(IEnumerable<Symbol> symbols,
            int days, Resolution resolution = Resolution.Daily, int batchSize = 500)
        {
            return UniverseOptimizer.BatchFetchHistory(Algorithm, symbols, days, resolution, batchSize);
        }
        protected Dictionary<Symbol, decimal> CalculateBatchedADV(IEnumerable<Symbol> symbols, int days = 21)
        {
            return UniverseOptimizer.CalculateBatchedADV(Algorithm, symbols, days);
        }
        protected Dictionary<Symbol, decimal> CalculateVolumeShock(ConcurrentDictionary<Symbol, long> intradayVolumes,
            IEnumerable<Symbol> symbols, int advDays = 21)
        {
            return UniverseOptimizer.CalculateVolumeShock(Algorithm, intradayVolumes, symbols, advDays);
        }
        protected void CleanupRemovedSecurities(SecurityChanges changes, params object[] trackingDictionaries)
        {
            UniverseOptimizer.CleanupRemovedSecurities(Algorithm, changes, trackingDictionaries);
        }
        protected IEnumerable<TResult> ProcessUniverseInParallel<TInput, TResult>(IEnumerable<TInput> data,
            Func<TInput, TResult> processor, int batchSize = 500)
        {
            return UniverseOptimizer.ProcessInParallel(data, processor, batchSize);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Orders;
using CoreAlgo.Architecture.Core.Models;
using CoreAlgo.Architecture.QC.Helpers;
using CoreAlgo.Architecture.Core.Configuration;
using CoreAlgo.Architecture.Core.Helpers;
namespace CoreAlgo.Architecture.Core.Implementations
{
    public partial class SimpleBaseStrategy
    {
        protected void TrackWorkingOrder(OrderTicket ticket, string strategy = "")
        {
            if (ticket?.OrderId != null)
            {
                if (!string.IsNullOrEmpty(ticket.Tag))
                {
                    var parsedTag = TradeTag.Parse(ticket.Tag);
                    if (parsedTag.IsExit() || parsedTag.IsStopLoss() || parsedTag.IsTakeProfit() || parsedTag.IsScaleIn())
                    {
                        Debug($"Skipping tracking for child order: {ticket.OrderId} (Tag: {ticket.Tag})");
                        return;
                    }
                }
                var orderId = ticket.OrderId.ToString();
                var symbol = ticket.Symbol?.Value ?? "Unknown";
                var strategyName = !string.IsNullOrEmpty(strategy) ? strategy : Name;
                var trade = new TradeRecord(orderId, symbol, strategyName, ticket.Tag);
                if (ticket.Symbol != null)
                {
                    trade.SymbolIds.Add(ticket.Symbol.ID.ToString());
                    if (ticket.Symbol.SecurityType == SecurityType.Option && ticket.Symbol.Underlying != null)
                    {
                        trade.Underlying = ticket.Symbol.Underlying.Value;
                    }
                }
                TradeTracker.AllTrades.Add(trade);
                TradeTracker.WorkingTrades.Add(trade);
                Debug($"Tracking working order: {orderId} for {symbol}");
                TradePersistence.SaveTrades(TradeTracker);
            }
        }
        protected TradeRecord TrackComboOrder(List<OrderTicket> tickets, List<string> symbolIds, string strategy = "", string tag = "")
        {
            if (tickets == null || tickets.Count == 0)
            {
                return null;
            }
            var sortedTickets = tickets.OrderBy(t => t.OrderId).ToList();
            var primaryTicket = sortedTickets.FirstOrDefault();
            if (primaryTicket?.OrderId == null)
            {
                return null;
            }
            var orderId = primaryTicket.OrderId.ToString();
            var symbol = primaryTicket.Symbol?.Value ?? "Unknown";
            var strategyName = !string.IsNullOrEmpty(strategy) ? strategy : Name;
            var algorithmTime = Algorithm?.Time ?? DateTime.MinValue;
            TradeRecord trade = null;
            foreach (var ticket in sortedTickets)
            {
                if (ticket?.OrderId != null)
                {
                    var existingTrade = TradeTracker.FindTradeByAnyOrderId(ticket.OrderId);
                    if (existingTrade != null)
                    {
                        trade = existingTrade;
                        SmartLog($"[TRACK COMBO] Race condition resolved: Using existing trade {trade.OrderId} bootstrapped from OnOrderFilled");
                        break;
                    }
                }
            }
            if (trade == null)
            {
                var workingTradesBefore = TradeTracker.WorkingTrades.Count;
                trade = new TradeRecord(orderId, symbol, strategyName, tag);
                TradeTracker.AllTrades.Add(trade);
                TradeTracker.WorkingTrades.Add(trade);
                var workingTradesAfter = TradeTracker.WorkingTrades.Count;
                SmartLog($"[TRACK COMBO] @ {algorithmTime:HH:mm:ss.fff} | Created new trade | Primary={orderId}, Strategy={strategyName}, Tag='{tag}' | " +
                        $"WorkingTrades: {workingTradesBefore}→{workingTradesAfter}");
            }
            else
            {
                if (string.IsNullOrEmpty(trade.Strategy) || trade.Strategy == Name) // Only update if generic or empty
                {
                    trade.Strategy = strategyName;
                }
                if (!string.IsNullOrEmpty(tag))
                {
                    trade.OrderTag = tag;
                }
            }
            foreach (var ticket in tickets)
            {
                if (ticket?.OrderId != null && !trade.OrderIds.Contains(ticket.OrderId))
                {
                    trade.OrderIds.Add(ticket.OrderId);
                }
            }
            if (symbolIds != null)
            {
                trade.SymbolIds.Clear(); // Clear potential partial/unordered symbols from bootstrap
                foreach (var symbolId in symbolIds)
                {
                    if (!string.IsNullOrEmpty(symbolId))
                    {
                        trade.SymbolIds.Add(symbolId);
                    }
                }
            }
            if (string.IsNullOrEmpty(trade.Underlying))
            {
                foreach (var ticket in tickets)
                {
                    if (ticket?.Symbol?.SecurityType == SecurityType.Option && ticket.Symbol.Underlying != null)
                    {
                        trade.Underlying = ticket.Symbol.Underlying.Value;
                        break;
                    }
                }
            }
            SmartLog($"[TRACK COMBO] @ {algorithmTime:HH:mm:ss.fff} | Trade={trade.OrderId} Fully Configured | " +
                    $"All OrderIDs=[{string.Join(", ", trade.OrderIds.OrderBy(id => id))}], Legs={trade.OrderIds.Count}, Symbols={trade.SymbolIds.Count}");
            TradePersistence.SaveTrades(TradeTracker);
            return trade;
        }
        public void TrackOrderFilled(OrderEvent orderEvent)
        {
            if (orderEvent?.OrderId != null)
            {
                var orderId = orderEvent.OrderId;
                var algorithmTime = Algorithm?.Time ?? DateTime.MinValue;
                var trade = TradeTracker.FindTradeByAnyOrderId(orderId);
                if (trade == null)
                {
                    var orderTicket = Algorithm?.Transactions?.GetOrderTicket(orderId);
                    var orderTag = orderTicket?.Tag ?? string.Empty;
                    if (string.IsNullOrEmpty(orderTag))
                    {
                        OnOrderFilled(orderEvent);
                        NotifyTradeAnalyzer(orderEvent);
                        return;
                    }
                    var symbolValue = orderEvent.Symbol?.Value ?? "Unknown";
                    trade = TryBootstrapComboTrade(orderEvent, orderTicket);
                    if (trade != null)
                    {
                        SmartLog($"[TRADE TRACKING BOOTSTRAP] Created trade {trade.OrderId} from OrderEvent {orderId} (Tag='{orderTag}')");
                    }
                    else
                    {
                        SmartWarn(
                            $"[TRADE TRACKING] @ {algorithmTime:HH:mm:ss.fff} | Filled order {orderId} ({symbolValue}) Tag='{orderTag}' " +
                            $"could not be mapped. AllTrades={TradeTracker.AllTrades.Count}, Open={TradeTracker.OpenTrades.Count}");
                    }
                    if (trade == null && orderTicket?.Tag != null)
                    {
                        var parsedTag = TradeTag.Parse(orderTicket.Tag);
                        if ((parsedTag.IsStopLoss() || parsedTag.IsExit()) && parsedTag.TryGetParentEid(out int parentEid))
                        {
                            var parentTrade = TradeTracker.AllTrades.FirstOrDefault(t => t.OrderId == parentEid.ToString());
                            if (parentTrade != null && parentTrade.Status == "Open")
                            {
                                var closePrice = orderEvent.FillPrice;
                                var pnl = (closePrice - parentTrade.OpenPrice) * parentTrade.Quantity;
                                var closeTime = Algorithm?.UtcTime ?? DateTime.UtcNow;
                                TradeTracker.MarkTradeAsClosed(parentTrade.OrderId, closePrice, pnl, closeTime);
                                TradePersistence.SaveTrades(TradeTracker);
                                var exitType = parsedTag.IsEod() ? "EOD" : "Fallback";
                                SmartLog($"[{exitType} CLOSE] Force-closed parent E{parentEid} via unmapped fill {orderId} | " +
                                        $"Tag='{orderTicket.Tag}' | FillPrice: {closePrice:F2} | PnL: {pnl:F2}");
                                trade = parentTrade;  // Set for downstream processing
                            }
                            else if (parentTrade != null)
                            {
                                SmartDebug($"[PARENT EID] Found parent E{parentEid} but status is '{parentTrade.Status}' (not Open), skipping close");
                            }
                            else
                            {
                                SmartWarn($"[PARENT EID] No trade found for parent E{parentEid} parsed from tag '{orderTicket.Tag}'");
                            }
                        }
                    }
                }
                else
                {
                    SmartDebug($"[TRADE TRACKING] @ {algorithmTime:HH:mm:ss.fff} | Filled order {orderId} mapped to trade {trade.OrderId} (registered IDs: [{string.Join(", ", trade.OrderIds)}])");
                }
                if (trade != null && orderEvent.Symbol != null)
                {
                    var symbolId = orderEvent.Symbol.ID.ToString();
                    if (!trade.SymbolIds.Contains(symbolId))
                    {
                        trade.SymbolIds.Add(symbolId);
                    }
                    if (string.IsNullOrEmpty(trade.Symbol) || trade.Symbol == "Unknown")
                    {
                        trade.Symbol = orderEvent.Symbol.Value;
                    }
                    if (string.IsNullOrEmpty(trade.Underlying) &&
                        orderEvent.Symbol.SecurityType == SecurityType.Option &&
                        orderEvent.Symbol.Underlying != null)
                    {
                        trade.Underlying = orderEvent.Symbol.Underlying.Value;
                    }
                }
                if (trade != null && trade.Status != "Open")
                {
                    TradeTracker.MarkTradeAsOpen(trade.OrderId, orderEvent.FillPrice, (int)orderEvent.FillQuantity, Algorithm.UtcTime);
                    Debug($"Order filled: {orderId} at {orderEvent.FillPrice}, trade {trade.OrderId} marked as Open");
                }
                else if (trade == null)
                {
                    Debug($"Order filled: {orderId} at {orderEvent.FillPrice}, but no trade found (likely child order)");
                }
                OnOrderFilled(orderEvent);
                NotifyTradeAnalyzer(orderEvent);
                TradePersistence.SaveTrades(TradeTracker);
            }
        }
        private TradeRecord TryBootstrapComboTrade(OrderEvent orderEvent, OrderTicket orderTicket)
        {
            if (orderTicket == null || string.IsNullOrEmpty(orderTicket.Tag))
            {
                return null;
            }
            var tag = orderTicket.Tag;
            var orderId = orderEvent.OrderId;
            var parsedTag = TradeTag.Parse(tag);
            var isCloseTag = parsedTag.IsExit();
            if (isCloseTag)
            {
                var today = Algorithm.Time.Date;
                var closeTickets = Algorithm.Transactions.GetOrderTickets()
                    .Where(t => t != null &&
                               t.Tag == tag &&
                               t.Time.Date == today)
                    .ToList();
                var closeSymbolIds = new HashSet<string>();
                foreach (var ticket in closeTickets)
                {
                    if (ticket?.Symbol != null)
                    {
                        closeSymbolIds.Add(ticket.Symbol.ID.ToString());
                    }
                }
                if (closeSymbolIds.Count > 0)
                {
                    var matchingTrade = TradeTracker.OpenTrades
                        .FirstOrDefault(t => t.SymbolIds.Any(sid => closeSymbolIds.Contains(sid)));
                    if (matchingTrade == null)
                    {
                        matchingTrade = TradeTracker.WorkingTrades
                            .FirstOrDefault(t => t.SymbolIds.Any(sid => closeSymbolIds.Contains(sid)));
                    }
                    if (matchingTrade != null)
                    {
                        foreach (var ticket in closeTickets)
                        {
                            if (ticket?.OrderId != null && !matchingTrade.OrderIds.Contains(ticket.OrderId))
                            {
                                matchingTrade.OrderIds.Add(ticket.OrderId);
                            }
                        }
                        SmartLog($"[BOOTSTRAP CLOSE] Mapped exit OrderIDs [{string.Join(", ", closeTickets.Where(t => t != null).Select(t => t.OrderId))}] " +
                                $"to existing {matchingTrade.Status} trade {matchingTrade.OrderId} (Tag='{tag}')");
                        return matchingTrade;
                    }
                }
                SmartLog($"[BOOTSTRAP CLOSE] WARNING: No matching Open trade found for close tag '{tag}', creating fallback trade");
            }
            var isComboEntryTag = parsedTag.IsEntry() || (parsedTag.Strategy.Equals(Name, StringComparison.OrdinalIgnoreCase) && !isCloseTag);
            if (!isComboEntryTag)
            {
                return null;
            }
            var today2 = Algorithm.Time.Date;
            var siblingTickets = Algorithm.Transactions.GetOrderTickets()
                .Where(t => t != null &&
                           t.Tag == tag &&
                           t.Time.Date == today2 &&
                           t.OrderId != orderId) // Exclude current order
                .OrderBy(t => t.OrderId)
                .ToList();
            var allLegTickets = new List<OrderTicket> { orderTicket };
            allLegTickets.AddRange(siblingTickets);
            foreach (var ticket in allLegTickets)
            {
                var existingTrade = TradeTracker.FindTradeByAnyOrderId(ticket.OrderId);
                if (existingTrade != null)
                {
                    if (!existingTrade.OrderIds.Contains(orderId))
                    {
                        existingTrade.OrderIds.Add(orderId);
                    }
                    return existingTrade;
                }
            }
            var primaryTicket = allLegTickets.OrderBy(t => t.OrderId).First();
            var primaryOrderId = primaryTicket.OrderId.ToString();
            var symbol = orderEvent.Symbol?.Value ?? primaryTicket.Symbol?.Value ?? "Unknown";
            var strategyName = Name;
            var trade = new TradeRecord(primaryOrderId, symbol, strategyName, tag);
            foreach (var ticket in allLegTickets)
            {
                if (ticket?.OrderId != null && !trade.OrderIds.Contains(ticket.OrderId))
                {
                    trade.OrderIds.Add(ticket.OrderId);
                }
                if (ticket?.Symbol != null)
                {
                    var symbolId = ticket.Symbol.ID.ToString();
                    if (!trade.SymbolIds.Contains(symbolId))
                    {
                        trade.SymbolIds.Add(symbolId);
                    }
                    if (string.IsNullOrEmpty(trade.Underlying) &&
                        ticket.Symbol.SecurityType == SecurityType.Option &&
                        ticket.Symbol.Underlying != null)
                    {
                        trade.Underlying = ticket.Symbol.Underlying.Value;
                    }
                }
            }
            TradeTracker.AllTrades.Add(trade);
            TradeTracker.WorkingTrades.Add(trade);
            SmartLog($"[BOOTSTRAP COMBO] Created trade {trade.OrderId} from {allLegTickets.Count} legs " +
                    $"(OrderIDs=[{string.Join(", ", trade.OrderIds.OrderBy(id => id))}], " +
                    $"SymbolIDs={trade.SymbolIds.Count}, Tag='{tag}')");
            return trade;
        }
        protected void RegisterChildOrder(int parentEntryOrderId, int childOrderId)
        {
            var trade = TradeTracker.FindTradeByAnyOrderId(parentEntryOrderId);
            if (trade != null && !trade.OrderIds.Contains(childOrderId))
            {
                trade.OrderIds.Add(childOrderId);
            }
        }
        protected virtual void OnOrderFilled(OrderEvent orderEvent) { }
        protected void NotifyTradeAnalyzer(OrderEvent orderEvent)
        {
            if (!Algorithm.LiveMode) return;
            var analyzerUrl = Algorithm.GetParameter("TradeAnalyzerWebhookUrl", "");
            if (string.IsNullOrEmpty(analyzerUrl)) return;
            try
            {
                var orderTicket = Algorithm.Transactions.GetOrderTicket(orderEvent.OrderId);
                var tag = orderTicket?.Tag ?? "";
                var payload = new System.Collections.Generic.Dictionary<string, object>
                {
                    { "type", "order_filled" },
                    { "strategy", Name },
                    { "orderId", orderEvent.OrderId },
                    { "symbol", orderEvent.Symbol?.Value ?? "UNKNOWN" },
                    { "quantity", orderEvent.FillQuantity },
                    { "price", orderEvent.FillPrice },
                    { "time", Algorithm.Time.ToString("o") },
                    { "tag", tag }
                };
                var authUser = Algorithm.GetParameter("TradeAnalyzerAuthUser", "");
                var authPass = Algorithm.GetParameter("TradeAnalyzerAuthPass", "");
                var headers = new System.Collections.Generic.Dictionary<string, string>();
                if (!string.IsNullOrEmpty(authUser) && !string.IsNullOrEmpty(authPass))
                {
                    var authString = $"{authUser}:{authPass}";
                    var authBytes = System.Text.Encoding.UTF8.GetBytes(authString);
                    var authBase64 = System.Convert.ToBase64String(authBytes);
                    headers["Authorization"] = $"Basic {authBase64}";
                }
                var jsonPayload = System.Text.Json.JsonSerializer.Serialize(payload);
                Algorithm.Notify.Web(analyzerUrl, jsonPayload, headers);
                SmartDebug($"[TRADE ANALYZER] Sent notification for order {orderEvent.OrderId}");
            }
            catch (Exception ex)
            {
                SmartWarn($"[TRADE ANALYZER] Failed to send notification: {ex.Message}");
            }
        }
        public virtual void OnOrderEventRouted(OrderEvent orderEvent) { }
        protected void TrackPositionClosed(string orderId, decimal closePrice, decimal pnl)
        {
            var closeTime = Algorithm?.UtcTime ?? DateTime.UtcNow;
            TradeTracker.MarkTradeAsClosed(orderId, closePrice, pnl, closeTime);
            var tag = "";
            if (int.TryParse(orderId, out var parsedOrderId))
            {
                var trade = TradeTracker.FindTradeByAnyOrderId(parsedOrderId);
                tag = trade != null && !string.IsNullOrEmpty(trade.OrderTag) ? $" (Entry: {trade.OrderTag})" : "";
            }
            Debug($"Position closed: {orderId}{tag}, P&L: {pnl}");
            TradePersistence.SaveTrades(TradeTracker);
        }
        public void TrackOrderCancelled(string orderId)
        {
            TradeTracker.CancelWorkingTrade(orderId);
            Debug($"Order cancelled: {orderId}");
            TradePersistence.SaveTrades(TradeTracker);
        }
        public virtual void OnAssignmentOrderEvent(OrderEvent assignmentEvent)
        {
            try
            {
                SmartLog($"Assignment event received: {assignmentEvent.Symbol} at {Algorithm.Time}");
                AssignmentHandler.HandleAssignment(Algorithm, assignmentEvent);
                OnAssignmentHandled(assignmentEvent);
            }
            catch (Exception ex)
            {
                SmartError($"Error in assignment handling: {ex.Message}");
                OnError(ex, ErrorSeverity.Error, true, "Assignment handling error");
            }
        }
        protected virtual void OnAssignmentHandled(OrderEvent assignmentEvent)
        {
            SmartLog($"Assignment handling completed for {assignmentEvent.Symbol}");
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Orders;
using QuantConnect.Securities;
using CoreAlgo.Architecture.QC.Helpers;
using CoreAlgo.Architecture.Core.Configuration;
using CoreAlgo.Architecture.Core.Services;
using CoreAlgo.Architecture.Core.Models;
using CoreAlgo.Architecture.Core.Execution;
namespace CoreAlgo.Architecture.Core.Implementations
{
    public partial class SimpleBaseStrategy
    {
        protected OrderTicket MarketOrder(Symbol symbol, decimal quantity, string tag = "")
        {
            OrderTicket ticket;
            if (ShouldUseSmartOrderManager())
            {
                ticket = SmartOrderManager.SmartMarketOrder(symbol, quantity, tag);
            }
            else
            {
                ticket = Algorithm.MarketOrder(symbol, quantity, tag: tag, asynchronous: true);
            }
            if (ticket != null && ticket.Status != OrderStatus.Invalid)
            {
                TrackWorkingOrder(ticket, Name);
            }
            return ticket;
        }
        protected OrderTicket LimitOrder(Symbol symbol, decimal quantity, decimal limitPrice, string tag = "")
        {
            var rounded = PriceRounding.RoundLimitPrice(Algorithm.Securities, symbol, quantity, limitPrice);
            var ticket = Algorithm.LimitOrder(symbol, quantity, rounded, tag: tag, asynchronous: true);
            if (ticket != null && ticket.Status != OrderStatus.Invalid)
            {
                TrackWorkingOrder(ticket, Name);
            }
            return ticket;
        }
        protected OrderTicket StopMarketOrder(Symbol symbol, decimal quantity, decimal stopPrice, string tag = "")
        {
            var rounded = PriceRounding.RoundStopPrice(Algorithm.Securities, symbol, quantity, stopPrice);
            var ticket = Algorithm.StopMarketOrder(symbol, quantity, rounded, tag: tag, asynchronous: true);
            if (ticket != null && ticket.Status != OrderStatus.Invalid)
            {
                TrackWorkingOrder(ticket, Name);
            }
            return ticket;
        }
        protected OrderTicket StopLimitOrder(Symbol symbol, decimal quantity, decimal stopPrice, decimal limitPrice, string tag = "")
        {
            OrderTicket ticket;
            try
            {
                var (roundedStop, roundedLimit) = PriceRounding.RoundStopLimitPrices(Algorithm.Securities, symbol, quantity, stopPrice, limitPrice);
                if (quantity < 0) // Sell stop-limit
                {
                    if (roundedLimit >= roundedStop)
                    {
                        SmartWarn($"[STOP-LIMIT INVALID] Sell: limit {roundedLimit} >= stop {roundedStop}. Falling back to StopMarketOrder.");
                        ticket = Algorithm.StopMarketOrder(symbol, quantity, roundedStop, tag: tag, asynchronous: true);
                    }
                    else
                    {
                        ticket = Algorithm.StopLimitOrder(symbol, quantity, roundedStop, roundedLimit, tag: tag, asynchronous: true);
                    }
                }
                else // Buy stop-limit
                {
                    if (roundedLimit <= roundedStop)
                    {
                        SmartWarn($"[STOP-LIMIT INVALID] Buy: limit {roundedLimit} <= stop {roundedStop}. Falling back to StopMarketOrder.");
                        ticket = Algorithm.StopMarketOrder(symbol, quantity, roundedStop, tag: tag, asynchronous: true);
                    }
                    else
                    {
                        ticket = Algorithm.StopLimitOrder(symbol, quantity, roundedStop, roundedLimit, tag: tag, asynchronous: true);
                    }
                }
            }
            catch (ArgumentException ex)
            {
                SmartError($"[STOP-LIMIT ERROR] {ex.Message}. Falling back to StopMarketOrder at {stopPrice:F4}.");
                ticket = Algorithm.StopMarketOrder(symbol, quantity, stopPrice, tag: tag, asynchronous: true);
            }
            if (ticket != null && ticket.Status != OrderStatus.Invalid)
            {
                TrackWorkingOrder(ticket, Name);
            }
            return ticket;
        }
        protected OrderTicket TrailingStopOrder(Symbol symbol, decimal quantity, decimal trailingAmount, bool isDollarTrailing, string tag = "")
        {
            var rounded = PriceRounding.RoundTrailingStopPrice(Algorithm.Securities, symbol, quantity, trailingAmount, isDollarTrailing);
            var ticket = Algorithm.TrailingStopOrder(symbol, quantity, rounded, isDollarTrailing, tag: tag, asynchronous: true);
            if (ticket != null && ticket.Status != OrderStatus.Invalid)
            {
                TrackWorkingOrder(ticket, Name);
            }
            return ticket;
        }
        protected List<OrderTicket> ComboMarketOrder(List<Leg> legs, int quantity, string tag = "")
		{
			List<OrderTicket> tickets;
			if (ShouldUseSmartOrderManager())
			{
				tickets = SmartOrderManager.SmartComboMarketOrder(legs, quantity, tag);
			}
			else
			{
				tickets = Algorithm.ComboMarketOrder(legs, quantity, tag: tag, asynchronous: true);
			}
			if (tickets != null && tickets.Count > 0 && tickets.Any(t => t != null && t.Status != OrderStatus.Invalid))
			{
				var symbolIds = legs?.Select(leg => leg.Symbol?.ID.ToString())
					.Where(id => !string.IsNullOrEmpty(id))
					.ToList() ?? new List<string>();
				TrackComboOrder(tickets, symbolIds, Name, tag);
			}
			return tickets;
		}
        protected OrderTicket MarketOnOpenOrder(Symbol symbol, int quantity, string tag = "")
        {
            var ticket = Algorithm.MarketOnOpenOrder(symbol, quantity, tag: tag, asynchronous: true);
            if (ticket != null && ticket.Status != OrderStatus.Invalid)
            {
                TrackWorkingOrder(ticket, Name);
            }
            return ticket;
        }
        protected OrderTicket MarketOnCloseOrder(Symbol symbol, int quantity, string tag = "")
        {
            var ticket = Algorithm.MarketOnCloseOrder(symbol, quantity, tag: tag, asynchronous: true);
            if (ticket != null && ticket.Status != OrderStatus.Invalid)
            {
                TrackWorkingOrder(ticket, Name);
            }
            return ticket;
        }
        protected List<OrderTicket> ComboLimitOrder(List<Leg> legs, int quantity, decimal limitPrice, string tag = "")
        {
            try
            {
                return Algorithm.ComboLimitOrder(legs, quantity, limitPrice, tag: tag, asynchronous: true);
            }
            catch (Exception)
            {
                return Algorithm.ComboLimitOrder(legs, quantity, limitPrice, tag: tag);
            }
        }
        protected OrderTicket Liquidate(Symbol symbol, string tag = "")
        {
            dynamic result = Algorithm.Liquidate(symbol: symbol, tag: tag, asynchronous: true);
            if (result is List<OrderTicket> tickets && tickets.Count > 0)
            {
                return tickets[0];
            }
            return result as OrderTicket ?? null;
        }
        protected void LiquidateAll(string tag = "")
        {
            Algorithm.Liquidate(asynchronous: true);
        }
        protected void SetHoldingsTargets(List<PortfolioTarget> targets, bool liquidateExistingHoldings = false)
        {
            Algorithm.SetHoldings(targets, liquidateExistingHoldings: liquidateExistingHoldings, asynchronous: true);
        }
        private bool ShouldUseSmartOrderManager()
        {
            return SmartOrderManager != null;
        }
        private void SetupSmartPricing()
        {
            if (SmartOrderManager == null)
            {
                Logger.Warning("SetupSmartPricing called but SmartOrderManager is null");
                return;
            }
            var smartPricingMode = "Off";
            if (Config is StrategyConfig strategyConfig)
            {
                smartPricingMode = strategyConfig.GetParameterValue("SmartPricingMode", "Off")?.ToString() ?? "Off";
                Logger.Info($"SmartPricing mode from Config: {smartPricingMode}");
            }
            else
            {
                smartPricingMode = Algorithm.GetParameter("SmartPricingMode", "Off");
                Logger.Info($"SmartPricing mode from Algorithm.GetParameter: {smartPricingMode}");
            }
            if (!string.Equals(smartPricingMode, "Off", StringComparison.OrdinalIgnoreCase))
            {
                try
                {
                    Logger.Info($"Creating SmartPricing engine for mode: {smartPricingMode}");
                    var pricingEngine = SmartPricingEngineFactory.Create(smartPricingMode);
                    SmartOrderManager.SetPricingEngine(pricingEngine);
                    Logger.Info($"SmartPricing enabled with mode: {smartPricingMode}");
                }
                catch (Exception ex)
                {
                    Logger.Error($"Failed to setup SmartPricing: {ex.Message}");
                }
            }
            else
            {
                Logger.Info("SmartPricing disabled (mode is Off)");
            }
        }
        protected OrderTicket SubmitComboOrder(List<Leg> legs, string tag = "")
        {
            try
            {
                var underlyingSymbol = legs.FirstOrDefault()?.Symbol.Underlying.Value ?? "";
                if (!string.IsNullOrEmpty(underlyingSymbol) &&
                    Algorithm.Portfolio.MarginRemaining < 0)
                {
                    SmartWarn($"Insufficient margin for combo order on {underlyingSymbol}");
                    return null;
                }
                var tickets = ComboMarketOrder(legs, 1, tag: tag);
                return tickets?.FirstOrDefault();
            }
            catch (Exception ex)
            {
                Error($"Failed to submit combo order: {ex.Message}");
                return null;
            }
        }
        protected OrderTicket SubmitOrderWithRetry(Symbol symbol, decimal quantity, decimal? limitPrice = null, int maxRetries = 3)
        {
            var marginUtilization = Algorithm.Portfolio.TotalMarginUsed / Algorithm.Portfolio.TotalPortfolioValue;
            if (marginUtilization > 0.7m)
            {
                SmartWarn($"Margin utilization too high, skipping order for {symbol}");
                return null;
            }
            for (int i = 0; i < maxRetries; i++)
            {
                try
                {
                    var ticket = limitPrice.HasValue ?
                        LimitOrder(symbol, quantity, limitPrice.Value) :
                        MarketOrder(symbol, quantity);
                    if (ticket != null)
                        return ticket;
                }
                catch (Exception ex)
                {
                    Debug($"Order attempt {i + 1} failed: {ex.Message}");
                    if (i == maxRetries - 1)
                        throw;
                }
            }
            return null;
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Data;
using QuantConnect.Orders;
using QuantConnect.Securities;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities.Option;
using CoreAlgo.Architecture.Core.Interfaces;
using CoreAlgo.Architecture.Core.Models;
using CoreAlgo.Architecture.Core.Configuration;
using CoreAlgo.Architecture.Core.Helpers;
using CoreAlgo.Architecture.Core.Services;
using CoreAlgo.Architecture.Core.Execution;
using CoreAlgo.Architecture.QC.Helpers;
using System.Collections.Concurrent;
namespace CoreAlgo.Architecture.Core.Implementations
{
    public abstract partial class SimpleBaseStrategy : IStrategy, IAlgorithmContext
    {
        public StrategyConfig Config { get; private set; }
        protected EntryRestrictions EntryRestrictions { get; private set; }
        protected ExitRestrictions ExitRestrictions { get; private set; }
        protected StrikeRangeCalculator StrikeRangeCalculator { get; private set; }
        private StrategyState _state = StrategyState.NotInitialized;
        private Dictionary<string, object> _parameters = new Dictionary<string, object>();
        public QCAlgorithm Algorithm { get; private set; }
        public ISmartLogger Logger { get; private set; }
        public SmartOrderManager SmartOrderManager { get; private set; }
        protected PositionOverlapManager OverlapManager { get; private set; }
        public TradeTracker TradeTracker { get; private set; } = new TradeTracker();
        public TradePersistenceService TradePersistence { get; private set; }
        public PortfolioReporter Reporter { get; private set; }
        public abstract string Name { get; }
        public abstract string Description { get; }
        public virtual string Version => "1.0.0";
        public StrategyState State
        {
            get => _state;
            private set
            {
                if (_state != value)
                {
                    var previousState = _state;
                    _state = value;
                    StateChanged?.Invoke(this, new StrategyStateChangedEventArgs(previousState, value));
                }
            }
        }
        public Dictionary<string, object> Parameters => _parameters;
        public event EventHandler<StrategyStateChangedEventArgs> StateChanged;
        public event EventHandler<StrategyErrorEventArgs> ErrorOccurred;
        public virtual void Initialize(QCAlgorithm algorithm)
        {
            Algorithm = algorithm;
            StrikeRangeCalculator = new StrikeRangeCalculator(algorithm);
            if (Logger != null)
            {
                var context = new SimpleAlgorithmContext(algorithm, Logger);
                SmartOrderManager = new SmartOrderManager(algorithm, context);
                TradePersistence = new TradePersistenceService(context);
                var riskManager = new CoreAlgoRiskManager(context);
                Reporter = new PortfolioReporter(context, riskManager);
                TradePersistence.ClearPositionsIfBacktest();
                var loadedTracker = TradePersistence.LoadTrades();
                if (loadedTracker != null)
                {
                    TradeTracker = loadedTracker;
                    Logger.Info($"[{Name}] Restored {TradeTracker.AllTrades.Count} trades from ObjectStore");
                }
                SetupSmartPricing();
            }
            State = StrategyState.Initializing;
            OnInitialize();
            State = StrategyState.Ready;
        }
        public virtual void Initialize(QCAlgorithm algorithm, Dictionary<string, object> parameters)
        {
            Algorithm = algorithm;
            _parameters = parameters ?? new Dictionary<string, object>();
            StrikeRangeCalculator = new StrikeRangeCalculator(algorithm);
            State = StrategyState.Initializing;
            OnInitialize();
            State = StrategyState.Ready;
        }
        public void SetContext(ISmartLogger logger)
        {
            Logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }
        public virtual void Configure<T>() where T : StrategyConfig, new()
        {
            Config = new T();
            Config.LoadFromParameters(this); // Pass context instead of separate parameters
            var validationErrors = Config.Validate();
            if (validationErrors.Length > 0)
            {
                var errorMessage = $"Configuration validation failed:\n{string.Join("\n", validationErrors)}";
                Logger.Error(errorMessage);
                throw new InvalidOperationException(errorMessage);
            }
            Logger.Debug($"Configuration validation passed for {typeof(T).Name}");
            EntryRestrictions = new EntryRestrictions(Config, Algorithm);
            ExitRestrictions = new ExitRestrictions(Config, Algorithm);
            OnConfigured();
        }
        public abstract void OnInitialize();
        protected SecurityPortfolioManager Portfolio => Algorithm.Portfolio;
        protected SecurityManager Securities => Algorithm.Securities;
        protected DateTime Time => Algorithm.Time;
        protected virtual void OnConfigured()
        {
            if (Config != null && Config.EnableOverlapPrevention && SmartOrderManager != null && Logger != null)
            {
                var context = new SimpleAlgorithmContext(Algorithm, Logger);
                OverlapManager = new PositionOverlapManager(context);
                SmartOrderManager.SetOverlapManager(OverlapManager);
                Logger.Info($"[{Name}] Position overlap prevention enabled (Mode: {Config.OverlapPreventionMode})");
            }
            if (Reporter != null && Config != null)
            {
                Reporter.Initialize(Config.AccountSize);
                Logger.Info($"[{Name}] Portfolio reporter initialized with ${Config.AccountSize:N0} starting capital");
            }
        }
        public void EnsureSmartPricingInitialized()
        {
            Logger.Info($"EnsureSmartPricingInitialized called. SmartOrderManager null: {SmartOrderManager == null}, Logger null: {Logger == null}, Algorithm null: {Algorithm == null}");
            if (SmartOrderManager == null && Logger != null && Algorithm != null)
            {
                Logger.Info("Creating SmartOrderManager...");
                var context = new SimpleAlgorithmContext(Algorithm, Logger);
                SmartOrderManager = new SmartOrderManager(Algorithm, context);
                SetupSmartPricing();
                Logger.Info("SmartOrderManager initialized after strategy setup");
            }
            else
            {
                Logger.Warning($"SmartOrderManager initialization skipped - already exists: {SmartOrderManager != null}");
            }
        }
        protected virtual bool ShouldExecuteTrade(Slice slice, out string blockReason)
        {
            blockReason = "";
            if (Config == null) return true;
            if (Config.TradingStartTime != TimeSpan.Zero || Config.TradingEndTime != TimeSpan.Zero)
            {
                var timeOfDay = slice.Time.TimeOfDay;
                if (timeOfDay < Config.TradingStartTime || timeOfDay > Config.TradingEndTime)
                {
                    blockReason = $"Outside trading hours ({Config.TradingStartTime:hh\\:mm}-{Config.TradingEndTime:hh\\:mm})";
                    return false;
                }
            }
            if (Config.MaxPositions > 0)
            {
                var activePositions = Portfolio.Where(p => p.Value.Invested).Count();
                if (activePositions >= Config.MaxPositions)
                {
                    blockReason = $"Max positions reached ({activePositions}/{Config.MaxPositions})";
                    return false;
                }
            }
            var marginThreshold = Config.GetParameterValue("MaxMarginUtilization", 0.7m) as decimal? ?? 0.7m;
            if (marginThreshold > 0 && Portfolio.TotalPortfolioValue > 0)
            {
                var marginUtilization = Portfolio.TotalMarginUsed / Portfolio.TotalPortfolioValue;
                if (marginUtilization > marginThreshold)
                {
                    blockReason = $"Margin utilization too high ({marginUtilization:P0} > {marginThreshold:P0})";
                    return false;
                }
            }
            if (Config.UseEntryTimeWindow)
            {
                var tod = slice.Time.TimeOfDay;
                if (tod < Config.EntryWindowStart || tod > Config.EntryWindowEnd)
                {
                    blockReason = $"Outside entry window ({Config.EntryWindowStart:hh\\:mm}-{Config.EntryWindowEnd:hh\\:mm})";
                    return false;
                }
            }
            var minCashReserveRatio = Config.GetParameterValue("MinCashReserveRatio", 0.05m) as decimal? ?? 0.05m;
            if (minCashReserveRatio > 0 && Portfolio.TotalPortfolioValue > 0)
            {
                var cashRatio = Portfolio.Cash / Portfolio.TotalPortfolioValue;
                if (cashRatio < minCashReserveRatio)
                {
                    blockReason = $"Insufficient cash reserves ({cashRatio:P1} < {minCashReserveRatio:P1})";
                    return false;
                }
            }
            if (!OnShouldExecuteTrade(slice, out var customReason))
            {
                blockReason = customReason;
                return false;
            }
            return true;
        }
        protected virtual bool OnShouldExecuteTrade(Slice slice, out string blockReason)
        {
            blockReason = "";
            return true;
        }
        protected virtual void CheckExitConditions(Slice slice)
        {
            if (ExitRestrictions == null) return;
            var positionsToCheck = Portfolio.Where(p => p.Value.Invested).ToList();
            foreach (var position in positionsToCheck)
            {
                if (ExitRestrictions.ShouldExitPosition(position.Key, slice, out var reason))
                {
                    SmartLog($"[EXIT SIGNAL] {position.Key}: {reason}");
                    OnExitSignal(position.Key, reason);
                }
            }
        }
        protected virtual void OnExitSignal(Symbol symbol, string reason)
        {
            try
            {
                var ticket = Liquidate(symbol, reason);
                if (ticket != null)
                {
                    SmartLog($"[EXIT ORDER] Liquidating {symbol}: {reason}");
                }
                else
                {
                    SmartError($"[EXIT ERROR] Failed to liquidate {symbol}: {reason}");
                }
            }
            catch (Exception ex)
            {
                SmartError($"[EXIT ERROR] Exception liquidating {symbol}: {ex.Message}");
            }
        }
        public virtual void Execute(Slice slice)
        {
            if (State == StrategyState.Ready)
                State = StrategyState.Running;
            if (State != StrategyState.Running)
            {
                return;
            }
            try
            {
                OnPreExecuteAlways(slice);
                CheckExitConditions(slice);
                if (!ShouldExecuteTrade(slice, out var blockReason))
                {
                    if (slice.Time.Minute == 0 && slice.Time.Second == 0)
                    {
                        SmartLog($"[TRADING BLOCKED] {blockReason}");
                    }
                    return;
                }
                OnExecute(slice);
            }
            catch (Exception ex)
            {
                Logger.Error($"Error during strategy execution: {ex.Message}");
                Logger.Error($"Stack trace: {ex.StackTrace}");
                OnError(ex, ErrorSeverity.Error, true, "Error during strategy execution");
                throw;
            }
        }
        public virtual void Shutdown()
        {
            if (State == StrategyState.Shutdown || State == StrategyState.ShuttingDown)
                return;
            try
            {
                State = StrategyState.ShuttingDown;
                OnShutdown();
                State = StrategyState.Shutdown;
            }
            catch (Exception ex)
            {
                OnError(ex, ErrorSeverity.Error, false, "Error during strategy shutdown");
                State = StrategyState.Error;
                throw;
            }
        }
        public virtual bool Validate()
        {
            try
            {
                return OnValidate();
            }
            catch (Exception ex)
            {
                OnError(ex, ErrorSeverity.Warning, true, "Error during strategy validation");
                return false;
            }
        }
        public virtual Dictionary<string, double> GetPerformanceMetrics()
        {
            var metrics = new Dictionary<string, double>();
            try
            {
                metrics["TotalPortfolioValue"] = (double)Portfolio.TotalPortfolioValue;
                metrics["Cash"] = (double)Portfolio.Cash;
                metrics["TotalHoldingsValue"] = (double)Portfolio.TotalHoldingsValue;
                metrics["UnrealizedProfit"] = (double)Portfolio.TotalUnrealizedProfit;
                metrics["TotalProfit"] = (double)Portfolio.TotalProfit;
                OnGetPerformanceMetrics(metrics);
            }
            catch (Exception ex)
            {
                OnError(ex, ErrorSeverity.Warning, true, "Error getting performance metrics");
            }
            return metrics;
        }
        public virtual void Reset()
        {
            if (State == StrategyState.Running)
                throw new InvalidOperationException("Cannot reset strategy while running");
            try
            {
                OnReset();
                State = StrategyState.Ready;
            }
            catch (Exception ex)
            {
                OnError(ex, ErrorSeverity.Error, false, "Error during strategy reset");
                throw;
            }
        }
        protected abstract void OnExecute(Slice slice);
        protected virtual void OnShutdown()
        {
            try
            {
                SmartLog("Exporting trade tracking data...");
                TradeTracker.ExportToLogs(message => SmartLog(message));
                SmartLog(TradeTracker.GetSummary());
            }
            catch (Exception ex)
            {
                SmartError($"Failed to export trade data: {ex.Message}");
            }
        }
        protected virtual bool OnValidate() => true;
        protected virtual void OnGetPerformanceMetrics(Dictionary<string, double> metrics) { }
        protected virtual void OnReset() { }
        public virtual void OnSecuritiesChanged(SecurityChanges changes)
        {
        }
    }
}
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Globalization;
    using System.Drawing;
    using QuantConnect;
    using QuantConnect.Algorithm.Framework;
    using QuantConnect.Algorithm.Framework.Selection;
    using QuantConnect.Algorithm.Framework.Alphas;
    using QuantConnect.Algorithm.Framework.Portfolio;
    using QuantConnect.Algorithm.Framework.Portfolio.SignalExports;
    using QuantConnect.Algorithm.Framework.Execution;
    using QuantConnect.Algorithm.Framework.Risk;
    using QuantConnect.Algorithm.Selection;
    using QuantConnect.Api;
    using QuantConnect.Parameters;
    using QuantConnect.Benchmarks;
    using QuantConnect.Brokerages;
    using QuantConnect.Commands;
    using QuantConnect.Configuration;
    using QuantConnect.Util;
    using QuantConnect.Interfaces;
    using QuantConnect.Algorithm;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Auxiliary;
    using QuantConnect.Data.Consolidators;
    using QuantConnect.Data.Custom;
    using QuantConnect.Data.Custom.IconicTypes;
    using QuantConnect.DataSource;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.Shortable;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Notifications;
    using QuantConnect.Orders;
    using QuantConnect.Orders.Fees;
    using QuantConnect.Orders.Fills;
    using QuantConnect.Orders.OptionExercise;
    using QuantConnect.Orders.Slippage;
    using QuantConnect.Orders.TimeInForces;
    using QuantConnect.Python;
    using QuantConnect.Scheduling;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Equity;
    using QuantConnect.Securities.Future;
    using QuantConnect.Securities.Option;
    using QuantConnect.Securities.Positions;
    using QuantConnect.Securities.Forex;
    using QuantConnect.Securities.Crypto;
    using QuantConnect.Securities.CryptoFuture;
    using QuantConnect.Securities.IndexOption;
    using QuantConnect.Securities.Interfaces;
    using QuantConnect.Securities.Volatility;
    using QuantConnect.Storage;
    using QuantConnect.Statistics;
    using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
    using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
    using Calendar = QuantConnect.Data.Consolidators.Calendar;
namespace CoreAlgo.Architecture.Core.Implementations
{
    public class StrategyStateChangedEventArgs : EventArgs
    {
        public StrategyState PreviousState { get; }
        public StrategyState NewState { get; }
        public DateTime Timestamp { get; }
        public string Message { get; }
        public StrategyStateChangedEventArgs(StrategyState previousState, StrategyState newState, string message = null)
        {
            PreviousState = previousState;
            NewState = newState;
            Timestamp = DateTime.UtcNow;
            Message = message;
        }
    }
    public class StrategyErrorEventArgs : EventArgs
    {
        public Exception Error { get; }
        public ErrorSeverity Severity { get; }
        public DateTime Timestamp { get; }
        public bool CanContinue { get; }
        public string Context { get; }
        public StrategyErrorEventArgs(Exception error, ErrorSeverity severity, bool canContinue, string context = null)
        {
            Error = error;
            Severity = severity;
            CanContinue = canContinue;
            Context = context;
            Timestamp = DateTime.UtcNow;
        }
    }
    public enum ErrorSeverity
    {
        Info,
        Warning,
        Error,
        Critical
    }
}
#region imports
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Globalization;
    using System.Drawing;
    using QuantConnect;
    using QuantConnect.Algorithm.Framework;
    using QuantConnect.Algorithm.Framework.Selection;
    using QuantConnect.Algorithm.Framework.Alphas;
    using QuantConnect.Algorithm.Framework.Portfolio;
    using QuantConnect.Algorithm.Framework.Portfolio.SignalExports;
    using QuantConnect.Algorithm.Framework.Execution;
    using QuantConnect.Algorithm.Framework.Risk;
    using QuantConnect.Algorithm.Selection;
    using QuantConnect.Api;
    using QuantConnect.Parameters;
    using QuantConnect.Benchmarks;
    using QuantConnect.Brokerages;
    using QuantConnect.Commands;
    using QuantConnect.Configuration;
    using QuantConnect.Util;
    using QuantConnect.Interfaces;
    using QuantConnect.Algorithm;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Auxiliary;
    using QuantConnect.Data.Consolidators;
    using QuantConnect.Data.Custom;
    using QuantConnect.Data.Custom.IconicTypes;
    using QuantConnect.DataSource;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.Shortable;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Notifications;
    using QuantConnect.Orders;
    using QuantConnect.Orders.Fees;
    using QuantConnect.Orders.Fills;
    using QuantConnect.Orders.OptionExercise;
    using QuantConnect.Orders.Slippage;
    using QuantConnect.Orders.TimeInForces;
    using QuantConnect.Python;
    using QuantConnect.Scheduling;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Equity;
    using QuantConnect.Securities.Future;
    using QuantConnect.Securities.Option;
    using QuantConnect.Securities.Positions;
    using QuantConnect.Securities.Forex;
    using QuantConnect.Securities.Crypto;
    using QuantConnect.Securities.CryptoFuture;
    using QuantConnect.Securities.IndexOption;
    using QuantConnect.Securities.Interfaces;
    using QuantConnect.Securities.Volatility;
    using QuantConnect.Storage;
    using QuantConnect.Statistics;
    using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
    using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
    using Calendar = QuantConnect.Data.Consolidators.Calendar;
#endregion
namespace CoreAlgo.Architecture.Core.Implementations
{
    /// <summary>
    /// Represents the state of a strategy
    /// </summary>
    public enum StrategyState
    {
        /// <summary>
        /// Strategy has not been initialized
        /// </summary>
        NotInitialized,

        /// <summary>
        /// Strategy is being initialized
        /// </summary>
        Initializing,

        /// <summary>
        /// Strategy is initialized and ready
        /// </summary>
        Ready,

        /// <summary>
        /// Strategy is actively running
        /// </summary>
        Running,

        /// <summary>
        /// Strategy is paused
        /// </summary>
        Paused,

        /// <summary>
        /// Strategy is being shut down
        /// </summary>
        ShuttingDown,

        /// <summary>
        /// Strategy has been shut down
        /// </summary>
        Shutdown,

        /// <summary>
        /// Strategy is in error state
        /// </summary>
        Error,

        /// <summary>
        /// Strategy is in maintenance mode
        /// </summary>
        Maintenance
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Securities;
using CoreAlgo.Architecture.Core.Interfaces;
namespace CoreAlgo.Architecture.Core.Implementations
{
    public class StrikeOverlapRule : IPositionOverlapRule
    {
        private readonly IAlgorithmContext _context;
        private readonly ISmartLogger _logger;
        private readonly decimal _minimumStrikeDistance = 5m; // Minimum distance between strikes
        public string RuleName => "StrikeOverlapRule";
        public string Description => "Prevents overlapping strikes that cause margin conflicts";
        public bool IsEnabled { get; set; } = true;
        public StrikeOverlapRule(IAlgorithmContext context, decimal minimumStrikeDistance = 5m)
        {
            _context = context;
            _logger = context.Logger;
            _minimumStrikeDistance = minimumStrikeDistance;
        }
        public ValidationResult Validate(
            Symbol proposedSymbol,
            decimal quantity,
            IEnumerable<KeyValuePair<Symbol, SecurityHolding>> existingPositions,
            string strategyTag = "")
        {
            if (!IsEnabled)
                return ValidationResult.Success();
            try
            {
                if (proposedSymbol.SecurityType != SecurityType.Option)
                    return ValidationResult.Success();
                var underlying = proposedSymbol.Underlying;
                var proposedStrike = proposedSymbol.ID.StrikePrice;
                var proposedExpiry = proposedSymbol.ID.Date;
                var proposedRight = proposedSymbol.ID.OptionRight;
                var existingOptions = existingPositions
                    .Where(p => p.Value.Invested &&
                               p.Key.SecurityType == SecurityType.Option &&
                               p.Key.Underlying == underlying)
                    .ToList();
                if (!existingOptions.Any())
                    return ValidationResult.Success();
                return ValidateExactDuplicates(
                    proposedStrike,
                    proposedExpiry,
                    proposedRight,
                    quantity,
                    existingOptions);
            }
            catch (System.Exception ex)
            {
                _logger.Error($"[{RuleName}] Error validating strike overlaps: {ex.Message}");
                return ValidationResult.Error($"Strike validation error: {ex.Message}");
            }
        }
        private ValidationResult ValidateExactDuplicates(
            decimal proposedStrike,
            System.DateTime proposedExpiry,
            OptionRight proposedRight,
            decimal quantity,
            List<KeyValuePair<Symbol, SecurityHolding>> existingOptions)
        {
            var exactMatch = existingOptions.FirstOrDefault(p =>
                p.Key.ID.StrikePrice == proposedStrike &&
                p.Key.ID.Date == proposedExpiry &&
                p.Key.ID.OptionRight == proposedRight);
            if (exactMatch.Key != null)
            {
                if (IsPositionModification(quantity, exactMatch.Value.Quantity))
                {
                    _logger.Debug($"[{RuleName}] Allowing position modification for {proposedStrike} {proposedRight} {proposedExpiry:yyyy-MM-dd}");
                    return ValidationResult.Success();
                }
                if (Math.Sign(quantity) == Math.Sign(exactMatch.Value.Quantity))
                {
                    return ValidationResult.Blocked(
                        $"Exact duplicate position: {proposedStrike} {proposedRight} {proposedExpiry:yyyy-MM-dd} " +
                        $"already exists with quantity {exactMatch.Value.Quantity}");
                }
            }
            return ValidationResult.Success();
        }
        private bool IsPositionModification(decimal proposedQuantity, decimal existingQuantity)
        {
            return Math.Abs(proposedQuantity) != Math.Abs(existingQuantity);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Globalization;
namespace CoreAlgo.Architecture.Core.Implementations
{
    public class TradeRecord
    {
        public string OrderId { get; set; } = string.Empty;
        public string Symbol { get; set; } = string.Empty;
        public string Strategy { get; set; } = string.Empty;
        public DateTime OpenTime { get; set; }
        public DateTime? CloseTime { get; set; }
        public decimal OpenPrice { get; set; }
        public decimal? ClosePrice { get; set; }
        public int Quantity { get; set; }
        public string Status { get; set; } = "Working"; // Working, Open, Closed, Cancelled, PartialFill
        public decimal PnL { get; set; }
        public string OrderTag { get; set; } = string.Empty;
        public string Underlying { get; set; } = string.Empty;
        public List<int> OrderIds { get; set; } = new List<int>();
        public List<string> SymbolIds { get; set; } = new List<string>();
        public int FilledQuantity { get; set; }
        public DateTime LastUpdateUtc { get; set; }
        public Dictionary<string, string> StrategyDetails { get; set; } = new Dictionary<string, string>();
        public TradeRecord(string orderId, string symbol, string strategy = "", string orderTag = "")
        {
            OrderId = orderId;
            Symbol = symbol;
            Strategy = strategy;
            OrderTag = orderTag;
            OpenTime = DateTime.UtcNow;
            LastUpdateUtc = DateTime.UtcNow;
            if (int.TryParse(orderId, out var parsedOrderId))
            {
                OrderIds.Add(parsedOrderId);
            }
        }
        public TradeRecord()
        {
            OpenTime = DateTime.UtcNow;
            LastUpdateUtc = DateTime.UtcNow;
        }
        public void MarkAsOpen(decimal fillPrice, int fillQuantity)
        {
            MarkAsOpen(fillPrice, fillQuantity, DateTime.UtcNow);
        }
        public void MarkAsOpen(decimal fillPrice, int fillQuantity, DateTime openTimeUtc)
        {
            Status = "Open";
            OpenPrice = fillPrice;
            Quantity = fillQuantity;
            FilledQuantity = fillQuantity;
            OpenTime = openTimeUtc;
            LastUpdateUtc = openTimeUtc;
        }
        public void MarkAsPartialFill(int filledQuantity, decimal averagePrice)
        {
            MarkAsPartialFill(filledQuantity, averagePrice, DateTime.UtcNow);
        }
        public void MarkAsPartialFill(int filledQuantity, decimal averagePrice, DateTime timeUtc)
        {
            Status = "PartialFill";
            FilledQuantity = filledQuantity;
            OpenPrice = averagePrice;
            LastUpdateUtc = timeUtc;
        }
        public void MarkAsClosed(decimal closePrice, decimal pnl)
        {
            MarkAsClosed(closePrice, pnl, DateTime.UtcNow);
        }
        public void MarkAsClosed(decimal closePrice, decimal pnl, DateTime closeTimeUtc)
        {
            Status = "Closed";
            CloseTime = closeTimeUtc;
            ClosePrice = closePrice;
            PnL = pnl;
            LastUpdateUtc = closeTimeUtc;
        }
        public void MarkAsCancelled()
        {
            MarkAsCancelled(DateTime.UtcNow);
        }
        public void MarkAsCancelled(DateTime timeUtc)
        {
            Status = "Cancelled";
            LastUpdateUtc = timeUtc;
        }
        public void SetDetail(string key, string value)
        {
            StrategyDetails[key] = value;
            LastUpdateUtc = DateTime.UtcNow;
        }
        public void SetDetail(string key, decimal value)
        {
            StrategyDetails[key] = value.ToString(CultureInfo.InvariantCulture);
            LastUpdateUtc = DateTime.UtcNow;
        }
        public void SetDetail(string key, int value)
        {
            StrategyDetails[key] = value.ToString(CultureInfo.InvariantCulture);
            LastUpdateUtc = DateTime.UtcNow;
        }
        public void SetDetail(string key, bool value)
        {
            StrategyDetails[key] = value ? "true" : "false";
            LastUpdateUtc = DateTime.UtcNow;
        }
        public void SetDetail(string key, DateTime valueUtc)
        {
            StrategyDetails[key] = valueUtc.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture);
            LastUpdateUtc = DateTime.UtcNow;
        }
        public bool TryGetDetail(string key, out string value) => StrategyDetails.TryGetValue(key, out value);
        public bool TryGetDecimal(string key, out decimal value)
        {
            value = 0m;
            return StrategyDetails.TryGetValue(key, out var s)
                && decimal.TryParse(s, NumberStyles.Number, CultureInfo.InvariantCulture, out value);
        }
        public bool TryGetInt(string key, out int value)
        {
            value = 0;
            return StrategyDetails.TryGetValue(key, out var s)
                && int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out value);
        }
        public bool TryGetBool(string key, out bool value)
        {
            value = false;
            return StrategyDetails.TryGetValue(key, out var s)
                && bool.TryParse(s, out value);
        }
        public bool TryGetDateTime(string key, out DateTime valueUtc)
        {
            valueUtc = default;
            return StrategyDetails.TryGetValue(key, out var s)
                && DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out valueUtc);
        }
        public static string CsvHeader()
        {
            return "OrderId,Symbol,Underlying,Strategy,OrderTag,Status,OpenTime,CloseTime,OpenPrice,ClosePrice,Quantity,PnL";
        }
        public string ToCsv()
        {
            return $"{OrderId},{Symbol},{Underlying},{Strategy},{OrderTag},{Status},{OpenTime:yyyy-MM-dd HH:mm:ss}," +
                   $"{CloseTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? ""}," +
                   $"{OpenPrice},{ClosePrice?.ToString() ?? ""},{Quantity},{PnL}";
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
namespace CoreAlgo.Architecture.Core.Implementations
{
    public class TradeTracker
    {
        public List<TradeRecord> AllTrades { get; set; } = new List<TradeRecord>();
        public List<TradeRecord> OpenTrades { get; set; } = new List<TradeRecord>();
        public List<TradeRecord> WorkingTrades { get; set; } = new List<TradeRecord>();
        public List<TradeRecord> ClosedTrades { get; set; } = new List<TradeRecord>();
        public void AddWorkingTrade(string orderId, string symbol, string strategy = "", string orderTag = "")
        {
            var trade = new TradeRecord(orderId, symbol, strategy, orderTag);
            AllTrades.Add(trade);
            WorkingTrades.Add(trade);
        }
        public TradeRecord FindTradeByAnyOrderId(int orderId)
        {
            var trade = AllTrades.FirstOrDefault(t => t.OrderId == orderId.ToString());
            if (trade != null)
            {
                return trade;
            }
            return AllTrades.FirstOrDefault(t => t.OrderIds.Contains(orderId));
        }
        public void MarkTradeAsOpen(string orderId, decimal fillPrice, int fillQuantity, DateTime fillTimeUtc)
        {
            var trade = WorkingTrades.FirstOrDefault(t => t.OrderId == orderId);
            if (trade != null)
            {
                trade.MarkAsOpen(fillPrice, fillQuantity, fillTimeUtc);
                WorkingTrades.RemoveAll(t => t.OrderId == orderId);
                if (!OpenTrades.Any(t => t.OrderId == orderId))
                {
                    OpenTrades.Add(trade);
                }
                return;
            }
            trade = AllTrades.FirstOrDefault(t => t.OrderId == orderId);
            if (trade != null && trade.Status == "Working")
            {
                trade.MarkAsOpen(fillPrice, fillQuantity, fillTimeUtc);
                if (!OpenTrades.Any(t => t.OrderId == orderId))
                {
                    OpenTrades.Add(trade);
                }
                return;
            }
        }
        public void MarkTradeAsClosed(string orderId, decimal closePrice, decimal pnl)
        {
            MarkTradeAsClosed(orderId, closePrice, pnl, DateTime.UtcNow);
        }
        public void MarkTradeAsClosed(string orderId, decimal closePrice, decimal pnl, DateTime closeTimeUtc)
        {
            var trade = OpenTrades.FirstOrDefault(t => t.OrderId == orderId);
            if (trade != null)
            {
                trade.MarkAsClosed(closePrice, pnl, closeTimeUtc);
                var removed = OpenTrades.RemoveAll(t => t.OrderId == orderId);
                if (removed == 0)
                {
                    System.Console.WriteLine($"[WARN TradeTracker] MarkTradeAsClosed: Found trade {orderId} in OpenTrades but RemoveAll returned 0");
                }
                if (!ClosedTrades.Any(t => t.OrderId == orderId))
                {
                    ClosedTrades.Add(trade);
                }
                return;
            }
            trade = AllTrades.FirstOrDefault(t => t.OrderId == orderId);
            if (trade != null && trade.Status == "Open")
            {
                trade.MarkAsClosed(closePrice, pnl, closeTimeUtc);
                OpenTrades.RemoveAll(t => t.OrderId == orderId);
                if (!ClosedTrades.Any(t => t.OrderId == orderId))
                {
                    ClosedTrades.Add(trade);
                }
            }
        }
        public void CancelWorkingTrade(string orderId)
        {
            var trade = WorkingTrades.FirstOrDefault(t => t.OrderId == orderId);
            if (trade != null)
            {
                WorkingTrades.RemoveAll(t => t.OrderId == orderId);
                trade.Status = "Cancelled";
            }
        }
        public void ExportToLogs(Action<string> logAction)
        {
            try
            {
                logAction?.Invoke($"=== TRADE EXPORT === {TradeRecord.CsvHeader()}");
                foreach (var trade in AllTrades.OrderBy(t => t.OpenTime))
                {
                    logAction?.Invoke($"TRADE_DATA: {trade.ToCsv()}");
                }
                logAction?.Invoke("=== END TRADE EXPORT ===");
            }
            catch (Exception ex)
            {
                logAction?.Invoke($"Failed to export trades to logs: {ex.Message}");
            }
        }
        public string GetSummary()
        {
            var totalTrades = AllTrades.Count;
            var workingCount = WorkingTrades.Count;
            var openCount = OpenTrades.Count;
            var closedCount = ClosedTrades.Count;
            var totalPnL = ClosedTrades.Sum(t => t.PnL);
            return $"Trade Summary: Total={totalTrades}, Working={workingCount}, Open={openCount}, Closed={closedCount}, PnL=${totalPnL:F2}";
        }
        public void Clear()
        {
            AllTrades.Clear();
            WorkingTrades.Clear();
            OpenTrades.Clear();
            ClosedTrades.Clear();
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Securities;
using CoreAlgo.Architecture.Core.Interfaces;

namespace CoreAlgo.Architecture.Core.Implementations
{
    /// <summary>
    /// Prevents cross-strategy conflicts on the same underlying asset
    /// Example: Blocks Iron Condor + Covered Call on SPY simultaneously
    /// </summary>
    public class UnderlyingConflictRule : IPositionOverlapRule
    {
        private readonly IAlgorithmContext _context;
        private readonly ISmartLogger _logger;

        public string RuleName => "UnderlyingConflictRule";
        public string Description => "Prevents multiple complex strategies on same underlying";
        public bool IsEnabled { get; set; } = true;

        public UnderlyingConflictRule(IAlgorithmContext context)
        {
            _context = context;
            _logger = context.Logger;
        }

        public ValidationResult Validate(
            Symbol proposedSymbol, 
            decimal quantity, 
            IEnumerable<KeyValuePair<Symbol, SecurityHolding>> existingPositions,
            string strategyTag = "")
        {
            if (!IsEnabled) 
                return ValidationResult.Success();

            try
            {
                // Determine the underlying asset for the proposed position
                var proposedUnderlying = GetUnderlying(proposedSymbol);
                if (proposedUnderlying == null)
                    return ValidationResult.Success();

                // Check for existing positions on the same underlying
                var conflictingPositions = existingPositions
                    .Where(p => p.Value.Invested)
                    .Where(p => GetUnderlying(p.Key) == proposedUnderlying)
                    .ToList();

                if (!conflictingPositions.Any())
                    return ValidationResult.Success();

                // Count different types of positions on this underlying
                var optionPositions = conflictingPositions.Count(p => p.Key.SecurityType == SecurityType.Option);
                var stockPositions = conflictingPositions.Count(p => p.Key.SecurityType == SecurityType.Equity);

                // Only check for truly conflicting positions (e.g., opposite directions on same asset)
                // Allow multiple strategies on same underlying - they can complement each other
                
                // For now, allow all combinations - let individual strategies manage their own limits
                // This rule focuses on preventing true conflicts, not limiting strategy diversity
                
                _logger.Debug($"[{RuleName}] Allowed: {proposedUnderlying.Value} has {optionPositions} options, {stockPositions} stock positions");
                return ValidationResult.Success();
            }
            catch (System.Exception ex)
            {
                _logger.Error($"[{RuleName}] Error validating underlying conflict: {ex.Message}");
                return ValidationResult.Error($"Validation error: {ex.Message}");
            }
        }

        /// <summary>
        /// Gets the underlying symbol for any security type
        /// </summary>
        private Symbol GetUnderlying(Symbol symbol)
        {
            if (symbol.SecurityType == SecurityType.Option)
                return symbol.Underlying;
            if (symbol.SecurityType == SecurityType.Equity)
                return symbol;
            
            return null; // Unsupported security type
        }

    }
}
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Globalization;
    using System.Drawing;
    using QuantConnect;
    using QuantConnect.Algorithm.Framework;
    using QuantConnect.Algorithm.Framework.Selection;
    using QuantConnect.Algorithm.Framework.Alphas;
    using QuantConnect.Algorithm.Framework.Portfolio;
    using QuantConnect.Algorithm.Framework.Portfolio.SignalExports;
    using QuantConnect.Algorithm.Framework.Execution;
    using QuantConnect.Algorithm.Framework.Risk;
    using QuantConnect.Algorithm.Selection;
    using QuantConnect.Api;
    using QuantConnect.Parameters;
    using QuantConnect.Benchmarks;
    using QuantConnect.Brokerages;
    using QuantConnect.Commands;
    using QuantConnect.Configuration;
    using QuantConnect.Util;
    using QuantConnect.Interfaces;
    using QuantConnect.Algorithm;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Auxiliary;
    using QuantConnect.Data.Consolidators;
    using QuantConnect.Data.Custom;
    using QuantConnect.Data.Custom.IconicTypes;
    using QuantConnect.DataSource;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.Shortable;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Notifications;
    using QuantConnect.Orders;
    using QuantConnect.Orders.Fees;
    using QuantConnect.Orders.Fills;
    using QuantConnect.Orders.OptionExercise;
    using QuantConnect.Orders.Slippage;
    using QuantConnect.Orders.TimeInForces;
    using QuantConnect.Python;
    using QuantConnect.Scheduling;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Equity;
    using QuantConnect.Securities.Future;
    using QuantConnect.Securities.Option;
    using QuantConnect.Securities.Positions;
    using QuantConnect.Securities.Forex;
    using QuantConnect.Securities.Crypto;
    using QuantConnect.Securities.CryptoFuture;
    using QuantConnect.Securities.IndexOption;
    using QuantConnect.Securities.Interfaces;
    using QuantConnect.Securities.Volatility;
    using QuantConnect.Storage;
    using QuantConnect.Statistics;
    using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
    using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
    using Calendar = QuantConnect.Data.Consolidators.Calendar;
namespace CoreAlgo.Architecture.Core.Implementations
{
    public class ValidationResult
    {
        public bool IsValid { get; private set; }
        public List<ValidationError> Errors { get; private set; }
        public List<ValidationWarning> Warnings { get; private set; }
        public string Message
        {
            get
            {
                if (Errors?.Any() == true)
                    return Errors.First().Message;
                if (Warnings?.Any() == true)
                    return Warnings.First().Message;
                return string.Empty;
            }
        }
        public static ValidationResult Success()
        {
            return new ValidationResult
            {
                IsValid = true,
                Errors = new List<ValidationError>(),
                Warnings = new List<ValidationWarning>()
            };
        }
        public static ValidationResult Failure(params ValidationError[] errors)
        {
            return new ValidationResult
            {
                IsValid = false,
                Errors = new List<ValidationError>(errors),
                Warnings = new List<ValidationWarning>()
            };
        }
        public static ValidationResult Blocked(string message)
        {
            var result = new ValidationResult
            {
                IsValid = false,
                Errors = new List<ValidationError>(),
                Warnings = new List<ValidationWarning>()
            };
            result.AddError("BLOCKED", message);
            return result;
        }
        public static ValidationResult Error(string message)
        {
            var result = new ValidationResult
            {
                IsValid = false,
                Errors = new List<ValidationError>(),
                Warnings = new List<ValidationWarning>()
            };
            result.AddError("ERROR", message);
            return result;
        }
        public static ValidationResult Warning(string message)
        {
            var result = new ValidationResult
            {
                IsValid = true,
                Errors = new List<ValidationError>(),
                Warnings = new List<ValidationWarning>()
            };
            result.AddWarning("WARNING", message);
            return result;
        }
        public void AddError(string code, string message, string field = null)
        {
            IsValid = false;
            Errors.Add(new ValidationError { Code = code, Message = message, Field = field });
        }
        public void AddWarning(string code, string message, string field = null)
        {
            Warnings.Add(new ValidationWarning { Code = code, Message = message, Field = field });
        }
        public void Merge(ValidationResult other)
        {
            if (!other.IsValid)
            {
                IsValid = false;
            }
            Errors.AddRange(other.Errors);
            Warnings.AddRange(other.Warnings);
        }
        public ValidationResult()
        {
            Errors = new List<ValidationError>();
            Warnings = new List<ValidationWarning>();
            IsValid = true;
        }
    }
    public class ValidationError
    {
        public string Code { get; set; }
        public string Message { get; set; }
        public string Field { get; set; }
    }
    public class ValidationWarning
    {
        public string Code { get; set; }
        public string Message { get; set; }
        public string Field { get; set; }
    }
}
using QuantConnect.Algorithm;

namespace CoreAlgo.Architecture.Core.Interfaces
{
    /// <summary>
    /// Context interface that provides access to algorithm instance and centralized logger
    /// Implements the context pattern like CentralAlgorithm where classes access context.logger
    /// </summary>
    public interface IAlgorithmContext
    {
        /// <summary>
        /// The QuantConnect algorithm instance - provides access to all QC functionality
        /// </summary>
        QCAlgorithm Algorithm { get; }

        /// <summary>
        /// Centralized smart logger with level checking and advanced features
        /// Access via context.Logger.Debug(), context.Logger.Info(), etc.
        /// </summary>
        ISmartLogger Logger { get; }
    }
}
#region imports
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Globalization;
    using System.Drawing;
    using QuantConnect;
    using QuantConnect.Algorithm.Framework;
    using QuantConnect.Algorithm.Framework.Selection;
    using QuantConnect.Algorithm.Framework.Alphas;
    using QuantConnect.Algorithm.Framework.Portfolio;
    using QuantConnect.Algorithm.Framework.Portfolio.SignalExports;
    using QuantConnect.Algorithm.Framework.Execution;
    using QuantConnect.Algorithm.Framework.Risk;
    using QuantConnect.Algorithm.Selection;
    using QuantConnect.Api;
    using QuantConnect.Parameters;
    using QuantConnect.Benchmarks;
    using QuantConnect.Brokerages;
    using QuantConnect.Commands;
    using QuantConnect.Configuration;
    using QuantConnect.Util;
    using QuantConnect.Interfaces;
    using QuantConnect.Algorithm;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Auxiliary;
    using QuantConnect.Data.Consolidators;
    using QuantConnect.Data.Custom;
    using QuantConnect.Data.Custom.IconicTypes;
    using QuantConnect.DataSource;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.Shortable;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Notifications;
    using QuantConnect.Orders;
    using QuantConnect.Orders.Fees;
    using QuantConnect.Orders.Fills;
    using QuantConnect.Orders.OptionExercise;
    using QuantConnect.Orders.Slippage;
    using QuantConnect.Orders.TimeInForces;
    using QuantConnect.Python;
    using QuantConnect.Scheduling;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Equity;
    using QuantConnect.Securities.Future;
    using QuantConnect.Securities.Option;
    using QuantConnect.Securities.Positions;
    using QuantConnect.Securities.Forex;
    using QuantConnect.Securities.Crypto;
    using QuantConnect.Securities.CryptoFuture;
    using QuantConnect.Securities.IndexOption;
    using QuantConnect.Securities.Interfaces;
    using QuantConnect.Securities.Volatility;
    using QuantConnect.Storage;
    using QuantConnect.Statistics;
    using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
    using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
    using Calendar = QuantConnect.Data.Consolidators.Calendar;
    using CoreAlgo.Architecture.Core.Implementations;
#endregion

namespace CoreAlgo.Architecture.Core.Interfaces
{
    /// <summary>
    /// Simple logger interface for the core infrastructure
    /// </summary>
    public interface ILogger<T>
    {
        /// <summary>
        /// Logs an informational message
        /// </summary>
        void LogInformation(string message, params object[] args);

        /// <summary>
        /// Logs a debug message
        /// </summary>
        void LogDebug(string message, params object[] args);

        /// <summary>
        /// Logs a trace message
        /// </summary>
        void LogTrace(string message, params object[] args);

        /// <summary>
        /// Logs a warning message
        /// </summary>
        void LogWarning(string message, params object[] args);

        /// <summary>
        /// Logs an error message
        /// </summary>
        void LogError(string message, params object[] args);

        /// <summary>
        /// Logs an error message with exception
        /// </summary>
        void LogError(Exception exception, string message, params object[] args);
        
        /// <summary>
        /// Logs a message with explicit context information for better traceability
        /// </summary>
        void LogWithContext(string message, string className, string methodName, LogLevel level = LogLevel.Info);
        
        /// <summary>
        /// Central logging method with level checking
        /// </summary>
        void LogMessage(string message, LogLevel level = LogLevel.Debug);
    }

    /// <summary>
    /// Non-generic logger interface for use in IAlgorithmContext.
    /// Provides convenience methods matching QCLogger's public API,
    /// eliminating the need for dynamic dispatch.
    /// </summary>
    public interface ISmartLogger
    {
        void Debug(string message);
        void Info(string message);
        void Warning(string message);
        void Error(string message);
        void Trace(string message);
        void LogMessage(string message, LogLevel level = LogLevel.Debug);
        void LogWithContext(string message, string className, string methodName, LogLevel level = LogLevel.Info);
    }
}
using System.Collections.Generic;
using QuantConnect;
using QuantConnect.Securities;
using CoreAlgo.Architecture.Core.Implementations;

namespace CoreAlgo.Architecture.Core.Interfaces
{
    /// <summary>
    /// Interface for position overlap validation rules
    /// Enables extensible rule system for different overlap scenarios
    /// </summary>
    public interface IPositionOverlapRule
    {
        /// <summary>
        /// Validates whether a new position can be opened without creating dangerous overlaps
        /// </summary>
        /// <param name="proposedSymbol">Symbol for the new position</param>
        /// <param name="quantity">Proposed quantity (positive for long, negative for short)</param>
        /// <param name="existingPositions">Current portfolio positions from QC's Portfolio API</param>
        /// <param name="strategyTag">Strategy identifier for context</param>
        /// <returns>ValidationResult indicating if position is allowed</returns>
        ValidationResult Validate(
            Symbol proposedSymbol, 
            decimal quantity, 
            IEnumerable<KeyValuePair<Symbol, SecurityHolding>> existingPositions,
            string strategyTag = "");

        /// <summary>
        /// Gets the name of this rule for logging and configuration
        /// </summary>
        string RuleName { get; }

        /// <summary>
        /// Gets description of what this rule validates
        /// </summary>
        string Description { get; }

        /// <summary>
        /// Whether this rule is enabled (allows dynamic rule toggling)
        /// </summary>
        bool IsEnabled { get; set; }
    }
}
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Globalization;
    using System.Drawing;
    using QuantConnect;
    using QuantConnect.Algorithm.Framework;
    using QuantConnect.Algorithm.Framework.Selection;
    using QuantConnect.Algorithm.Framework.Alphas;
    using QuantConnect.Algorithm.Framework.Portfolio;
    using QuantConnect.Algorithm.Framework.Portfolio.SignalExports;
    using QuantConnect.Algorithm.Framework.Execution;
    using QuantConnect.Algorithm.Framework.Risk;
    using QuantConnect.Algorithm.Selection;
    using QuantConnect.Api;
    using QuantConnect.Parameters;
    using QuantConnect.Benchmarks;
    using QuantConnect.Brokerages;
    using QuantConnect.Commands;
    using QuantConnect.Configuration;
    using QuantConnect.Util;
    using QuantConnect.Interfaces;
    using QuantConnect.Algorithm;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Auxiliary;
    using QuantConnect.Data.Consolidators;
    using QuantConnect.Data.Custom;
    using QuantConnect.Data.Custom.IconicTypes;
    using QuantConnect.DataSource;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.Shortable;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Notifications;
    using QuantConnect.Orders;
    using QuantConnect.Orders.Fees;
    using QuantConnect.Orders.Fills;
    using QuantConnect.Orders.OptionExercise;
    using QuantConnect.Orders.Slippage;
    using QuantConnect.Orders.TimeInForces;
    using QuantConnect.Python;
    using QuantConnect.Scheduling;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Equity;
    using QuantConnect.Securities.Future;
    using QuantConnect.Securities.Option;
    using QuantConnect.Securities.Positions;
    using QuantConnect.Securities.Forex;
    using QuantConnect.Securities.Crypto;
    using QuantConnect.Securities.CryptoFuture;
    using QuantConnect.Securities.IndexOption;
    using QuantConnect.Securities.Interfaces;
    using QuantConnect.Securities.Volatility;
    using QuantConnect.Storage;
    using QuantConnect.Statistics;
    using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
    using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
    using Calendar = QuantConnect.Data.Consolidators.Calendar;
    using CoreAlgo.Architecture.Core;
    using CoreAlgo.Architecture.Core.Implementations;
namespace CoreAlgo.Architecture.Core.Interfaces
{
    public interface IStrategy
    {
        string Name { get; }
        string Description { get; }
        string Version { get; }
        StrategyState State { get; }
        Dictionary<string, object> Parameters { get; }
        void Initialize(QCAlgorithm algorithm);
        void Initialize(QCAlgorithm algorithm, Dictionary<string, object> parameters);
        void Execute(Slice slice);
        void Shutdown();
        bool Validate();
        Dictionary<string, double> GetPerformanceMetrics();
        void Reset();
        event EventHandler<StrategyStateChangedEventArgs> StateChanged;
        event EventHandler<StrategyErrorEventArgs> ErrorOccurred;
    }
}
using System;
using System.Collections.Generic;
namespace CoreAlgo.Architecture.Core.Models
{
    public class AdvancedPerformanceMetrics
    {
        public DateTime AsOfUtc { get; set; }
        public string StrategyName { get; set; } = string.Empty;
        public decimal TotalReturn { get; set; }
        public decimal UnrealizedReturn { get; set; }
        public decimal TotalPortfolioReturn => TotalReturn + UnrealizedReturn;
        public decimal ReturnPercent { get; set; }
        public decimal CAGR { get; set; }
        public decimal SharpeRatio { get; set; }
        public decimal SortinoRatio { get; set; }
        public decimal CalmarRatio { get; set; }
        public decimal Volatility { get; set; }
        public decimal DownsideDeviation { get; set; }
        public decimal MaxDrawdown { get; set; }
        public decimal MaxDrawdownPercent { get; set; }
        public DateTime? MaxDrawdownStartDate { get; set; }
        public DateTime? MaxDrawdownEndDate { get; set; }
        public int MaxDrawdownDurationDays { get; set; }
        public DateTime? MaxDrawdownRecoveryDate { get; set; }
        public int? MaxDrawdownRecoveryDays { get; set; }
        public decimal CurrentDrawdown { get; set; }
        public decimal CurrentDrawdownPercent { get; set; }
        public decimal AverageDrawdown { get; set; }
        public TradeStatistics TradeStats { get; set; } = new TradeStatistics();
        public PeriodPerformance DailyPerformance { get; set; } = new PeriodPerformance();
        public PeriodPerformance WeeklyPerformance { get; set; } = new PeriodPerformance();
        public PeriodPerformance MonthlyPerformance { get; set; } = new PeriodPerformance();
        public Dictionary<string, decimal> PnLByStrategy { get; set; } = new Dictionary<string, decimal>();
        public Dictionary<string, decimal> PnLByUnderlying { get; set; } = new Dictionary<string, decimal>();
        public Dictionary<string, int> TradeCountByStrategy { get; set; } = new Dictionary<string, int>();
        public Dictionary<string, decimal> WinRateByStrategy { get; set; } = new Dictionary<string, decimal>();
    }
    public class TradeStatistics
    {
        public int TotalTrades { get; set; }
        public int WinningTrades { get; set; }
        public int LosingTrades { get; set; }
        public int BreakEvenTrades { get; set; }
        public decimal WinRate { get; set; }
        public decimal LossRate { get; set; }
        public decimal AverageWin { get; set; }
        public decimal AverageLoss { get; set; }
        public decimal LargestWin { get; set; }
        public decimal LargestLoss { get; set; }
        public decimal AverageTrade { get; set; }
        public decimal TradeStdDev { get; set; }
        public decimal ProfitFactor { get; set; }
        public decimal PayoffRatio { get; set; }
        public decimal KellyCriterion { get; set; }
        public decimal Expectancy { get; set; }
        public decimal ExpectancyRatio { get; set; }
        public int CurrentWinStreak { get; set; }
        public int CurrentLossStreak { get; set; }
        public int MaxWinStreak { get; set; }
        public int MaxLossStreak { get; set; }
        public decimal AvgWinStreak { get; set; }
        public decimal AvgLossStreak { get; set; }
        public decimal AvgTradeDurationHours { get; set; }
        public decimal AvgWinningTradeDurationHours { get; set; }
        public decimal AvgLosingTradeDurationHours { get; set; }
        public decimal MinTradeDurationHours { get; set; }
        public decimal MaxTradeDurationHours { get; set; }
        public decimal GrossProfit { get; set; }
        public decimal GrossLoss { get; set; }
        public decimal NetProfit { get; set; }
    }
    public class PeriodPerformance
    {
        public int PeriodCount { get; set; }
        public int WinningPeriods { get; set; }
        public int LosingPeriods { get; set; }
        public decimal WinRate { get; set; }
        public decimal BestPeriod { get; set; }
        public DateTime? BestPeriodDate { get; set; }
        public decimal WorstPeriod { get; set; }
        public DateTime? WorstPeriodDate { get; set; }
        public decimal AveragePeriod { get; set; }
        public decimal StdDev { get; set; }
        public Dictionary<DateTime, decimal> Returns { get; set; } = new Dictionary<DateTime, decimal>();
    }
    public class EquityPoint
    {
        public DateTime Timestamp { get; set; }
        public decimal Equity { get; set; }
        public decimal HighWaterMark { get; set; }
        public decimal Drawdown { get; set; }
        public decimal DrawdownPercent { get; set; }
    }
}
using CoreAlgo.Architecture.Core.Attributes;

namespace CoreAlgo.Architecture.Core.Models
{
    /// <summary>
    /// Basic strategy configuration for simple strategies like Iron Condor, Covered Call, etc.
    /// Provides concrete implementation of StrategyConfig for strategies that don't need special configuration.
    /// </summary>
    public class BasicStrategyConfig : StrategyConfig
    {
        // This class inherits all [StrategyParameter] attributes from StrategyConfig
        // and provides a concrete implementation that can be instantiated
        
        // No additional properties needed - base class has all the standard parameters:
        // - Symbols, UnderlyingSymbol
        // - EntryDeltaMin/Max, ExitDelta
        // - AllocationPerPosition, MaxPositions
        // - TradingStartTime/EndTime
        // - ProfitTarget, StopLoss, MaxDaysInTrade
        // - MinImpliedVolatility
        // - UseUniverseSelection

        /// <summary>
        /// Basic strategies use the core optimization parameters from base class
        /// No strategy-specific parameters need to be added for optimization
        /// </summary>
        public override string[] OptimizationParameters => base.OptimizationParameters;
    }
}
using System;
using System.Linq;
using QuantConnect;
using CoreAlgo.Architecture.Core.Attributes;
namespace CoreAlgo.Architecture.Core.Models
{
    public abstract class FuturesConfig : StrategyConfig
    {
        [StrategyParameter("Futures symbol", "ES")]
        public string FutureSymbol { get; set; } = "ES";
        [StrategyParameter("Rollover days to expiry", 5)]
        public int ContractRolloverDays { get; set; } = 5;
        [StrategyParameter("Use continuous contracts", true)]
        public bool UseContinuousContract { get; set; } = true;
        [StrategyParameter("Contract depth offset", 0)]
        public int ContractDepthOffset { get; set; } = 0;
        [StrategyParameter("Position size multiplier", 1.0)]
        public decimal PositionSizeMultiplier { get; set; } = 1.0m;
        [StrategyParameter("Maximum contracts per trade", 5)]
        public int MaxContractsPerTrade { get; set; } = 5;
        [StrategyParameter("Data resolution", "Minute")]
        public string DataResolution { get; set; } = "Minute";
        public Resolution GetDataResolution() => ParseResolution(DataResolution);
        public string GetFuturesCategory()
        {
            var symbol = FutureSymbol?.ToUpperInvariant();
            if (new[] { "ES", "NQ", "YM", "RTY", "EMD", "NKD" }.Contains(symbol))
                return "equity";
            if (new[] { "CL", "NG", "RB", "HO", "BZ" }.Contains(symbol))
                return "energy";
            if (new[] { "GC", "SI", "HG", "PA", "PL" }.Contains(symbol))
                return "metals";
            if (new[] { "ZC", "ZS", "ZW", "ZM", "ZL", "KC", "CT", "SB", "CC", "OJ" }.Contains(symbol))
                return "agricultural";
            if (new[] { "ZB", "ZN", "ZF", "TU", "UB", "ED", "SR1", "SR3" }.Contains(symbol))
                return "bonds";
            if (new[] { "6E", "6J", "6B", "6S", "6C", "6A", "6N", "6M", "E7", "J7" }.Contains(symbol))
                return "currency";
            if (new[] { "VX" }.Contains(symbol))
                return "volatility";
            if (new[] { "BTC", "ETH" }.Contains(symbol))
                return "crypto";
            return "unknown";
        }
        public override string[] OptimizationParameters => base.OptimizationParameters.Concat(new[]
        {
            "FutureSymbol",           // Asset selection for futures
            "PositionSizeMultiplier", // Position sizing relative to equity
            "MaxContractsPerTrade",   // Risk management - contract limit
            "ContractRolloverDays",   // Contract management timing
            "UseContinuousContract",  // Contract type selection
            "ContractDepthOffset",    // Contract selection depth
            "DataResolution"          // Data granularity for strategy
        }).ToArray();
    }
}
using System;
using System.Linq;
using QuantConnect.Algorithm;
using CoreAlgo.Architecture.Core.Attributes;
using CoreAlgo.Architecture.QC.Helpers;
using CoreAlgo.Architecture.Core.Interfaces;
namespace CoreAlgo.Architecture.Core.Models
{
    public class MultiAssetConfig : StrategyConfig
    {
        [StrategyParameter("EnableAssetAdaptation", true)]
        public bool EnableAssetAdaptation { get; set; } = true;
        [StrategyParameter("MaxAssets", 3)]
        public int MaxAssets { get; set; } = 3;
        [StrategyParameter("BaseStrikeWidth", 0.05)]
        public decimal BaseStrikeWidth { get; set; } = 0.05m; // 5% base strike width
        [StrategyParameter("UseEqualAllocation", false)]
        public bool UseEqualAllocation { get; set; } = false;
        [StrategyParameter("MaxCorrelation", 0.7)]
        public decimal MaxCorrelation { get; set; } = 0.7m;
        [StrategyParameter("MinAssetPrice", 10.0)]
        public decimal MinAssetPrice { get; set; } = 10.0m;
        public override void LoadFromParameters(IAlgorithmContext context)
        {
            base.LoadFromParameters(context);
            if (EnableAssetAdaptation && Symbols != null && Symbols.Length > 0)
            {
                ApplyAssetAdaptations(context);
            }
            ValidateMultiAssetConstraints(context);
        }
        private void ApplyAssetAdaptations(IAlgorithmContext context)
        {
            context.Logger.Info("MultiAssetConfig: Applying asset-specific adaptations");
            var primarySymbol = Symbols[0];
            var (adaptedDeltaMin, adaptedDeltaMax) = MultiAssetHelper.GetAssetDeltaTargets(
                primarySymbol, EntryDeltaMin, EntryDeltaMax);
            EntryDeltaMin = adaptedDeltaMin;
            EntryDeltaMax = adaptedDeltaMax;
            var (minPositions, maxPositions, recommendedAllocation) = MultiAssetHelper.GetAssetPositionLimits(
                primarySymbol, StartingCash);
            if (!UseEqualAllocation)
            {
                MaxPositions = Math.Min(MaxPositions, maxPositions);
                AllocationPerPosition = recommendedAllocation;
            }
            context.Logger.Info($"MultiAssetConfig: Adapted for {primarySymbol} - " +
                               $"Delta: {EntryDeltaMin:F2}-{EntryDeltaMax:F2}, " +
                               $"MaxPos: {MaxPositions}, " +
                               $"Allocation: {AllocationPerPosition:P1}");
            foreach (var symbol in Symbols)
            {
                var profile = MultiAssetHelper.GetAssetProfile(symbol);
                if (profile != null)
                {
                    context.Logger.Info($"MultiAssetConfig: {symbol} profile - {profile}");
                }
                else
                {
                    context.Logger.Info($"MultiAssetConfig: {symbol} - using default profile (unknown asset)");
                }
            }
        }
        private void ValidateMultiAssetConstraints(IAlgorithmContext context)
        {
            if (Symbols.Length > MaxAssets)
            {
                context.Logger.Error($"MultiAssetConfig: Too many assets ({Symbols.Length}), maximum allowed is {MaxAssets}");
            }
            var unsupportedSymbols = Symbols.Where(symbol => !MultiAssetHelper.HasLiquidOptions(symbol)).ToList();
            if (unsupportedSymbols.Any())
            {
                context.Logger.Warning($"MultiAssetConfig: Warning - symbols may have limited options liquidity: {string.Join(", ", unsupportedSymbols)}");
            }
            var summaryMessages = new[]
            {
                $"MultiAssetConfig: Trading {Symbols.Length} assets: {string.Join(", ", Symbols)}",
                $"MultiAssetConfig: Asset adaptation: {(EnableAssetAdaptation ? "Enabled" : "Disabled")}",
                $"MultiAssetConfig: Equal allocation: {(UseEqualAllocation ? "Enabled" : "Disabled")}"
            };
            foreach (var message in summaryMessages)
            {
                context.Logger.Info(message);
            }
        }
        public decimal GetStrikeWidthForAsset(string symbol)
        {
            return MultiAssetHelper.GetAssetStrikeWidth(symbol, BaseStrikeWidth);
        }
        public (int MinPositions, int MaxPositions, decimal RecommendedAllocation) GetPositionLimitsForAsset(string symbol)
        {
            return MultiAssetHelper.GetAssetPositionLimits(symbol, StartingCash);
        }
        public (decimal DeltaMin, decimal DeltaMax) GetDeltaTargetsForAsset(string symbol)
        {
            return MultiAssetHelper.GetAssetDeltaTargets(symbol, EntryDeltaMin, EntryDeltaMax);
        }
        public override string[] Validate()
        {
            var errors = base.Validate().ToList();
            if (Symbols == null || Symbols.Length == 0)
            {
                errors.Add("Symbols array cannot be null or empty");
            }
            if (MaxAssets < 1)
            {
                errors.Add("MaxAssets must be at least 1");
            }
            if (BaseStrikeWidth <= 0)
            {
                errors.Add("BaseStrikeWidth must be positive");
            }
            if (MaxCorrelation < 0 || MaxCorrelation > 1)
            {
                errors.Add("MaxCorrelation must be between 0.0 and 1.0");
            }
            if (MinAssetPrice <= 0)
            {
                errors.Add("MinAssetPrice must be positive");
            }
            if (Symbols != null && Symbols.Length > 0)
            {
                foreach (var symbol in Symbols)
                {
                    var profile = MultiAssetHelper.GetAssetProfile(symbol);
                    if (profile != null)
                    {
                        if (AccountSize < profile.MinAccountSize)
                        {
                            errors.Add($"Account size ${AccountSize:F0} below minimum ${profile.MinAccountSize:F0} required for {symbol}");
                        }
                        if (AssetManager.IsIndex(symbol) && AllocationPerPosition > 0.1m)
                        {
                            errors.Add($"Allocation {AllocationPerPosition:P0} too high for index option {symbol} (recommended max 10%)");
                        }
                    }
                    else
                    {
                        errors.Add($"Unknown asset {symbol} - option liquidity not verified");
                    }
                }
            }
            return errors.ToArray();
        }
        public override string[] OptimizationParameters => base.OptimizationParameters.Concat(new[]
        {
            "EnableAssetAdaptation", // Asset-specific adaptation toggle
            "MaxAssets",             // Portfolio diversification limit
            "BaseStrikeWidth",       // Strike selection base width
            "UseEqualAllocation",    // Allocation strategy choice
            "MaxCorrelation",        // Risk management - correlation limit
            "MinAssetPrice"          // Asset filtering - minimum price
        }).ToArray();
    }
}
using System.Linq;
using CoreAlgo.Architecture.Core.Attributes;

namespace CoreAlgo.Architecture.Core.Models
{
    /// <summary>
    /// Configuration for multi-asset Iron Condor strategy focused on Equity options (AAPL, MSFT, GOOGL)
    /// Demonstrates asset-specific parameter defaults optimized for individual stock options
    /// </summary>
    public class MultiAssetEquityConfig : MultiAssetConfig
    {
        /// <summary>
        /// Default to major tech stocks with liquid options
        /// </summary>
        [StrategyParameter("Symbols", "AAPL,MSFT,GOOGL")]
        public new string[] Symbols { get; set; } = new[] { "AAPL", "MSFT", "GOOGL" };

        /// <summary>
        /// Enable asset adaptation for equity options
        /// </summary>
        [StrategyParameter("EnableAssetAdaptation", true)]
        public new bool EnableAssetAdaptation { get; set; } = true;

        /// <summary>
        /// Allow more equity assets for diversification
        /// </summary>
        [StrategyParameter("MaxAssets", 5)]
        public new int MaxAssets { get; set; } = 5;

        /// <summary>
        /// Narrower strike width for equity options (smaller premium)
        /// </summary>
        [StrategyParameter("BaseStrikeWidth", 0.04)]
        public new decimal BaseStrikeWidth { get; set; } = 0.04m; // 4% for equities

        /// <summary>
        /// Use equal allocation across equity positions
        /// </summary>
        [StrategyParameter("UseEqualAllocation", true)]
        public new bool UseEqualAllocation { get; set; } = true;

        /// <summary>
        /// Lower minimum price for equity options
        /// </summary>
        [StrategyParameter("MinAssetPrice", 50.0)]
        public new decimal MinAssetPrice { get; set; } = 50.0m;

        /// <summary>
        /// Wider delta range for equities (lower gamma risk)
        /// </summary>
        [StrategyParameter("EntryDeltaMin", 0.15)]
        public new decimal EntryDeltaMin { get; set; } = 0.15m;

        [StrategyParameter("EntryDeltaMax", 0.35)]
        public new decimal EntryDeltaMax { get; set; } = 0.35m;

        /// <summary>
        /// Lower allocation per position for equities (more but smaller trades)
        /// </summary>
        [StrategyParameter("AllocationPerPosition", 0.08)]
        public new decimal AllocationPerPosition { get; set; } = 0.08m;

        /// <summary>
        /// More max positions for equity strategies
        /// </summary>
        [StrategyParameter("MaxPositions", 8)]
        public new int MaxPositions { get; set; } = 8;

        /// <summary>
        /// Lower profit target for equity options (faster premium decay)
        /// </summary>
        [StrategyParameter("ProfitTarget", 0.40)]
        public new decimal ProfitTarget { get; set; } = 0.40m;

        /// <summary>
        /// Wider stop loss for equity options (lower volatility)
        /// </summary>
        [StrategyParameter("StopLoss", -0.60)]
        public new decimal StopLoss { get; set; } = -0.60m;

        /// <summary>
        /// Shorter hold time for equity options (faster moves)
        /// </summary>
        [StrategyParameter("MaxDaysInTrade", 30)]
        public new int MaxDaysInTrade { get; set; } = 30;

        /// <summary>
        /// Higher volatility requirement for equity options
        /// </summary>
        [StrategyParameter("MinImpliedVolatility", 0.25)]
        public new decimal MinImpliedVolatility { get; set; } = 0.25m;

        /// <summary>
        /// Optimization parameters for multi-asset equity strategy.
        /// Focuses on equity-specific adaptations and portfolio management
        /// </summary>
        public override string[] OptimizationParameters => base.OptimizationParameters.Concat(new[]
        {
            "BaseStrikeWidth",       // Strike positioning for equity options
            "EntryDeltaMin",         // Delta targeting - minimum
            "EntryDeltaMax",         // Delta targeting - maximum
            "AllocationPerPosition",  // Position sizing per equity
            "MaxPositions",          // Portfolio diversification limit
            "ProfitTarget",          // Exit target for equity volatility
            "StopLoss",              // Risk management
            "MaxDaysInTrade",        // Holding period for equity moves
            "MinImpliedVolatility",  // Volatility filter for equity options
            "MaxAssets"              // Asset diversification control
        }).ToArray();
    }
}
using System;
using System.Linq;
using CoreAlgo.Architecture.Core.Attributes;

namespace CoreAlgo.Architecture.Core.Models
{
    /// <summary>
    /// Configuration for multi-asset Iron Condor strategy focused on Index options (SPX, NDX)
    /// Demonstrates asset-specific parameter defaults optimized for high-value index options
    /// </summary>
    public class MultiAssetSPXConfig : MultiAssetConfig
    {
        /// <summary>
        /// Default to SPX and SPY for viable margin strategy (SPX index + SPY equity)
        /// </summary>
        [StrategyParameter("Symbols", "SPX,SPY")]
        public new string[] Symbols { get; set; } = new[] { "SPX", "SPY" };

        /// <summary>
        /// Enable asset adaptation for index options
        /// </summary>
        [StrategyParameter("EnableAssetAdaptation", true)]
        public new bool EnableAssetAdaptation { get; set; } = true;

        /// <summary>
        /// Limit to 2 index assets to control correlation risk
        /// </summary>
        [StrategyParameter("MaxAssets", 2)]
        public new int MaxAssets { get; set; } = 2;

        /// <summary>
        /// Wider strike width for index options (higher premium)
        /// </summary>
        [StrategyParameter("BaseStrikeWidth", 0.06)]
        public new decimal BaseStrikeWidth { get; set; } = 0.06m; // 6% for indices

        /// <summary>
        /// Use asset-specific allocation (indices need less diversification)
        /// </summary>
        [StrategyParameter("UseEqualAllocation", false)]
        public new bool UseEqualAllocation { get; set; } = false;

        /// <summary>
        /// Higher minimum price for index options
        /// </summary>
        [StrategyParameter("MinAssetPrice", 3000.0)]
        public new decimal MinAssetPrice { get; set; } = 3000.0m;

        /// <summary>
        /// Tighter delta range for indices (higher gamma risk)
        /// </summary>
        [StrategyParameter("EntryDeltaMin", 0.20)]
        public new decimal EntryDeltaMin { get; set; } = 0.20m;

        [StrategyParameter("EntryDeltaMax", 0.30)]
        public new decimal EntryDeltaMax { get; set; } = 0.30m;

        /// <summary>
        /// Conservative allocation per position for margin-intensive strategies
        /// </summary>
        [StrategyParameter("AllocationPerPosition", 0.05)]
        public new decimal AllocationPerPosition { get; set; } = 0.05m;

        /// <summary>
        /// Fewer max positions for index strategies
        /// </summary>
        [StrategyParameter("MaxPositions", 3)]
        public new int MaxPositions { get; set; } = 3;

        /// <summary>
        /// Higher profit target for index options (better premium decay)
        /// </summary>
        [StrategyParameter("ProfitTarget", 0.60)]
        public new decimal ProfitTarget { get; set; } = 0.60m;

        /// <summary>
        /// Tighter stop loss for index options (higher volatility)
        /// </summary>
        [StrategyParameter("StopLoss", -0.80)]
        public new decimal StopLoss { get; set; } = -0.80m;

        /// <summary>
        /// Longer hold time for index options (better time decay)
        /// </summary>
        [StrategyParameter("MaxDaysInTrade", 45)]
        public new int MaxDaysInTrade { get; set; } = 45;

        /// <summary>
        /// Higher account size for index options (required for SPX margin requirements)
        /// </summary>
        [StrategyParameter("AccountSize", 300000)]
        public override decimal AccountSize { get; set; } = 300000m;

        /// <summary>
        /// Shorter test period for performance testing (1 month instead of 1 year)
        /// </summary>
        [StrategyParameter("StartDate", "2024-01-01")]
        public new DateTime StartDate { get; set; } = new DateTime(2024, 1, 1);

        [StrategyParameter("EndDate", "2024-01-31")]
        public new DateTime EndDate { get; set; } = new DateTime(2024, 1, 31);

        /// <summary>
        /// Key parameters for Multi-Asset SPX strategy optimization
        /// Focuses on index-specific parameters and high-value option characteristics
        /// </summary>
        public override string[] OptimizationParameters => base.OptimizationParameters.Concat(new[]
        {
            "BaseStrikeWidth",        // Strike width for index options
            "EntryDeltaMin",          // Delta targeting - minimum
            "EntryDeltaMax",          // Delta targeting - maximum
            "AllocationPerPosition",  // Position sizing for high-margin strategies
            "MaxPositions",           // Portfolio concentration for indices
            "ProfitTarget",           // Exit strategy - profit taking
            "StopLoss",               // Exit strategy - loss cutting
            "MaxDaysInTrade"          // Hold period for index options
        }).ToArray();
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using QuantConnect;
using QuantConnect.Algorithm;
using CoreAlgo.Architecture.Core.Attributes;
using CoreAlgo.Architecture.Core.Interfaces;
namespace CoreAlgo.Architecture.Core.Models
{
    public abstract class StrategyConfig
    {
        public string UnderlyingSymbol { get; set; } = "SPY";
        [StrategyParameter("StartDate", "2023-01-01")]
        public DateTime StartDate { get; set; } = new DateTime(2023, 1, 1);
        [StrategyParameter("EndDate", "2023-12-31")]
        public DateTime EndDate { get; set; } = new DateTime(2023, 12, 31);
        public decimal StartingCash { get; set; } = 100000m;
        [StrategyParameter("AccountSize", 100000)]
        public virtual decimal AccountSize { get; set; } = 100000m;
        [StrategyParameter("EnableOverlapPrevention", true)]
        public bool EnableOverlapPrevention { get; set; } = true;
        [StrategyParameter("OverlapPreventionMode", "Block")]
        public string OverlapPreventionMode { get; set; } = "Block";
        [StrategyParameter("AllowMultiStrategyUnderlying", false)]
        public bool AllowSameUnderlyingMultiStrategy { get; set; } = false;
        [StrategyParameter("MaxPositionsPerUnderlying", 1)]
        public int MaxPositionsPerUnderlying { get; set; } = 1;
        [StrategyParameter("MaxPositionsPerCombo", 2)]
        public int MaxPositionsPerCombo { get; set; } = 2;
        [StrategyParameter("ComboOrderLegCount", 0)]
        public int ComboOrderLegCount { get; set; } = 0;
        [StrategyParameter("MinimumStrikeDistance", 5.0)]
        public decimal MinimumStrikeDistance { get; set; } = 5.0m;
        [StrategyParameter("Symbols", "SPY")]
        public string[] Symbols { get; set; } = new[] { "SPY" };
        [StrategyParameter("UseUniverseSelection", false)]
        public bool UseUniverseSelection { get; set; } = false;
        [StrategyParameter("EntryDeltaMin", 0.25)]
        public decimal EntryDeltaMin { get; set; } = 0.25m;
        [StrategyParameter("EntryDeltaMax", 0.35)]
        public decimal EntryDeltaMax { get; set; } = 0.35m;
        [StrategyParameter("ExitDelta", 0.10)]
        public decimal ExitDelta { get; set; } = 0.10m;
        [StrategyParameter("AllocationPerPosition", 0.1)]
        public decimal AllocationPerPosition { get; set; } = 0.1m;
        [StrategyParameter("MaxPositions", 5)]
        public int MaxPositions { get; set; } = 5;
        [StrategyParameter("TradingStartTime", "09:30:00")]
        public TimeSpan TradingStartTime { get; set; } = new TimeSpan(9, 30, 0);
        [StrategyParameter("TradingEndTime", "15:30:00")]
        public TimeSpan TradingEndTime { get; set; } = new TimeSpan(15, 30, 0);
        [StrategyParameter("UseEntryTimeWindow", false)]
        public bool UseEntryTimeWindow { get; set; } = false;
        [StrategyParameter("EntryWindowStart", "00:00:00")]
        public TimeSpan EntryWindowStart { get; set; } = TimeSpan.Zero;
        [StrategyParameter("EntryWindowEnd", "23:59:59")]
        public TimeSpan EntryWindowEnd { get; set; } = new TimeSpan(23, 59, 59);
        [StrategyParameter("ProfitTarget", 0.5)]
        public decimal ProfitTarget { get; set; } = 0.5m;
        [StrategyParameter("StopLoss", -0.5)]
        public decimal StopLoss { get; set; } = -0.5m;
        [StrategyParameter("MaxDaysInTrade", 30)]
        public int MaxDaysInTrade { get; set; } = 30;
        [StrategyParameter("LogEntryRestrictions", false)]
        public virtual bool LogEntryRestrictions { get; set; }
        [StrategyParameter("MinImpliedVolatility", 0.15)]
        public decimal MinImpliedVolatility { get; set; } = 0.15m;
        [StrategyParameter("SmartPricingMode", "Normal")]
        public string SmartPricingMode { get; set; } = "Normal";
        [StrategyParameter("ComboMaxNetSpreadWidth", 5.0)]
        public decimal ComboMaxNetSpreadWidth { get; set; } = 5.0m;
        [StrategyParameter("ComboSmartPricingMode", "")]
        public string ComboSmartPricingMode { get; set; } = "";
        [StrategyParameter("EnableComboSmartPricing", true)]
        public bool EnableComboSmartPricing { get; set; } = true;
        [StrategyParameter("DataResolution", "Minute")]
        public string UnderlyingResolution { get; set; } = "Minute";
        public static Resolution ParseResolution(string resolution)
        {
            return resolution?.ToUpperInvariant() switch
            {
                "SECOND" => Resolution.Second,
                "MINUTE" => Resolution.Minute,
                "HOUR" => Resolution.Hour,
                "DAILY" => Resolution.Daily,
                _ => Resolution.Minute
            };
        }
        public virtual Resolution GetUnderlyingResolution()
        {
            return ParseResolution(UnderlyingResolution);
        }
        public virtual string[] OptimizationParameters => new[]
        {
            "Strategy",              // Always required for strategy selection
            "StartDate",             // Backtest period start
            "EndDate",               // Backtest period end
            "AccountSize",           // Position sizing base
            "MaxPositions",          // Risk management - position count
            "ProfitTarget",          // Exit strategy - profit taking
            "StopLoss",              // Exit strategy - loss cutting
            "AllocationPerPosition"  // Position sizing per trade
        };
        public virtual void LoadFromParameters(IAlgorithmContext context)
        {
            var properties = GetType().GetProperties();
            var loadedParams = new System.Collections.Generic.List<string>();
            foreach (var property in properties)
            {
                var attribute = property.GetCustomAttribute<StrategyParameterAttribute>();
                if (attribute != null)
                {
                    var parameterName = attribute.Name;
                    var defaultValue = property.GetValue(this)?.ToString() ?? attribute.DefaultValue?.ToString() ?? "";
                    var parameterValue = context.Algorithm.GetParameter(parameterName, defaultValue);
                    var convertedValue = ConvertParameterValue(parameterValue, property.PropertyType);
                    property.SetValue(this, convertedValue);
                    var formattedValue = convertedValue is string[] arrayValue
                        ? $"[{string.Join(", ", arrayValue)}]"
                        : $"{convertedValue}";
                    loadedParams.Add($"{parameterName}={formattedValue}");
                }
            }
            if (loadedParams.Count > 0)
            {
                context.Logger.Debug($"Loaded parameters: {string.Join(", ", loadedParams)}");
            }
        }
        private object ConvertParameterValue(string value, Type targetType)
        {
            if (targetType == typeof(string))
                return value;
            if (targetType == typeof(int))
                return int.TryParse(value, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out var intVal)
                    ? intVal : 0;
            if (targetType == typeof(decimal))
                return decimal.TryParse(value, System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.InvariantCulture, out var decVal)
                    ? decVal : 0m;
            if (targetType == typeof(bool))
                return bool.TryParse(value, out var boolVal) && boolVal;
            if (targetType == typeof(DateTime))
                return DateTime.TryParse(value, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out var dtVal)
                    ? dtVal : DateTime.MinValue;
            if (targetType == typeof(TimeSpan))
                return TimeSpan.TryParse(value, System.Globalization.CultureInfo.InvariantCulture, out var tsVal)
                    ? tsVal : TimeSpan.Zero;
            if (targetType == typeof(string[]))
            {
                if (string.IsNullOrWhiteSpace(value))
                {
                    return new string[] { "SPY" }; // Default fallback
                }
                if (value.StartsWith("System.String[]"))
                {
                    return new string[] { "SPY" }; // Fallback, will be overridden by template logic
                }
                var result = value.Split(',').Select(s => s.Trim()).Where(s => !string.IsNullOrEmpty(s)).ToArray();
                return result.Length > 0 ? result : new string[] { "SPY" }; // Ensure we always have at least one symbol
            }
            return Convert.ChangeType(value, targetType);
        }
        public virtual string[] Validate()
        {
            var errors = new List<string>();
            if (AccountSize < 10000m)
                errors.Add($"Account size ${AccountSize:F0} too small (minimum $10,000)");
            if (AllocationPerPosition > 0.5m)
                errors.Add($"Allocation {AllocationPerPosition:P0} too high (maximum 50%)");
            if (MaxPositions <= 0)
                errors.Add($"MaxPositions must be greater than 0 (current: {MaxPositions})");
            if (ProfitTarget != 0 && ProfitTarget <= 0)
                errors.Add($"ProfitTarget must be positive (current: {ProfitTarget:P1}) or 0 to disable");
            if (StopLoss != 0 && StopLoss >= 0)
                errors.Add($"StopLoss must be negative (current: {StopLoss:P1}) or 0 to disable");
            if (!CoreAlgo.Architecture.Core.Execution.SmartPricingEngineFactory.IsValidMode(SmartPricingMode))
                errors.Add($"Invalid SmartPricingMode '{SmartPricingMode}' (valid: Normal, Fast, Patient, Off)");
            if (UseEntryTimeWindow)
            {
                if (EntryWindowStart > EntryWindowEnd)
                {
                    errors.Add($"EntryWindowStart {EntryWindowStart:hh\\:mm} must be <= EntryWindowEnd {EntryWindowEnd:hh\\:mm}");
                }
            }
            return errors.ToArray();
        }
        public object GetParameterValue(string parameterName, object defaultValue)
        {
            var property = GetType().GetProperty(parameterName);
            if (property != null)
            {
                return property.GetValue(this);
            }
            return defaultValue;
        }
        public CoreAlgo.Architecture.Core.Execution.ISmartPricingEngine CreateSmartPricingEngine()
        {
            var mode = CoreAlgo.Architecture.Core.Execution.SmartPricingEngineFactory.ParseMode(SmartPricingMode);
            if (mode == CoreAlgo.Architecture.Core.Execution.SmartPricingMode.Off)
                return null;
            return CoreAlgo.Architecture.Core.Execution.SmartPricingEngineFactory.Create(mode);
        }
    }
}
using System;
using CoreAlgo.Architecture.Core.Attributes;
using QuantConnect;
namespace CoreAlgo.Architecture.Core.Models
{
    public class THTConfig : StrategyConfig
    {
        public THTConfig()
        {
            StartDate = new DateTime(2015, 1, 1);
            EndDate = new DateTime(2025, 12, 31);
            ProfitTarget = 0m;
            StopLoss = 0m;
            MaxPositions = 100;
            TradingStartTime = TimeSpan.Zero;
            TradingEndTime = TimeSpan.Zero;
        }
        [StrategyParameter("FVB33Period", 33)]
        public int FVB33Period { get; set; } = 33;
        [StrategyParameter("FVB20Period", 20)]
        public int FVB20Period { get; set; } = 20;
        [StrategyParameter("FVBPivotLookback", 5)]
        public int FVBPivotLookback { get; set; } = 5;
        [StrategyParameter("BXEmaShort", 5)]
        public int BXEmaShort { get; set; } = 5;
        [StrategyParameter("BXEmaLong", 20)]
        public int BXEmaLong { get; set; } = 20;
        [StrategyParameter("BXRsiPeriod", 5)]
        public int BXRsiPeriod { get; set; } = 5;
        [StrategyParameter("UniverseSize", 100)]
        public int UniverseSize { get; set; } = 100;
        [StrategyParameter("Benchmark", "SPY")]
        public string Benchmark { get; set; } = "SPY";
        [StrategyParameter("MinPrice", 5)]
        public decimal MinPrice { get; set; } = 5m;
        [StrategyParameter("RebalanceDayOfWeek", 1)]
        public int RebalanceDayOfWeek { get; set; } = 1;
        [StrategyParameter("RebalanceAfterOpenMinutes", 30)]
        public int RebalanceAfterOpenMinutes { get; set; } = 30;
        [StrategyParameter("StopLossDayOfMonthThreshold", 14)]
        public int StopLossDayOfMonthThreshold { get; set; } = 14;
        [StrategyParameter("MaxPositionWeight", 0.05)]
        public decimal MaxPositionWeight { get; set; } = 0.05m;
        [StrategyParameter("WarmupDays", 600)]
        public int WarmupDays { get; set; } = 600;
        public override Resolution GetUnderlyingResolution()
        {
            return Resolution.Daily;
        }
        public override string[] OptimizationParameters => new[]
        {
            "Strategy",
            "StartDate",
            "EndDate",
            "AccountSize",
            "FVB33Period",
            "FVB20Period",
            "BXEmaShort",
            "BXEmaLong",
            "BXRsiPeriod",
            "Benchmark",
            "StopLossDayOfMonthThreshold",
            "MaxPositionWeight"
        };
        public override string[] Validate()
        {
            var errors = new System.Collections.Generic.List<string>();
            if (FVB33Period <= 0)
                errors.Add("FVB33Period must be greater than 0");
            if (FVB20Period <= 0)
                errors.Add("FVB20Period must be greater than 0");
            if (FVBPivotLookback <= 0)
                errors.Add("FVBPivotLookback must be greater than 0");
            if (BXEmaShort <= 0 || BXEmaLong <= 0 || BXRsiPeriod <= 0)
                errors.Add("BX parameters must be greater than 0");
            if (BXEmaShort >= BXEmaLong)
                errors.Add("BXEmaShort must be less than BXEmaLong");
            if (UniverseSize <= 0)
                errors.Add("UniverseSize must be greater than 0");
            if (string.IsNullOrWhiteSpace(Benchmark))
                errors.Add("Benchmark must be specified");
            if (MinPrice < 0)
                errors.Add("MinPrice must be non-negative");
            if (RebalanceDayOfWeek < 0 || RebalanceDayOfWeek > 4)
                errors.Add("RebalanceDayOfWeek must be 0-4 (Mon-Fri)");
            if (RebalanceAfterOpenMinutes < 0)
                errors.Add("RebalanceAfterOpenMinutes must be non-negative");
            if (WarmupDays < 50)
                errors.Add("WarmupDays must be at least 50 for meaningful FVB calculation");
            if (StopLossDayOfMonthThreshold < 1 || StopLossDayOfMonthThreshold > 28)
                errors.Add("StopLossDayOfMonthThreshold must be between 1 and 28");
            if (MaxPositionWeight <= 0m || MaxPositionWeight > 1.0m)
                errors.Add("MaxPositionWeight must be between 0 and 1");
            return errors.ToArray();
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using CoreAlgo.Architecture.Core.Interfaces;
using CoreAlgo.Architecture.Core.Implementations;
using CoreAlgo.Architecture.Core.Models;
namespace CoreAlgo.Architecture.Core.Services
{
    public class AdvancedMetricsCalculator
    {
        private readonly IAlgorithmContext _context;
        private const decimal RiskFreeRate = 0.05m;
        private const int TradingDaysPerYear = 252;
        public AdvancedMetricsCalculator(IAlgorithmContext context)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
        }
        public AdvancedPerformanceMetrics CalculateMetrics(
            TradeTracker tracker,
            List<EquityPoint> equityCurve,
            decimal startingCapital,
            string strategyName = "")
        {
            var metrics = new AdvancedPerformanceMetrics
            {
                AsOfUtc = _context.Algorithm.UtcTime,
                StrategyName = strategyName
            };
            var closedTrades = tracker?.ClosedTrades ?? new List<TradeRecord>();
            var portfolio = _context.Algorithm.Portfolio;
            metrics.TradeStats = CalculateTradeStatistics(closedTrades);
            CalculateReturnMetrics(metrics, closedTrades, portfolio, startingCapital);
            CalculateDrawdownMetrics(metrics, equityCurve);
            CalculateRiskAdjustedMetrics(metrics, equityCurve, startingCapital);
            CalculatePeriodPerformance(metrics, closedTrades, equityCurve);
            CalculateAttribution(metrics, closedTrades);
            return metrics;
        }
        public TradeStatistics CalculateTradeStatistics(List<TradeRecord> closedTrades)
        {
            var stats = new TradeStatistics();
            if (closedTrades == null || closedTrades.Count == 0)
            {
                return stats;
            }
            var trades = closedTrades.ToList();
            var pnls = trades.Select(t => t.PnL).ToList();
            stats.TotalTrades = trades.Count;
            stats.WinningTrades = trades.Count(t => t.PnL > 0);
            stats.LosingTrades = trades.Count(t => t.PnL < 0);
            stats.BreakEvenTrades = trades.Count(t => t.PnL == 0);
            stats.WinRate = stats.TotalTrades > 0
                ? (decimal)stats.WinningTrades / stats.TotalTrades
                : 0;
            stats.LossRate = stats.TotalTrades > 0
                ? (decimal)stats.LosingTrades / stats.TotalTrades
                : 0;
            var winners = trades.Where(t => t.PnL > 0).ToList();
            var losers = trades.Where(t => t.PnL < 0).ToList();
            stats.GrossProfit = winners.Sum(t => t.PnL);
            stats.GrossLoss = losers.Sum(t => t.PnL); // Will be negative
            stats.NetProfit = stats.GrossProfit + stats.GrossLoss;
            stats.AverageWin = winners.Count > 0 ? winners.Average(t => t.PnL) : 0;
            stats.AverageLoss = losers.Count > 0 ? losers.Average(t => t.PnL) : 0;
            stats.AverageTrade = pnls.Count > 0 ? pnls.Average() : 0;
            stats.LargestWin = winners.Count > 0 ? winners.Max(t => t.PnL) : 0;
            stats.LargestLoss = losers.Count > 0 ? losers.Min(t => t.PnL) : 0;
            if (pnls.Count > 1)
            {
                var avg = pnls.Average();
                var sumSquares = pnls.Sum(p => (p - avg) * (p - avg));
                stats.TradeStdDev = (decimal)Math.Sqrt((double)(sumSquares / (pnls.Count - 1)));
            }
            stats.ProfitFactor = stats.GrossLoss != 0
                ? Math.Abs(stats.GrossProfit / stats.GrossLoss)
                : stats.GrossProfit > 0 ? decimal.MaxValue : 0;
            stats.PayoffRatio = stats.AverageLoss != 0
                ? Math.Abs(stats.AverageWin / stats.AverageLoss)
                : stats.AverageWin > 0 ? decimal.MaxValue : 0;
            stats.KellyCriterion = stats.PayoffRatio > 0
                ? stats.WinRate - (stats.LossRate / stats.PayoffRatio)
                : 0;
            stats.KellyCriterion = Math.Max(0, stats.KellyCriterion); // Can't bet negative
            stats.Expectancy = (stats.WinRate * stats.AverageWin) + (stats.LossRate * stats.AverageLoss);
            stats.ExpectancyRatio = stats.TradeStdDev > 0
                ? stats.Expectancy / stats.TradeStdDev
                : 0;
            CalculateStreaks(trades, stats);
            CalculateDurations(trades, stats);
            return stats;
        }
        private void CalculateStreaks(List<TradeRecord> trades, TradeStatistics stats)
        {
            if (trades.Count == 0) return;
            var orderedTrades = trades.OrderBy(t => t.CloseTime ?? t.OpenTime).ToList();
            int currentWinStreak = 0;
            int currentLossStreak = 0;
            int maxWinStreak = 0;
            int maxLossStreak = 0;
            var winStreaks = new List<int>();
            var lossStreaks = new List<int>();
            foreach (var trade in orderedTrades)
            {
                if (trade.PnL > 0)
                {
                    currentWinStreak++;
                    if (currentLossStreak > 0)
                    {
                        lossStreaks.Add(currentLossStreak);
                        currentLossStreak = 0;
                    }
                    maxWinStreak = Math.Max(maxWinStreak, currentWinStreak);
                }
                else if (trade.PnL < 0)
                {
                    currentLossStreak++;
                    if (currentWinStreak > 0)
                    {
                        winStreaks.Add(currentWinStreak);
                        currentWinStreak = 0;
                    }
                    maxLossStreak = Math.Max(maxLossStreak, currentLossStreak);
                }
            }
            if (currentWinStreak > 0) winStreaks.Add(currentWinStreak);
            if (currentLossStreak > 0) lossStreaks.Add(currentLossStreak);
            stats.CurrentWinStreak = currentWinStreak;
            stats.CurrentLossStreak = currentLossStreak;
            stats.MaxWinStreak = maxWinStreak;
            stats.MaxLossStreak = maxLossStreak;
            stats.AvgWinStreak = winStreaks.Count > 0 ? (decimal)winStreaks.Average() : 0;
            stats.AvgLossStreak = lossStreaks.Count > 0 ? (decimal)lossStreaks.Average() : 0;
        }
        private void CalculateDurations(List<TradeRecord> trades, TradeStatistics stats)
        {
            var tradesWithDuration = trades
                .Where(t => t.CloseTime.HasValue)
                .Select(t => new
                {
                    Trade = t,
                    DurationHours = (decimal)(t.CloseTime.Value - t.OpenTime).TotalHours
                })
                .ToList();
            if (tradesWithDuration.Count == 0) return;
            stats.AvgTradeDurationHours = tradesWithDuration.Average(t => t.DurationHours);
            stats.MinTradeDurationHours = tradesWithDuration.Min(t => t.DurationHours);
            stats.MaxTradeDurationHours = tradesWithDuration.Max(t => t.DurationHours);
            var winners = tradesWithDuration.Where(t => t.Trade.PnL > 0).ToList();
            var losers = tradesWithDuration.Where(t => t.Trade.PnL < 0).ToList();
            stats.AvgWinningTradeDurationHours = winners.Count > 0
                ? winners.Average(t => t.DurationHours)
                : 0;
            stats.AvgLosingTradeDurationHours = losers.Count > 0
                ? losers.Average(t => t.DurationHours)
                : 0;
        }
        private void CalculateReturnMetrics(
            AdvancedPerformanceMetrics metrics,
            List<TradeRecord> closedTrades,
            dynamic portfolio,
            decimal startingCapital)
        {
            metrics.TotalReturn = closedTrades.Sum(t => t.PnL);
            metrics.UnrealizedReturn = portfolio?.TotalUnrealizedProfit ?? 0m;
            if (startingCapital > 0)
            {
                metrics.ReturnPercent = metrics.TotalPortfolioReturn / startingCapital;
            }
            var firstTrade = closedTrades.OrderBy(t => t.OpenTime).FirstOrDefault();
            if (firstTrade != null && startingCapital > 0)
            {
                var totalDays = (_context.Algorithm.Time - firstTrade.OpenTime).TotalDays;
                if (totalDays > 0)
                {
                    var years = totalDays / 365.25;
                    var totalValue = startingCapital + metrics.TotalPortfolioReturn;
                    if (totalValue > 0 && years > 0)
                    {
                        metrics.CAGR = (decimal)(Math.Pow((double)(totalValue / startingCapital), 1.0 / years) - 1);
                    }
                }
            }
        }
        private void CalculateDrawdownMetrics(
            AdvancedPerformanceMetrics metrics,
            List<EquityPoint> equityCurve)
        {
            if (equityCurve == null || equityCurve.Count == 0)
            {
                return;
            }
            var maxDd = 0m;
            DateTime? maxDdStart = null;
            DateTime? maxDdEnd = null;
            DateTime? maxDdRecovery = null;
            var currentPeakDate = equityCurve[0].Timestamp;
            var allDrawdowns = new List<decimal>();
            var inDrawdown = false;
            DateTime? currentDrawdownStart = null;
            foreach (var point in equityCurve)
            {
                if (point.Drawdown < 0) // Drawdown is typically negative
                {
                    if (!inDrawdown)
                    {
                        inDrawdown = true;
                        currentDrawdownStart = point.Timestamp;
                    }
                    if (point.Drawdown < maxDd)
                    {
                        maxDd = point.Drawdown;
                        maxDdEnd = point.Timestamp;
                        maxDdStart = currentDrawdownStart;
                    }
                    allDrawdowns.Add(point.Drawdown);
                }
                else if (inDrawdown && point.Drawdown >= 0)
                {
                    if (maxDdStart.HasValue && maxDdRecovery == null && currentDrawdownStart == maxDdStart)
                    {
                        maxDdRecovery = point.Timestamp;
                    }
                    inDrawdown = false;
                    currentDrawdownStart = null;
                }
            }
            var latestPoint = equityCurve.Last();
            metrics.MaxDrawdown = Math.Abs(maxDd);
            metrics.MaxDrawdownPercent = Math.Abs(equityCurve.Min(p => p.DrawdownPercent));
            metrics.MaxDrawdownStartDate = maxDdStart;
            metrics.MaxDrawdownEndDate = maxDdEnd;
            metrics.MaxDrawdownRecoveryDate = maxDdRecovery;
            if (maxDdStart.HasValue && maxDdEnd.HasValue)
            {
                metrics.MaxDrawdownDurationDays = (int)(maxDdEnd.Value - maxDdStart.Value).TotalDays;
            }
            if (maxDdStart.HasValue && maxDdRecovery.HasValue)
            {
                metrics.MaxDrawdownRecoveryDays = (int)(maxDdRecovery.Value - maxDdStart.Value).TotalDays;
            }
            metrics.CurrentDrawdown = Math.Abs(latestPoint.Drawdown);
            metrics.CurrentDrawdownPercent = Math.Abs(latestPoint.DrawdownPercent);
            metrics.AverageDrawdown = allDrawdowns.Count > 0
                ? Math.Abs(allDrawdowns.Average())
                : 0;
        }
        private void CalculateRiskAdjustedMetrics(
            AdvancedPerformanceMetrics metrics,
            List<EquityPoint> equityCurve,
            decimal startingCapital)
        {
            if (equityCurve == null || equityCurve.Count < 2)
            {
                return;
            }
            var dailyReturns = new List<decimal>();
            for (int i = 1; i < equityCurve.Count; i++)
            {
                if (equityCurve[i - 1].Equity > 0)
                {
                    var ret = (equityCurve[i].Equity - equityCurve[i - 1].Equity) / equityCurve[i - 1].Equity;
                    dailyReturns.Add(ret);
                }
            }
            if (dailyReturns.Count < 2)
            {
                return;
            }
            var avgReturn = dailyReturns.Average();
            var variance = dailyReturns.Sum(r => (r - avgReturn) * (r - avgReturn)) / (dailyReturns.Count - 1);
            var dailyVol = (decimal)Math.Sqrt((double)variance);
            metrics.Volatility = dailyVol * (decimal)Math.Sqrt(TradingDaysPerYear);
            var negReturns = dailyReturns.Where(r => r < 0).ToList();
            if (negReturns.Count > 1)
            {
                var negVariance = negReturns.Sum(r => r * r) / (negReturns.Count - 1);
                metrics.DownsideDeviation = (decimal)Math.Sqrt((double)negVariance) * (decimal)Math.Sqrt(TradingDaysPerYear);
            }
            var annualizedReturn = avgReturn * TradingDaysPerYear;
            var excessReturn = annualizedReturn - RiskFreeRate;
            if (metrics.Volatility > 0)
            {
                metrics.SharpeRatio = excessReturn / metrics.Volatility;
            }
            if (metrics.DownsideDeviation > 0)
            {
                metrics.SortinoRatio = excessReturn / metrics.DownsideDeviation;
            }
            if (metrics.MaxDrawdownPercent > 0)
            {
                metrics.CalmarRatio = metrics.CAGR / metrics.MaxDrawdownPercent;
            }
        }
        private void CalculatePeriodPerformance(
            AdvancedPerformanceMetrics metrics,
            List<TradeRecord> closedTrades,
            List<EquityPoint> equityCurve)
        {
            if (equityCurve == null || equityCurve.Count < 2)
            {
                return;
            }
            var byDay = equityCurve
                .GroupBy(e => e.Timestamp.Date)
                .ToDictionary(g => g.Key, g => g.Last());
            var byWeek = equityCurve
                .GroupBy(e => GetWeekStart(e.Timestamp))
                .ToDictionary(g => g.Key, g => g.Last());
            var byMonth = equityCurve
                .GroupBy(e => new DateTime(e.Timestamp.Year, e.Timestamp.Month, 1))
                .ToDictionary(g => g.Key, g => g.Last());
            metrics.DailyPerformance = CalculatePeriodStats(byDay);
            metrics.WeeklyPerformance = CalculatePeriodStats(byWeek);
            metrics.MonthlyPerformance = CalculatePeriodStats(byMonth);
        }
        private PeriodPerformance CalculatePeriodStats(Dictionary<DateTime, EquityPoint> periodEnds)
        {
            var perf = new PeriodPerformance();
            if (periodEnds.Count < 2)
            {
                return perf;
            }
            var orderedPeriods = periodEnds.OrderBy(p => p.Key).ToList();
            var returns = new Dictionary<DateTime, decimal>();
            for (int i = 1; i < orderedPeriods.Count; i++)
            {
                var prev = orderedPeriods[i - 1].Value.Equity;
                var curr = orderedPeriods[i].Value.Equity;
                if (prev > 0)
                {
                    var ret = (curr - prev) / prev;
                    returns[orderedPeriods[i].Key] = ret;
                }
            }
            if (returns.Count == 0)
            {
                return perf;
            }
            perf.PeriodCount = returns.Count;
            perf.WinningPeriods = returns.Count(r => r.Value > 0);
            perf.LosingPeriods = returns.Count(r => r.Value < 0);
            perf.WinRate = perf.PeriodCount > 0 ? (decimal)perf.WinningPeriods / perf.PeriodCount : 0;
            var best = returns.OrderByDescending(r => r.Value).First();
            var worst = returns.OrderBy(r => r.Value).First();
            perf.BestPeriod = best.Value;
            perf.BestPeriodDate = best.Key;
            perf.WorstPeriod = worst.Value;
            perf.WorstPeriodDate = worst.Key;
            perf.AveragePeriod = returns.Values.Average();
            perf.Returns = returns;
            if (returns.Count > 1)
            {
                var avg = perf.AveragePeriod;
                var sumSq = returns.Values.Sum(r => (r - avg) * (r - avg));
                perf.StdDev = (decimal)Math.Sqrt((double)(sumSq / (returns.Count - 1)));
            }
            return perf;
        }
        private void CalculateAttribution(
            AdvancedPerformanceMetrics metrics,
            List<TradeRecord> closedTrades)
        {
            if (closedTrades == null || closedTrades.Count == 0)
            {
                return;
            }
            var byStrategy = closedTrades
                .Where(t => !string.IsNullOrEmpty(t.Strategy))
                .GroupBy(t => t.Strategy);
            foreach (var group in byStrategy)
            {
                var trades = group.ToList();
                metrics.PnLByStrategy[group.Key] = trades.Sum(t => t.PnL);
                metrics.TradeCountByStrategy[group.Key] = trades.Count;
                var winners = trades.Count(t => t.PnL > 0);
                metrics.WinRateByStrategy[group.Key] = trades.Count > 0
                    ? (decimal)winners / trades.Count
                    : 0;
            }
            var byUnderlying = closedTrades
                .Where(t => !string.IsNullOrEmpty(t.Underlying))
                .GroupBy(t => t.Underlying);
            foreach (var group in byUnderlying)
            {
                metrics.PnLByUnderlying[group.Key] = group.Sum(t => t.PnL);
            }
        }
        private DateTime GetWeekStart(DateTime date)
        {
            int diff = (7 + (date.DayOfWeek - DayOfWeek.Monday)) % 7;
            return date.AddDays(-diff).Date;
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Securities;
using CoreAlgo.Architecture.Core.Interfaces;
namespace CoreAlgo.Architecture.Core.Services
{
    public class CoreAlgoRiskManager
    {
        private readonly IAlgorithmContext _context;
        private readonly decimal _maxPortfolioMarginUtilization;
        private readonly decimal _maxAssetConcentration;
        private readonly decimal _maxCorrelatedAssetsAllocation;
        public CoreAlgoRiskManager(IAlgorithmContext context,
            decimal maxMarginUtilization = 0.70m,
            decimal maxAssetConcentration = 0.30m,
            decimal maxCorrelatedAllocation = 0.50m)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
            _maxPortfolioMarginUtilization = maxMarginUtilization;
            _maxAssetConcentration = maxAssetConcentration;
            _maxCorrelatedAssetsAllocation = maxCorrelatedAllocation;
        }
        public bool ValidateNewPosition(string symbol, decimal quantity, decimal estimatedPrice)
        {
            try
            {
                var algorithm = _context.Algorithm;
                var portfolio = algorithm.Portfolio;
                var currentMarginUtilization = GetPortfolioMarginUtilization();
                if (currentMarginUtilization > _maxPortfolioMarginUtilization)
                {
                    _context.Logger.Warning($"Risk: Portfolio margin utilization too high: {currentMarginUtilization:P1} > {_maxPortfolioMarginUtilization:P1}");
                    return false;
                }
                var proposedConcentration = CalculateAssetConcentrationAfterTrade(symbol, quantity, estimatedPrice);
                if (proposedConcentration > _maxAssetConcentration)
                {
                    _context.Logger.Warning($"Risk: Asset concentration would exceed limit: {proposedConcentration:P1} > {_maxAssetConcentration:P1} for {symbol}");
                    return false;
                }
                var totalOptionPositions = portfolio.Values.Count(x => x.Invested && x.Symbol.SecurityType == SecurityType.Option);
                if (totalOptionPositions >= 20) // Reasonable upper limit for option strategies
                {
                    _context.Logger.Warning($"Risk: Too many option positions: {totalOptionPositions} >= 20");
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"Risk validation error for {symbol}: {ex.Message}");
                return false; // Fail safe - reject if we can't validate
            }
        }
        public PortfolioRiskMetrics GetPortfolioRisk()
        {
            var algorithm = _context.Algorithm;
            var portfolio = algorithm.Portfolio;
            return new PortfolioRiskMetrics
            {
                TotalPortfolioValue = portfolio.TotalPortfolioValue,
                TotalMarginUsed = portfolio.TotalMarginUsed,
                MarginRemaining = portfolio.MarginRemaining,
                Cash = portfolio.Cash,
                TotalUnrealizedProfit = portfolio.TotalUnrealizedProfit,
                MarginUtilization = GetPortfolioMarginUtilization(),
                AssetConcentrations = GetAssetConcentrations(),
                OptionPositionCount = portfolio.Values.Count(x => x.Invested && x.Symbol.SecurityType == SecurityType.Option),
                LargestAssetExposure = GetLargestAssetExposure(),
                RiskAlerts = GenerateRiskAlerts()
            };
        }
        private decimal GetPortfolioMarginUtilization()
        {
            var portfolio = _context.Algorithm.Portfolio;
            if (portfolio.TotalPortfolioValue <= 0)
                return 0;
            return portfolio.TotalMarginUsed / portfolio.TotalPortfolioValue;
        }
        private Dictionary<string, decimal> GetAssetConcentrations()
        {
            var portfolio = _context.Algorithm.Portfolio;
            var concentrations = new Dictionary<string, decimal>();
            var totalValue = portfolio.TotalPortfolioValue;
            if (totalValue <= 0) return concentrations;
            var assetGroups = portfolio.Values
                .Where(x => x.Invested)
                .GroupBy(x => x.Symbol.SecurityType == SecurityType.Option ?
                    x.Symbol.Underlying.Value : x.Symbol.Value);
            foreach (var group in assetGroups)
            {
                var assetValue = group.Sum(x => Math.Abs(x.HoldingsValue));
                var concentration = assetValue / totalValue;
                concentrations[group.Key] = concentration;
            }
            return concentrations;
        }
        private decimal CalculateAssetConcentrationAfterTrade(string symbol, decimal quantity, decimal price)
        {
            var concentrations = GetAssetConcentrations();
            var portfolio = _context.Algorithm.Portfolio;
            var totalValue = portfolio.TotalPortfolioValue;
            var additionalValue = Math.Abs(quantity * price);
            var newTotalValue = totalValue + additionalValue;
            var underlyingSymbol = symbol; // Assume equity by default
            var currentAssetValue = concentrations.ContainsKey(underlyingSymbol)
                ? concentrations[underlyingSymbol] * totalValue
                : 0;
            var newAssetValue = currentAssetValue + additionalValue;
            return newTotalValue > 0 ? newAssetValue / newTotalValue : 0;
        }
        private decimal GetLargestAssetExposure()
        {
            var concentrations = GetAssetConcentrations();
            return concentrations.Values.DefaultIfEmpty(0).Max();
        }
        private List<RiskAlert> GenerateRiskAlerts()
        {
            var alerts = new List<RiskAlert>();
            var marginUtil = GetPortfolioMarginUtilization();
            if (marginUtil > _maxPortfolioMarginUtilization)
            {
                alerts.Add(new RiskAlert
                {
                    Type = RiskAlertType.MarginUtilization,
                    Severity = marginUtil > 0.85m ? RiskSeverity.High : RiskSeverity.Medium,
                    Message = $"Margin utilization: {marginUtil:P1} exceeds limit: {_maxPortfolioMarginUtilization:P1}",
                    Value = marginUtil
                });
            }
            var concentrations = GetAssetConcentrations();
            foreach (var kvp in concentrations.Where(x => x.Value > _maxAssetConcentration))
            {
                alerts.Add(new RiskAlert
                {
                    Type = RiskAlertType.AssetConcentration,
                    Severity = kvp.Value > 0.50m ? RiskSeverity.High : RiskSeverity.Medium,
                    Message = $"Asset {kvp.Key} concentration: {kvp.Value:P1} exceeds limit: {_maxAssetConcentration:P1}",
                    Symbol = kvp.Key,
                    Value = kvp.Value
                });
            }
            return alerts;
        }
    }
    public class PortfolioRiskMetrics
    {
        public decimal TotalPortfolioValue { get; set; }
        public decimal TotalMarginUsed { get; set; }
        public decimal MarginRemaining { get; set; }
        public decimal Cash { get; set; }
        public decimal TotalUnrealizedProfit { get; set; }
        public decimal MarginUtilization { get; set; }
        public Dictionary<string, decimal> AssetConcentrations { get; set; } = new Dictionary<string, decimal>();
        public int OptionPositionCount { get; set; }
        public decimal LargestAssetExposure { get; set; }
        public List<RiskAlert> RiskAlerts { get; set; } = new List<RiskAlert>();
    }
    public class RiskAlert
    {
        public RiskAlertType Type { get; set; }
        public RiskSeverity Severity { get; set; }
        public string Message { get; set; }
        public string Symbol { get; set; }
        public decimal Value { get; set; }
        public DateTime Timestamp { get; set; } = DateTime.UtcNow;
    }
    public enum RiskAlertType
    {
        MarginUtilization,
        AssetConcentration,
        PositionCount,
        CorrelationRisk
    }
    public enum RiskSeverity
    {
        Low,
        Medium,
        High,
        Critical
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using CoreAlgo.Architecture.Core.Interfaces;
using CoreAlgo.Architecture.Core.Models;
namespace CoreAlgo.Architecture.Core.Services
{
    public class EquityCurveTracker
    {
        private readonly IAlgorithmContext _context;
        private readonly List<EquityPoint> _equityCurve;
        private decimal _highWaterMark;
        private DateTime _lastRecordDate;
        private const string EquityCurveKey = "metrics/equity-curve.json";
        private const int MaxDataPoints = 2520; // ~10 years of daily data
        private static readonly JsonSerializerOptions SerializerOptions = new JsonSerializerOptions
        {
            WriteIndented = false,
            DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        };
        public EquityCurveTracker(IAlgorithmContext context)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
            _equityCurve = new List<EquityPoint>();
            _highWaterMark = 0;
            _lastRecordDate = DateTime.MinValue;
        }
        public List<EquityPoint> GetEquityCurve()
        {
            return _equityCurve.ToList();
        }
        public decimal HighWaterMark => _highWaterMark;
        public decimal CurrentDrawdown
        {
            get
            {
                if (_highWaterMark <= 0) return 0;
                var equity = _context.Algorithm.Portfolio.TotalPortfolioValue;
                return Math.Min(0, equity - _highWaterMark);
            }
        }
        public decimal CurrentDrawdownPercent
        {
            get
            {
                if (_highWaterMark <= 0) return 0;
                var equity = _context.Algorithm.Portfolio.TotalPortfolioValue;
                return Math.Min(0, (equity - _highWaterMark) / _highWaterMark);
            }
        }
        public void RecordEquity()
        {
            var now = _context.Algorithm.Time;
            var equity = _context.Algorithm.Portfolio.TotalPortfolioValue;
            if (now.Date == _lastRecordDate.Date && _equityCurve.Count > 0)
            {
                var lastPoint = _equityCurve.Last();
                UpdatePoint(lastPoint, equity);
                return;
            }
            var point = new EquityPoint
            {
                Timestamp = now,
                Equity = equity
            };
            UpdatePoint(point, equity);
            _equityCurve.Add(point);
            _lastRecordDate = now.Date;
            while (_equityCurve.Count > MaxDataPoints)
            {
                _equityCurve.RemoveAt(0);
            }
        }
        public void RecordEquity(decimal equity, DateTime timestamp)
        {
            if (timestamp.Date == _lastRecordDate.Date && _equityCurve.Count > 0)
            {
                var lastPoint = _equityCurve.Last();
                UpdatePoint(lastPoint, equity);
                return;
            }
            var point = new EquityPoint
            {
                Timestamp = timestamp,
                Equity = equity
            };
            UpdatePoint(point, equity);
            _equityCurve.Add(point);
            _lastRecordDate = timestamp.Date;
            while (_equityCurve.Count > MaxDataPoints)
            {
                _equityCurve.RemoveAt(0);
            }
        }
        private void UpdatePoint(EquityPoint point, decimal equity)
        {
            point.Equity = equity;
            if (equity > _highWaterMark)
            {
                _highWaterMark = equity;
            }
            point.HighWaterMark = _highWaterMark;
            point.Drawdown = equity - _highWaterMark; // Negative when in drawdown
            point.DrawdownPercent = _highWaterMark > 0
                ? (equity - _highWaterMark) / _highWaterMark
                : 0;
        }
        public void Initialize(decimal startingCapital)
        {
            if (_equityCurve.Count == 0)
            {
                _highWaterMark = startingCapital;
                var point = new EquityPoint
                {
                    Timestamp = _context.Algorithm.Time,
                    Equity = startingCapital,
                    HighWaterMark = startingCapital,
                    Drawdown = 0,
                    DrawdownPercent = 0
                };
                _equityCurve.Add(point);
                _lastRecordDate = _context.Algorithm.Time.Date;
            }
        }
        public void Save()
        {
            try
            {
                var data = new EquityCurveData
                {
                    Version = "1.0",
                    HighWaterMark = _highWaterMark,
                    LastRecordDate = _lastRecordDate,
                    Points = _equityCurve
                };
                var json = JsonSerializer.Serialize(data, SerializerOptions);
                var bytes = System.Text.Encoding.UTF8.GetBytes(json);
                _context.Algorithm.ObjectStore.SaveBytes(EquityCurveKey, bytes);
                _context.Logger.Trace($"EquityCurveTracker: Saved {_equityCurve.Count} equity points");
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"EquityCurveTracker: Error saving: {ex.Message}");
            }
        }
        public bool Load()
        {
            try
            {
                if (!_context.Algorithm.ObjectStore.ContainsKey(EquityCurveKey))
                {
                    _context.Logger.Info("EquityCurveTracker: No saved data found");
                    return false;
                }
                var bytes = _context.Algorithm.ObjectStore.ReadBytes(EquityCurveKey);
                var json = System.Text.Encoding.UTF8.GetString(bytes);
                var data = JsonSerializer.Deserialize<EquityCurveData>(json, SerializerOptions);
                if (data == null || data.Points == null)
                {
                    _context.Logger.Warning("EquityCurveTracker: Invalid saved data");
                    return false;
                }
                _equityCurve.Clear();
                _equityCurve.AddRange(data.Points);
                _highWaterMark = data.HighWaterMark;
                _lastRecordDate = data.LastRecordDate;
                _context.Logger.Info($"EquityCurveTracker: Loaded {_equityCurve.Count} equity points, HWM: ${_highWaterMark:N2}");
                return true;
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"EquityCurveTracker: Error loading: {ex.Message}");
                return false;
            }
        }
        public void ClearIfBacktest()
        {
            if (_context.Algorithm.LiveMode)
            {
                return;
            }
            _equityCurve.Clear();
            _highWaterMark = 0;
            _lastRecordDate = DateTime.MinValue;
            if (_context.Algorithm.ObjectStore.ContainsKey(EquityCurveKey))
            {
                _context.Algorithm.ObjectStore.Delete(EquityCurveKey);
                _context.Logger.Info("EquityCurveTracker: Cleared for backtest");
            }
        }
        public DrawdownSummary GetDrawdownSummary()
        {
            var summary = new DrawdownSummary();
            if (_equityCurve.Count == 0)
            {
                return summary;
            }
            var current = _equityCurve.Last();
            summary.CurrentEquity = current.Equity;
            summary.HighWaterMark = _highWaterMark;
            summary.CurrentDrawdown = Math.Abs(current.Drawdown);
            summary.CurrentDrawdownPercent = Math.Abs(current.DrawdownPercent);
            var maxDdPoint = _equityCurve.OrderBy(p => p.Drawdown).First();
            summary.MaxDrawdown = Math.Abs(maxDdPoint.Drawdown);
            summary.MaxDrawdownPercent = Math.Abs(maxDdPoint.DrawdownPercent);
            summary.MaxDrawdownDate = maxDdPoint.Timestamp;
            summary.IsInDrawdown = current.Drawdown < 0;
            if (summary.IsInDrawdown)
            {
                for (int i = _equityCurve.Count - 1; i >= 0; i--)
                {
                    if (_equityCurve[i].Drawdown >= 0)
                    {
                        summary.DrawdownStartDate = _equityCurve[i].Timestamp;
                        summary.DrawdownDurationDays = (int)(_context.Algorithm.Time - summary.DrawdownStartDate.Value).TotalDays;
                        break;
                    }
                }
            }
            return summary;
        }
    }
    public class EquityCurveData
    {
        public string Version { get; set; }
        public decimal HighWaterMark { get; set; }
        public DateTime LastRecordDate { get; set; }
        public List<EquityPoint> Points { get; set; } = new List<EquityPoint>();
    }
    public class DrawdownSummary
    {
        public decimal CurrentEquity { get; set; }
        public decimal HighWaterMark { get; set; }
        public decimal CurrentDrawdown { get; set; }
        public decimal CurrentDrawdownPercent { get; set; }
        public decimal MaxDrawdown { get; set; }
        public decimal MaxDrawdownPercent { get; set; }
        public DateTime? MaxDrawdownDate { get; set; }
        public bool IsInDrawdown { get; set; }
        public DateTime? DrawdownStartDate { get; set; }
        public int DrawdownDurationDays { get; set; }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Securities;
using CoreAlgo.Architecture.Core.Interfaces;
using CoreAlgo.Architecture.Core.Implementations;
using CoreAlgo.Architecture.Core.Models;
namespace CoreAlgo.Architecture.Core.Services
{
    public class PortfolioReporter
    {
        private readonly IAlgorithmContext _context;
        private readonly CoreAlgoRiskManager _riskManager;
        private readonly AdvancedMetricsCalculator _metricsCalculator;
        private readonly EquityCurveTracker _equityCurveTracker;
        private decimal _startingCapital;
        public PortfolioReporter(IAlgorithmContext context, CoreAlgoRiskManager riskManager)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
            _riskManager = riskManager ?? throw new ArgumentNullException(nameof(riskManager));
            _metricsCalculator = new AdvancedMetricsCalculator(context);
            _equityCurveTracker = new EquityCurveTracker(context);
        }
        public void Initialize(decimal startingCapital)
        {
            _startingCapital = startingCapital;
            _equityCurveTracker.ClearIfBacktest();
            _equityCurveTracker.Initialize(startingCapital);
        }
        public void RecordDailyEquity()
        {
            _equityCurveTracker.RecordEquity();
        }
        public EquityCurveTracker EquityCurveTracker => _equityCurveTracker;
        public DrawdownSummary GetDrawdownSummary()
        {
            return _equityCurveTracker.GetDrawdownSummary();
        }
        public PortfolioRiskReport GenerateRiskReport()
        {
            try
            {
                var portfolio = _context.Algorithm.Portfolio;
                var riskMetrics = _riskManager.GetPortfolioRisk();
                var report = new PortfolioRiskReport
                {
                    Timestamp = _context.Algorithm.Time,
                    PortfolioSummary = new PortfolioSummary
                    {
                        TotalValue = portfolio.TotalPortfolioValue,
                        Cash = portfolio.Cash,
                        InvestedCapital = portfolio.TotalHoldingsValue,
                        UnrealizedPnL = portfolio.TotalUnrealizedProfit,
                        RealizedPnL = portfolio.TotalProfit - portfolio.TotalUnrealizedProfit,
                        MarginUsed = portfolio.TotalMarginUsed,
                        MarginUtilization = riskMetrics.MarginUtilization
                    },
                    AssetBreakdown = GenerateAssetBreakdown(),
                    RiskMetrics = riskMetrics,
                    PositionSummary = GeneratePositionSummary(),
                    PerformanceMetrics = GeneratePerformanceMetrics()
                };
                _context.Logger.Debug($"Portfolio risk report generated: {report.AssetBreakdown.Count} assets, {report.PositionSummary.TotalPositions} positions");
                return report;
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"Error generating portfolio risk report: {ex.Message}");
                return new PortfolioRiskReport { Timestamp = _context.Algorithm.Time };
            }
        }
        private List<AssetBreakdown> GenerateAssetBreakdown()
        {
            var portfolio = _context.Algorithm.Portfolio;
            var breakdown = new List<AssetBreakdown>();
            var assetGroups = portfolio.Values
                .Where(x => x.Invested)
                .GroupBy(x => GetUnderlyingSymbol(x.Symbol));
            foreach (var group in assetGroups)
            {
                var positions = group.ToList();
                var totalValue = positions.Sum(x => x.HoldingsValue);
                var totalUnrealized = positions.Sum(x => x.UnrealizedProfit);
                breakdown.Add(new AssetBreakdown
                {
                    UnderlyingSymbol = group.Key,
                    PositionCount = positions.Count,
                    TotalValue = totalValue,
                    UnrealizedPnL = totalUnrealized,
                    Concentration = portfolio.TotalPortfolioValue > 0 ?
                        Math.Abs(totalValue) / portfolio.TotalPortfolioValue : 0,
                    Positions = positions.Select(x => new PositionDetail
                    {
                        Symbol = x.Symbol.Value,
                        Quantity = x.Quantity,
                        AveragePrice = x.AveragePrice,
                        MarketPrice = x.Price,
                        HoldingsValue = x.HoldingsValue,
                        UnrealizedPnL = x.UnrealizedProfit,
                        SecurityType = x.Symbol.SecurityType
                    }).ToList()
                });
            }
            return breakdown.OrderByDescending(x => Math.Abs(x.TotalValue)).ToList();
        }
        private PositionSummary GeneratePositionSummary()
        {
            var portfolio = _context.Algorithm.Portfolio;
            var investedPositions = portfolio.Values.Where(x => x.Invested).ToList();
            var summary = new PositionSummary
            {
                TotalPositions = investedPositions.Count,
                OptionPositions = investedPositions.Count(x => x.Symbol.SecurityType == SecurityType.Option),
                EquityPositions = investedPositions.Count(x => x.Symbol.SecurityType == SecurityType.Equity),
                ProfitablePositions = investedPositions.Count(x => x.UnrealizedProfit > 0),
                LosingPositions = investedPositions.Count(x => x.UnrealizedProfit < 0),
                LargestPosition = investedPositions.DefaultIfEmpty()
                    .OrderByDescending(x => Math.Abs(x?.HoldingsValue ?? 0))
                    .FirstOrDefault()?.Symbol.Value ?? "None",
                LargestPositionValue = investedPositions.DefaultIfEmpty()
                    .Max(x => Math.Abs(x?.HoldingsValue ?? 0))
            };
            var optionPositions = investedPositions
                .Where(x => x.Symbol.SecurityType == SecurityType.Option)
                .ToList();
            if (optionPositions.Count > 0)
            {
                var greeksByUnderlying = new Dictionary<string, UnderlyingGreeksSummary>();
                var totalGreeks = new PortfolioGreeks();
                foreach (var holding in optionPositions)
                {
                    var greeks = GetOptionGreeks(holding.Symbol);
                    if (greeks == null) continue;
                    var multiplier = holding.Quantity;
                    var underlyingPrice = GetUnderlyingPrice(holding.Symbol);
                    var posDelta = greeks.Delta * multiplier * 100m;
                    var posGamma = greeks.Gamma * multiplier * 100m;
                    var posTheta = greeks.Theta * multiplier * 100m;
                    var posVega = greeks.Vega * multiplier * 100m;
                    var notional = Math.Abs(multiplier) * underlyingPrice * 100m;
                    totalGreeks.Delta += posDelta;
                    totalGreeks.Gamma += posGamma;
                    totalGreeks.Theta += posTheta;
                    totalGreeks.Vega += posVega;
                    totalGreeks.NotionalExposure += notional;
                    var underlying = GetUnderlyingSymbol(holding.Symbol);
                    if (!greeksByUnderlying.ContainsKey(underlying))
                    {
                        greeksByUnderlying[underlying] = new UnderlyingGreeksSummary
                        {
                            UnderlyingSymbol = underlying
                        };
                    }
                    var underlyingSummary = greeksByUnderlying[underlying];
                    underlyingSummary.PositionCount++;
                    underlyingSummary.Delta += posDelta;
                    underlyingSummary.Gamma += posGamma;
                    underlyingSummary.Theta += posTheta;
                    underlyingSummary.Vega += posVega;
                    underlyingSummary.NotionalExposure += notional;
                    underlyingSummary.HoldingsValue += holding.HoldingsValue;
                    underlyingSummary.UnrealizedPnL += holding.UnrealizedProfit;
                }
                summary.AggregatedGreeks = totalGreeks;
                summary.GreeksByUnderlying = greeksByUnderlying.Values
                    .OrderByDescending(x => Math.Abs(x.NotionalExposure))
                    .ToList();
            }
            return summary;
        }
        private OptionGreeksData GetOptionGreeks(Symbol optionSymbol)
        {
            try
            {
                var slice = _context.Algorithm.CurrentSlice;
                if (slice?.OptionChains == null)
                    return null;
                foreach (var chain in slice.OptionChains.Values)
                {
                    foreach (var contract in chain)
                    {
                        if (contract.Symbol == optionSymbol && contract.Greeks != null)
                        {
                            return new OptionGreeksData
                            {
                                Delta = contract.Greeks.Delta,
                                Gamma = contract.Greeks.Gamma,
                                Theta = contract.Greeks.Theta,
                                Vega = contract.Greeks.Vega
                            };
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _context.Logger.Trace($"Failed to get Greeks for {optionSymbol}: {ex.Message}");
            }
            return null;
        }
        private decimal GetUnderlyingPrice(Symbol optionSymbol)
        {
            try
            {
                var underlying = optionSymbol.Underlying;
                if (underlying != null && _context.Algorithm.Securities.ContainsKey(underlying))
                {
                    return _context.Algorithm.Securities[underlying].Price;
                }
            }
            catch (Exception)
            {
            }
            return 0m;
        }
        private class OptionGreeksData
        {
            public decimal Delta { get; set; }
            public decimal Gamma { get; set; }
            public decimal Theta { get; set; }
            public decimal Vega { get; set; }
        }
        private PerformanceMetrics GeneratePerformanceMetrics()
        {
            var portfolio = _context.Algorithm.Portfolio;
            return new PerformanceMetrics
            {
                TotalReturn = portfolio.TotalProfit,
                UnrealizedReturn = portfolio.TotalUnrealizedProfit,
                RealizedReturn = portfolio.TotalProfit - portfolio.TotalUnrealizedProfit,
                WinRate = CalculateWinRate(),
                AverageWin = 0,
                AverageLoss = 0
            };
        }
        public AdvancedPerformanceMetrics GenerateAdvancedMetrics(TradeTracker tracker, string strategyName = "")
        {
            var equityCurve = _equityCurveTracker.GetEquityCurve();
            return _metricsCalculator.CalculateMetrics(
                tracker,
                equityCurve,
                _startingCapital,
                strategyName
            );
        }
        public void LogPerformanceSummary(TradeTracker tracker, string strategyName = "")
        {
            try
            {
                var metrics = GenerateAdvancedMetrics(tracker, strategyName);
                var portfolio = _context.Algorithm.Portfolio;
                var dd = _equityCurveTracker.GetDrawdownSummary();
                _context.Logger.Info("=== ADVANCED PERFORMANCE METRICS ===");
                _context.Logger.Info($"Strategy: {strategyName}");
                _context.Logger.Info($"Portfolio Value: ${portfolio.TotalPortfolioValue:N2}");
                _context.Logger.Info($"Starting Capital: ${_startingCapital:N2}");
                _context.Logger.Info("");
                _context.Logger.Info("--- RETURNS ---");
                _context.Logger.Info($"Total Return: ${metrics.TotalReturn:N2} ({metrics.ReturnPercent:P2})");
                _context.Logger.Info($"  Realized: ${metrics.TradeStats.NetProfit:N2}");
                _context.Logger.Info($"  Unrealized: ${metrics.UnrealizedReturn:N2}");
                _context.Logger.Info($"CAGR: {metrics.CAGR:P2}");
                _context.Logger.Info("");
                _context.Logger.Info("--- RISK-ADJUSTED METRICS ---");
                _context.Logger.Info($"Sharpe Ratio: {metrics.SharpeRatio:F3}");
                _context.Logger.Info($"Sortino Ratio: {metrics.SortinoRatio:F3}");
                _context.Logger.Info($"Calmar Ratio: {metrics.CalmarRatio:F3}");
                _context.Logger.Info($"Volatility (Ann.): {metrics.Volatility:P2}");
                _context.Logger.Info("");
                _context.Logger.Info("--- DRAWDOWN ---");
                _context.Logger.Info($"Current Drawdown: ${dd.CurrentDrawdown:N2} ({dd.CurrentDrawdownPercent:P2})");
                _context.Logger.Info($"Max Drawdown: ${dd.MaxDrawdown:N2} ({dd.MaxDrawdownPercent:P2})");
                if (dd.IsInDrawdown && dd.DrawdownStartDate.HasValue)
                {
                    _context.Logger.Info($"In Drawdown: {dd.DrawdownDurationDays} days (since {dd.DrawdownStartDate:yyyy-MM-dd})");
                }
                _context.Logger.Info("");
                var stats = metrics.TradeStats;
                _context.Logger.Info("--- TRADE STATISTICS ---");
                _context.Logger.Info($"Total Trades: {stats.TotalTrades} (W:{stats.WinningTrades} / L:{stats.LosingTrades})");
                _context.Logger.Info($"Win Rate: {stats.WinRate:P1}");
                _context.Logger.Info($"Avg Win: ${stats.AverageWin:N2} | Avg Loss: ${stats.AverageLoss:N2}");
                _context.Logger.Info($"Largest Win: ${stats.LargestWin:N2} | Largest Loss: ${stats.LargestLoss:N2}");
                _context.Logger.Info($"Profit Factor: {stats.ProfitFactor:F2}");
                _context.Logger.Info($"Expectancy: ${stats.Expectancy:N2} per trade");
                _context.Logger.Info($"Kelly Criterion: {stats.KellyCriterion:P1}");
                _context.Logger.Info("");
                _context.Logger.Info("--- STREAKS ---");
                _context.Logger.Info($"Current: {(stats.CurrentWinStreak > 0 ? $"{stats.CurrentWinStreak}W" : $"{stats.CurrentLossStreak}L")}");
                _context.Logger.Info($"Max Win Streak: {stats.MaxWinStreak} | Max Loss Streak: {stats.MaxLossStreak}");
                _context.Logger.Info("");
                if (metrics.MonthlyPerformance.PeriodCount > 0)
                {
                    var monthly = metrics.MonthlyPerformance;
                    _context.Logger.Info("--- MONTHLY PERFORMANCE ---");
                    _context.Logger.Info($"Profitable Months: {monthly.WinningPeriods}/{monthly.PeriodCount} ({monthly.WinRate:P0})");
                    _context.Logger.Info($"Best Month: {monthly.BestPeriod:P2} ({monthly.BestPeriodDate:yyyy-MM})");
                    _context.Logger.Info($"Worst Month: {monthly.WorstPeriod:P2} ({monthly.WorstPeriodDate:yyyy-MM})");
                    _context.Logger.Info($"Avg Monthly Return: {monthly.AveragePeriod:P2}");
                }
                if (metrics.PnLByStrategy.Count > 1)
                {
                    _context.Logger.Info("");
                    _context.Logger.Info("--- P&L BY STRATEGY ---");
                    foreach (var kvp in metrics.PnLByStrategy.OrderByDescending(x => x.Value))
                    {
                        var winRate = metrics.WinRateByStrategy.GetValueOrDefault(kvp.Key, 0);
                        var count = metrics.TradeCountByStrategy.GetValueOrDefault(kvp.Key, 0);
                        _context.Logger.Info($"  {kvp.Key}: ${kvp.Value:N2} ({count} trades, {winRate:P0} win)");
                    }
                }
                if (metrics.PnLByUnderlying.Count > 1)
                {
                    _context.Logger.Info("");
                    _context.Logger.Info("--- P&L BY UNDERLYING ---");
                    foreach (var kvp in metrics.PnLByUnderlying.OrderByDescending(x => x.Value).Take(5))
                    {
                        _context.Logger.Info($"  {kvp.Key}: ${kvp.Value:N2}");
                    }
                }
                _context.Logger.Info("=====================================");
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"Error generating performance summary: {ex.Message}");
            }
        }
        public void SaveEquityCurve()
        {
            _equityCurveTracker.Save();
        }
        public bool LoadEquityCurve()
        {
            return _equityCurveTracker.Load();
        }
        private string GetUnderlyingSymbol(Symbol symbol)
        {
            return symbol.SecurityType == SecurityType.Option ?
                symbol.Underlying.Value : symbol.Value;
        }
        private decimal CalculateWinRate()
        {
            var portfolio = _context.Algorithm.Portfolio;
            var investedPositions = portfolio.Values.Where(x => x.Invested).ToList();
            if (investedPositions.Count == 0) return 0;
            var winners = investedPositions.Count(x => x.UnrealizedProfit > 0);
            return (decimal)winners / investedPositions.Count;
        }
        public void LogDailyOrderPositions(TradePersistenceService persistenceService, int maxPositionsToLog = 10)
        {
            if (persistenceService == null) return;
            try
            {
                var summary = persistenceService.GetTradeSummary(null); // Will be called with tracker from Main.cs
                _context.Logger.Info("=== DAILY ORDER POSITION REPORT ===");
                _context.Logger.Info($"Total Trades: {summary.TotalTrades}");
                _context.Logger.Info($"  Working (Pending): {summary.WorkingCount}");
                _context.Logger.Info($"  Partial Fills: {summary.PartialFillCount}");
                _context.Logger.Info($"  Open (Filled): {summary.OpenCount}");
                _context.Logger.Info($"  Closed: {summary.ClosedCount}");
                _context.Logger.Info($"  Cancelled: {summary.CancelledCount}");
                _context.Logger.Info($"Total P&L (Closed): ${summary.TotalPnL:F2}");
                _context.Logger.Info($"Report Time: {summary.AsOfUtc:yyyy-MM-dd HH:mm:ss} UTC");
                _context.Logger.Info("=================================");
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"Error generating daily order position report: {ex.Message}");
            }
        }
        public void LogPositionSummaryWithGreeks()
        {
            try
            {
                var summary = GeneratePositionSummary();
                var portfolio = _context.Algorithm.Portfolio;
                _context.Logger.Info("=== POSITION SUMMARY WITH GREEKS ===");
                _context.Logger.Info($"Portfolio Value: ${portfolio.TotalPortfolioValue:N2}");
                _context.Logger.Info($"Total Positions: {summary.TotalPositions} (Options: {summary.OptionPositions}, Equity: {summary.EquityPositions})");
                _context.Logger.Info($"Profitable: {summary.ProfitablePositions} | Losing: {summary.LosingPositions}");
                _context.Logger.Info($"Largest Position: {summary.LargestPosition} (${summary.LargestPositionValue:N2})");
                var greeks = summary.AggregatedGreeks;
                if (summary.OptionPositions > 0)
                {
                    _context.Logger.Info("");
                    _context.Logger.Info("Portfolio Greeks (Position-Adjusted):");
                    _context.Logger.Info($"  Delta: {greeks.Delta:+#,##0.00;-#,##0.00;0.00} ($ move per $1 underlying move)");
                    _context.Logger.Info($"  Gamma: {greeks.Gamma:+#,##0.0000;-#,##0.0000;0.0000} (delta change per $1 underlying move)");
                    _context.Logger.Info($"  Theta: {greeks.Theta:+#,##0.00;-#,##0.00;0.00} ($/day time decay)");
                    _context.Logger.Info($"  Vega:  {greeks.Vega:+#,##0.00;-#,##0.00;0.00} ($/1% IV change)");
                    _context.Logger.Info($"  Notional Exposure: ${greeks.NotionalExposure:N2}");
                }
                if (summary.GreeksByUnderlying.Count > 0)
                {
                    _context.Logger.Info("");
                    _context.Logger.Info("Greeks by Underlying:");
                    foreach (var underlying in summary.GreeksByUnderlying)
                    {
                        _context.Logger.Info($"  {underlying.UnderlyingSymbol} ({underlying.PositionCount} legs):");
                        _context.Logger.Info($"    Delta: {underlying.Delta:+#,##0.00;-#,##0.00;0.00} | Theta: {underlying.Theta:+#,##0.00;-#,##0.00;0.00}/day | Vega: {underlying.Vega:+#,##0.00;-#,##0.00;0.00}");
                        _context.Logger.Info($"    Notional: ${underlying.NotionalExposure:N2} | P&L: ${underlying.UnrealizedPnL:+#,##0.00;-#,##0.00;0.00}");
                    }
                }
                _context.Logger.Info($"Report Time: {_context.Algorithm.Time:yyyy-MM-dd HH:mm:ss}");
                _context.Logger.Info("====================================");
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"Error generating position summary with Greeks: {ex.Message}");
            }
        }
        public PositionSummary GetPositionSummary()
        {
            return GeneratePositionSummary();
        }
        public void LogDailyOrderPositionsDetailed(TradeTracker tracker, int maxPositionsToLog = 10)
        {
            if (tracker == null) return;
            try
            {
                _context.Logger.Info("=== DAILY ORDER POSITION REPORT (DETAILED) ===");
                _context.Logger.Info($"Total Trades: {tracker.AllTrades.Count}");
                _context.Logger.Info($"  Working (Pending): {tracker.WorkingTrades.Count}");
                _context.Logger.Info($"  Open (Filled): {tracker.OpenTrades.Count}");
                _context.Logger.Info($"  Closed: {tracker.ClosedTrades.Count}");
                var partialFillCount = tracker.AllTrades.Count(t => t.Status == "PartialFill");
                var cancelledCount = tracker.AllTrades.Count(t => t.Status == "Cancelled");
                _context.Logger.Info($"  Partial Fills: {partialFillCount}");
                _context.Logger.Info($"  Cancelled: {cancelledCount}");
                var totalPnL = tracker.ClosedTrades.Sum(t => t.PnL);
                _context.Logger.Info($"Total P&L (Closed): ${totalPnL:F2}");
                if (tracker.OpenTrades.Count > 0)
                {
                    _context.Logger.Info($"\nOpen Positions (showing up to {maxPositionsToLog}):");
                    foreach (var trade in tracker.OpenTrades.Take(maxPositionsToLog))
                    {
                        var symbolsDisplay = trade.SymbolIds.Count > 0
                            ? string.Join(", ", trade.SymbolIds.Take(2)) + (trade.SymbolIds.Count > 2 ? "..." : "")
                            : trade.Symbol;
                        _context.Logger.Info($"  [{trade.Strategy}] {trade.OrderTag} | " +
                                                       $"Orders: {string.Join(",", trade.OrderIds)} | " +
                                                       $"Qty: {trade.FilledQuantity}/{trade.Quantity} | " +
                                                       $"Symbols: {symbolsDisplay}");
                    }
                }
                if (tracker.WorkingTrades.Count > 0)
                {
                    _context.Logger.Info($"\nWorking Orders (showing up to {Math.Min(5, maxPositionsToLog)}):");
                    foreach (var trade in tracker.WorkingTrades.Take(Math.Min(5, maxPositionsToLog)))
                    {
                        _context.Logger.Info($"  [{trade.Strategy}] {trade.OrderTag} | Order: {trade.OrderId} | Status: {trade.Status}");
                    }
                }
                _context.Logger.Info($"Report Time: {_context.Algorithm.Time:yyyy-MM-dd HH:mm:ss}");
                _context.Logger.Info("=============================================");
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"Error generating detailed order position report: {ex.Message}");
            }
        }
    }
    public class PortfolioRiskReport
    {
        public DateTime Timestamp { get; set; }
        public PortfolioSummary PortfolioSummary { get; set; }
        public List<AssetBreakdown> AssetBreakdown { get; set; } = new List<AssetBreakdown>();
        public PortfolioRiskMetrics RiskMetrics { get; set; }
        public PositionSummary PositionSummary { get; set; }
        public PerformanceMetrics PerformanceMetrics { get; set; }
    }
    public class PortfolioSummary
    {
        public decimal TotalValue { get; set; }
        public decimal Cash { get; set; }
        public decimal InvestedCapital { get; set; }
        public decimal UnrealizedPnL { get; set; }
        public decimal RealizedPnL { get; set; }
        public decimal MarginUsed { get; set; }
        public decimal MarginUtilization { get; set; }
    }
    public class AssetBreakdown
    {
        public string UnderlyingSymbol { get; set; }
        public int PositionCount { get; set; }
        public decimal TotalValue { get; set; }
        public decimal UnrealizedPnL { get; set; }
        public decimal Concentration { get; set; }
        public List<PositionDetail> Positions { get; set; } = new List<PositionDetail>();
    }
    public class PositionDetail
    {
        public string Symbol { get; set; }
        public decimal Quantity { get; set; }
        public decimal AveragePrice { get; set; }
        public decimal MarketPrice { get; set; }
        public decimal HoldingsValue { get; set; }
        public decimal UnrealizedPnL { get; set; }
        public SecurityType SecurityType { get; set; }
        public decimal Delta { get; set; }
        public decimal Gamma { get; set; }
        public decimal Theta { get; set; }
        public decimal Vega { get; set; }
        public decimal NotionalExposure { get; set; }
    }
    public class PositionSummary
    {
        public int TotalPositions { get; set; }
        public int OptionPositions { get; set; }
        public int EquityPositions { get; set; }
        public int ProfitablePositions { get; set; }
        public int LosingPositions { get; set; }
        public string LargestPosition { get; set; }
        public decimal LargestPositionValue { get; set; }
        public PortfolioGreeks AggregatedGreeks { get; set; } = new PortfolioGreeks();
        public List<UnderlyingGreeksSummary> GreeksByUnderlying { get; set; } = new List<UnderlyingGreeksSummary>();
    }
    public class PortfolioGreeks
    {
        public decimal Delta { get; set; }
        public decimal Gamma { get; set; }
        public decimal Theta { get; set; }
        public decimal Vega { get; set; }
        public decimal NotionalExposure { get; set; }
    }
    public class UnderlyingGreeksSummary
    {
        public string UnderlyingSymbol { get; set; }
        public int PositionCount { get; set; }
        public decimal Delta { get; set; }
        public decimal Gamma { get; set; }
        public decimal Theta { get; set; }
        public decimal Vega { get; set; }
        public decimal NotionalExposure { get; set; }
        public decimal HoldingsValue { get; set; }
        public decimal UnrealizedPnL { get; set; }
    }
    public class PerformanceMetrics
    {
        public decimal TotalReturn { get; set; }
        public decimal UnrealizedReturn { get; set; }
        public decimal RealizedReturn { get; set; }
        public decimal WinRate { get; set; }
        public decimal AverageWin { get; set; }
        public decimal AverageLoss { get; set; }
        public decimal SharpeRatio { get; set; }
        public decimal SortinoRatio { get; set; }
        public decimal MaxDrawdown { get; set; }
        public decimal MaxDrawdownPercent { get; set; }
        public decimal Volatility { get; set; }
        public decimal ProfitFactor { get; set; }
        public decimal Expectancy { get; set; }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Securities;
using CoreAlgo.Architecture.Core.Interfaces;
using CoreAlgo.Architecture.Core.Implementations;
namespace CoreAlgo.Architecture.Core.Services
{
    public class PositionOverlapManager
    {
        private readonly IAlgorithmContext _context;
        private readonly List<IPositionOverlapRule> _rules;
        private readonly ISmartLogger _logger;
        public PositionOverlapManager(IAlgorithmContext context)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
            _logger = context.Logger;
            _rules = new List<IPositionOverlapRule>();
            InitializeBuiltInRules();
        }
        public ValidationResult ValidateComboOrder(List<QuantConnect.Orders.Leg> legs, int quantity, string strategyTag = "")
        {
            try
            {
                _logger.Debug($"[COMBO VALIDATION] Validating combo order: {legs.Count} legs, qty:{quantity}, strategy:{strategyTag}");
                var existingPositions = _context.Algorithm.Portfolio
                    .Where(p => p.Value.Invested)
                    .ToList();
                return ValidateComboStrategy(legs, quantity, existingPositions, strategyTag);
            }
            catch (Exception ex)
            {
                var errorMsg = $"Error in combo order validation: {ex.Message}";
                _logger.Error(errorMsg);
                return ValidationResult.Error(errorMsg);
            }
        }
        public ValidationResult ValidateNewPosition(Symbol symbol, decimal quantity, string strategyTag = "")
        {
            try
            {
                var existingPositions = _context.Algorithm.Portfolio
                    .Where(p => p.Value.Invested)
                    .ToList();
                _logger.Debug($"[OVERLAP CHECK] Validating {symbol} qty:{quantity} strategy:{strategyTag}");
                _logger.Debug($"[OVERLAP CHECK] Existing positions: {existingPositions.Count}");
                foreach (var rule in _rules)
                {
                    var result = rule.Validate(symbol, quantity, existingPositions, strategyTag);
                    if (!result.IsValid)
                    {
                        _logger.Warning($"[OVERLAP BLOCKED] {rule.GetType().Name}: {result.Message}");
                        return result;
                    }
                }
                _logger.Debug($"[OVERLAP ALLOWED] Position validated successfully");
                return ValidationResult.Success();
            }
            catch (Exception ex)
            {
                var errorMsg = $"Error in position overlap validation: {ex.Message}";
                _logger.Error(errorMsg);
                return ValidationResult.Error(errorMsg);
            }
        }
        private ValidationResult ValidateComboStrategy(List<QuantConnect.Orders.Leg> legs, int quantity,
            List<KeyValuePair<Symbol, SecurityHolding>> existingPositions, string strategyTag)
        {
            var orderStructure = AnalyzeOrderStructure(legs);
            _logger.Debug($"[COMBO STRATEGY] Analyzed structure: {orderStructure.LegCount} legs, {orderStructure.PutCount} puts, {orderStructure.CallCount} calls");
            if (orderStructure.LegCount == 4 && orderStructure.PutCount == 2 && orderStructure.CallCount == 2)
            {
                return ValidateFourLegCombo(legs, quantity, existingPositions, orderStructure);
            }
            else if (orderStructure.LegCount == 2)
            {
                return ValidateTwoLegCombo(legs, quantity, existingPositions, orderStructure);
            }
            else
            {
                return ValidateGenericCombo(legs, quantity, existingPositions, orderStructure);
            }
        }
        private ValidationResult ValidateFourLegCombo(List<QuantConnect.Orders.Leg> legs, int quantity,
            List<KeyValuePair<Symbol, SecurityHolding>> existingPositions, OrderStructureAnalysis structure)
        {
            var underlying = legs.First().Symbol.Underlying;
            var expiry = legs.First().Symbol.ID.Date;
            var activeFourLegCombos = existingPositions
                .Where(h => h.Key.SecurityType == SecurityType.Option &&
                           h.Key.Underlying == underlying &&
                           h.Key.ID.Date == expiry)
                .GroupBy(h => h.Key.ID.Date)
                .Where(g => g.Count() == 4) // 4-leg combo pattern
                .Count();
            var maxComboPositions = GetComboPositionLimit(4); // 4-leg combos default to strict limit
            if (activeFourLegCombos >= maxComboPositions)
            {
                return ValidationResult.Blocked(
                    $"4-leg combo limit exceeded: {activeFourLegCombos} active positions on {underlying.Value} {expiry:yyyy-MM-dd} (max: {maxComboPositions})");
            }
            _logger.Debug($"[4-LEG COMBO] Validation passed: {activeFourLegCombos}/{maxComboPositions} positions");
            return ValidationResult.Success();
        }
        private ValidationResult ValidateTwoLegCombo(List<QuantConnect.Orders.Leg> legs, int quantity,
            List<KeyValuePair<Symbol, SecurityHolding>> existingPositions, OrderStructureAnalysis structure)
        {
            var underlying = legs.First().Symbol.Underlying;
            var activeTwoLegCombos = existingPositions
                .Where(h => h.Key.SecurityType == SecurityType.Option &&
                           h.Key.Underlying == underlying)
                .GroupBy(h => h.Key.ID.Date)
                .Where(g => g.Count() == 2) // 2-leg combo pattern
                .Count();
            var maxComboPositions = GetComboPositionLimit(2);
            if (activeTwoLegCombos >= maxComboPositions)
            {
                return ValidationResult.Blocked(
                    $"2-leg combo limit exceeded: {activeTwoLegCombos} active positions on {underlying.Value} (max: {maxComboPositions})");
            }
            _logger.Debug($"[2-LEG COMBO] Validation passed: {activeTwoLegCombos}/{maxComboPositions} positions");
            return ValidationResult.Success();
        }
        private ValidationResult ValidateGenericCombo(List<QuantConnect.Orders.Leg> legs, int quantity,
            List<KeyValuePair<Symbol, SecurityHolding>> existingPositions, OrderStructureAnalysis structure)
        {
            var underlying = legs.First().Symbol.Underlying;
            var activeComboPositions = existingPositions
                .Where(h => h.Key.SecurityType == SecurityType.Option &&
                           h.Key.Underlying == underlying)
                .GroupBy(h => h.Key.ID.Date)
                .Where(g => g.Count() >= structure.LegCount)
                .Count();
            var maxComboPositions = GetComboPositionLimit(structure.LegCount);
            if (activeComboPositions >= maxComboPositions)
            {
                return ValidationResult.Blocked(
                    $"{structure.LegCount}-leg combo limit exceeded: {activeComboPositions} active positions on {underlying.Value} (max: {maxComboPositions})");
            }
            _logger.Debug($"[GENERIC COMBO] Validation passed: {activeComboPositions}/{maxComboPositions} positions");
            return ValidationResult.Success();
        }
        private OrderStructureAnalysis AnalyzeOrderStructure(List<QuantConnect.Orders.Leg> legs)
        {
            var analysis = new OrderStructureAnalysis
            {
                LegCount = legs.Count,
                PutCount = legs.Count(l => l.Symbol.SecurityType == SecurityType.Option && l.Symbol.ID.OptionRight == OptionRight.Put),
                CallCount = legs.Count(l => l.Symbol.SecurityType == SecurityType.Option && l.Symbol.ID.OptionRight == OptionRight.Call),
                EquityCount = legs.Count(l => l.Symbol.SecurityType == SecurityType.Equity),
                HasOptions = legs.Any(l => l.Symbol.SecurityType == SecurityType.Option),
                HasEquity = legs.Any(l => l.Symbol.SecurityType == SecurityType.Equity)
            };
            if (analysis.HasOptions)
            {
                var strikes = legs.Where(l => l.Symbol.SecurityType == SecurityType.Option)
                                 .Select(l => l.Symbol.ID.StrikePrice)
                                 .OrderBy(s => s)
                                 .ToList();
                analysis.UniqueStrikes = strikes.Distinct().Count();
                analysis.StrikeSpread = strikes.Count > 1 ? strikes.Max() - strikes.Min() : 0;
            }
            return analysis;
        }
        private int GetComboPositionLimit(int legCount)
        {
            try
            {
                var algorithm = _context.Algorithm;
                if (algorithm != null)
                {
                    var maxComboPositions = algorithm.GetParameter("MaxPositionsPerCombo");
                    if (int.TryParse(maxComboPositions, out var configLimit))
                    {
                        return configLimit;
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.Debug($"[CONFIG] Could not read MaxPositionsPerCombo: {ex.Message}");
            }
            return legCount switch
            {
                4 => 1,  // 4-leg combos (Iron Condor pattern) - most restrictive
                3 => 2,  // 3-leg combos - moderate
                2 => 3,  // 2-leg combos (Spreads) - more permissive
                _ => 2   // Other patterns - moderate default
            };
        }
        private class OrderStructureAnalysis
        {
            public int LegCount { get; set; }
            public int PutCount { get; set; }
            public int CallCount { get; set; }
            public int EquityCount { get; set; }
            public bool HasOptions { get; set; }
            public bool HasEquity { get; set; }
            public int UniqueStrikes { get; set; }
            public decimal StrikeSpread { get; set; }
        }
        public void AddRule(IPositionOverlapRule rule)
        {
            if (rule == null) throw new ArgumentNullException(nameof(rule));
            _rules.Add(rule);
            _logger.Debug($"[OVERLAP MANAGER] Added rule: {rule.GetType().Name}");
        }
        public void RemoveRule<T>() where T : IPositionOverlapRule
        {
            var removed = _rules.RemoveAll(r => r is T);
            if (removed > 0)
            {
                _logger.Debug($"[OVERLAP MANAGER] Removed {removed} rule(s) of type: {typeof(T).Name}");
            }
        }
        public string GetConfigurationSummary()
        {
            var summary = $"Position Overlap Manager - {_rules.Count} active rules:\n";
            foreach (var rule in _rules)
            {
                summary += $"  - {rule.GetType().Name}\n";
            }
            return summary;
        }
        private void InitializeBuiltInRules()
        {
            AddRule(new UnderlyingConflictRule(_context));
            AddRule(new CollateralValidationRule(_context));
            AddRule(new StrikeOverlapRule(_context));
            _logger.Info($"[OVERLAP MANAGER] Initialized with {_rules.Count} built-in rules");
        }
        public List<SecurityHolding> GetPositionsForUnderlying(Symbol underlying)
        {
            return _context.Algorithm.Portfolio.Values
                .Where(h => h.Invested &&
                           (h.Symbol == underlying ||
                            (h.Symbol.SecurityType == SecurityType.Option && h.Symbol.Underlying == underlying)))
                .ToList();
        }
        public bool HasActivePositions(Symbol underlying)
        {
            return GetPositionsForUnderlying(underlying).Any();
        }
        public int GetActiveOptionPositionCount(Symbol underlying)
        {
            return _context.Algorithm.Portfolio.Values
                .Count(h => h.Invested &&
                           h.Symbol.SecurityType == SecurityType.Option &&
                           h.Symbol.Underlying == underlying);
        }
        public decimal GetTotalMarginRequirement(Symbol underlying)
        {
            try
            {
                var positions = GetPositionsForUnderlying(underlying);
                return positions.Sum(p => Math.Abs(p.HoldingsValue * 0.2m)); // Simplified margin calculation
            }
            catch (Exception ex)
            {
                _logger.Warning($"Error calculating margin requirement: {ex.Message}");
                return 0m;
            }
        }
    }
}
using System;
using QuantConnect;
using QuantConnect.Algorithm;
using CoreAlgo.Architecture.Core.Interfaces;

namespace CoreAlgo.Architecture.Core.Services
{
    /// <summary>
    /// Simple implementation of IAlgorithmContext for SmartOrderManager
    /// </summary>
    public class SimpleAlgorithmContext : IAlgorithmContext
    {
        public QCAlgorithm Algorithm { get; }
        public ISmartLogger Logger { get; }

        public SimpleAlgorithmContext(QCAlgorithm algorithm, ISmartLogger logger)
        {
            Algorithm = algorithm ?? throw new ArgumentNullException(nameof(algorithm));
            Logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Orders;
using QuantConnect.Scheduling;
using QuantConnect.Securities;
using CoreAlgo.Architecture.Core.Execution;
using CoreAlgo.Architecture.Core.Interfaces;
using CoreAlgo.Architecture.QC.Helpers;
namespace CoreAlgo.Architecture.Core.Services
{
    public class SmartOrderManager
    {
        private readonly QCAlgorithm _algorithm;
        private readonly IAlgorithmContext _context;
        private readonly Dictionary<int, SmartOrderTracker> _activeOrders;
        private readonly Dictionary<int, ComboOrderTracker> _activeComboOrders;
        private readonly HashSet<ScheduledEvent> _scheduledEvents;
        private ISmartPricingEngine _pricingEngine;
        private PositionOverlapManager _overlapManager;
        public SmartOrderManager(QCAlgorithm algorithm, IAlgorithmContext context)
        {
            _algorithm = algorithm ?? throw new ArgumentNullException(nameof(algorithm));
            _context = context ?? throw new ArgumentNullException(nameof(context));
            _activeOrders = new Dictionary<int, SmartOrderTracker>();
            _activeComboOrders = new Dictionary<int, ComboOrderTracker>();
            _scheduledEvents = new HashSet<ScheduledEvent>();
        }
        public void SetPricingEngine(ISmartPricingEngine pricingEngine)
        {
            _pricingEngine = pricingEngine;
        }
        public void SetOverlapManager(PositionOverlapManager overlapManager)
        {
            _overlapManager = overlapManager;
        }
        public OrderTicket SmartMarketOrder(Symbol symbol, decimal quantity, string tag = "")
        {
            if (_overlapManager != null)
            {
                var validation = _overlapManager.ValidateNewPosition(symbol, quantity, tag);
                if (!validation.IsValid)
                {
                    _context.Logger.Warning($"[OVERLAP PREVENTION] Order blocked: {validation.Message}");
                    return null;
                }
            }
            if (_pricingEngine == null)
            {
                return _algorithm.MarketOrder(symbol, quantity, tag: tag, asynchronous: true);
            }
            try
            {
                var security = _algorithm.Securities[symbol];
                var quote = GetCurrentQuote(security);
                if (quote == null || quote.Spread > 10m) // Skip if spread too wide
                {
                    _context.Logger.Debug($"SmartOrder: Using market order for {symbol} due to wide spread or no quote");
                    return _algorithm.MarketOrder(symbol, quantity, tag: tag, asynchronous: true);
                }
                var direction = quantity > 0 ? CoreAlgo.Architecture.Core.Execution.OrderDirection.Buy : CoreAlgo.Architecture.Core.Execution.OrderDirection.Sell;
                var initialPrice = _pricingEngine.CalculateInitialPrice(quote, direction);
                var roundedInitialPrice = PriceRounding.RoundLimitPrice(_algorithm.Securities, symbol, quantity, initialPrice);
                var ticket = _algorithm.LimitOrder(symbol, quantity, roundedInitialPrice, tag: tag + " [Smart]", asynchronous: true);
                if (ticket == null || ticket.Status == OrderStatus.Invalid)
                {
                    _context.Logger.Error($"SmartOrder: Failed to place initial limit order for {symbol}");
                    return _algorithm.MarketOrder(symbol, quantity, tag: tag, asynchronous: true);
                }
                var tracker = new SmartOrderTracker(ticket, quote, direction, SmartPricingMode.Normal, roundedInitialPrice);
                _activeOrders[ticket.OrderId] = tracker;
                _context.Logger.Info($"SmartOrder: Placed initial limit order for {symbol} at ${initialPrice:F2} " +
                                              $"(Mid: ${quote.Price:F2}, Spread: ${quote.Spread:F2})");
                ScheduleNextPricingUpdate(tracker);
                return ticket;
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"SmartOrder error: {ex.Message}");
                return _algorithm.MarketOrder(symbol, quantity, tag: tag, asynchronous: true);
            }
        }
        public List<OrderTicket> SmartComboMarketOrder(List<Leg> legs, int quantity, string tag = "")
        {
            if (_overlapManager != null)
            {
                var validation = _overlapManager.ValidateComboOrder(legs, quantity, tag);
                if (!validation.IsValid)
                {
                    _context.Logger.Warning($"[COMBO ORDER BLOCKED] {validation.Message}");
                    return new List<OrderTicket>(); // Return empty list to indicate order was blocked
                }
                _context.Logger.Debug($"[COMBO ORDER APPROVED] {legs.Count}-leg order validated for {tag}");
            }
            if (_pricingEngine == null || _pricingEngine.Mode == SmartPricingMode.Off)
            {
                _context.Logger.Debug($"SmartCombo: Using basic combo market order (no smart pricing)");
                return _algorithm.ComboMarketOrder(legs, quantity, tag: tag, asynchronous: true);
            }
            try
            {
                var comboQuote = ComboQuote.FromSecurities(legs, _algorithm.Securities);
                if (comboQuote == null)
                {
                    _context.Logger.Warning($"SmartCombo: No valid quotes available for combo, using market order");
                    return _algorithm.ComboMarketOrder(legs, quantity, tag: tag, asynchronous: true);
                }
                var comboDirection = ComboPricingEngine.DetermineComboDirection(legs);
                if (!_pricingEngine.ShouldAttemptComboPricing(comboQuote, comboDirection))
                {
                    _context.Logger.Debug($"SmartCombo: Conditions not suitable for smart pricing, using market order");
                    return _algorithm.ComboMarketOrder(legs, quantity, tag: tag, asynchronous: true);
                }
                var initialNetPrice = _pricingEngine.CalculateInitialComboPrice(legs, comboQuote, comboDirection);
                if (!initialNetPrice.HasValue)
                {
                    _context.Logger.Debug($"SmartCombo: Could not calculate initial price, using market order");
                    return _algorithm.ComboMarketOrder(legs, quantity, tag: tag, asynchronous: true);
                }
                var comboTickets = _algorithm.ComboLimitOrder(legs, quantity, initialNetPrice.Value, tag: tag + " [SmartCombo]", asynchronous: true);
                if (comboTickets == null || comboTickets.Count == 0)
                {
                    _context.Logger.Error($"SmartCombo: Failed to place combo limit order, trying market order fallback");
                    return _algorithm.ComboMarketOrder(legs, quantity, tag: tag, asynchronous: true);
                }
                var roundedInitialNetPrice = PriceRounding.RoundComboNetPrice(
                    _algorithm.Securities,
                    legs,
                    initialNetPrice.Value,
                    comboDirection == CoreAlgo.Architecture.Core.Execution.OrderDirection.Buy);
                if (Math.Abs(roundedInitialNetPrice - initialNetPrice.Value) > 0m)
                {
                    foreach (var comboTicket in comboTickets)
                    {
                        comboTicket.Update(new UpdateOrderFields { LimitPrice = roundedInitialNetPrice });
                    }
                }
                var comboTracker = new ComboOrderTracker(comboTickets, legs, comboQuote,
                    comboDirection, _pricingEngine.Mode, roundedInitialNetPrice);
                _activeComboOrders[comboTracker.PrimaryOrderId] = comboTracker;
                _context.Logger.Info($"SmartCombo: Placed {legs.Count}-leg combo limit order " +
                                              $"at net price ${roundedInitialNetPrice:F2} " +
                                              $"(NetBid: ${comboQuote.NetBid:F2}, NetAsk: ${comboQuote.NetAsk:F2}, " +
                                              $"NetMid: ${comboQuote.NetMid:F2}, Direction: {comboDirection})");
                ScheduleNextComboPricingUpdate(comboTracker);
                return comboTickets;
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"SmartCombo error: {ex.Message}");
                return _algorithm.ComboMarketOrder(legs, quantity, tag: tag, asynchronous: true);
            }
        }
        public void OnOrderEvent(OrderEvent orderEvent)
        {
            if (_activeOrders.TryGetValue(orderEvent.OrderId, out var tracker))
            {
                switch (orderEvent.Status)
                {
                    case OrderStatus.Filled:
                        _context.Logger.Info($"SmartOrder: Order {orderEvent.OrderId} filled at ${orderEvent.FillPrice:F2} " +
                                                      $"after {tracker.AttemptNumber} attempts");
                        CleanupOrder(tracker);
                        break;
                    case OrderStatus.PartiallyFilled:
                        _context.Logger.Debug($"SmartOrder: Order {orderEvent.OrderId} partially filled " +
                                                       $"({orderEvent.FillQuantity}/{tracker.OrderTicket.Quantity})");
                        tracker.UpdatePartialFill(orderEvent);
                        break;
                    case OrderStatus.Canceled:
                    case OrderStatus.Invalid:
                        _context.Logger.Warning($"SmartOrder: Order {orderEvent.OrderId} {orderEvent.Status}");
                        CleanupOrder(tracker);
                        break;
                }
                return;
            }
            foreach (var comboTracker in _activeComboOrders.Values)
            {
                var matchingTicket = comboTracker.ComboTickets.FirstOrDefault(t => t.OrderId == orderEvent.OrderId);
                if (matchingTicket != null)
                {
                    HandleComboOrderEvent(comboTracker, orderEvent, matchingTicket);
                    return;
                }
            }
        }
        private void HandleComboOrderEvent(ComboOrderTracker comboTracker, OrderEvent orderEvent, OrderTicket matchingTicket)
        {
            switch (orderEvent.Status)
            {
                case OrderStatus.Filled:
                    _context.Logger.Debug($"SmartCombo: Leg {orderEvent.OrderId} of combo {comboTracker.PrimaryOrderId} " +
                                                   $"filled at ${orderEvent.FillPrice:F2}");
                    if (comboTracker.IsCompletelyFilled)
                    {
                        _context.Logger.Info($"SmartCombo: Combo order {comboTracker.PrimaryOrderId} completely filled " +
                                                      $"after {comboTracker.AttemptNumber} pricing attempts");
                        CleanupComboOrder(comboTracker);
                    }
                    break;
                case OrderStatus.PartiallyFilled:
                    _context.Logger.Debug($"SmartCombo: Leg {orderEvent.OrderId} of combo {comboTracker.PrimaryOrderId} " +
                                                   $"partially filled ({orderEvent.FillQuantity}/{matchingTicket.Quantity})");
                    comboTracker.UpdatePartialFill(orderEvent);
                    break;
                case OrderStatus.Canceled:
                case OrderStatus.Invalid:
                    _context.Logger.Warning($"SmartCombo: Leg {orderEvent.OrderId} of combo {comboTracker.PrimaryOrderId} {orderEvent.Status}");
                    _context.Logger.Warning($"SmartCombo: Combo order {comboTracker.PrimaryOrderId} failed due to leg {orderEvent.OrderId}");
                    CleanupComboOrder(comboTracker);
                    break;
            }
        }
        private void UpdateOrderPrice(SmartOrderTracker tracker)
        {
            try
            {
                if (tracker.ScheduledEvent != null)
                {
                    _scheduledEvents.Remove(tracker.ScheduledEvent);
                    tracker.ScheduledEvent = null;
                }
                if (!_activeOrders.ContainsKey(tracker.OrderTicket.OrderId) ||
                    tracker.OrderTicket.Status == OrderStatus.Filled)
                {
                    return;
                }
                if (tracker.AttemptNumber >= _pricingEngine.GetMaxAttempts())
                {
                    _context.Logger.Info($"SmartOrder: Max attempts reached for order {tracker.OrderTicket.OrderId}, " +
                                                  "converting to market order");
                    tracker.OrderTicket.Cancel("Max pricing attempts reached");
                    var remainingQty = tracker.OrderTicket.Quantity - tracker.OrderTicket.QuantityFilled;
                    if (remainingQty != 0)
                    {
                        _algorithm.MarketOrder(tracker.OrderTicket.Symbol, remainingQty,
                            tag: tracker.OrderTicket.Tag + " [Smart-Market]", asynchronous: true);
                    }
                    CleanupOrder(tracker);
                    return;
                }
                var security = _algorithm.Securities[tracker.OrderTicket.Symbol];
                var currentQuote = GetCurrentQuote(security);
                if (currentQuote == null)
                {
                    _context.Logger.Warning($"SmartOrder: No quote available for {tracker.OrderTicket.Symbol}");
                    ScheduleNextPricingUpdate(tracker);
                    return;
                }
                tracker.AttemptNumber++;
                var nextPrice = _pricingEngine.CalculateNextPrice(
                    tracker.CurrentPrice, currentQuote, tracker.OrderDirection, tracker.AttemptNumber);
                if (nextPrice.HasValue && Math.Abs(nextPrice.Value - tracker.CurrentPrice) > 0.01m)
                {
                    var roundedNextPrice = PriceRounding.RoundLimitPrice(
                        _algorithm.Securities,
                        tracker.OrderTicket.Symbol,
                        tracker.OrderTicket.Quantity,
                        nextPrice.Value);
                    var updateFields = new UpdateOrderFields { LimitPrice = roundedNextPrice };
                    var response = tracker.OrderTicket.Update(updateFields);
                    if (response.IsSuccess)
                    {
                        tracker.CurrentPrice = roundedNextPrice;
                        _context.Logger.Debug($"SmartOrder: Updated order {tracker.OrderTicket.OrderId} " +
                                                       $"price to ${roundedNextPrice:F2} (attempt {tracker.AttemptNumber})");
                    }
                    else
                    {
                        _context.Logger.Warning($"SmartOrder: Failed to update order price: {response.ErrorMessage}");
                    }
                }
                ScheduleNextPricingUpdate(tracker);
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"SmartOrder update error: {ex.Message}");
                CleanupOrder(tracker);
            }
        }
        private void ScheduleNextPricingUpdate(SmartOrderTracker tracker)
        {
            var interval = _pricingEngine.GetPricingInterval();
            var updateTime = _algorithm.Time.Add(interval);
            var scheduledEvent = _algorithm.Schedule.On(
                _algorithm.DateRules.On(updateTime.Date),
                _algorithm.TimeRules.At(updateTime.Hour, updateTime.Minute, updateTime.Second),
                () => UpdateOrderPrice(tracker));
            _scheduledEvents.Add(scheduledEvent);
            tracker.ScheduledEvent = scheduledEvent;
        }
        private void CleanupOrder(SmartOrderTracker tracker)
        {
            _activeOrders.Remove(tracker.OrderTicket.OrderId);
            if (tracker.ScheduledEvent != null)
            {
                _algorithm.Schedule.Remove(tracker.ScheduledEvent);
                _scheduledEvents.Remove(tracker.ScheduledEvent);
            }
        }
        private Quote GetCurrentQuote(Security security)
        {
            if (security.BidPrice == 0 || security.AskPrice == 0)
                return null;
            return new Quote(security.BidPrice, security.AskPrice);
        }
        private void ScheduleNextComboPricingUpdate(ComboOrderTracker tracker)
        {
            var interval = _pricingEngine.GetPricingInterval();
            var updateTime = _algorithm.Time.Add(interval);
            var scheduledEvent = _algorithm.Schedule.On(_algorithm.DateRules.On(updateTime.Date),
                _algorithm.TimeRules.At(updateTime.Hour, updateTime.Minute, updateTime.Second),
                () => UpdateComboOrderPrice(tracker));
            _scheduledEvents.Add(scheduledEvent);
            tracker.ScheduledEvent = scheduledEvent;
        }
        private void UpdateComboOrderPrice(ComboOrderTracker tracker)
        {
            try
            {
                if (tracker.ScheduledEvent != null)
                {
                    _scheduledEvents.Remove(tracker.ScheduledEvent);
                    tracker.ScheduledEvent = null;
                }
                if (!_activeComboOrders.ContainsKey(tracker.PrimaryOrderId) || tracker.IsCompletelyFilled)
                {
                    return;
                }
                var maxAttempts = _pricingEngine.GetMaxAttempts();
                var maxRuntime = TimeSpan.FromMinutes(5); // Max 5 minutes for combo orders
                if (!tracker.ShouldContinuePricing(maxAttempts, maxRuntime))
                {
                    _context.Logger.Info($"SmartCombo: Max attempts/timeout reached for combo {tracker.PrimaryOrderId} " +
                                                   $"(attempts: {tracker.AttemptNumber}/{maxAttempts}, runtime: {tracker.GetRuntime().TotalSeconds:F0}s), " +
                                                   "converting to market order");
                    CancelComboOrder(tracker, "Max pricing attempts/timeout reached");
                    if (!tracker.IsCompletelyFilled)
                    {
                        var remainingQty = CalculateComboRemainingQuantity(tracker);
                        if (remainingQty != 0)
                        {
                            var originalTag = tracker.PrimaryTicket?.Tag?.Replace(" [SmartCombo]", "") ?? "";
                            _algorithm.ComboMarketOrder(tracker.Legs, remainingQty,
                                tag: originalTag + " [SmartCombo-Market]", asynchronous: true);
                            _context.Logger.Info($"SmartCombo: Placed market order fallback for combo {tracker.PrimaryOrderId} " +
                                                          $"with {tracker.Legs.Count} legs, qty={remainingQty}");
                        }
                    }
                    CleanupComboOrder(tracker);
                    return;
                }
                var currentComboQuote = ComboQuote.FromSecurities(tracker.Legs, _algorithm.Securities);
                if (currentComboQuote == null)
                {
                    _context.Logger.Warning($"SmartCombo: No quotes available for combo {tracker.PrimaryOrderId}, stopping updates");
                    CleanupComboOrder(tracker);
                    return;
                }
                var nextNetPrice = _pricingEngine.CalculateNextComboPrice(
                    tracker.CurrentNetPrice, currentComboQuote, tracker.ComboDirection, tracker.AttemptNumber + 1);
                if (nextNetPrice.HasValue)
                {
                    var updateSuccess = true;
                    foreach (var ticket in tracker.ComboTickets)
                    {
                        if (ticket.Status == OrderStatus.Submitted || ticket.Status == OrderStatus.PartiallyFilled)
                        {
                            var result = ticket.Update(new UpdateOrderFields { LimitPrice = nextNetPrice.Value });
                            if (!result.IsSuccess)
                            {
                                _context.Logger.Warning($"SmartCombo: Failed to update combo ticket {ticket.OrderId}: {result.ErrorMessage}");
                                updateSuccess = false;
                            }
                        }
                    }
                    if (updateSuccess)
                    {
                        var isDebit = tracker.ComboDirection == CoreAlgo.Architecture.Core.Execution.OrderDirection.Buy;
                        var roundedNetPrice = PriceRounding.RoundComboNetPrice(_algorithm.Securities, tracker.Legs, nextNetPrice.Value, isDebit);
                        if (Math.Abs(roundedNetPrice - nextNetPrice.Value) > 0m)
                        {
                            foreach (var ticket in tracker.ComboTickets)
                            {
                                if (ticket.Status == OrderStatus.Submitted || ticket.Status == OrderStatus.PartiallyFilled)
                                {
                                    ticket.Update(new UpdateOrderFields { LimitPrice = roundedNetPrice });
                                }
                            }
                        }
                        tracker.UpdateNetPrice(roundedNetPrice, currentComboQuote);
                        _context.Logger.Debug($"SmartCombo: Updated combo {tracker.PrimaryOrderId} " +
                                                       $"to net price ${nextNetPrice.Value:F2} (attempt {tracker.AttemptNumber}) " +
                                                       $"NetMid: ${currentComboQuote.NetMid:F2}");
                        if (tracker.AttemptNumber < maxAttempts)
                        {
                            ScheduleNextComboPricingUpdate(tracker);
                        }
                        else
                        {
                            _context.Logger.Debug($"SmartCombo: Reached max attempts for combo {tracker.PrimaryOrderId}");
                        }
                    }
                    else
                    {
                        _context.Logger.Warning($"SmartCombo: Failed to update combo order, stopping progressive pricing");
                        CleanupComboOrder(tracker);
                    }
                }
                else
                {
                    _context.Logger.Debug($"SmartCombo: No more price improvements for combo {tracker.PrimaryOrderId}");
                    CleanupComboOrder(tracker);
                }
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"SmartCombo update error for combo {tracker.PrimaryOrderId}: {ex.Message}");
                CleanupComboOrder(tracker);
            }
        }
        private void CleanupComboOrder(ComboOrderTracker tracker)
        {
            _activeComboOrders.Remove(tracker.PrimaryOrderId);
            if (tracker.ScheduledEvent != null)
            {
                _algorithm.Schedule.Remove(tracker.ScheduledEvent);
                _scheduledEvents.Remove(tracker.ScheduledEvent);
            }
        }
        private void CancelComboOrder(ComboOrderTracker tracker, string reason)
        {
            foreach (var ticket in tracker.ComboTickets)
            {
                if (ticket.Status == OrderStatus.Submitted || ticket.Status == OrderStatus.PartiallyFilled)
                {
                    ticket.Cancel(reason);
                }
            }
        }
        private int CalculateComboRemainingQuantity(ComboOrderTracker tracker)
        {
            var primaryTicket = tracker.PrimaryTicket;
            if (primaryTicket == null)
                return 0;
            var originalQty = (int)primaryTicket.Quantity;
            var filledQty = (int)primaryTicket.QuantityFilled;
            return originalQty - filledQty;
        }
        public string GetPricingMode()
        {
            return _pricingEngine?.GetType().Name.Replace("PricingStrategy", "") ?? "Off";
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using CoreAlgo.Architecture.Core.Interfaces;
using CoreAlgo.Architecture.Core.Models;

namespace CoreAlgo.Architecture.Core.Services
{
    /// <summary>
    /// Utility class for discovering strategy implementations through reflection
    /// Eliminates hardcoded switch statements by dynamically finding Templates
    /// </summary>
    public static class StrategyDiscovery
    {
        private static readonly Dictionary<string, Type> _strategyTypes = new Dictionary<string, Type>();
        private static readonly Dictionary<string, Type> _configTypes = new Dictionary<string, Type>();
        private static readonly object _lock = new object();
        private static bool _initialized = false;

        /// <summary>
        /// Initialize discovery cache by scanning Templates namespace
        /// </summary>
        private static void EnsureInitialized()
        {
            if (_initialized) return;

            lock (_lock)
            {
                if (_initialized) return;

                // Discover all IStrategy implementations in Templates namespace
                var assembly = Assembly.GetExecutingAssembly();
                var strategyTypes = assembly.GetTypes()
                    .Where(t => typeof(IStrategy).IsAssignableFrom(t) &&
                               !t.IsInterface &&
                               !t.IsAbstract &&
                               t.Namespace == "CoreAlgo.Architecture.Core.Templates")
                    .ToList();

                // Build strategy name mappings
                foreach (var type in strategyTypes)
                {
                    var strategyName = GetStrategyNameFromType(type);
                    _strategyTypes[strategyName.ToUpperInvariant()] = type;

                    // Find corresponding config type
                    var configType = FindConfigType(strategyName);
                    if (configType != null)
                    {
                        _configTypes[strategyName.ToUpperInvariant()] = configType;
                    }
                }

                _initialized = true;
            }
        }

        /// <summary>
        /// Extract strategy name from template class name
        /// IronCondorTemplate -> IronCondor
        /// </summary>
        private static string GetStrategyNameFromType(Type type)
        {
            var name = type.Name;
            return name.EndsWith("Template") ? name.Substring(0, name.Length - 8) : name;
        }

        /// <summary>
        /// Find config type by naming convention
        /// IronCondor -> IronCondorConfig
        /// </summary>
        private static Type FindConfigType(string strategyName)
        {
            var configTypeName = $"{strategyName}Config";
            var assembly = Assembly.GetExecutingAssembly();
            
            return assembly.GetTypes()
                .FirstOrDefault(t => t.Name == configTypeName &&
                                    typeof(StrategyConfig).IsAssignableFrom(t));
        }

        /// <summary>
        /// Get all discovered strategy names
        /// </summary>
        public static IEnumerable<string> GetAllStrategyNames()
        {
            EnsureInitialized();
            return _strategyTypes.Keys.Select(k => k.ToLowerInvariant());
        }

        /// <summary>
        /// Get strategy type by name
        /// </summary>
        public static Type GetStrategyType(string strategyName)
        {
            EnsureInitialized();
            _strategyTypes.TryGetValue(strategyName.ToUpperInvariant(), out var type);
            return type;
        }

        /// <summary>
        /// Get config type by strategy name
        /// </summary>
        public static Type GetConfigType(string strategyName)
        {
            EnsureInitialized();
            _configTypes.TryGetValue(strategyName.ToUpperInvariant(), out var type);
            return type;
        }

        /// <summary>
        /// Create strategy instance by name
        /// </summary>
        public static IStrategy CreateStrategy(string strategyName)
        {
            var strategyType = GetStrategyType(strategyName);
            if (strategyType == null)
                throw new ArgumentException($"Unknown strategy '{strategyName}'. Available strategies: {string.Join(", ", GetAllStrategyNames())}");

            return (IStrategy)Activator.CreateInstance(strategyType);
        }

        /// <summary>
        /// Check if strategy exists
        /// </summary>
        public static bool StrategyExists(string strategyName)
        {
            return GetStrategyType(strategyName) != null;
        }
    }
}
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using QuantConnect;
    using QuantConnect.Data;
    using QuantConnect.Orders;
    using QuantConnect.Securities;
    using CoreAlgo.Architecture.Core.Implementations;
namespace CoreAlgo.Architecture.Core.Services
{
    public static class TradeHighlightFormatter
    {
        public static string BuildEntry(
            string symbol,
            DateTime time,
            decimal entryPrice,
            int quantity,
            decimal? stopLoss = null,
            decimal? takeProfit = null,
            int? orderId = null)
        {
            var parts = new List<string>
            {
                $"==> ENTRY {symbol} {time:HH:mm} @{entryPrice:F2} x{quantity}"
            };
            if (stopLoss.HasValue)
                parts.Add($"SL {stopLoss.Value:F2}");
            if (takeProfit.HasValue)
                parts.Add($"TP {takeProfit.Value:F2}");
            if (orderId.HasValue)
                parts.Add($"O:{orderId.Value}");
            return string.Join(" | ", parts);
        }
        public static string BuildExit(
            string symbol,
            DateTime time,
            decimal exitPrice,
            decimal pnl,
            ExitType exitType,
            int? orderId = null)
        {
            var prefix = exitType switch
            {
                ExitType.TakeProfit => "<== TP",
                ExitType.StopLoss => "!! SL",
                ExitType.TrailingStop => "~~> TS",
                ExitType.Cancel => "X CANCEL",
                _ => "<==" // fallback
            };
            var pnlSign = pnl >= 0 ? "+" : "";
            var parts = new List<string>
            {
                $"{prefix} {symbol} {time:HH:mm} @{exitPrice:F2}",
                $"PnL {pnlSign}{pnl:F2}"
            };
            if (orderId.HasValue)
                parts.Add($"O:{orderId.Value}");
            return string.Join(" | ", parts);
        }
        public static string FormatMetricsSuffix(IDictionary<string, string> metrics)
        {
            if (metrics == null || metrics.Count == 0)
                return string.Empty;
            var sorted = metrics.OrderBy(kvp => kvp.Key).ToList();
            var pairs = sorted.Select(kvp => $"{kvp.Key}={kvp.Value}");
            var content = string.Join(", ", pairs);
            if (content.Length > 120)
                content = content.Substring(0, 117) + "...";
            return $"[{content}]";
        }
        public static string FormatTradeTracking(
            int entryId,
            DateTime time,
            decimal currentPrice,
            TradeRecord trade,
            decimal highestPriceSinceEntry,
            bool trailingArmed,
            decimal currentStopPrice,
            OrderTicket slTicket = null,
            bool isLongPosition = true,
            bool includeQuotes = false,
            Security security = null)
        {
            var parts = new List<string>
            {
                $"[TT] E{entryId} @{time:HH:mm:ss}",
                $"Price:{currentPrice:F2}",
                $"Entry:{trade.OpenPrice:F2}@{trade.OpenTime:HH:mm:ss}",
                $"High:{highestPriceSinceEntry:F2}"
            };
            var unrealizedPnL = (currentPrice - trade.OpenPrice) * trade.Quantity;
            var pnlSign = unrealizedPnL >= 0 ? "+" : "";
            parts.Add($"UnrealPnL:{pnlSign}{unrealizedPnL:F2}");
            parts.Add($"Trail:{(trailingArmed ? "ARMED" : "DISARMED")}");
            if (currentStopPrice > 0)
            {
                parts.Add($"Stop:{currentStopPrice:F2}");
            }
            if (slTicket != null)
            {
                var stopPrice = slTicket.Get(OrderField.StopPrice);
                var limitPrice = slTicket.Get(OrderField.LimitPrice);
                var ticketParts = new List<string> { $"SL:O{slTicket.OrderId}" };
                if (stopPrice != 0)
                {
                    ticketParts.Add($"Stop:{stopPrice:F2}");
                    bool stopTriggered = isLongPosition
                        ? currentPrice <= stopPrice
                        : currentPrice >= stopPrice;
                    if (stopTriggered && slTicket.Status == OrderStatus.Submitted)
                    {
                        ticketParts.Add("[TRIGGERED]");
                    }
                }
                if (limitPrice != 0)
                {
                    ticketParts.Add($"Limit:{limitPrice:F2}");
                    if (stopPrice != 0)
                    {
                        var distanceToLimit = Math.Abs(limitPrice - currentPrice);
                        ticketParts.Add($"Dist:{distanceToLimit:F4}");
                    }
                }
                ticketParts.Add($"Status:{slTicket.Status}");
                parts.Add(string.Join(" ", ticketParts));
            }
            if (includeQuotes && security != null)
            {
                var bid = security.BidPrice;
                var ask = security.AskPrice;
                if (bid > 0 && ask > 0)
                {
                    var spread = ask - bid;
                    parts.Add($"Bid:{bid:F2} Ask:{ask:F2} Spread:{spread:F4}");
                }
            }
            return string.Join(" | ", parts);
        }
        public static string FormatEntryFill(
            int entryId,
            DateTime time,
            decimal fillPrice,
            int quantity,
            decimal stopDist,
            decimal tpDist,
            decimal atrAtEntry,
            decimal adaptFactor,
            decimal slPrice,
            decimal slLimitPrice,
            decimal stopLimitBuffer,
            decimal tick,
            int openTradesBefore,
            bool includeQuotes = false,
            Security security = null)
        {
            var parts = new List<string>
            {
                $"[TT ENTRY FILL] E{entryId} @{time:HH:mm:ss}",
                $"Fill:{fillPrice:F2} Qty:{quantity}",
                $"StopDist:{stopDist:F4} TpDist:{tpDist:F4} ATR:{atrAtEntry:F4} Adapt:{adaptFactor:F4}",
                $"SL:{slPrice:F2} Limit:{slLimitPrice:F2} Buffer:{stopLimitBuffer:F4} Tick:{tick:F4}",
                $"OpenTrades:{openTradesBefore}"
            };
            if (includeQuotes && security != null)
            {
                var bid = security.BidPrice;
                var ask = security.AskPrice;
                if (bid > 0 && ask > 0)
                {
                    parts.Add($"Bid:{bid:F2} Ask:{ask:F2}");
                }
            }
            return string.Join(" | ", parts);
        }
        public static string FormatExitFill(
            int entryId,
            DateTime time,
            string exitReason,
            decimal entryPrice,
            DateTime entryTime,
            decimal exitPrice,
            decimal pnl,
            bool trailingArmed,
            decimal highestPriceSinceEntry,
            decimal currentStopPrice,
            int openTradesBefore,
            OrderTicket slTicket = null,
            bool isLongPosition = true,
            bool includeQuotes = false,
            Security security = null)
        {
            var pnlSign = pnl >= 0 ? "+" : "";
            var parts = new List<string>
            {
                $"[TT EXIT FILL] E{entryId} @{time:HH:mm:ss} Reason:{exitReason}",
                $"Entry:{entryPrice:F2}@{entryTime:HH:mm:ss} Exit:{exitPrice:F2} PnL:{pnlSign}{pnl:F2}",
                $"Trail:{(trailingArmed ? "ARMED" : "DISARMED")} High:{highestPriceSinceEntry:F2} Stop:{currentStopPrice:F2}",
                $"OpenTrades:{openTradesBefore}→{openTradesBefore - 1}"
            };
            if (slTicket != null)
            {
                var stopPrice = slTicket.Get(OrderField.StopPrice);
                var limitPrice = slTicket.Get(OrderField.LimitPrice);
                var ticketParts = new List<string> { $"SL:O{slTicket.OrderId}" };
                if (stopPrice != 0) ticketParts.Add($"Stop:{stopPrice:F2}");
                if (limitPrice != 0) ticketParts.Add($"Limit:{limitPrice:F2}");
                ticketParts.Add($"Status:{slTicket.Status}");
                parts.Add(string.Join(" ", ticketParts));
            }
            if (includeQuotes && security != null)
            {
                var bid = security.BidPrice;
                var ask = security.AskPrice;
                if (bid > 0 && ask > 0)
                {
                    parts.Add($"Bid:{bid:F2} Ask:{ask:F2}");
                }
            }
            return string.Join(" | ", parts);
        }
        public static string FormatEodFlattenTrade(
            int entryId,
            DateTime time,
            TradeRecord trade,
            decimal currentPrice,
            decimal unrealizedPnL,
            bool trailingArmed,
            decimal highestPriceSinceEntry,
            decimal currentStopPrice,
            OrderTicket slTicket = null,
            bool isLongPosition = true,
            bool includeQuotes = false,
            Security security = null)
        {
            var pnlSign = unrealizedPnL >= 0 ? "+" : "";
            var parts = new List<string>
            {
                $"[TT EOD PRE-FLATTEN] E{entryId} @{time:HH:mm:ss}",
                $"Entry:{trade.OpenPrice:F2}@{trade.OpenTime:HH:mm:ss} Current:{currentPrice:F2} UnrealPnL:{pnlSign}{unrealizedPnL:F2}",
                $"Trail:{(trailingArmed ? "ARMED" : "DISARMED")} High:{highestPriceSinceEntry:F2} Stop:{currentStopPrice:F2}"
            };
            if (slTicket != null)
            {
                var stopPrice = slTicket.Get(OrderField.StopPrice);
                var limitPrice = slTicket.Get(OrderField.LimitPrice);
                var ticketParts = new List<string> { $"SL:O{slTicket.OrderId}" };
                if (stopPrice != 0)
                {
                    ticketParts.Add($"Stop:{stopPrice:F2}");
                    bool stopTriggered = isLongPosition
                        ? currentPrice <= stopPrice
                        : currentPrice >= stopPrice;
                    if (stopTriggered)
                    {
                        ticketParts.Add("[TRIGGERED]");
                    }
                }
                if (limitPrice != 0) ticketParts.Add($"Limit:{limitPrice:F2}");
                ticketParts.Add($"Status:{slTicket.Status}");
                parts.Add(string.Join(" ", ticketParts));
            }
            if (includeQuotes && security != null)
            {
                var bid = security.BidPrice;
                var ask = security.AskPrice;
                if (bid > 0 && ask > 0)
                {
                    parts.Add($"Bid:{bid:F2} Ask:{ask:F2}");
                }
            }
            return string.Join(" | ", parts);
        }
        public static string FormatSlCreated(
            int entryId,
            int slOrderId,
            OrderStatus status,
            decimal stopPrice,
            decimal limitPrice,
            decimal fillPrice,
            bool isLongPosition)
        {
            var parts = new List<string>
            {
                $"[TT SL CREATED] E{entryId}",
                $"SL OrderId:{slOrderId} Status:{status}",
                $"Stop:{stopPrice:F2} Limit:{limitPrice:F2}",
                $"Fill:{fillPrice:F2}"
            };
            return string.Join(" | ", parts);
        }
    }
    public enum ExitType
    {
        TakeProfit,
        StopLoss,
        TrailingStop,
        Cancel
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using CoreAlgo.Architecture.Core.Interfaces;
using CoreAlgo.Architecture.Core.Implementations;
namespace CoreAlgo.Architecture.Core.Services
{
    public class TradePersistenceService
    {
        private readonly IAlgorithmContext _context;
        private const string CurrentPositionsKey = "positions/current.json";
        private const string SnapshotKeyPrefix = "positions/snapshots/";
        private DateTime _lastSaveDate = DateTime.MinValue;
        private static readonly JsonSerializerOptions SerializerOptions = new JsonSerializerOptions
        {
            WriteIndented = true,
            DefaultIgnoreCondition = JsonIgnoreCondition.Never,
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        };
        public TradePersistenceService(IAlgorithmContext context)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
        }
        public void SaveTrades(TradeTracker tracker)
        {
            if (tracker == null) return;
            if (!_context.Algorithm.LiveMode)
            {
                var today = _context.Algorithm.Time.Date;
                if (today == _lastSaveDate)
                    return;
                _lastSaveDate = today;
            }
            try
            {
                var openAndWorking = tracker.WorkingTrades.Concat(tracker.OpenTrades).ToList();
                var book = new TradeBook
                {
                    Version = "1.0",
                    AsOfUtc = _context.Algorithm.UtcTime,
                    AllTrades = openAndWorking,
                    WorkingTrades = tracker.WorkingTrades.Select(t => t.OrderId).ToList(),
                    OpenTrades = tracker.OpenTrades.Select(t => t.OrderId).ToList(),
                    ClosedTrades = new List<string>() // Don't persist closed trades
                };
                var json = JsonSerializer.Serialize(book, SerializerOptions);
                var bytes = System.Text.Encoding.UTF8.GetBytes(json);
                _context.Algorithm.ObjectStore.SaveBytes(CurrentPositionsKey, bytes);
                _context.Logger.Trace($"TradePersistence: Saved {openAndWorking.Count} open/working trades to ObjectStore (excluded {tracker.ClosedTrades.Count} closed)");
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"TradePersistence: Error saving trades: {ex.Message}");
            }
        }
        public void ClearPositionsIfBacktest()
        {
            if (_context.Algorithm.LiveMode)
            {
                return;
            }
            if (_context.Algorithm.ObjectStore.ContainsKey(CurrentPositionsKey))
            {
                _context.Algorithm.ObjectStore.Delete(CurrentPositionsKey);
                _context.Logger.Info("TradePersistence: Cleared current positions for backtest");
            }
        }
        public void ClearSnapshotsIfBacktest()
        {
            if (_context.Algorithm.LiveMode)
            {
                return;
            }
            try
            {
                var deletedCount = 0;
                var keysToDelete = new List<string>();
                foreach (var key in _context.Algorithm.ObjectStore.Keys)
                {
                    if (key.StartsWith(SnapshotKeyPrefix))
                    {
                        keysToDelete.Add(key);
                    }
                }
                foreach (var key in keysToDelete)
                {
                    _context.Algorithm.ObjectStore.Delete(key);
                    deletedCount++;
                }
                if (deletedCount > 0)
                {
                    _context.Logger.Info($"TradePersistence: Cleared {deletedCount} daily snapshots for backtest");
                }
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"TradePersistence: Error clearing snapshots: {ex.Message}");
            }
        }
        public TradeTracker LoadTrades()
        {
            try
            {
                if (!_context.Algorithm.ObjectStore.ContainsKey(CurrentPositionsKey))
                {
                    _context.Logger.Info("TradePersistence: No saved trades found, starting fresh");
                    return null;
                }
                var bytes = _context.Algorithm.ObjectStore.ReadBytes(CurrentPositionsKey);
                var json = System.Text.Encoding.UTF8.GetString(bytes);
                var book = JsonSerializer.Deserialize<TradeBook>(json, SerializerOptions);
                if (book == null || book.AllTrades == null)
                {
                    _context.Logger.Warning("TradePersistence: Invalid saved data, starting fresh");
                    return null;
                }
                var tracker = new TradeTracker
                {
                    AllTrades = book.AllTrades
                };
                foreach (var trade in book.AllTrades)
                {
                    if (book.WorkingTrades.Contains(trade.OrderId))
                    {
                        tracker.WorkingTrades.Add(trade);
                    }
                    else if (book.OpenTrades.Contains(trade.OrderId))
                    {
                        tracker.OpenTrades.Add(trade);
                    }
                    else if (book.ClosedTrades.Contains(trade.OrderId))
                    {
                        tracker.ClosedTrades.Add(trade);
                    }
                }
                _context.Logger.Info($"TradePersistence: Loaded {tracker.AllTrades.Count} trades " +
                                               $"(Working: {tracker.WorkingTrades.Count}, Open: {tracker.OpenTrades.Count}, " +
                                               $"Closed: {tracker.ClosedTrades.Count})");
                return tracker;
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"TradePersistence: Error loading trades: {ex.Message}");
                return null;
            }
        }
        public void SaveDailySnapshot(TradeTracker tracker)
        {
            if (tracker == null) return;
            try
            {
                var date = _context.Algorithm.Time.ToString("yyyy-MM-dd");
                var snapshotKey = $"{SnapshotKeyPrefix}{date}.json";
                var openAndWorking = tracker.WorkingTrades.Concat(tracker.OpenTrades).ToList();
                var book = new TradeBook
                {
                    Version = "1.0",
                    AsOfUtc = _context.Algorithm.UtcTime,
                    AllTrades = openAndWorking,
                    WorkingTrades = tracker.WorkingTrades.Select(t => t.OrderId).ToList(),
                    OpenTrades = tracker.OpenTrades.Select(t => t.OrderId).ToList(),
                    ClosedTrades = new List<string>() // Don't persist closed trades
                };
                var json = JsonSerializer.Serialize(book, SerializerOptions);
                var bytes = System.Text.Encoding.UTF8.GetBytes(json);
                _context.Algorithm.ObjectStore.SaveBytes(snapshotKey, bytes);
                _context.Logger.Trace($"TradePersistence: Saved daily snapshot for {date} ({openAndWorking.Count} open/working trades)");
            }
            catch (Exception ex)
            {
                _context.Logger.Error($"TradePersistence: Error saving daily snapshot: {ex.Message}");
            }
        }
        public TradeSummary GetTradeSummary(TradeTracker tracker)
        {
            if (tracker == null)
            {
                return new TradeSummary();
            }
            return new TradeSummary
            {
                TotalTrades = tracker.AllTrades.Count,
                WorkingCount = tracker.WorkingTrades.Count,
                OpenCount = tracker.OpenTrades.Count,
                ClosedCount = tracker.ClosedTrades.Count,
                PartialFillCount = tracker.AllTrades.Count(t => t.Status == "PartialFill"),
                CancelledCount = tracker.AllTrades.Count(t => t.Status == "Cancelled"),
                TotalPnL = tracker.ClosedTrades.Sum(t => t.PnL),
                AsOfUtc = DateTime.UtcNow
            };
        }
    }
    public class TradeBook
    {
        public string Version { get; set; }
        public DateTime AsOfUtc { get; set; }
        public List<TradeRecord> AllTrades { get; set; } = new List<TradeRecord>();
        public List<string> WorkingTrades { get; set; } = new List<string>();
        public List<string> OpenTrades { get; set; } = new List<string>();
        public List<string> ClosedTrades { get; set; } = new List<string>();
    }
    public class TradeSummary
    {
        public int TotalTrades { get; set; }
        public int WorkingCount { get; set; }
        public int OpenCount { get; set; }
        public int ClosedCount { get; set; }
        public int PartialFillCount { get; set; }
        public int CancelledCount { get; set; }
        public decimal TotalPnL { get; set; }
        public DateTime AsOfUtc { get; set; }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Data;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities;
using CoreAlgo.Architecture.Core.Implementations;
using CoreAlgo.Architecture.Core.Models;
using CoreAlgo.Architecture.Core.Helpers;
using CoreAlgo.Architecture.QC.Helpers;
namespace CoreAlgo.Architecture.Core.Templates
{
    public class THTTemplate : SimpleBaseStrategy
    {
        private THTConfig _config;
        private Symbol _spySymbol;
        private readonly Dictionary<Symbol, THTSymbolState> _symbolStates = new Dictionary<Symbol, THTSymbolState>();
        private readonly HashSet<Symbol> _warmedSymbols = new HashSet<Symbol>();
        private bool _needsRebalance;
        private bool _firstTradeLogged;
        public override string Name => "THT";
        public override string Description =>
            "Trend-High-Timing strategy using FVB + BX-Trender indicators on S&P 500 universe with weekly rebalancing";
        public override void OnInitialize()
        {
            SmartLog("THTTemplate.OnInitialize() starting...");
            Configure<THTConfig>();
            _config = (THTConfig)Config;
            SmartLog($"THT Configuration:");
            SmartLog($"  FVB Periods: {_config.FVB33Period} / {_config.FVB20Period}");
            SmartLog($"  BX Params: EMA({_config.BXEmaShort},{_config.BXEmaLong}), RSI({_config.BXRsiPeriod})");
            SmartLog($"  Universe: SPY ETF constituents (S&P 500)");
            SmartLog($"  Warmup: {_config.WarmupDays} daily bars");
            SmartLog($"  StopLoss day threshold: >{_config.StopLossDayOfMonthThreshold}");
            SmartLog($"  MaxPositionWeight: {_config.MaxPositionWeight:P0}");
            Algorithm.UniverseSettings.Resolution = Resolution.Daily;
            Algorithm.UniverseSettings.Asynchronous = true;
            var spySecurity = AssetManager.AddAsset(this, _config.Benchmark, Resolution.Daily);
            _spySymbol = spySecurity.Symbol;
            Algorithm.SetBenchmark(_config.Benchmark);
            Algorithm.AddUniverse(Algorithm.Universe.ETF("SPY"));
            Algorithm.Schedule.On(
                Algorithm.DateRules.WeekStart(),
                Algorithm.TimeRules.AfterMarketOpen(_spySymbol, _config.RebalanceAfterOpenMinutes),
                ScheduledRebalance
            );
            Algorithm.Schedule.On(
                Algorithm.DateRules.EveryDay(_spySymbol),
                Algorithm.TimeRules.BeforeMarketClose(_spySymbol, 1),
                DailySignalCheck
            );
            SmartLog("THT strategy initialized successfully");
        }
        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
            var newStates = new Dictionary<Symbol, THTSymbolState>();
            foreach (var security in changes.AddedSecurities)
            {
                var symbol = security.Symbol;
                if (symbol.SecurityType != SecurityType.Equity || symbol == _spySymbol)
                    continue;
                if (_symbolStates.ContainsKey(symbol))
                    continue;
                if (_warmedSymbols.Contains(symbol))
                {
                    var restoredState = new THTSymbolState
                    {
                        Symbol = symbol,
                        FVB33 = new FairValueBand(_config.FVB33Period),
                        FVB20 = new FairValueBand(_config.FVB20Period, computePivotBands: true, pivotLookback: _config.FVBPivotLookback),
                        MonthlyBX = new BXTrender(_config.BXEmaShort, _config.BXEmaLong, _config.BXRsiPeriod),
                        WeeklyBX = new BXTrender(_config.BXEmaShort, _config.BXEmaLong, _config.BXRsiPeriod)
                    };
                    var wc = new TradeBarConsolidator(CalendarType.Weekly);
                    wc.DataConsolidated += (s, bar) => restoredState.WeeklyBX.Update(bar.EndTime, bar.Close);
                    Algorithm.SubscriptionManager.AddConsolidator(symbol, wc);
                    restoredState.WeeklyConsolidator = wc;
                    var mc = new TradeBarConsolidator(CalendarType.Monthly);
                    mc.DataConsolidated += (s, bar) => restoredState.MonthlyBX.Update(bar.EndTime, bar.Close);
                    Algorithm.SubscriptionManager.AddConsolidator(symbol, mc);
                    restoredState.MonthlyConsolidator = mc;
                    _symbolStates[symbol] = restoredState;
                    continue;
                }
                var state = new THTSymbolState
                {
                    Symbol = symbol,
                    FVB33 = new FairValueBand(_config.FVB33Period),
                    FVB20 = new FairValueBand(_config.FVB20Period, computePivotBands: true, pivotLookback: _config.FVBPivotLookback),
                    MonthlyBX = new BXTrender(_config.BXEmaShort, _config.BXEmaLong, _config.BXRsiPeriod),
                    WeeklyBX = new BXTrender(_config.BXEmaShort, _config.BXEmaLong, _config.BXRsiPeriod)
                };
                var weeklyConsolidator = new TradeBarConsolidator(CalendarType.Weekly);
                weeklyConsolidator.DataConsolidated += (s, bar) =>
                {
                    state.WeeklyBX.Update(bar.EndTime, bar.Close);
                };
                Algorithm.SubscriptionManager.AddConsolidator(symbol, weeklyConsolidator);
                state.WeeklyConsolidator = weeklyConsolidator;
                var monthlyConsolidator = new TradeBarConsolidator(CalendarType.Monthly);
                monthlyConsolidator.DataConsolidated += (s, bar) =>
                {
                    state.MonthlyBX.Update(bar.EndTime, bar.Close);
                };
                Algorithm.SubscriptionManager.AddConsolidator(symbol, monthlyConsolidator);
                state.MonthlyConsolidator = monthlyConsolidator;
                newStates[symbol] = state;
                _symbolStates[symbol] = state;
            }
            if (newStates.Count > 0)
            {
                BatchWarmUpIndicators(newStates);
                foreach (var sym in newStates.Keys)
                    _warmedSymbols.Add(sym);
            }
            foreach (var security in changes.RemovedSecurities)
            {
                var symbol = security.Symbol;
                if (!_symbolStates.ContainsKey(symbol))
                    continue;
                if (Portfolio[symbol].Invested)
                    continue;
                RemoveSymbolState(symbol);
            }
        }
        private void BatchWarmUpIndicators(Dictionary<Symbol, THTSymbolState> newStates)
        {
            try
            {
                var today = Algorithm.Time.Date;
                var symbols = newStates.Keys.ToList();
                var allHistory = Algorithm.History<TradeBar>(symbols, _config.WarmupDays, Resolution.Daily)
                    .SelectMany(dict => dict.Values)
                    .Where(b => b.EndTime.Date < today)
                    .GroupBy(b => b.Symbol)
                    .ToDictionary(g => g.Key, g => g.OrderBy(b => b.EndTime).ToList());
                var currentWeekStart = GetWeekStart(Algorithm.Time);
                var currentMonthStart = new DateTime(Algorithm.Time.Year, Algorithm.Time.Month, 1);
                foreach (var kvp in newStates)
                {
                    var symbol = kvp.Key;
                    var state = kvp.Value;
                    if (!allHistory.TryGetValue(symbol, out var history) || history.Count == 0)
                    {
                        SmartLog($"[THT] No history for {symbol}, indicators will warm up organically");
                        continue;
                    }
                    DateTime? weekStart = null;
                    TradeBar weekBar = null;
                    DateTime? monthStart = null;
                    TradeBar monthBar = null;
                    foreach (var bar in history)
                    {
                        state.FVB33.Update(bar.EndTime, bar.Open, bar.High, bar.Low, bar.Close);
                        state.FVB20.Update(bar.EndTime, bar.Open, bar.High, bar.Low, bar.Close);
                        var weekOfBar = GetWeekStart(bar.EndTime);
                        if (weekStart == null || weekOfBar != weekStart)
                        {
                            if (weekBar != null)
                                state.WeeklyBX.Update(weekBar.EndTime, weekBar.Close);
                            weekStart = weekOfBar;
                            weekBar = new TradeBar
                            {
                                Time = bar.Time, Open = bar.Open, High = bar.High,
                                Low = bar.Low, Close = bar.Close, EndTime = bar.EndTime
                            };
                        }
                        else if (weekBar != null)
                        {
                            if (bar.High > weekBar.High) weekBar.High = bar.High;
                            if (bar.Low < weekBar.Low) weekBar.Low = bar.Low;
                            weekBar.Close = bar.Close;
                            weekBar.EndTime = bar.EndTime;
                        }
                        var monthKey = new DateTime(bar.EndTime.Year, bar.EndTime.Month, 1);
                        if (monthStart == null || monthKey != monthStart)
                        {
                            if (monthBar != null)
                                state.MonthlyBX.Update(monthBar.EndTime, monthBar.Close);
                            monthStart = monthKey;
                            monthBar = new TradeBar
                            {
                                Time = bar.Time, Open = bar.Open, High = bar.High,
                                Low = bar.Low, Close = bar.Close, EndTime = bar.EndTime
                            };
                        }
                        else if (monthBar != null)
                        {
                            if (bar.High > monthBar.High) monthBar.High = bar.High;
                            if (bar.Low < monthBar.Low) monthBar.Low = bar.Low;
                            monthBar.Close = bar.Close;
                            monthBar.EndTime = bar.EndTime;
                        }
                    }
                    if (weekBar != null && weekStart != currentWeekStart)
                        state.WeeklyBX.Update(weekBar.EndTime, weekBar.Close);
                    if (monthBar != null && monthStart != currentMonthStart)
                        state.MonthlyBX.Update(monthBar.EndTime, monthBar.Close);
                }
                SmartLog($"[THT] Batch warmup complete: {newStates.Count} symbols, {allHistory.Values.Sum(h => h.Count)} bars processed");
            }
            catch (Exception ex)
            {
                SmartWarn($"[THT] Batch warmup failed: {ex.Message}");
            }
        }
        private static DateTime GetWeekStart(DateTime date)
        {
            var diff = (7 + (date.DayOfWeek - DayOfWeek.Monday)) % 7;
            return date.Date.AddDays(-diff);
        }
        private int _diagnosticDaysLogged;
        private void DailySignalCheck()
        {
            _needsRebalance = false;
            if (_diagnosticDaysLogged < 5)
            {
                var total = _symbolStates.Count;
                var fvb33Ready = _symbolStates.Values.Count(s => s.FVB33.IsReady);
                var fvb20Ready = _symbolStates.Values.Count(s => s.FVB20.IsReady);
                var monthlyReady = _symbolStates.Values.Count(s => s.MonthlyBX.IsReady);
                var weeklyReady = _symbolStates.Values.Count(s => s.WeeklyBX.IsReady);
                SmartLog($"[THT] Readiness ({Algorithm.Time:yyyy-MM-dd}): {total} symbols — FVB33:{fvb33Ready} FVB20:{fvb20Ready} MonthlyBX:{monthlyReady} WeeklyBX:{weeklyReady}");
                _diagnosticDaysLogged++;
            }
            foreach (var kvp in _symbolStates)
            {
                var symbol = kvp.Key;
                var state = kvp.Value;
                if (!Securities.ContainsKey(symbol) || Securities[symbol].Price <= 0)
                    continue;
                var security = Securities[symbol];
                var bar = security.Cache;
                var open = bar.Open;
                var high = bar.High;
                var low = bar.Low;
                var close = bar.Close;
                if (close <= 0) continue;
                state.FVB33.Update(Algorithm.Time, open, high, low, close);
                state.FVB20.Update(Algorithm.Time, open, high, low, close);
                var fvb33Green = state.FVB33.IsReady && state.FVB33.DirSwitch == 1;
                var monthlyBullish = state.MonthlyBX.IsReady && state.MonthlyBX.IsBullish;
                var monthlyDarkRed = state.MonthlyBX.IsReady && state.MonthlyBX.ColorState == "DarkRed";
                var weeklyJustDarkGreen = state.WeeklyBX.IsReady && state.WeeklyBX.JustTurnedDarkGreen;
                var pivotBandTP = state.FVB20.IsReady
                    && state.FVB20.PivotBandUp > 0
                    && high > state.FVB20.PivotBandUp
                    && state.PrevHigh <= state.PrevPivotBandUp;
                var inReEntryBandZone = state.FVB20.IsReady
                    && state.FVB20.LowerBand > 0
                    && state.FVB20.UpperBand > 0
                    && low <= state.FVB20.UpperBand
                    && high >= state.FVB20.LowerBand;
                var reEntryBandFresh = inReEntryBandZone && !state.PrevReEntryBandTouch;
                if (state.Position == 1)
                {
                    if (monthlyDarkRed && Algorithm.Time.Day > _config.StopLossDayOfMonthThreshold
                        && !state.StopLossTriggeredThisMonth)
                    {
                        state.Position = 0;
                        state.LastExitWasTakeProfit = false;
                        state.StopLossTriggeredThisMonth = true;
                        state.PendingSignal = "StopLoss";
                        _needsRebalance = true;
                        SmartLog($"[THT] STOP LOSS: {symbol} — Monthly BX DarkRed, day={Algorithm.Time.Day}");
                    }
                    else if (pivotBandTP || weeklyJustDarkGreen)
                    {
                        state.Position = 0;
                        state.LastExitWasTakeProfit = true;
                        state.PendingSignal = "TakeProfit";
                        _needsRebalance = true;
                        var reason = pivotBandTP ? "PivotBandUp crossover" : "Weekly BX DarkGreen";
                        SmartLog($"[THT] TAKE PROFIT: {symbol} — {reason}");
                    }
                }
                else // Position == 0
                {
                    if (state.LastExitWasTakeProfit && reEntryBandFresh
                        && monthlyBullish && fvb33Green)
                    {
                        state.Position = 1;
                        state.LastExitWasTakeProfit = false;
                        state.PendingSignal = "ReEntry";
                        _needsRebalance = true;
                        SmartLog($"[THT] RE-ENTRY: {symbol} — FVB20 band touch + Monthly bullish + FVB33 green");
                    }
                    else if (fvb33Green && monthlyBullish)
                    {
                        state.Position = 1;
                        state.LastExitWasTakeProfit = false;
                        state.PendingSignal = "Entry";
                        _needsRebalance = true;
                        SmartLog($"[THT] ENTRY: {symbol} — FVB33 green + Monthly BX bullish");
                    }
                }
                state.PrevHigh = high;
                if (state.FVB20.IsReady && state.FVB20.PivotBandUp > 0)
                    state.PrevPivotBandUp = state.FVB20.PivotBandUp;
                state.PrevReEntryBandTouch = inReEntryBandZone;
                if (Algorithm.Time.Month != state.LastStopLossResetMonth)
                {
                    state.StopLossTriggeredThisMonth = false;
                    state.LastStopLossResetMonth = Algorithm.Time.Month;
                }
            }
            if (_needsRebalance)
            {
                if (!_firstTradeLogged)
                {
                    var activeCount = _symbolStates.Values.Count(s => s.Position == 1);
                    SmartLog($"[THT] *** FIRST SIGNAL TRIGGERED on {Algorithm.Time:yyyy-MM-dd} — {activeCount} positions activated ***");
                    _firstTradeLogged = true;
                }
                Rebalance("signal");
            }
        }
        private void ScheduledRebalance()
        {
            Rebalance("weekly");
        }
        private void Rebalance(string trigger)
        {
            var activeSymbols = _symbolStates.Values
                .Where(s => s.Position == 1)
                .Select(s => s.Symbol)
                .ToList();
            foreach (var kvp in _symbolStates)
            {
                var symbol = kvp.Key;
                var state = kvp.Value;
                if (state.Position == 0 && Portfolio[symbol].Invested)
                {
                    var reason = state.PendingSignal ?? "Exit";
                    Liquidate(symbol, $"THT_Close_{reason}");
                    state.PendingSignal = null;
                }
            }
            foreach (var holding in Portfolio.Values)
            {
                if (holding.Invested && holding.Symbol.SecurityType == SecurityType.Equity
                    && holding.Symbol != _spySymbol && !_symbolStates.ContainsKey(holding.Symbol))
                {
                    Liquidate(holding.Symbol, "THT_Close_UniverseRemoved");
                }
            }
            if (activeSymbols.Count == 0)
            {
                SmartLog($"[THT] Rebalance ({trigger}): No active positions");
                return;
            }
            var targetWeight = Math.Min(1.0m / activeSymbols.Count, _config.MaxPositionWeight);
            SmartLog($"[THT] Rebalance ({trigger}): {activeSymbols.Count} active positions @ {targetWeight:P2} each");
            foreach (var symbol in activeSymbols)
            {
                var state = _symbolStates[symbol];
                var isNewEntry = !Portfolio[symbol].Invested;
                if (isNewEntry)
                {
                    var signal = state.PendingSignal ?? "Entry";
                    Algorithm.SetHoldings(symbol, targetWeight, tag: $"THT_Entry_{signal}");
                }
                else
                {
                    Algorithm.SetHoldings(symbol, targetWeight);
                }
                state.PendingSignal = null;
            }
        }
        protected override void OnExecute(Slice slice)
        {
        }
        protected override void CheckExitConditions(Slice slice)
        {
        }
        private void RemoveSymbolState(Symbol symbol)
        {
            if (!_symbolStates.ContainsKey(symbol))
                return;
            var state = _symbolStates[symbol];
            if (state.WeeklyConsolidator != null)
                Algorithm.SubscriptionManager.RemoveConsolidator(symbol, state.WeeklyConsolidator);
            if (state.MonthlyConsolidator != null)
                Algorithm.SubscriptionManager.RemoveConsolidator(symbol, state.MonthlyConsolidator);
            _symbolStates.Remove(symbol);
        }
        private class THTSymbolState
        {
            public Symbol Symbol { get; set; }
            public FairValueBand FVB33 { get; set; }
            public FairValueBand FVB20 { get; set; }
            public BXTrender MonthlyBX { get; set; }
            public BXTrender WeeklyBX { get; set; }
            public IDataConsolidator WeeklyConsolidator { get; set; }
            public IDataConsolidator MonthlyConsolidator { get; set; }
            public int Position { get; set; }                   // 0=flat, 1=long
            public bool LastExitWasTakeProfit { get; set; }
            public bool StopLossTriggeredThisMonth { get; set; }
            public int LastStopLossResetMonth { get; set; }
            public string PendingSignal { get; set; }           // Signal that caused last position change (Entry/ReEntry/TakeProfit/StopLoss)
            public decimal PrevHigh { get; set; }
            public decimal PrevPivotBandUp { get; set; } = decimal.MaxValue;
            public bool PrevReEntryBandTouch { get; set; }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Data;
using QuantConnect.Securities;
using QuantConnect.Securities.Future;
using CoreAlgo.Architecture.Core.Interfaces;
namespace CoreAlgo.Architecture.QC.Helpers
{
    public static class AssetManager
    {
        private static readonly HashSet<string> CashIndices = new HashSet<string>
        {
            "SPX", "VIX", "NDX", "RUT", "DJX"
        };
        private static readonly HashSet<string> Futures = new HashSet<string>
        {
            "ES", "NQ", "YM", "RTY", "EMD", "NKD",
            "CL", "NG", "RB", "HO", "BZ",
            "GC", "SI", "HG", "PA", "PL",
            "ZC", "ZS", "ZW", "ZM", "ZL", "KC", "CT", "SB", "CC", "OJ",
            "ZB", "ZN", "ZF", "TU", "UB", "ED", "SR1", "SR3",
            "6E", "6J", "6B", "6S", "6C", "6A", "6N", "6M", "E7", "J7",
            "VX",
            "BTC", "ETH"
        };
        public static Security AddAsset(IAlgorithmContext context, string symbol, Resolution resolution = Resolution.Minute,
            bool useContinuousContract = true, int contractDepthOffset = 0, bool extendedMarketHours = false)
        {
            if (context == null)
                throw new ArgumentNullException(nameof(context));
            if (string.IsNullOrWhiteSpace(symbol))
                throw new ArgumentException("Symbol cannot be null or empty", nameof(symbol));
            var upperSymbol = symbol.ToUpperInvariant();
            try
            {
                Security addedSecurity;
                if (CashIndices.Contains(upperSymbol))
                {
                    context.Logger.Info($"AssetManager: Adding index {upperSymbol} using AddIndex()");
                    addedSecurity = context.Algorithm.AddIndex(upperSymbol, resolution);
                }
                else if (Futures.Contains(upperSymbol))
                {
                    context.Logger.Info($"AssetManager: Adding future {upperSymbol} using AddFuture() with continuous contract: {useContinuousContract}, extendedMarketHours: {extendedMarketHours}");
                    if (useContinuousContract)
                    {
                        addedSecurity = context.Algorithm.AddFuture(upperSymbol, resolution,
                            extendedMarketHours: extendedMarketHours,
                            dataMappingMode: DataMappingMode.OpenInterest,
                            dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
                            contractDepthOffset: contractDepthOffset);
                        context.Logger.Info($"AssetManager: Continuous contract configured - DataMapping: OpenInterest, DataNormalization: BackwardsRatio, Depth: {contractDepthOffset}");
                    }
                    else
                    {
                        addedSecurity = context.Algorithm.AddFuture(upperSymbol, resolution, extendedMarketHours: extendedMarketHours);
                    }
                }
                else
                {
                    context.Logger.Info($"AssetManager: Adding equity {upperSymbol} using AddEquity()");
                    addedSecurity = context.Algorithm.AddEquity(upperSymbol, resolution);
                }
                if (addedSecurity != null)
                {
                    var isInSecurities = context.Algorithm.Securities.ContainsKey(addedSecurity.Symbol);
                    context.Logger.Info($"AssetManager: [SUCCESS] {upperSymbol} successfully added to Securities collection: {isInSecurities}");
                    context.Logger.Info($"AssetManager: Security details - Type: {addedSecurity.Type}, Resolution: {addedSecurity.Subscriptions.GetHighestResolution()}, Exchange: {addedSecurity.Exchange}");
                    var hasPrice = addedSecurity.Price > 0;
                    var priceStatus = hasPrice ? $"${addedSecurity.Price:F2}" : "No price data yet";
                    context.Logger.Info($"AssetManager: Price data status: {priceStatus}");
                    if (!hasPrice)
                    {
                        context.Logger.Info($"AssetManager: [WARNING] No price data yet for {upperSymbol} - this is normal during initialization, data should arrive during backtest");
                    }
                }
                else
                {
                    context.Logger.Error($"AssetManager: [FAILED] Failed to add {upperSymbol} - returned null security");
                }
                return addedSecurity;
            }
            catch (Exception ex)
            {
                context.Logger.Error($"AssetManager: Failed to add asset {upperSymbol}: {ex.Message}");
                throw;
            }
        }
        public static Symbol AddOptionsChain(IAlgorithmContext context, Security underlying, Resolution resolution = Resolution.Minute)
        {
            if (context == null)
                throw new ArgumentNullException(nameof(context));
            if (underlying == null)
                throw new ArgumentNullException(nameof(underlying));
            var symbol = underlying.Symbol.Value.ToUpperInvariant();
            try
            {
                Symbol optionSymbol;
                if (underlying.Type == SecurityType.Index)
                {
                    if (symbol == "SPX")
                    {
                        context.Logger.Info($"AssetManager: Adding SPX index options using SPXW with {resolution} resolution");
                        context.Logger.Info($"AssetManager: [UNIVERSE] UniverseSettings.Resolution={context.Algorithm.UniverseSettings.Resolution}, FillForward={context.Algorithm.UniverseSettings.FillForward}, ExtendedMktHours={context.Algorithm.UniverseSettings.ExtendedMarketHours}");
                        var requestedResolution = resolution == Resolution.Daily ? Resolution.Minute : resolution;
                        if (requestedResolution != resolution)
                        {
                            context.Logger.Info($"AssetManager: [RESOLUTION OVERRIDE] Changing from {resolution} to {requestedResolution} for intraday option data");
                        }
                        optionSymbol = context.Algorithm.AddIndexOption(underlying.Symbol, "SPXW", requestedResolution).Symbol;
                        context.Logger.Info($"AssetManager: [RESOLUTION VERIFICATION] Requested: {requestedResolution}");
                    }
                    else
                    {
                        context.Logger.Info($"AssetManager: Adding index options for {symbol} with {resolution} resolution");
                        context.Logger.Info($"AssetManager: [UNIVERSE] UniverseSettings.Resolution={context.Algorithm.UniverseSettings.Resolution}, FillForward={context.Algorithm.UniverseSettings.FillForward}, ExtendedMktHours={context.Algorithm.UniverseSettings.ExtendedMarketHours}");
                        var requestedResolution = resolution == Resolution.Daily ? Resolution.Minute : resolution;
                        if (requestedResolution != resolution)
                        {
                            context.Logger.Info($"AssetManager: [RESOLUTION OVERRIDE] Changing from {resolution} to {requestedResolution} for intraday option data");
                        }
                        context.Algorithm.AddIndexOption(underlying.Symbol, requestedResolution);
                        optionSymbol = Symbol.CreateCanonicalOption(underlying.Symbol);
                        context.Logger.Info($"AssetManager: [RESOLUTION VERIFICATION] Requested: {requestedResolution}");
                    }
                }
                else if (underlying.Type == SecurityType.Future)
                {
                    context.Logger.Info($"AssetManager: Adding future options for {symbol}");
                    context.Algorithm.AddFutureOption(underlying.Symbol);
                    optionSymbol = Symbol.CreateCanonicalOption(underlying.Symbol);
                }
                else
                {
                    context.Logger.Info($"AssetManager: Adding equity options for {symbol}");
                    optionSymbol = context.Algorithm.AddOption(symbol, resolution).Symbol;
                }
                if (optionSymbol != null)
                {
                    context.Logger.Info($"AssetManager: [SUCCESS] Options chain created for {symbol}");
                    context.Logger.Info($"AssetManager: [DEBUG] DETAILED Option Symbol Analysis:");
                    context.Logger.Info($"   optionSymbol: {optionSymbol}");
                    context.Logger.Info($"   optionSymbol.Value: {optionSymbol.Value}");
                    context.Logger.Info($"   optionSymbol.SecurityType: {optionSymbol.SecurityType}");
                    context.Logger.Info($"   optionSymbol.ID: {optionSymbol.ID}");
                    context.Logger.Info($"   optionSymbol.HasCanonical: {optionSymbol.HasCanonical()}");
                    if (optionSymbol.HasCanonical())
                    {
                        context.Logger.Info($"   optionSymbol.Canonical: {optionSymbol.Canonical}");
                    }
                    var isInSecurities = context.Algorithm.Securities.ContainsKey(optionSymbol);
                    context.Logger.Info($"AssetManager: Option chain in Securities collection: {isInSecurities}");
                    if (isInSecurities)
                    {
                        var optionSecurity = context.Algorithm.Securities[optionSymbol];
                        var subscriptionResolutions = new System.Collections.Generic.List<Resolution>();
                        foreach (var sub in optionSecurity.Subscriptions)
                        {
                            subscriptionResolutions.Add(sub.Resolution);
                            context.Logger.Info($"AssetManager: [SUB] DataType={sub.Type?.Name}, Resolution={sub.Resolution}, TickType={sub.TickType}");
                        }
                        var distinctRes = subscriptionResolutions.Distinct().OrderBy(x => x).ToList();
                        context.Logger.Info($"AssetManager: [DEBUG] Option Security Details:");
                        context.Logger.Info($"   Type: {optionSecurity.Type}");
                        context.Logger.Info($"   Subscriptions: {string.Join(", ", distinctRes)}");
                        context.Logger.Info($"   Exchange: {optionSecurity.Exchange}");
                        context.Logger.Info($"   IsMarketOpen: {optionSecurity.Exchange.ExchangeOpen}");
                        var hasMinuteData = distinctRes.Contains(Resolution.Minute) ||
                                          distinctRes.Contains(Resolution.Second);
                        if (!hasMinuteData && distinctRes.All(r => r == Resolution.Daily))
                        {
                            context.Logger.Warning($"AssetManager: [RESOLUTION WARNING] Only Daily resolution subscriptions found");
                            context.Logger.Warning($"AssetManager: [RESOLUTION WARNING] Intraday option chains may not be available");
                            context.Logger.Warning($"AssetManager: [HINT] If Minute was requested, ensure UniverseSettings.Resolution=Minute before adding options.");
                        }
                        else
                        {
                            context.Logger.Info($"AssetManager: [RESOLUTION SUCCESS] Intraday subscriptions available");
                        }
                    }
                    var optionRelatedSecurities = new List<Symbol>();
                    foreach (var sec in context.Algorithm.Securities.Keys)
                    {
                        if (sec.SecurityType == SecurityType.Option ||
                            sec.Value.Contains(symbol) ||
                            sec.Value.Contains("?"))
                        {
                            optionRelatedSecurities.Add(sec);
                            if (optionRelatedSecurities.Count >= 10) break;
                        }
                    }
                    if (optionRelatedSecurities.Any())
                    {
                        context.Logger.Info($"AssetManager: [DEBUG] Option-related securities in collection ({optionRelatedSecurities.Count}):");
                        foreach (var sec in optionRelatedSecurities)
                        {
                            context.Logger.Info($"   {sec} (Type: {sec.SecurityType})");
                        }
                    }
                    else
                    {
                        context.Logger.Info($"AssetManager: [ERROR] No option-related securities found in collection");
                    }
                    context.Logger.Info($"AssetManager: Option chain ready for filtering and data feed");
                    context.Logger.Info($"AssetManager: [TARGET] Symbol to use for slice.OptionChains access: {optionSymbol}");
                }
                else
                {
                    context.Logger.Error($"AssetManager: [FAILED] Failed to create options chain for {symbol} - returned null symbol");
                }
                return optionSymbol;
            }
            catch (Exception ex)
            {
                context.Logger.Error($"AssetManager: Failed to add options chain for {symbol}: {ex.Message}");
                throw;
            }
        }
        public static bool IsIndex(string symbol)
        {
            if (string.IsNullOrWhiteSpace(symbol))
                return false;
            return CashIndices.Contains(symbol.ToUpperInvariant());
        }
        public static bool IsFuture(string symbol)
        {
            if (string.IsNullOrWhiteSpace(symbol))
                return false;
            return Futures.Contains(symbol.ToUpperInvariant());
        }
        public static bool IsEquity(string symbol)
        {
            if (string.IsNullOrWhiteSpace(symbol))
                return false;
            return !IsIndex(symbol) && !IsFuture(symbol);
        }
        public static string GetAssetType(string symbol)
        {
            if (IsIndex(symbol)) return "INDEX";
            if (IsFuture(symbol)) return "FUTURE";
            return "EQUITY";
        }
        public static string[] GetSupportedIndices()
        {
            return CashIndices.ToArray();
        }
        public static string[] GetSupportedFutures()
        {
            return Futures.ToArray();
        }
        public static string[] GetFuturesByCategory(string category)
        {
            switch (category?.ToLowerInvariant())
            {
                case "equity":
                case "index":
                    return new[] { "ES", "NQ", "YM", "RTY", "EMD", "NKD" };
                case "energy":
                    return new[] { "CL", "NG", "RB", "HO", "BZ" };
                case "metals":
                case "metal":
                    return new[] { "GC", "SI", "HG", "PA", "PL" };
                case "agricultural":
                case "agri":
                case "grains":
                    return new[] { "ZC", "ZS", "ZW", "ZM", "ZL", "KC", "CT", "SB", "CC", "OJ" };
                case "bonds":
                case "rates":
                case "interest":
                    return new[] { "ZB", "ZN", "ZF", "TU", "UB", "ED", "SR1", "SR3" };
                case "currency":
                case "fx":
                    return new[] { "6E", "6J", "6B", "6S", "6C", "6A", "6N", "6M", "E7", "J7" };
                case "volatility":
                case "vol":
                    return new[] { "VX" };
                case "crypto":
                    return new[] { "BTC", "ETH" };
                default:
                    return new string[0];
            }
        }
        public static bool ShouldRolloverContract(IAlgorithmContext context, Symbol futureSymbol, int rolloverDays = 5)
        {
            if (context?.Algorithm?.Securities == null || !context.Algorithm.Securities.ContainsKey(futureSymbol))
                return false;
            var security = context.Algorithm.Securities[futureSymbol];
            if (security?.Type != SecurityType.Future)
                return false;
            try
            {
                var daysToExpiry = (security.Symbol.ID.Date - context.Algorithm.Time).Days;
                return daysToExpiry <= rolloverDays;
            }
            catch
            {
                return false;
            }
        }
    }
}
using System;
using System.Collections.Generic;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Data;
using QuantConnect.Securities;
using CoreAlgo.Architecture.Core.Interfaces;
namespace CoreAlgo.Architecture.QC.Helpers
{
    public static class MultiAssetHelper
    {
        private static readonly Dictionary<string, AssetProfile> AssetProfiles = new Dictionary<string, AssetProfile>
        {
            ["SPX"] = new AssetProfile { TypicalVolatility = 0.20m, StrikeWidthMultiplier = 1.5m, MinPosition = 1, MaxPosition = 3, EstimatedMarginMultiplier = 1.7m, MinAccountSize = 120000m },
            ["NDX"] = new AssetProfile { TypicalVolatility = 0.25m, StrikeWidthMultiplier = 1.5m, MinPosition = 1, MaxPosition = 3, EstimatedMarginMultiplier = 2.2m, MinAccountSize = 200000m },
            ["RUT"] = new AssetProfile { TypicalVolatility = 0.30m, StrikeWidthMultiplier = 1.5m, MinPosition = 1, MaxPosition = 3, EstimatedMarginMultiplier = 1.5m, MinAccountSize = 100000m },
            ["VIX"] = new AssetProfile { TypicalVolatility = 0.80m, StrikeWidthMultiplier = 2.0m, MinPosition = 1, MaxPosition = 2, EstimatedMarginMultiplier = 1.0m, MinAccountSize = 50000m },
            ["SPY"] = new AssetProfile { TypicalVolatility = 0.18m, StrikeWidthMultiplier = 1.0m, MinPosition = 1, MaxPosition = 5, EstimatedMarginMultiplier = 0.3m, MinAccountSize = 25000m },
            ["QQQ"] = new AssetProfile { TypicalVolatility = 0.22m, StrikeWidthMultiplier = 1.0m, MinPosition = 1, MaxPosition = 5, EstimatedMarginMultiplier = 0.4m, MinAccountSize = 30000m },
            ["IWM"] = new AssetProfile { TypicalVolatility = 0.28m, StrikeWidthMultiplier = 1.2m, MinPosition = 1, MaxPosition = 4, EstimatedMarginMultiplier = 0.5m, MinAccountSize = 40000m },
            ["AAPL"] = new AssetProfile { TypicalVolatility = 0.35m, StrikeWidthMultiplier = 1.0m, MinPosition = 1, MaxPosition = 10, EstimatedMarginMultiplier = 0.4m, MinAccountSize = 20000m },
            ["TSLA"] = new AssetProfile { TypicalVolatility = 0.60m, StrikeWidthMultiplier = 1.5m, MinPosition = 1, MaxPosition = 5, EstimatedMarginMultiplier = 0.6m, MinAccountSize = 50000m },
            ["AMZN"] = new AssetProfile { TypicalVolatility = 0.40m, StrikeWidthMultiplier = 1.2m, MinPosition = 1, MaxPosition = 8, EstimatedMarginMultiplier = 0.5m, MinAccountSize = 30000m },
            ["GOOGL"] = new AssetProfile { TypicalVolatility = 0.35m, StrikeWidthMultiplier = 1.1m, MinPosition = 1, MaxPosition = 8, EstimatedMarginMultiplier = 0.5m, MinAccountSize = 30000m },
            ["MSFT"] = new AssetProfile { TypicalVolatility = 0.30m, StrikeWidthMultiplier = 1.0m, MinPosition = 1, MaxPosition = 10, EstimatedMarginMultiplier = 0.4m, MinAccountSize = 25000m },
            ["ES"] = new AssetProfile { TypicalVolatility = 0.22m, StrikeWidthMultiplier = 1.3m, MinPosition = 1, MaxPosition = 3, EstimatedMarginMultiplier = 1.2m, MinAccountSize = 75000m },
            ["NQ"] = new AssetProfile { TypicalVolatility = 0.28m, StrikeWidthMultiplier = 1.4m, MinPosition = 1, MaxPosition = 3, EstimatedMarginMultiplier = 1.4m, MinAccountSize = 100000m },
            ["YM"] = new AssetProfile { TypicalVolatility = 0.20m, StrikeWidthMultiplier = 1.2m, MinPosition = 1, MaxPosition = 3, EstimatedMarginMultiplier = 1.1m, MinAccountSize = 60000m },
        };
        public static Dictionary<string, (Security Security, Symbol OptionsSymbol)> AddMultiAssetOptions(
            IAlgorithmContext context,
            string[] symbols,
            Resolution resolution = Resolution.Minute)
        {
            if (context == null)
                throw new ArgumentNullException(nameof(context));
            if (symbols == null || symbols.Length == 0)
                throw new ArgumentException("Symbols array cannot be null or empty", nameof(symbols));
            var result = new Dictionary<string, (Security, Symbol)>();
            foreach (var symbol in symbols)
            {
                try
                {
                    var security = AssetManager.AddAsset(context, symbol, resolution);
                    var optionsSymbol = AssetManager.AddOptionsChain(context, security, resolution);
                    result[symbol.ToUpperInvariant()] = (security, optionsSymbol);
                    context.Logger.Debug($"MultiAssetHelper: Successfully added {symbol} with options chain");
                }
                catch (Exception ex)
                {
                    context.Logger.Error($"MultiAssetHelper: Failed to add {symbol}: {ex.Message}");
                    throw;
                }
            }
            return result;
        }
        public static decimal GetAssetStrikeWidth(string symbol, decimal baseStrikeWidth)
        {
            if (string.IsNullOrWhiteSpace(symbol))
                return baseStrikeWidth;
            var upperSymbol = symbol.ToUpperInvariant();
            if (AssetProfiles.TryGetValue(upperSymbol, out var profile))
            {
                return baseStrikeWidth * profile.StrikeWidthMultiplier;
            }
            return baseStrikeWidth;
        }
        public static (int MinPositions, int MaxPositions, decimal RecommendedAllocation) GetAssetPositionLimits(
            string symbol,
            decimal totalPortfolioValue)
        {
            if (string.IsNullOrWhiteSpace(symbol))
                return (1, 5, 0.1m); // Default
            var upperSymbol = symbol.ToUpperInvariant();
            if (AssetProfiles.TryGetValue(upperSymbol, out var profile))
            {
                var recommendedAllocation = Math.Max(0.05m, Math.Min(0.2m, 0.15m / profile.TypicalVolatility));
                return (profile.MinPosition, profile.MaxPosition, recommendedAllocation);
            }
            return (1, 5, 0.1m);
        }
        public static (decimal DeltaMin, decimal DeltaMax) GetAssetDeltaTargets(
            string symbol,
            decimal baseDeltaMin,
            decimal baseDeltaMax)
        {
            if (string.IsNullOrWhiteSpace(symbol))
                return (baseDeltaMin, baseDeltaMax);
            var upperSymbol = symbol.ToUpperInvariant();
            if (AssetProfiles.TryGetValue(upperSymbol, out var profile))
            {
                if (profile.TypicalVolatility > 0.4m)
                {
                    var adjustment = 0.05m;
                    return (baseDeltaMin + adjustment, baseDeltaMax - adjustment);
                }
                else if (profile.TypicalVolatility < 0.2m)
                {
                    var adjustment = 0.03m;
                    return (Math.Max(0.05m, baseDeltaMin - adjustment), Math.Min(0.45m, baseDeltaMax + adjustment));
                }
            }
            return (baseDeltaMin, baseDeltaMax);
        }
        public static bool HasLiquidOptions(string symbol)
        {
            if (string.IsNullOrWhiteSpace(symbol))
                return false;
            var upperSymbol = symbol.ToUpperInvariant();
            return AssetProfiles.ContainsKey(upperSymbol);
        }
        public static string[] GetSupportedSymbols()
        {
            var result = new string[AssetProfiles.Count];
            AssetProfiles.Keys.CopyTo(result, 0);
            return result;
        }
        public static AssetProfile GetAssetProfile(string symbol)
        {
            if (string.IsNullOrWhiteSpace(symbol))
                return null;
            var upperSymbol = symbol.ToUpperInvariant();
            return AssetProfiles.TryGetValue(upperSymbol, out var profile) ? profile : null;
        }
        public static decimal GetStrikeIncrement(string symbol)
        {
            if (string.IsNullOrWhiteSpace(symbol))
                return 1m;
            var upperSymbol = symbol.ToUpperInvariant();
            switch (upperSymbol)
            {
                case "SPX":
                case "NDX":
                case "RUT":
                case "VIX":
                    return 5m; // Index options typically use $5 increments
                case "SPY":
                case "QQQ":
                case "IWM":
                    return 1m; // ETF options typically use $1 increments
                case "AAPL":
                case "MSFT":
                case "GOOGL":
                case "AMZN":
                case "TSLA":
                    return 2.5m; // High-value stocks often use $2.50 increments
                case "ES":
                case "NQ":
                case "YM":
                    return 5m; // Futures options typically use $5 increments
                default:
                    return 1m; // Default for unknown symbols
            }
        }
    }
    public class AssetProfile
    {
        public decimal TypicalVolatility { get; set; }
        public decimal StrikeWidthMultiplier { get; set; }
        public int MinPosition { get; set; }
        public int MaxPosition { get; set; }
        public decimal EstimatedMarginMultiplier { get; set; } = 0.3m;
        public decimal MinAccountSize { get; set; } = 10000m;
        public override string ToString()
        {
            return $"Vol: {TypicalVolatility:P1}, StrikeMultiplier: {StrikeWidthMultiplier:F1}x, Positions: {MinPosition}-{MaxPosition}, MinAccount: ${MinAccountSize:F0}";
        }
    }
}
using System;
using System.Threading;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Orders;
namespace CoreAlgo.Architecture.QC.Helpers
{
    public static class OrderExtensions
    {
        public static OrderTicket SubmitWithRetry(this QCAlgorithm algorithm, SubmitOrderRequest request,
            int maxRetries = 3, TimeSpan retryDelay = default)
        {
            if (algorithm == null)
                throw new ArgumentNullException(nameof(algorithm));
            if (request == null)
                throw new ArgumentNullException(nameof(request));
            if (retryDelay == default)
                retryDelay = TimeSpan.FromSeconds(1);
            OrderTicket ticket = null;
            Exception lastException = null;
            for (int attempt = 1; attempt <= maxRetries + 1; attempt++)
            {
                try
                {
                    algorithm.Debug($"OrderExtensions: Submitting order attempt {attempt}/{maxRetries + 1} for {request.Symbol}");
                    if (request.OrderType == OrderType.Market)
                    {
                        ticket = algorithm.MarketOrder(request.Symbol, request.Quantity, tag: request.Tag, asynchronous: true);
                    }
                    else if (request.OrderType == OrderType.Limit)
                    {
                        ticket = algorithm.LimitOrder(request.Symbol, request.Quantity, request.LimitPrice, tag: request.Tag, asynchronous: true);
                    }
                    else
                    {
                        ticket = algorithm.MarketOrder(request.Symbol, request.Quantity, tag: request.Tag, asynchronous: true);
                    }
                    if (ticket != null)
                    {
                        if (attempt > 1)
                        {
                            algorithm.Debug($"OrderExtensions: Order submitted successfully on attempt {attempt}");
                        }
                        return ticket;
                    }
                    else
                    {
                        algorithm.Debug($"OrderExtensions: Order submission returned null ticket on attempt {attempt}");
                    }
                }
                catch (Exception ex)
                {
                    lastException = ex;
                    algorithm.Debug($"OrderExtensions: Order submission failed on attempt {attempt}: {ex.Message}");
                    if (attempt <= maxRetries)
                    {
                        algorithm.Debug($"OrderExtensions: Waiting {retryDelay.TotalSeconds} seconds before retry");
                        Thread.Sleep(retryDelay);
                    }
                }
            }
            algorithm.Error($"OrderExtensions: Failed to submit order after {maxRetries + 1} attempts. Last error: {lastException?.Message}");
            return null;
        }
        public static OrderTicket MarketOrderWithRetry(this QCAlgorithm algorithm, Symbol symbol, int quantity,
            int maxRetries = 3, TimeSpan retryDelay = default)
        {
            var request = new SubmitOrderRequest(OrderType.Market, algorithm.Securities[symbol].Type,
                symbol, quantity, 0, 0, algorithm.UtcTime, $"Market order with retry");
            return algorithm.SubmitWithRetry(request, maxRetries, retryDelay);
        }
        public static OrderTicket LimitOrderWithRetry(this QCAlgorithm algorithm, Symbol symbol, int quantity,
            decimal limitPrice, int maxRetries = 3, TimeSpan retryDelay = default)
        {
            var request = new SubmitOrderRequest(OrderType.Limit, algorithm.Securities[symbol].Type,
                symbol, quantity, 0, limitPrice, algorithm.UtcTime, $"Limit order with retry");
            return algorithm.SubmitWithRetry(request, maxRetries, retryDelay);
        }
        public static bool WasSuccessful(this OrderTicket ticket)
        {
            if (ticket == null)
                return false;
            var status = ticket.Status;
            return status == OrderStatus.Filled ||
                   status == OrderStatus.PartiallyFilled ||
                   status == OrderStatus.Submitted;
        }
        public static bool HasFailed(this OrderTicket ticket)
        {
            if (ticket == null)
                return true;
            var status = ticket.Status;
            return status == OrderStatus.Invalid ||
                   status == OrderStatus.Canceled ||
                   status == OrderStatus.CancelPending;
        }
        public static string GetStatusDescription(this OrderTicket ticket)
        {
            if (ticket == null)
                return "Null ticket";
            var status = ticket.Status;
            var filled = ticket.QuantityFilled;
            var remaining = ticket.Quantity - filled;
            return status switch
            {
                OrderStatus.New => "Order created but not yet submitted",
                OrderStatus.Submitted => $"Order submitted, waiting for fill",
                OrderStatus.PartiallyFilled => $"Partially filled: {filled}/{ticket.Quantity}, {remaining} remaining",
                OrderStatus.Filled => $"Completely filled: {filled} shares",
                OrderStatus.Canceled => "Order was canceled",
                OrderStatus.None => "Order status unknown",
                OrderStatus.Invalid => "Order is invalid",
                OrderStatus.CancelPending => "Cancel request pending",
                OrderStatus.UpdateSubmitted => "Order update submitted",
                _ => $"Unknown status: {status}"
            };
        }
        public static OrderTicket MarketOnCloseWithRetry(this QCAlgorithm algorithm, Symbol symbol, int quantity,
            int maxRetries = 1, TimeSpan retryDelay = default, string tag = "")
        {
            if (retryDelay == default) retryDelay = TimeSpan.FromSeconds(1);
            OrderTicket ticket = null;
            for (int attempt = 1; attempt <= Math.Max(1, maxRetries); attempt++)
            {
                try
                {
                    algorithm.Debug($"OrderExtensions: Submitting MOC attempt {attempt} for {symbol}");
                    ticket = algorithm.MarketOnCloseOrder(symbol, quantity, tag: tag, asynchronous: true);
                    if (ticket != null) return ticket;
                }
                catch (Exception ex)
                {
                    algorithm.Debug($"OrderExtensions: MOC submission failed attempt {attempt}: {ex.Message}");
                    if (attempt < maxRetries) Thread.Sleep(retryDelay);
                }
            }
            algorithm.Error($"OrderExtensions: Failed to submit MOC for {symbol}");
            return ticket;
        }
        public static OrderTicket MarketOnOpenWithRetry(this QCAlgorithm algorithm, Symbol symbol, int quantity,
            int maxRetries = 1, TimeSpan retryDelay = default, string tag = "")
        {
            if (retryDelay == default) retryDelay = TimeSpan.FromSeconds(1);
            OrderTicket ticket = null;
            for (int attempt = 1; attempt <= Math.Max(1, maxRetries); attempt++)
            {
                try
                {
                    algorithm.Debug($"OrderExtensions: Submitting MOO attempt {attempt} for {symbol}");
                    ticket = algorithm.MarketOnOpenOrder(symbol, quantity, tag: tag, asynchronous: true);
                    if (ticket != null) return ticket;
                }
                catch (Exception ex)
                {
                    algorithm.Debug($"OrderExtensions: MOO submission failed attempt {attempt}: {ex.Message}");
                    if (attempt < maxRetries) Thread.Sleep(retryDelay);
                }
            }
            algorithm.Error($"OrderExtensions: Failed to submit MOO for {symbol}");
            return ticket;
        }
    }
}
using System;
using QuantConnect.Securities;
namespace CoreAlgo.Architecture.QC.Helpers
{
    public static class PositionSizer
    {
        public const int StandardOptionsMultiplier = 100;
        public static int CalculateQuantity(SecurityPortfolioManager portfolio, decimal allocationPercent, decimal price)
        {
            if (portfolio == null)
                throw new ArgumentNullException(nameof(portfolio));
            if (allocationPercent <= 0 || allocationPercent > 1)
                throw new ArgumentException("Allocation percent must be between 0 and 1", nameof(allocationPercent));
            if (price <= 0)
                throw new ArgumentException("Price must be greater than 0", nameof(price));
            var totalValue = portfolio.TotalPortfolioValue;
            var allocationAmount = totalValue * allocationPercent;
            var quantity = (int)Math.Floor(allocationAmount / price);
            return Math.Max(0, quantity);
        }
        public static int CalculateOptionsQuantity(SecurityPortfolioManager portfolio, decimal allocationPercent,
            decimal premium, int multiplier = StandardOptionsMultiplier)
        {
            if (portfolio == null)
                throw new ArgumentNullException(nameof(portfolio));
            if (allocationPercent <= 0 || allocationPercent > 1)
                throw new ArgumentException("Allocation percent must be between 0 and 1", nameof(allocationPercent));
            if (premium <= 0)
                throw new ArgumentException("Premium must be greater than 0", nameof(premium));
            if (multiplier <= 0)
                throw new ArgumentException("Multiplier must be greater than 0", nameof(multiplier));
            var totalValue = portfolio.TotalPortfolioValue;
            var allocationAmount = totalValue * allocationPercent;
            var costPerContract = premium * multiplier;
            var contracts = (int)Math.Floor(allocationAmount / costPerContract);
            return Math.Max(0, contracts);
        }
        public static int CalculateFixedDollarQuantity(decimal dollarAmount, decimal price)
        {
            if (dollarAmount <= 0)
                throw new ArgumentException("Dollar amount must be greater than 0", nameof(dollarAmount));
            if (price <= 0)
                throw new ArgumentException("Price must be greater than 0", nameof(price));
            var quantity = (int)Math.Floor(dollarAmount / price);
            return Math.Max(0, quantity);
        }
        public static int CalculateFixedDollarOptionsQuantity(decimal dollarAmount, decimal premium,
            int multiplier = StandardOptionsMultiplier)
        {
            if (dollarAmount <= 0)
                throw new ArgumentException("Dollar amount must be greater than 0", nameof(dollarAmount));
            if (premium <= 0)
                throw new ArgumentException("Premium must be greater than 0", nameof(premium));
            if (multiplier <= 0)
                throw new ArgumentException("Multiplier must be greater than 0", nameof(multiplier));
            var costPerContract = premium * multiplier;
            var contracts = (int)Math.Floor(dollarAmount / costPerContract);
            return Math.Max(0, contracts);
        }
        public static int CalculateSafeQuantity(SecurityPortfolioManager portfolio, decimal allocationPercent,
            decimal price, decimal safetyBuffer = 0.05m)
        {
            if (safetyBuffer < 0 || safetyBuffer > 0.5m)
                throw new ArgumentException("Safety buffer must be between 0 and 0.5", nameof(safetyBuffer));
            var adjustedAllocation = allocationPercent * (1 - safetyBuffer);
            return CalculateQuantity(portfolio, adjustedAllocation, price);
        }
        public static int CalculateSafeOptionsQuantity(SecurityPortfolioManager portfolio, decimal allocationPercent,
            decimal premium, decimal safetyBuffer = 0.05m, int multiplier = StandardOptionsMultiplier)
        {
            if (safetyBuffer < 0 || safetyBuffer > 0.5m)
                throw new ArgumentException("Safety buffer must be between 0 and 0.5", nameof(safetyBuffer));
            var adjustedAllocation = allocationPercent * (1 - safetyBuffer);
            return CalculateOptionsQuantity(portfolio, adjustedAllocation, premium, multiplier);
        }
        public static decimal GetAvailableBuyingPower(SecurityPortfolioManager portfolio)
        {
            if (portfolio == null)
                throw new ArgumentNullException(nameof(portfolio));
            return portfolio.Cash;
        }
        public static decimal CalculatePositionValue(int quantity, decimal price, int multiplier = 1)
        {
            if (quantity < 0)
                throw new ArgumentException("Quantity cannot be negative", nameof(quantity));
            if (price < 0)
                throw new ArgumentException("Price cannot be negative", nameof(price));
            if (multiplier <= 0)
                throw new ArgumentException("Multiplier must be greater than 0", nameof(multiplier));
            return quantity * price * multiplier;
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace CoreAlgo.Architecture.QC.Helpers
{
    public static class PriceRounding
    {
        public static decimal GetMinPriceVariation(Security security)
        {
            if (security == null)
            {
                return 0.01m;
            }
            try
            {
                var tick = security.PriceVariationModel?.GetMinimumPriceVariation(new GetMinimumPriceVariationParameters(security, security.Price)) ?? 0m;
                if (tick <= 0m)
                {
                    tick = security.SymbolProperties?.MinimumPriceVariation ?? 0m;
                }
                if (tick > 0m)
                {
                    return tick;
                }
                if (security.Type == SecurityType.Equity)
                {
                    return security.Price < 1m ? 0.0001m : 0.01m;
                }
            }
            catch
            {
            }
            return 0.01m;
        }
        public static decimal GetMinPriceVariation(SecurityManager securities, Symbol symbol)
        {
            if (securities == null || symbol == null)
            {
                return 0.01m;
            }
            return securities.TryGetValue(symbol, out var security)
                ? GetMinPriceVariation(security)
                : 0.01m;
        }
        public static decimal FloorToTick(decimal price, decimal tick)
        {
            return tick > 0m ? Math.Floor(price / tick) * tick : price;
        }
        public static decimal CeilToTick(decimal price, decimal tick)
        {
            return tick > 0m ? Math.Ceiling(price / tick) * tick : price;
        }
        public static decimal RoundToNearestTick(decimal price, decimal tick)
        {
            return tick > 0m ? Math.Round(price / tick, MidpointRounding.AwayFromZero) * tick : price;
        }
        public static decimal RoundLimitPrice(SecurityManager securities, Symbol symbol, decimal quantity, decimal limitPrice)
        {
            var tick = GetMinPriceVariation(securities, symbol);
            return quantity >= 0 ? FloorToTick(limitPrice, tick) : CeilToTick(limitPrice, tick);
        }
        public static decimal RoundStopPrice(SecurityManager securities, Symbol symbol, decimal quantity, decimal stopPrice)
        {
            var tick = GetMinPriceVariation(securities, symbol);
            return quantity >= 0 ? CeilToTick(stopPrice, tick) : FloorToTick(stopPrice, tick);
        }
        public static decimal RoundTrailingStopPrice(SecurityManager securities, Symbol symbol, decimal quantity, decimal trailingAmount, bool isDollarTrailing)
        {
            var tick = GetMinPriceVariation(securities, symbol);
            return CeilToTick(trailingAmount, tick);
        }
        public static (decimal RoundedStop, decimal RoundedLimit) RoundStopLimitPrices(
            SecurityManager securities,
            Symbol symbol,
            decimal quantity,
            decimal stopPrice,
            decimal limitPrice)
        {
            var tick = GetMinPriceVariation(securities, symbol);
            if (tick <= 0m)
            {
                tick = 0.01m;
            }
            if (stopPrice <= 0m || limitPrice <= 0m)
            {
                throw new ArgumentException($"Invalid stop-limit prices: stop={stopPrice}, limit={limitPrice} must be > 0");
            }
            if (quantity >= 0)
            {
                var roundedStop = CeilToTick(stopPrice, tick);
                var roundedLimit = FloorToTick(limitPrice, tick);
                if (roundedLimit < roundedStop + tick)
                {
                    roundedLimit = roundedStop + tick;
                }
                if (roundedLimit < roundedStop)
                {
                    roundedLimit = roundedStop + tick;
                }
                return (roundedStop, roundedLimit);
            }
            else
            {
                var roundedStop = FloorToTick(stopPrice, tick);
                var roundedLimit = CeilToTick(limitPrice, tick);
                if (roundedLimit > roundedStop - tick)
                {
                    roundedLimit = roundedStop - tick;
                }
                if (roundedLimit >= roundedStop)
                {
                    roundedLimit = roundedStop - tick;
                }
                if (roundedLimit >= roundedStop - tick)
                {
                    roundedLimit = roundedStop - tick;
                }
                if (roundedLimit <= 0m)
                {
                    throw new ArgumentException($"Stop-limit prices invalid after rounding: stop={roundedStop}, limit={roundedLimit}, tick={tick}. Buffer too small for tick size.");
                }
                return (roundedStop, roundedLimit);
            }
        }
        public static decimal GetComboNetTick(SecurityManager securities, IReadOnlyList<Leg> legs)
        {
            if (legs == null || legs.Count == 0)
            {
                return 0.01m;
            }
            var ticks = legs
                .Select(leg => securities != null && securities.TryGetValue(leg.Symbol, out var security)
                    ? GetMinPriceVariation(security)
                    : 0.01m)
                .Where(t => t > 0m);
            var minTick = ticks.DefaultIfEmpty(0.01m).Min();
            return minTick > 0m ? minTick : 0.01m;
        }
        public static decimal RoundComboNetPrice(
            SecurityManager securities,
            IReadOnlyList<Leg> legs,
            decimal netPrice,
            bool isDebit)
        {
            var netTick = GetComboNetTick(securities, legs);
            return isDebit ? FloorToTick(netPrice, netTick) : CeilToTick(netPrice, netTick);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using CoreAlgo.Architecture.Core.Models;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Data.Market;
using QuantConnect.Securities.Option;
namespace CoreAlgo.Architecture.QC.Helpers
{
    public class StrikeRangeCalculator
    {
        private readonly QCAlgorithm _algorithm;
        private readonly decimal _highVolThreshold;
        private readonly decimal _volAdjustment;
        public StrikeRangeCalculator(QCAlgorithm algorithm,
            decimal highVolThreshold = 0.30m, decimal volAdjustment = 0.05m)
        {
            _algorithm = algorithm;
            _highVolThreshold = highVolThreshold;
            _volAdjustment = volAdjustment;
        }
        public class DeltaTargets
        {
            public decimal ShortPut { get; set; }
            public decimal LongPut { get; set; }
            public decimal ShortCall { get; set; }
            public decimal LongCall { get; set; }
        }
        public class StrikeRange
        {
            public decimal ShortPutStrike { get; set; }
            public decimal LongPutStrike { get; set; }
            public decimal ShortCallStrike { get; set; }
            public decimal LongCallStrike { get; set; }
            public decimal ATMStrike { get; set; }
        }
        public DeltaTargets GetAssetSpecificDeltas(string symbol, decimal currentVolatility)
        {
            var (deltaMin, deltaMax) = MultiAssetHelper.GetAssetDeltaTargets(symbol, 0.15m, 0.25m);
            var adjustment = currentVolatility > _highVolThreshold ? _volAdjustment : 0m;
            return new DeltaTargets
            {
                ShortPut = Math.Max(0.05m, deltaMin - adjustment),
                LongPut = Math.Max(0.01m, deltaMin - adjustment - 0.05m),
                ShortCall = Math.Max(0.05m, deltaMin + adjustment),
                LongCall = Math.Max(0.01m, deltaMin + adjustment + 0.05m)
            };
        }
        public StrikeRange CalculateStrikeRange(OptionChain chain, DeltaTargets targets)
        {
            if (chain == null || !chain.Any())
            {
                throw new InvalidOperationException("Option chain is empty");
            }
            var underlying = chain.Underlying;
            var atmStrike = GetATMStrike(chain);
            var strikes = new StrikeRange { ATMStrike = atmStrike };
            var puts = chain.Where(x => x.Right == OptionRight.Put)
                           .OrderBy(x => x.Strike)
                           .ToList();
            var calls = chain.Where(x => x.Right == OptionRight.Call)
                            .OrderBy(x => x.Strike)
                            .ToList();
            strikes.ShortPutStrike = FindStrikeByDelta(puts, targets.ShortPut, atmStrike);
            strikes.LongPutStrike = FindStrikeByDelta(puts, targets.LongPut, atmStrike);
            strikes.ShortCallStrike = FindStrikeByDelta(calls, -targets.ShortCall, atmStrike); // Call deltas are negative
            strikes.LongCallStrike = FindStrikeByDelta(calls, -targets.LongCall, atmStrike);
            return ValidateAndAdjustStrikes(strikes, chain.Symbol.Underlying.Value);
        }
        public decimal GetATMStrike(OptionChain chain)
        {
            var underlyingPrice = chain.Underlying.Price;
            var atmStrike = chain
                .Select(x => x.Strike)
                .Distinct()
                .OrderBy(strike => Math.Abs(strike - underlyingPrice))
                .FirstOrDefault();
            return atmStrike > 0 ? atmStrike : underlyingPrice;
        }
        private decimal FindStrikeByDelta(List<OptionContract> contracts, decimal targetDelta, decimal atmStrike)
        {
            if (!contracts.Any()) return atmStrike;
            OptionContract bestContract = null;
            decimal bestDeltaDiff = decimal.MaxValue;
            foreach (var contract in contracts)
            {
                if (contract.Greeks?.Delta == null) continue;
                var deltaDiff = Math.Abs(contract.Greeks.Delta - targetDelta);
                if (deltaDiff < bestDeltaDiff)
                {
                    bestDeltaDiff = deltaDiff;
                    bestContract = contract;
                }
            }
            if (bestContract == null)
            {
                return EstimateStrikeByDelta(contracts, targetDelta, atmStrike);
            }
            return bestContract.Strike;
        }
        private decimal EstimateStrikeByDelta(List<OptionContract> contracts, decimal targetDelta, decimal atmStrike)
        {
            var isPut = contracts.FirstOrDefault()?.Right == OptionRight.Put;
            var absTargetDelta = Math.Abs(targetDelta);
            decimal targetDistance;
            if (absTargetDelta >= 0.40m) targetDistance = 0.02m;      // 2% OTM
            else if (absTargetDelta >= 0.30m) targetDistance = 0.04m; // 4% OTM
            else if (absTargetDelta >= 0.20m) targetDistance = 0.06m; // 6% OTM
            else if (absTargetDelta >= 0.10m) targetDistance = 0.10m; // 10% OTM
            else targetDistance = 0.15m;                               // 15% OTM
            var targetStrike = isPut
                ? atmStrike * (1 - targetDistance)
                : atmStrike * (1 + targetDistance);
            return contracts
                .Select(x => x.Strike)
                .OrderBy(strike => Math.Abs(strike - targetStrike))
                .FirstOrDefault();
        }
        private StrikeRange ValidateAndAdjustStrikes(StrikeRange strikes, string symbol)
        {
            var increment = MultiAssetHelper.GetStrikeIncrement(symbol);
            var minSpreadWidth = increment * 2; // Minimum 2 strikes apart
            if (strikes.ShortPutStrike - strikes.LongPutStrike < minSpreadWidth)
            {
                strikes.LongPutStrike = strikes.ShortPutStrike - minSpreadWidth;
            }
            if (strikes.LongCallStrike - strikes.ShortCallStrike < minSpreadWidth)
            {
                strikes.LongCallStrike = strikes.ShortCallStrike + minSpreadWidth;
            }
            strikes.ShortPutStrike = RoundToIncrement(strikes.ShortPutStrike, increment);
            strikes.LongPutStrike = RoundToIncrement(strikes.LongPutStrike, increment);
            strikes.ShortCallStrike = RoundToIncrement(strikes.ShortCallStrike, increment);
            strikes.LongCallStrike = RoundToIncrement(strikes.LongCallStrike, increment);
            return strikes;
        }
        private decimal RoundToIncrement(decimal strike, decimal increment)
        {
            return Math.Round(strike / increment) * increment;
        }
        public bool ValidateStrikeSpacing(StrikeRange strikes)
        {
            var hasLongStrikes = strikes.LongPutStrike > 0 || strikes.LongCallStrike > 0;
            var hasShortStrikes = strikes.ShortPutStrike > 0 || strikes.ShortCallStrike > 0;
            var hasBothPutAndCall = strikes.ShortPutStrike > 0 && strikes.ShortCallStrike > 0;
            if (hasLongStrikes && hasShortStrikes && hasBothPutAndCall &&
                strikes.LongPutStrike > 0 && strikes.LongCallStrike > 0)
            {
                return strikes.LongPutStrike < strikes.ShortPutStrike &&
                       strikes.ShortPutStrike < strikes.ATMStrike &&
                       strikes.ATMStrike < strikes.ShortCallStrike &&
                       strikes.ShortCallStrike < strikes.LongCallStrike;
            }
            if (hasShortStrikes && hasBothPutAndCall && !hasLongStrikes)
            {
                return strikes.ShortPutStrike < strikes.ATMStrike &&
                       strikes.ShortCallStrike > strikes.ATMStrike;
            }
            return ValidateBasicStrikeOrder(strikes);
        }
        private bool ValidateBasicStrikeOrder(StrikeRange strikes)
        {
            bool validPutSide = strikes.ShortPutStrike <= 0 || strikes.ShortPutStrike < strikes.ATMStrike;
            bool validCallSide = strikes.ShortCallStrike <= 0 || strikes.ShortCallStrike > strikes.ATMStrike;
            bool validLongPut = strikes.LongPutStrike <= 0 || strikes.ShortPutStrike <= 0 ||
                               strikes.LongPutStrike < strikes.ShortPutStrike;
            bool validLongCall = strikes.LongCallStrike <= 0 || strikes.ShortCallStrike <= 0 ||
                                strikes.LongCallStrike > strikes.ShortCallStrike;
            return validPutSide && validCallSide && validLongPut && validLongCall;
        }
        public Dictionary<string, object> GetStrikeStats(StrikeRange strikes)
        {
            return new Dictionary<string, object>
            {
                ["ATMStrike"] = strikes.ATMStrike,
                ["PutSpreadWidth"] = strikes.ShortPutStrike - strikes.LongPutStrike,
                ["CallSpreadWidth"] = strikes.LongCallStrike - strikes.ShortCallStrike,
                ["TotalWidth"] = strikes.LongCallStrike - strikes.LongPutStrike,
                ["PutDistance"] = (strikes.ATMStrike - strikes.ShortPutStrike) / strikes.ATMStrike,
                ["CallDistance"] = (strikes.ShortCallStrike - strikes.ATMStrike) / strikes.ATMStrike
            };
        }
    }
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities;
namespace CoreAlgo.Architecture.QC.Helpers
{
    public static class UniverseOptimizer
    {
        public static Dictionary<Symbol, List<decimal>> BatchFetchHistory(
            QCAlgorithm algorithm,
            IEnumerable<Symbol> symbols,
            int days,
            Resolution resolution = Resolution.Daily,
            int batchSize = 500)
        {
            var results = new ConcurrentDictionary<Symbol, List<decimal>>();
            var symbolList = symbols.ToList();
            for (int i = 0; i < symbolList.Count; i += batchSize)
            {
                var batch = symbolList.Skip(i).Take(batchSize).ToList();
                try
                {
                    var history = algorithm.History<TradeBar>(batch, days, resolution);
                    foreach (var dateGroup in history)
                    {
                        foreach (var symbolData in dateGroup)
                        {
                            var symbol = symbolData.Key;
                            var bar = symbolData.Value;
                            if (!results.ContainsKey(symbol))
                            {
                                results[symbol] = new List<decimal>();
                            }
                            results[symbol].Add(bar.Volume);
                        }
                    }
                }
                catch (Exception ex)
                {
                    algorithm.Log($"Error fetching history for batch starting at index {i}: {ex.Message}");
                }
            }
            return results.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
        }
        public static Dictionary<Symbol, decimal> CalculateBatchedADV(
            QCAlgorithm algorithm,
            IEnumerable<Symbol> symbols,
            int days = 21)
        {
            var historyData = BatchFetchHistory(algorithm, symbols, days, Resolution.Daily);
            var advResults = new Dictionary<Symbol, decimal>();
            foreach (var kvp in historyData)
            {
                var symbol = kvp.Key;
                var volumes = kvp.Value;
                if (volumes.Any())
                {
                    advResults[symbol] = volumes.Average();
                }
                else
                {
                    advResults[symbol] = 0;
                }
            }
            return advResults;
        }
        public static Dictionary<Symbol, decimal> CalculateVolumeShock(
            QCAlgorithm algorithm,
            ConcurrentDictionary<Symbol, long> intradayVolumes,
            IEnumerable<Symbol> symbols,
            int advDays = 21)
        {
            var advData = CalculateBatchedADV(algorithm, symbols, advDays);
            var shockRatios = new Dictionary<Symbol, decimal>();
            foreach (var symbol in symbols)
            {
                var intradayVol = intradayVolumes.GetValueOrDefault(symbol, 0L);
                var adv = advData.GetValueOrDefault(symbol, 0m);
                var ratio = adv > 0 ? (decimal)intradayVol / adv : 0m;
                shockRatios[symbol] = ratio;
            }
            return shockRatios;
        }
        public static void CleanupRemovedSecurities(
            QCAlgorithm algorithm,
            SecurityChanges changes,
            params object[] trackingDictionaries)
        {
            foreach (var removed in changes.RemovedSecurities)
            {
                if (algorithm.Portfolio[removed.Symbol].Invested)
                {
                    algorithm.Log($"Liquidating position in removed security: {removed.Symbol}");
                    algorithm.Liquidate(symbol: removed.Symbol, tag: "Security removed from universe", asynchronous: true);
                }
                foreach (var dict in trackingDictionaries)
                {
                    switch (dict)
                    {
                        case ConcurrentDictionary<Symbol, long> longDict:
                            longDict.TryRemove(removed.Symbol, out _);
                            break;
                        case ConcurrentDictionary<Symbol, decimal> decimalDict:
                            decimalDict.TryRemove(removed.Symbol, out _);
                            break;
                        case Dictionary<Symbol, long> simpleLongDict:
                            simpleLongDict.Remove(removed.Symbol);
                            break;
                        case Dictionary<Symbol, decimal> simpleDecimalDict:
                            simpleDecimalDict.Remove(removed.Symbol);
                            break;
                        case HashSet<Symbol> symbolSet:
                            symbolSet.Remove(removed.Symbol);
                            break;
                    }
                }
            }
        }
        public static void SetupOptimizedUniverse(
            QCAlgorithm algorithm,
            Resolution resolution = Resolution.Minute,
            bool enableAsync = true,
            bool extendedHours = false)
        {
            algorithm.UniverseSettings.Resolution = resolution;
            algorithm.UniverseSettings.Asynchronous = enableAsync;
            algorithm.UniverseSettings.ExtendedMarketHours = extendedHours;
            algorithm.Log($"Universe configured: Resolution={resolution}, Async={enableAsync}, ExtendedHours={extendedHours}");
        }
        public static IEnumerable<TResult> ProcessInParallel<TInput, TResult>(
            IEnumerable<TInput> data,
            Func<TInput, TResult> processor,
            int batchSize = 500)
        {
            var results = new ConcurrentBag<TResult>();
            var batches = data.Batch(batchSize);
            System.Threading.Tasks.Parallel.ForEach(batches, batch =>
            {
                foreach (var item in batch)
                {
                    try
                    {
                        var result = processor(item);
                        results.Add(result);
                    }
                    catch
                    {
                    }
                }
            });
            return results;
        }
    }
    public static class UniverseExtensions
    {
        public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int batchSize)
        {
            var batch = new List<T>(batchSize);
            foreach (var item in source)
            {
                batch.Add(item);
                if (batch.Count == batchSize)
                {
                    yield return batch;
                    batch = new List<T>(batchSize);
                }
            }
            if (batch.Count > 0)
                yield return batch;
        }
    }
}
#region imports
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Globalization;
    using System.Drawing;
    using QuantConnect;
    using QuantConnect.Algorithm.Framework;
    using QuantConnect.Algorithm.Framework.Selection;
    using QuantConnect.Algorithm.Framework.Alphas;
    using QuantConnect.Algorithm.Framework.Portfolio;
    using QuantConnect.Algorithm.Framework.Portfolio.SignalExports;
    using QuantConnect.Algorithm.Framework.Execution;
    using QuantConnect.Algorithm.Framework.Risk;
    using QuantConnect.Algorithm.Selection;
    using QuantConnect.Api;
    using QuantConnect.Parameters;
    using QuantConnect.Benchmarks;
    using QuantConnect.Brokerages;
    using QuantConnect.Commands;
    using QuantConnect.Configuration;
    using QuantConnect.Util;
    using QuantConnect.Interfaces;
    using QuantConnect.Algorithm;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Auxiliary;
    using QuantConnect.Data.Consolidators;
    using QuantConnect.Data.Custom;
    using QuantConnect.Data.Custom.IconicTypes;
    using QuantConnect.DataSource;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.Shortable;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Notifications;
    using QuantConnect.Orders;
    using QuantConnect.Orders.Fees;
    using QuantConnect.Orders.Fills;
    using QuantConnect.Orders.OptionExercise;
    using QuantConnect.Orders.Slippage;
    using QuantConnect.Orders.TimeInForces;
    using QuantConnect.Python;
    using QuantConnect.Scheduling;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Equity;
    using QuantConnect.Securities.Future;
    using QuantConnect.Securities.Option;
    using QuantConnect.Securities.Positions;
    using QuantConnect.Securities.Forex;
    using QuantConnect.Securities.Crypto;
    using QuantConnect.Securities.CryptoFuture;
    using QuantConnect.Securities.IndexOption;
    using QuantConnect.Securities.Interfaces;
    using QuantConnect.Securities.Volatility;
    using QuantConnect.Storage;
    using QuantConnect.Statistics;
    using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
    using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
    using Calendar = QuantConnect.Data.Consolidators.Calendar;
    using CoreAlgo.Architecture.Core.Interfaces;
    using CoreAlgo.Architecture.Core.Implementations;
    using CoreAlgo.Architecture.Core.Templates;
    using CoreAlgo.Architecture.Core.Models;
    using CoreAlgo.Architecture.Core.Services;
#endregion
namespace QuantConnect.Algorithm.CSharp
{
    public class CoreAlgo : QCAlgorithm
    {
        private IStrategy _strategy;
        private int _debugCallCount = 0;
        public QCLogger<CoreAlgo> Logger { get; private set; }
        private bool _postWarmupSliceLogged = false;
        private bool _warmupCompleteLogged = false;
        
        // Static constructor to verify class loading
        static CoreAlgo()
        {
            // This should execute when the class is first loaded
            System.Diagnostics.Debug.WriteLine("=== STATIC CONSTRUCTOR: CoreAlgo class loaded ===");
        }
        
        // Constructor to verify instance creation
        public CoreAlgo()
        {
            // This should execute when instance is created
            System.Diagnostics.Debug.WriteLine("=== CONSTRUCTOR: CoreAlgo instance created ===");
        }

        public override void Initialize()
        {
            try 
            {
                // Apply basic algorithm configuration using GetParameter with defaults
                var environment = GetParameter("Environment", "development");
                var startDate = GetParameter("StartDate", "2023-12-25");
                var endDate = GetParameter("EndDate", "2024-12-25");
                var accountSize = GetParameter("AccountSize", 100000m);
                
                SetStartDate(DateTime.Parse(startDate));
                SetEndDate(DateTime.Parse(endDate));
                SetCash(accountSize);

                SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);

                // CRITICAL FIX: Disable LEAN's broken position group buying power model
                // LEAN doesn't recognize combo orders as spreads and calculates margin incorrectly
                Portfolio.SetPositions(SecurityPositionGroupModel.Null);
                
                // Global security initializer - disables buying power for all securities (matches Python)
                SetSecurityInitializer(CompleteSecurityInitializer);
                // Ensure universe-added securities (including option chain universes) are intraday
                UniverseSettings.Resolution = Resolution.Minute;
                UniverseSettings.ExtendedMarketHours = false;
                UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw;
                Log($"UNIVERSE SETTINGS -> Resolution: {UniverseSettings.Resolution}, FillForward: {UniverseSettings.FillForward}, ExtendedMktHours: {UniverseSettings.ExtendedMarketHours}, DataNormalizationMode: {UniverseSettings.DataNormalizationMode}");
                Logger?.Info($"UniverseSettings configured. Resolution={UniverseSettings.Resolution}, FillForward={UniverseSettings.FillForward}, ExtendedMarketHours={UniverseSettings.ExtendedMarketHours}, DataNormalizationMode={UniverseSettings.DataNormalizationMode}");
                
                Log("BASIC SETUP COMPLETED");
                
                // Initialize smart logger with debug logging
                Log("=== DEBUG: About to create QCLogger ===");
                var logLevel = GetParameter("LogLevel", 2); // Default to Info level (2 = Info, 3 = Debug)
                var verboseMode = bool.Parse(GetParameter("VerboseMode", "false")); // Allow forcing verbose output in backtest
                
                if (verboseMode)
                {
                    Log("=== VERBOSE MODE ENABLED - All INFO logs will output immediately ===");
                    Log("=== Use VerboseMode only for debugging - it may exceed cloud log limits ===");
                }
                
                Logger = new QCLogger<CoreAlgo>(this, logLevel, verboseMode);
                Log($"=== DEBUG: Smart logger created: {Logger != null} ===");
                Log($"=== DEBUG: LogLevel: {logLevel}, VerboseMode: {verboseMode}, LiveMode: {LiveMode} ===");
                
                // Test smart logger immediately
                Logger.Info($"Algorithm initialized for environment: {environment}");
                
                Log("ABOUT TO INITIALIZE STRATEGY");
            }
            catch (Exception ex)
            {
                Error($"INITIALIZE FAILED: {ex.Message}");
                Error($"STACK TRACE: {ex.StackTrace}");
                throw;
            }
            
            // Strategy Selection - dynamically discovered from Templates folder
            var strategyType = GetParameter("Strategy", "IronCondor");
            Log($"Initializing strategy: {strategyType}");
            Logger.Info($"Initializing strategy: {strategyType}");
            
            try
            {
                _strategy = StrategyDiscovery.CreateStrategy(strategyType);
            }
            catch (ArgumentException ex)
            {
                Log($"Unknown strategy '{strategyType}', defaulting to IronCondor. {ex.Message}");
                Logger.Warning($"Unknown strategy '{strategyType}', defaulting to IronCondor. {ex.Message}");
                _strategy = StrategyDiscovery.CreateStrategy("IronCondor");
            }
            
            // Initialize the strategy with this algorithm instance
            try
            {
                // Inject the centralized logger into the strategy BEFORE initialization (context pattern)
                if (_strategy is SimpleBaseStrategy baseStrategy)
                {
                    baseStrategy.SetContext(Logger);
                    Logger.Debug("Logger injected into strategy via context pattern");
                }
                
                // Allow strategy configs to override UniverseSettings prior to initialization
                if (_strategy is SimpleBaseStrategy simpleStrategy)
                {
                    var desiredResolution = simpleStrategy?.Config?.GetUnderlyingResolution() ?? Resolution.Minute;
                    UniverseSettings.Resolution = desiredResolution;
                    Logger.Debug($"Strategy requested UniverseSettings resolution: {desiredResolution}");
                }

                _strategy.Initialize(this);
                
                Log($"Strategy '{_strategy.Name}' initialized successfully");
                Logger.Info($"Strategy '{_strategy.Name}' initialized successfully");
                
                Log($"Strategy state: {_strategy.State}");
                Logger.Info($"Strategy state: {_strategy.State}");
                
                Log($"Description: {_strategy.Description}");
                Logger.Info($"Description: {_strategy.Description}");
                
                Log("Ready for options trading!");
                Logger.Info("Ready for options trading!");
                
                // Initialize SmartPricing if configured
                if (_strategy is SimpleBaseStrategy baseStrategy2)
                {
                    Logger.Info("Calling EnsureSmartPricingInitialized...");
                    baseStrategy2.EnsureSmartPricingInitialized();
                    Logger.Info("EnsureSmartPricingInitialized completed");

                    // Ensure ObjectStore positions are cleared for backtests (guarantee clean state)
                    baseStrategy2.TradePersistence?.ClearPositionsIfBacktest();
                    baseStrategy2.TradePersistence?.ClearSnapshotsIfBacktest();
                    Logger.Info("ObjectStore backtest cleanup completed");

                    // Schedule centralized EOD summary at 16:05 ET (5 minutes after market close)
                    // This replaces symbol-specific OnEndOfDay() calls with a single, unified event
                    ScheduleEodSummary();
                    Logger.Info("Scheduled centralized EOD summary at 16:05 ET");
                }
                else
                {
                    Logger.Warning($"Strategy is not SimpleBaseStrategy: {_strategy?.GetType().Name}");
                }
            }
            catch (Exception ex)
            {
                Error($"Failed to initialize strategy: {ex.Message}");
                Logger.LogError(ex, "Failed to initialize strategy");
                
                _strategy = null; // Prevent strategy execution
                Error("Strategy initialization failed - algorithm cannot trade");
                Logger.Error("Strategy initialization failed - algorithm cannot trade");
            }
        }

        /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
        /// Slice object keyed by symbol containing the stock data
        public override void OnData(Slice data)
        {
            // SMART DEBUG: Track execution flow with automatic deduplication
            _debugCallCount++;
            
            // Skip strategy execution during warmup period
            if (IsWarmingUp)
            {
                return;
            }
            // One-time warmup completion diagnostics with option chain visibility
            if (!_warmupCompleteLogged)
            {
                var chainsCount = data.OptionChains.Count;
                var keys = chainsCount > 0 ? string.Join(", ", data.OptionChains.Keys) : "<none>";
                Logger?.Info($"WARMUP COMPLETED at {Time:yyyy-MM-dd HH:mm:ss} - LiveMode: {LiveMode}, Resolution: {UniverseSettings.Resolution}");
                Logger?.Info($"Option chain snapshot - Count: {chainsCount}, Keys: [{keys}]");
                _warmupCompleteLogged = true;
            }
            
            // Keep existing post-warmup slice logging for additional diagnostics
            if (!_postWarmupSliceLogged)
            {
                var chainsCount = data.OptionChains.Count;
                var keys = chainsCount > 0 ? string.Join(", ", data.OptionChains.Keys) : "<none>";
                Logger?.Info($"POST-WARMUP SLICE: OptionChains.Count={chainsCount}; Keys=[{keys}] at {Time:yyyy-MM-dd HH:mm:ss}");
                _postWarmupSliceLogged = true;
            }
            
            			// Only log option chain activity when chains are available (demoted to Debug to reduce noise)
			if (data.OptionChains.Count > 0)
			{
				Logger.Debug($"Options chains available: {data.OptionChains.Count}");
			}
            
            // Execute strategy if properly initialized
            if (_strategy != null)
            {
                try
                {
                    _strategy.Execute(data);
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex, "Strategy execution error");
                }
            }
            else
            {
                // CRITICAL: Strategy failed to initialize - algorithm cannot trade
                if (_debugCallCount <= 5)
                {
                    Logger.Error("CRITICAL: No strategy initialized - algorithm cannot trade");
                    Logger.Error("Check strategy initialization logs above for errors");
                }
            }
        }

        /// <summary>
        /// Handle security changes from universe selection and forward to strategy
        /// </summary>
        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
            base.OnSecuritiesChanged(changes);
            
            // Forward to strategy if it implements universe handling
            if (_strategy is SimpleBaseStrategy baseStrategy)
            {
                try
                {
                    baseStrategy.OnSecuritiesChanged(changes);
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex, "Error in strategy OnSecuritiesChanged");
                }
            }
        }

        /// <summary>
        /// Called when order events occur (fills, cancellations, etc.)
        /// Routes order events to SmartOrderManager for progressive pricing and trade tracking
        /// </summary>
        public override void OnOrderEvent(OrderEvent orderEvent)
        {
            // Let the base class handle the event first
            base.OnOrderEvent(orderEvent);
            
            // Single cast - exit early if not a SimpleBaseStrategy
            if (!(_strategy is SimpleBaseStrategy s)) return;
            
            // Route to SmartOrderManager if available
            s.SmartOrderManager?.OnOrderEvent(orderEvent);
            
            // Track order events for trade tracking system (like Python position tracking)
            var orderId = orderEvent.OrderId.ToString();
            
            switch (orderEvent.Status)
            {
                case OrderStatus.Submitted:
                    // Working orders are tracked at order placement via TrackWorkingOrder
                    // Just persist state change here
                    s.TradePersistence.SaveTrades(s.TradeTracker);
                    break;
                    
                case OrderStatus.PartiallyFilled:
                    var trade = s.TradeTracker.AllTrades.FirstOrDefault(t => t.OrderId == orderId);
                    if (trade != null)
                    {
                        trade.MarkAsPartialFill((int)orderEvent.FillQuantity, orderEvent.FillPrice);
                        Logger.Debug($"Partial fill: {orderId} - {orderEvent.FillQuantity} @ {orderEvent.FillPrice}");
                        s.TradePersistence.SaveTrades(s.TradeTracker);
                    }
                    break;
                    
                case OrderStatus.Filled:
                    s.TrackOrderFilled(orderEvent);
                    break;
                    
                case OrderStatus.Canceled:
                    s.TrackOrderCancelled(orderId);
                    break;
            }
            
            // Route ALL events to strategy for event-time logic (swap-to-TP, etc.)
            s.OnOrderEventRouted(orderEvent);
        }
        
        /// <summary>
        /// Called at the end of each trading day for each symbol.
        /// NOTE: This is now a lightweight fallback - the main EOD summary is handled by ScheduleEodSummary
        /// at a fixed time (16:05 ET) to avoid multiple calls for different symbols.
        /// </summary>
        public override void OnEndOfDay(Symbol symbol)
        {
            // Debug message to verify OnEndOfDay is called
            Logger?.Debug($"OnEndOfDay called for symbol {symbol}");
        }

        /// <summary>
        /// Schedules the centralized EOD summary at 16:05 ET (5 minutes after market close).
        /// This replaces per-symbol OnEndOfDay() with a single, unified event for:
        /// - Recording daily equity for drawdown tracking
        /// - Saving daily trade snapshots
        /// - Processing batched logs (DEBUG and non-verbose INFO)
        /// - Generating trade summary metrics
        /// </summary>
        private void ScheduleEodSummary()
        {
            // Schedule at 16:05 ET - after market close but before end of algorithm day
            Schedule.On(DateRules.EveryDay(), TimeRules.At(16, 5), () =>
            {
                Logger?.Debug("ScheduleEodSummary triggered at 16:05 ET");

                // Get trade summary for unified EOD output
                TradeSummary summary = null;
                if (_strategy is SimpleBaseStrategy strategy)
                {
                    // Record daily equity for drawdown and performance tracking
                    if (strategy.Reporter != null)
                    {
                        strategy.Reporter.RecordDailyEquity();
                        strategy.Reporter.SaveEquityCurve();
                    }

                    // Save trade snapshot
                    if (strategy.TradePersistence != null)
                    {
                        strategy.TradePersistence.SaveDailySnapshot(strategy.TradeTracker);
                        summary = strategy.TradePersistence.GetTradeSummary(strategy.TradeTracker);
                    }
                }

                // Process batched logs for daily summary
                // This includes DEBUG logs (always batched) and INFO logs (when not verbose)
                SmartLoggerStore.ProcessDailyLogs(this, summary);
            });
        }

        public override void OnEndOfAlgorithm()
        {
            // DEBUG: Immediate verification that OnEndOfAlgorithm() is called
            Log("=== DEBUG: OnEndOfAlgorithm() CALLED - IMMEDIATE CONFIRMATION ===");

            // Save final trade state to ObjectStore
            if (_strategy is SimpleBaseStrategy strategy)
            {
                // Record final equity point
                if (strategy.Reporter != null)
                {
                    strategy.Reporter.RecordDailyEquity();
                    strategy.Reporter.SaveEquityCurve();

                    // Log comprehensive performance summary at end of algorithm
                    strategy.Reporter.LogPerformanceSummary(strategy.TradeTracker, strategy.Name);
                }

                if (strategy.TradePersistence != null)
                {
                    try
                    {
                        strategy.TradePersistence.SaveTrades(strategy.TradeTracker);
                        var summary = strategy.TradePersistence.GetTradeSummary(strategy.TradeTracker);
                        Logger?.Info($"=== Final Trade Summary ===\n" +
                            $"Total Trades: {summary.TotalTrades}\n" +
                            $"  Working: {summary.WorkingCount}, Open: {summary.OpenCount}, Closed: {summary.ClosedCount}\n" +
                            $"  Total P&L: ${summary.TotalPnL:F2}");
                        Logger?.Info("=== Final Trade Summary saved to ObjectStore ===");
                    }
                    catch (Exception ex)
                    {
                        Logger?.Error($"=== ERROR saving final trade state: {ex.Message} ===");
                    }
                }
                else
                {
                    Logger?.Error("=== DEBUG: TradePersistence is null in OnEndOfAlgorithm ===");
                }
            }
            
            // Check smart logger state
            Log($"=== DEBUG: Smart logger null check: {Logger == null} ===");
            
            // Check static collection state before processing
            var (dailyCount, groupCount) = SmartLoggerStore.GetCollectionCounts();
            Log($"=== DEBUG: Collection counts - Daily: {dailyCount}, Groups: {groupCount} ===");
            
            // Process any remaining accumulated smart logs (fallback for symbols that didn't trigger OnEndOfDay)
            Log("=== DEBUG: About to call ProcessDailyLogs ===");
            SmartLoggerStore.ProcessDailyLogs(this);
            Log("=== DEBUG: ProcessDailyLogs completed ===");
            
            // Test smart logger one more time
            if (Logger != null)
            {
                Logger.Info($"Algorithm finished. Final portfolio value: ${Portfolio.TotalPortfolioValue:N2}");
                Logger.Info($"Total trades: {Transactions.GetOrderTickets().Count()}");
                Log("=== DEBUG: Smart logger calls in OnEndOfAlgorithm completed ===");
            }
            else
            {
                Log("=== DEBUG: Smart logger is NULL in OnEndOfAlgorithm ===");
            }
            
            // Final confirmation
            Log("=== DEBUG: OnEndOfAlgorithm() FINISHED ===");
        }

        /// <summary>
        /// Forward assignment events to the strategy for handling
        /// </summary>
        public override void OnAssignmentOrderEvent(OrderEvent assignmentEvent)
        {
            Log($"Assignment event forwarded to strategy: {assignmentEvent.Symbol}");
            
            // Forward to strategy if it supports assignment handling
            if (_strategy is SimpleBaseStrategy strategy)
            {
                strategy.OnAssignmentOrderEvent(assignmentEvent);
            }
            else
            {
                Log($"Strategy does not support assignment handling: {_strategy?.GetType().Name ?? "null"}");
            }
        }
        
        /// <summary>
        /// Global security initializer that matches Python setupbasestructure.py CompleteSecurityInitializer
        /// Disables buying power for most securities and applies backtesting configurations
        /// Special handling for SPX index options (cash-settled)
        /// </summary>
        private void CompleteSecurityInitializer(Security security)
        {
            Logger?.Debug($"CompleteSecurityInitializer: {security.Symbol} ({security.Type})");
            
            // Special handling for SPX index options (cash-settled European-style options)
            if (security.Type == SecurityType.IndexOption)
            {
                Logger?.Trace($"Configuring SPX index option: {security.Symbol}");
                
                // Use null buying power model for index options (no margin requirements)
                security.SetBuyingPowerModel(BuyingPowerModel.Null);
                
                // CRITICAL FIX: Disable option assignment for cash-settled index options
                // SPX options are cash-settled and should not simulate physical assignment
                if (security is IndexOption indexOption)
                {
                    indexOption.SetOptionAssignmentModel(new NullOptionAssignmentModel());
                    Logger?.Trace($"Applied NullOptionAssignmentModel to {security.Symbol}");
                }
            }
            else
            {
                // CRITICAL: Disable buying power on other securities (matches Python)
                security.SetBuyingPowerModel(BuyingPowerModel.Null);
            }
            
            // Apply dynamic slippage model that adapts to volume and price
            // VolumeShareSlippageModel automatically handles different security types
            // Default parameters: volumeLimit=0.025 (2.5%), priceImpact=0.1
            security.SetSlippageModel(new VolumeShareSlippageModel());
            
            // Skip backtesting-specific configurations in live mode (matches Python pattern)
            if (LiveMode)
                return;
            
            // Backtesting configurations - set RAW normalization via subscription configs
            security.Subscriptions.SetDataNormalizationMode(DataNormalizationMode.Raw);
            var mode = security.Subscriptions.FirstOrDefault()?.DataNormalizationMode;
            Logger?.Trace($"Initializer RAW set: {security.Symbol} => {mode}");
            
            var lastPrices = GetLastKnownPrices(security);
            if (lastPrices != null && lastPrices.Any())
            {
                security.SetMarketPrice(lastPrices.First());
            }
        }

    }
}