Overall Statistics
Total Orders
50
Average Win
21.51%
Average Loss
-13.52%
Compounding Annual Return
12.987%
Drawdown
26.500%
Expectancy
0.239
Start Equity
1000000.0
End Equity
1130250.01
Net Profit
13.025%
Sharpe Ratio
0.461
Sortino Ratio
0.292
Probabilistic Sharpe Ratio
26.785%
Loss Rate
52%
Win Rate
48%
Profit-Loss Ratio
1.59
Alpha
0
Beta
0
Annual Standard Deviation
0.258
Annual Variance
0.066
Information Ratio
0.49
Tracking Error
0.258
Treynor Ratio
0
Total Fees
$42867.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
BMY VDEQOO4K3SO6|BMY R735QTJ8XC9X
Portfolio Turnover
2.81%
Drawdown Recovery
209
#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
{
    /// <summary>
    /// Lightweight entry restriction checks that work with StrategyConfig parameters.
    /// QC-First approach - uses QC's native market data and portfolio state.
    /// </summary>
    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));
        }

        /// <summary>
        /// Check if all entry restrictions are satisfied for a potential trade.
        /// Returns true if trade is allowed, false otherwise.
        /// </summary>
        public bool CanEnterPosition(Symbol symbol, Slice slice, out string reason)
        {
            reason = string.Empty;

            // 1. Trading Hours Check
            if (!IsWithinTradingHours(slice.Time))
            {
                reason = $"Outside trading hours ({_config.TradingStartTime}-{_config.TradingEndTime})";
                return false;
            }

            // 2. Max Positions Check
            if (!HasCapacityForNewPosition())
            {
                reason = $"Max positions reached ({_config.MaxPositions})";
                return false;
            }

            // 3. Existing Position Check
            if (HasExistingPosition(symbol))
            {
                reason = "Already have position in this symbol";
                return false;
            }

            // 4. Available Capital Check
            if (!HasSufficientCapital())
            {
                reason = "Insufficient capital for new position";
                return false;
            }

            // 5. Volatility Check (if configured)
            if (_config.MinImpliedVolatility > 0 && !MeetsVolatilityRequirement(symbol, slice))
            {
                reason = $"Implied volatility below minimum ({_config.MinImpliedVolatility:P0})";
                return false;
            }

            // All checks passed
            return true;
        }

        /// <summary>
        /// Check if entry is allowed for options based on delta requirements.
        /// </summary>
        public bool CanEnterOptionPosition(dynamic contract, out string reason)
        {
            reason = string.Empty;

            // Check if Greeks are available
            if (contract.Greeks == null)
            {
                reason = "Greeks not available for option contract";
                return false;
            }

            var delta = Math.Abs(contract.Greeks.Delta);

            // Check delta is within configured range
            if (delta < _config.EntryDeltaMin || delta > _config.EntryDeltaMax)
            {
                reason = $"Delta {delta:F2} outside range [{_config.EntryDeltaMin:F2}-{_config.EntryDeltaMax:F2}]";
                return false;
            }

            return true;
        }

        /// <summary>
        /// Check if current time is within configured trading hours.
        /// </summary>
        private bool IsWithinTradingHours(DateTime currentTime)
        {
            var timeOfDay = currentTime.TimeOfDay;
            return timeOfDay >= _config.TradingStartTime && timeOfDay <= _config.TradingEndTime;
        }

        /// <summary>
        /// Check if we have capacity for a new position based on MaxPositions.
        /// </summary>
        private bool HasCapacityForNewPosition()
        {
            var currentPositions = _algorithm.Portfolio
                .Where(kvp => kvp.Value.Invested)
                .Count();
            
            return currentPositions < _config.MaxPositions;
        }

        /// <summary>
        /// Check if we already have a position in this symbol.
        /// </summary>
        private bool HasExistingPosition(Symbol symbol)
        {
            // Check direct position
            if (_algorithm.Portfolio[symbol].Invested)
                return true;

            // For options, also check if we have positions in the underlying
            if (symbol.SecurityType == SecurityType.Option)
            {
                var underlying = symbol.Underlying;
                return _algorithm.Portfolio.Any(kvp => 
                    kvp.Key.Underlying == underlying && kvp.Value.Invested);
            }

            return false;
        }

        /// <summary>
        /// Check if we have sufficient capital for a new position.
        /// </summary>
        private bool HasSufficientCapital()
        {
            var portfolioValue = _algorithm.Portfolio.TotalPortfolioValue;
            var requiredCapital = portfolioValue * _config.AllocationPerPosition;
            var availableCash = _algorithm.Portfolio.Cash;

            // Need at least the allocation amount in cash
            return availableCash >= requiredCapital;
        }

        /// <summary>
        /// Check if the symbol meets minimum volatility requirements.
        /// This is primarily for options strategies.
        /// </summary>
        private bool MeetsVolatilityRequirement(Symbol symbol, Slice slice)
        {
            // For options, IV check would be done in the strategy template
            // where the option chain data is available
            // For now, return true to allow entry restrictions to focus on position/capital limits
            return true;
        }

        /// <summary>
        /// Get a summary of current restriction status.
        /// Useful for logging and debugging.
        /// </summary>
        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
{
    /// <summary>
    /// Lightweight exit restriction checks that work with StrategyConfig parameters.
    /// QC-First approach - uses QC's native portfolio metrics and market data.
    /// </summary>
    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>();
        }

        /// <summary>
        /// Record when a position was entered for time-based exit rules.
        /// </summary>
        public void RecordPositionEntry(Symbol symbol, DateTime entryTime)
        {
            _positionEntryTimes[symbol] = entryTime;
        }

        /// <summary>
        /// Check if a position should be exited based on configured rules.
        /// Returns true if position should be closed, false otherwise.
        /// </summary>
        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;
            }

            // 1. Profit Target Check
            if (IsProfitTargetReached(holding))
            {
                reason = $"Profit target reached ({holding.UnrealizedProfitPercent:P2} >= {_config.ProfitTarget:P2})";
                return true;
            }

            // 2. Stop Loss Check
            if (IsStopLossTriggered(holding))
            {
                reason = $"Stop loss triggered ({holding.UnrealizedProfitPercent:P2} <= {_config.StopLoss:P2})";
                return true;
            }

            // 3. Time-Based Exit Check
            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;
            }

            // 4. Trading Hours Exit (optional - exit outside hours)
            if (!IsWithinTradingHours(slice.Time))
            {
                // Optional: some strategies may want to exit outside trading hours
                // For now, we don't force exit, but strategies can override
            }

            // No exit conditions met
            return false;
        }

        /// <summary>
        /// Check if an option position should be exited based on delta.
        /// </summary>
        public bool ShouldExitOptionPosition(Symbol symbol, dynamic contract, out string reason)
        {
            reason = string.Empty;

            // First check standard exit rules
            if (ShouldExitPosition(symbol, _algorithm.CurrentSlice, out reason))
            {
                return true;
            }

            // Check option-specific exit rules
            if (contract.Greeks != null)
            {
                var delta = Math.Abs(contract.Greeks.Delta);
                
                // Exit if delta drops below threshold
                if (delta <= _config.ExitDelta)
                {
                    reason = $"Delta exit triggered ({delta:F3} <= {_config.ExitDelta:F3})";
                    return true;
                }
            }

            // Check days to expiration
            var daysToExpiry = (contract.Expiry - _algorithm.Time).TotalDays;
            if (daysToExpiry <= 1) // Exit if expiring tomorrow
            {
                reason = $"Near expiration ({daysToExpiry:F1} days)";
                return true;
            }

            return false;
        }

        /// <summary>
        /// Check if profit target has been reached.
        /// Returns false if profit target is disabled (0).
        /// </summary>
        private bool IsProfitTargetReached(SecurityHolding holding)
        {
            // Skip check if profit target is disabled
            if (_config.ProfitTarget == 0) return false;
            
            return holding.UnrealizedProfitPercent >= _config.ProfitTarget;
        }

        /// <summary>
        /// Check if stop loss has been triggered.
        /// Returns false if stop loss is disabled (0).
        /// </summary>
        private bool IsStopLossTriggered(SecurityHolding holding)
        {
            // Skip check if stop loss is disabled
            if (_config.StopLoss == 0) return false;
            
            return holding.UnrealizedProfitPercent <= _config.StopLoss;
        }

        /// <summary>
        /// Check if position has been held for maximum allowed time.
        /// </summary>
        private bool IsMaxTimeReached(Symbol symbol, DateTime currentTime)
        {
            if (!_positionEntryTimes.ContainsKey(symbol))
                return false;

            var daysInTrade = (currentTime - _positionEntryTimes[symbol]).TotalDays;
            return daysInTrade >= _config.MaxDaysInTrade;
        }

        /// <summary>
        /// Get the number of days a position has been held.
        /// </summary>
        private double GetDaysInTrade(Symbol symbol, DateTime currentTime)
        {
            if (!_positionEntryTimes.ContainsKey(symbol))
                return 0;

            return (currentTime - _positionEntryTimes[symbol]).TotalDays;
        }

        /// <summary>
        /// Check if current time is within trading hours.
        /// </summary>
        private bool IsWithinTradingHours(DateTime currentTime)
        {
            var timeOfDay = currentTime.TimeOfDay;
            return timeOfDay >= _config.TradingStartTime && timeOfDay <= _config.TradingEndTime;
        }

        /// <summary>
        /// Get exit urgency level (for prioritizing exits).
        /// Higher values mean more urgent exit.
        /// </summary>
        public double GetExitUrgency(Symbol symbol)
        {
            var holding = _algorithm.Portfolio[symbol];
            if (!holding.Invested)
                return 0;

            var urgency = 0.0;

            // Stop loss is most urgent (if enabled)
            if (_config.StopLoss != 0 && holding.UnrealizedProfitPercent <= _config.StopLoss)
            {
                urgency = 1.0;
            }
            // Profit target is high priority (if enabled)
            else if (_config.ProfitTarget != 0 && holding.UnrealizedProfitPercent >= _config.ProfitTarget)
            {
                urgency = 0.8;
            }
            // Time-based exit increases urgency as we approach max days
            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;
        }

        /// <summary>
        /// Get a summary of exit conditions for all positions.
        /// Useful for logging and strategy decisions.
        /// </summary>
        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();
        }

        /// <summary>
        /// Clear entry time for a symbol after position is closed.
        /// </summary>
        public void ClearPositionEntry(Symbol symbol)
        {
            _positionEntryTimes.Remove(symbol);
        }
    }

    /// <summary>
    /// Status information for position exit decisions.
    /// </summary>
    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
{
    /// <summary>
    /// Tracks combo orders as single atomic units for progressive net pricing.
    /// Unlike single-leg orders, combo orders are managed through their collective net price.
    /// </summary>
    public class ComboOrderTracker
    {
        /// <summary>
        /// List of order tickets returned by ComboLimitOrder (one per leg)
        /// </summary>
        public List<OrderTicket> ComboTickets { get; }

        /// <summary>
        /// The legs that define this combo order structure
        /// </summary>
        public List<Leg> Legs { get; }

        /// <summary>
        /// Current net limit price for the combo order
        /// </summary>
        public decimal CurrentNetPrice { get; private set; }

        /// <summary>
        /// Last market quote used for pricing calculations
        /// </summary>
        public ComboQuote LastQuote { get; private set; }

        /// <summary>
        /// Overall direction of the combo order (Buy = net debit, Sell = net credit)
        /// </summary>
        public OrderDirection ComboDirection { get; }

        /// <summary>
        /// Smart pricing mode being used for this combo
        /// </summary>
        public SmartPricingMode PricingMode { get; }

        /// <summary>
        /// Current attempt number for progressive pricing (1-based)
        /// </summary>
        public int AttemptNumber { get; private set; }

        /// <summary>
        /// When this combo order was first placed
        /// </summary>
        public DateTime StartTime { get; }

        /// <summary>
        /// Scheduled event for the next pricing update (if any)
        /// </summary>
        public ScheduledEvent ScheduledEvent { get; set; }

        /// <summary>
        /// Whether all legs of the combo have been filled
        /// </summary>
        public bool IsCompletelyFilled => ComboTickets.All(ticket => 
            ticket.Status == OrderStatus.Filled);

        /// <summary>
        /// Whether any leg has been partially filled
        /// </summary>
        public bool HasPartialFills => ComboTickets.Any(ticket => 
            ticket.Status == OrderStatus.PartiallyFilled || 
            (ticket.Status == OrderStatus.Filled && ticket.QuantityFilled != ticket.Quantity));

        /// <summary>
        /// Whether the combo order is still active (not filled, cancelled, or invalid)
        /// </summary>
        public bool IsActive => ComboTickets.Any(ticket => 
            ticket.Status == OrderStatus.Submitted || 
            ticket.Status == OrderStatus.PartiallyFilled);

        /// <summary>
        /// Gets the primary order ticket (first leg) for identification purposes
        /// </summary>
        public OrderTicket PrimaryTicket => ComboTickets.FirstOrDefault();

        /// <summary>
        /// Gets the primary order ID for logging and tracking
        /// </summary>
        public int PrimaryOrderId => PrimaryTicket?.OrderId ?? -1;

        /// <summary>
        /// Creates a new combo order tracker
        /// </summary>
        /// <param name="comboTickets">Order tickets returned by ComboLimitOrder</param>
        /// <param name="legs">Legs that define the combo structure</param>
        /// <param name="initialQuote">Initial market quote used for pricing</param>
        /// <param name="comboDirection">Overall direction of the combo order</param>
        /// <param name="pricingMode">Smart pricing mode to use</param>
        /// <param name="initialNetPrice">Initial net limit price</param>
        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));
        }

        /// <summary>
        /// Updates the net price and quote information for the next pricing attempt
        /// </summary>
        /// <param name="newNetPrice">New net limit price</param>
        /// <param name="newQuote">Updated market quote</param>
        public void UpdateNetPrice(decimal newNetPrice, ComboQuote newQuote)
        {
            CurrentNetPrice = newNetPrice;
            LastQuote = newQuote ?? throw new ArgumentNullException(nameof(newQuote));
            AttemptNumber++;
        }

        /// <summary>
        /// Records a partial fill event for tracking purposes
        /// </summary>
        /// <param name="orderEvent">Order event representing the partial fill</param>
        public void UpdatePartialFill(OrderEvent orderEvent)
        {
            // For combo orders, we mainly track this for logging
            // The actual fill logic is handled by QuantConnect's combo order system
            // We could enhance this later to track per-leg fill status if needed
        }

        /// <summary>
        /// Gets summary information about this combo order for logging
        /// </summary>
        /// <returns>Formatted string with combo order details</returns>
        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}";
        }

        /// <summary>
        /// Gets detailed status of all leg tickets
        /// </summary>
        /// <returns>String with status of each leg</returns>
        public string GetLegStatus()
        {
            var legStatuses = ComboTickets.Select((ticket, index) => 
                $"Leg{index + 1}[{ticket.OrderId}]: {ticket.Status} " +
                $"({ticket.QuantityFilled}/{ticket.Quantity})");
            
            return string.Join(", ", legStatuses);
        }

        /// <summary>
        /// Calculates total runtime since combo order was placed
        /// </summary>
        /// <returns>Time elapsed since order placement</returns>
        public TimeSpan GetRuntime()
        {
            return DateTime.UtcNow - StartTime;
        }

        /// <summary>
        /// Determines if this combo order should continue with progressive pricing
        /// </summary>
        /// <param name="maxAttempts">Maximum number of attempts allowed</param>
        /// <param name="maxRuntime">Maximum runtime before giving up</param>
        /// <returns>True if progressive pricing should continue</returns>
        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
{
    /// <summary>
    /// Pricing engine for combo orders that calculates net limit prices and progressive pricing
    /// for multi-leg options strategies. Applies smart pricing logic to the entire combo as a unit.
    /// </summary>
    public class ComboPricingEngine
    {
        private readonly SmartPricingMode _mode;
        private readonly decimal _maxNetSpreadWidth;

        /// <summary>
        /// Creates a new combo pricing engine with the specified mode and constraints
        /// </summary>
        /// <param name="mode">Smart pricing mode (Normal, Fast, Patient)</param>
        /// <param name="maxNetSpreadWidth">Maximum acceptable net spread width</param>
        public ComboPricingEngine(SmartPricingMode mode, decimal maxNetSpreadWidth = 5.0m)
        {
            _mode = mode;
            _maxNetSpreadWidth = maxNetSpreadWidth;
        }

        /// <summary>
        /// Calculates the initial net limit price for a combo order based on current market conditions
        /// </summary>
        /// <param name="legs">List of legs in the combo order</param>
        /// <param name="comboQuote">Current market quote for the combo</param>
        /// <param name="orderDirection">Overall direction of the combo (Buy = paying net debit, Sell = receiving net credit)</param>
        /// <returns>Initial net limit price, or null if combo should not be priced intelligently</returns>
        public decimal? CalculateInitialComboPrice(List<Leg> legs, ComboQuote comboQuote, OrderDirection orderDirection)
        {
            if (legs == null || legs.Count == 0 || comboQuote == null || !comboQuote.IsValid)
                return null;

            // Skip smart pricing if net spread is too wide
            if (comboQuote.NetSpread > _maxNetSpreadWidth)
                return null;

            // Start at net mid-price for both buy and sell combos
            // This gives us the best initial price while still being realistic
            return comboQuote.NetMid;
        }

        /// <summary>
        /// Calculates the next progressive price step for an existing combo order
        /// </summary>
        /// <param name="currentNetPrice">Current net limit price of the combo order</param>
        /// <param name="comboQuote">Current market quote for the combo</param>
        /// <param name="orderDirection">Overall direction of the combo</param>
        /// <param name="attemptNumber">Current attempt number (1-based)</param>
        /// <returns>Next progressive net price, or null if no more steps available</returns>
        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;

            // Calculate how far to progress toward the target price
            var progressionRatio = CalculateProgressionRatio(attemptNumber, maxAttempts);
            
            decimal targetPrice;
            decimal startPrice = comboQuote.NetMid;

            if (orderDirection == OrderDirection.Buy)
            {
                // For buy combos (net debit): progress from mid toward ask
                // We're willing to pay more to get filled
                targetPrice = comboQuote.NetAsk;
            }
            else
            {
                // For sell combos (net credit): progress from mid toward bid  
                // We're willing to accept less to get filled
                targetPrice = comboQuote.NetBid;
            }

            // Calculate next price using linear interpolation
            var nextPrice = startPrice + (targetPrice - startPrice) * progressionRatio;

            // Ensure we don't go backwards or exceed target
            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);
            }

            // Round to nearest cent for practical execution
            return Math.Round(nextPrice, 2);
        }

        /// <summary>
        /// Gets the pricing interval between attempts based on the current mode
        /// </summary>
        /// <returns>Time interval between pricing updates</returns>
        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)
            };
        }

        /// <summary>
        /// Gets the maximum number of pricing attempts for the current mode
        /// </summary>
        /// <returns>Maximum number of attempts</returns>
        public int GetMaxAttempts()
        {
            return _mode switch
            {
                SmartPricingMode.Fast => 3,
                SmartPricingMode.Normal => 4,
                SmartPricingMode.Patient => 5,
                _ => 4
            };
        }

        /// <summary>
        /// Validates if the combo pricing engine should attempt to improve the price
        /// </summary>
        /// <param name="comboQuote">Current combo market quote</param>
        /// <param name="orderDirection">Overall combo direction</param>
        /// <returns>True if smart combo pricing should be attempted</returns>
        public bool ShouldAttemptComboPricing(ComboQuote comboQuote, OrderDirection orderDirection)
        {
            if (comboQuote == null || !comboQuote.IsValid)
                return false;

            // Skip if net spread is too wide (slippage would be excessive)
            if (comboQuote.NetSpread > _maxNetSpreadWidth)
                return false;

            // Skip if net spread is too narrow (little room for improvement)
            if (comboQuote.NetSpread < 0.10m)
                return false;

            return true;
        }

        /// <summary>
        /// Determines the overall direction of a combo order based on its legs
        /// </summary>
        /// <param name="legs">List of legs in the combo</param>
        /// <returns>Buy if net debit expected, Sell if net credit expected</returns>
        public static OrderDirection DetermineComboDirection(List<Leg> legs)
        {
            if (legs == null || legs.Count == 0)
                return OrderDirection.Buy; // Default

            // Simple heuristic: if we have more short legs than long legs, it's likely a credit spread
            // This works for most common strategies (Iron Condor, Credit Spreads, etc.)
            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);
            }

            // If more short than long, assume it's a credit spread (we receive money)
            return shortLegs > longLegs ? OrderDirection.Sell : OrderDirection.Buy;
        }

        /// <summary>
        /// Calculates the progression ratio for moving from mid-price toward target
        /// </summary>
        /// <param name="attemptNumber">Current attempt (1-based)</param>
        /// <param name="maxAttempts">Maximum number of attempts</param>
        /// <returns>Ratio from 0.0 (start) to 1.0 (target)</returns>
        private decimal CalculateProgressionRatio(int attemptNumber, int maxAttempts)
        {
            if (attemptNumber <= 0 || maxAttempts <= 1)
                return 0m;

            // Linear progression: attempt 1 = 25%, attempt 2 = 50%, etc.
            var ratio = (decimal)attemptNumber / maxAttempts;
            
            // Cap at 90% to avoid hitting exact bid/ask (leave room for market movement)
            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
{
    /// <summary>
    /// Represents net bid/ask pricing for a combo order based on individual leg quotes.
    /// Used for calculating intelligent limit prices for multi-leg options strategies.
    /// </summary>
    public class ComboQuote
    {
        /// <summary>
        /// Net bid price for the combo order (sum of leg bids adjusted for direction)
        /// </summary>
        public decimal NetBid { get; }

        /// <summary>
        /// Net ask price for the combo order (sum of leg asks adjusted for direction)
        /// </summary>
        public decimal NetAsk { get; }

        /// <summary>
        /// Mid-point between net bid and net ask
        /// </summary>
        public decimal NetMid => (NetBid + NetAsk) / 2;

        /// <summary>
        /// Net spread width (ask - bid)
        /// </summary>
        public decimal NetSpread => NetAsk - NetBid;

        /// <summary>
        /// Individual leg quotes used to calculate net pricing
        /// </summary>
        public Dictionary<Symbol, Quote> LegQuotes { get; }

        /// <summary>
        /// Timestamp when this quote was calculated
        /// </summary>
        public DateTime Timestamp { get; }

        /// <summary>
        /// Whether all legs have valid quotes (bid > 0 and ask > bid)
        /// </summary>
        public bool IsValid => LegQuotes.Values.All(q => q.Bid > 0 && q.Ask > q.Bid) && NetSpread >= 0;

        /// <summary>
        /// Creates a new ComboQuote from individual leg quotes and their quantities
        /// </summary>
        /// <param name="legs">List of legs with symbols and quantities</param>
        /// <param name="legQuotes">Quotes for each leg symbol</param>
        /// <param name="timestamp">When this quote was calculated</param>
        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;

            // Calculate net bid and ask based on leg direction
            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));
                }

                // For buy legs (positive quantity): we pay the ask and receive the bid
                // For sell legs (negative quantity): we receive the bid and pay the ask
                if (leg.Quantity > 0)
                {
                    // Buying this leg: pay ask price, receive bid price
                    netAsk += quote.Ask * Math.Abs(leg.Quantity);
                    netBid += quote.Bid * Math.Abs(leg.Quantity);
                }
                else if (leg.Quantity < 0)
                {
                    // Selling this leg: receive bid price, pay ask price
                    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;
        }

        /// <summary>
        /// Creates a ComboQuote from a list of legs and securities (fetches current quotes)
        /// </summary>
        /// <param name="legs">List of legs with symbols and quantities</param>
        /// <param name="securities">Securities collection to get current quotes from</param>
        /// <returns>ComboQuote with current market data, or null if quotes unavailable</returns>
        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;
            }
        }

        /// <summary>
        /// Gets the current market quote for a security (similar to SmartPricingExecutionModel logic)
        /// </summary>
        private static Quote GetCurrentQuote(Security security)
        {
            try
            {
                // Try to get the most recent quote using Cache.GetData for QuoteBar
                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);
                }

                // Fall back to using last price if quote is not available
                var price = security.Price;
                if (price > 0)
                {
                    // Estimate spread as 0.5% of price (conservative estimate for options)
                    var estimatedSpread = price * 0.005m;
                    return new Quote(price - estimatedSpread / 2, price + estimatedSpread / 2);
                }

                return null;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Returns a string representation of the combo quote
        /// </summary>
        public override string ToString()
        {
            return $"ComboQuote: NetBid={NetBid:F2}, NetAsk={NetAsk:F2}, NetMid={NetMid:F2}, NetSpread={NetSpread:F2}, Valid={IsValid}";
        }

        /// <summary>
        /// Determines if this combo quote is stale based on age threshold
        /// </summary>
        /// <param name="maxAge">Maximum age before considering stale</param>
        /// <returns>True if the quote is older than maxAge</returns>
        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
{
    /// <summary>
    /// Interface for SmartPricing engine that provides progressive pricing capabilities
    /// for improving options spread fill rates using QuantConnect's native execution framework.
    /// </summary>
    public interface ISmartPricingEngine
    {
        /// <summary>
        /// Gets the current pricing mode configuration
        /// </summary>
        SmartPricingMode Mode { get; }

        /// <summary>
        /// Calculates the initial limit price for a new order based on current market conditions
        /// </summary>
        /// <param name="quote">Current bid/ask quote for the security</param>
        /// <param name="orderDirection">Direction of the order (Buy/Sell)</param>
        /// <returns>Initial limit price starting at mid-spread</returns>
        decimal CalculateInitialPrice(Quote quote, OrderDirection orderDirection);

        /// <summary>
        /// Calculates the next progressive price step for an existing order
        /// </summary>
        /// <param name="currentPrice">Current limit price of the order</param>
        /// <param name="quote">Current bid/ask quote for the security</param>
        /// <param name="orderDirection">Direction of the order (Buy/Sell)</param>
        /// <param name="attemptNumber">Current attempt number (1-based)</param>
        /// <returns>Next progressive price, or null if no more steps available</returns>
        decimal? CalculateNextPrice(decimal currentPrice, Quote quote, OrderDirection orderDirection, int attemptNumber);

        /// <summary>
        /// Gets the time interval between pricing attempts for the current mode
        /// </summary>
        /// <returns>Time interval in seconds</returns>
        TimeSpan GetPricingInterval();

        /// <summary>
        /// Gets the maximum number of pricing attempts for the current mode
        /// </summary>
        /// <returns>Maximum number of attempts</returns>
        int GetMaxAttempts();

        /// <summary>
        /// Validates if the pricing engine should attempt to improve the price
        /// </summary>
        /// <param name="quote">Current market quote</param>
        /// <param name="orderDirection">Order direction</param>
        /// <returns>True if pricing improvement should be attempted</returns>
        bool ShouldAttemptPricing(Quote quote, OrderDirection orderDirection);

        /// <summary>
        /// Calculates the initial net limit price for a combo order based on current market conditions
        /// </summary>
        /// <param name="legs">List of legs in the combo order</param>
        /// <param name="comboQuote">Current market quote for the combo</param>
        /// <param name="orderDirection">Overall direction of the combo (Buy = paying net debit, Sell = receiving net credit)</param>
        /// <returns>Initial net limit price, or null if combo should not be priced intelligently</returns>
        decimal? CalculateInitialComboPrice(List<Leg> legs, ComboQuote comboQuote, OrderDirection orderDirection);

        /// <summary>
        /// Calculates the next progressive price step for an existing combo order
        /// </summary>
        /// <param name="currentNetPrice">Current net limit price of the combo order</param>
        /// <param name="comboQuote">Current market quote for the combo</param>
        /// <param name="orderDirection">Overall direction of the combo</param>
        /// <param name="attemptNumber">Current attempt number (1-based)</param>
        /// <returns>Next progressive net price, or null if no more steps available</returns>
        decimal? CalculateNextComboPrice(decimal currentNetPrice, ComboQuote comboQuote, OrderDirection orderDirection, int attemptNumber);

        /// <summary>
        /// Validates if the combo pricing engine should attempt to improve the price
        /// </summary>
        /// <param name="comboQuote">Current combo market quote</param>
        /// <param name="orderDirection">Overall combo direction</param>
        /// <returns>True if smart combo pricing should be attempted</returns>
        bool ShouldAttemptComboPricing(ComboQuote comboQuote, OrderDirection orderDirection);
    }

    /// <summary>
    /// Order direction for SmartPricing calculations
    /// </summary>
    public enum OrderDirection
    {
        /// <summary>
        /// Buy order (long position)
        /// </summary>
        Buy,

        /// <summary>
        /// Sell order (short position)
        /// </summary>
        Sell
    }

    /// <summary>
    /// Smart pricing modes with different aggressiveness levels
    /// </summary>
    public enum SmartPricingMode
    {
        /// <summary>
        /// SmartPricing disabled - use standard execution
        /// </summary>
        Off,

        /// <summary>
        /// Normal mode: 4 steps over 40 seconds (10s intervals)
        /// </summary>
        Normal,

        /// <summary>
        /// Fast mode: 3 steps over 15 seconds (5s intervals)
        /// </summary>
        Fast,

        /// <summary>
        /// Patient mode: 5 steps over 100 seconds (20s intervals)
        /// </summary>
        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
{
    /// <summary>
    /// Base class for SmartPricing strategies that implement progressive pricing logic
    /// </summary>
    public abstract class PricingStrategy : ISmartPricingEngine
    {
        /// <summary>
        /// Gets the pricing mode for this strategy
        /// </summary>
        public abstract SmartPricingMode Mode { get; }

        /// <summary>
        /// Gets the number of pricing steps for this strategy
        /// </summary>
        protected abstract int StepCount { get; }

        /// <summary>
        /// Gets the time interval between pricing steps
        /// </summary>
        protected abstract TimeSpan StepInterval { get; }

        /// <summary>
        /// Calculates the initial limit price at mid-spread
        /// </summary>
        public virtual decimal CalculateInitialPrice(Quote quote, OrderDirection orderDirection)
        {
            if (quote == null) 
                throw new ArgumentNullException(nameof(quote));

            // Start at mid-spread for better execution probability
            return quote.Price;
        }

        /// <summary>
        /// Calculates the next progressive price step moving toward ask/bid
        /// </summary>
        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

            // Calculate progression percentage based on attempt number
            var progressionPct = (decimal)attemptNumber / StepCount;
            
            // Move from mid-spread toward ask (buy) or bid (sell)
            decimal targetPrice;
            if (orderDirection == OrderDirection.Buy)
            {
                // Progress from mid toward ask
                targetPrice = quote.Price + (quote.Spread / 2 * progressionPct);
                // Don't exceed ask price
                targetPrice = Math.Min(targetPrice, quote.Ask);
            }
            else
            {
                // Progress from mid toward bid
                targetPrice = quote.Price - (quote.Spread / 2 * progressionPct);
                // Don't go below bid price
                targetPrice = Math.Max(targetPrice, quote.Bid);
            }

            // Only update if price has meaningfully changed
            var priceChange = Math.Abs(targetPrice - currentPrice);
            var minChange = quote.Spread * 0.1m; // Minimum 10% of spread movement

            return priceChange >= minChange ? targetPrice : null;
        }

        /// <summary>
        /// Gets the time interval between pricing attempts
        /// </summary>
        public virtual TimeSpan GetPricingInterval()
        {
            return StepInterval;
        }

        /// <summary>
        /// Gets the maximum number of pricing attempts
        /// </summary>
        public virtual int GetMaxAttempts()
        {
            return StepCount;
        }

        /// <summary>
        /// Validates if pricing should be attempted based on market conditions
        /// </summary>
        public virtual bool ShouldAttemptPricing(Quote quote, OrderDirection orderDirection)
        {
            if (quote == null)
                return false;

            // Don't attempt pricing if spread is too narrow (less than $0.05)
            if (quote.Spread < 0.05m)
                return false;

            // Don't attempt pricing if spread is too wide (more than 10% of mid-price)
            if (quote.Spread > quote.Price * 0.10m)
                return false;

            // Valid for pricing
            return true;
        }

        /// <summary>
        /// Calculates the initial net limit price for a combo order based on current market conditions
        /// </summary>
        /// <param name="legs">List of legs in the combo order</param>
        /// <param name="comboQuote">Current market quote for the combo</param>
        /// <param name="orderDirection">Overall direction of the combo (Buy = paying net debit, Sell = receiving net credit)</param>
        /// <returns>Initial net limit price, or null if combo should not be priced intelligently</returns>
        public virtual decimal? CalculateInitialComboPrice(List<Leg> legs, ComboQuote comboQuote, OrderDirection orderDirection)
        {
            if (legs == null || legs.Count == 0 || comboQuote == null || !comboQuote.IsValid)
                return null;

            // Use the embedded ComboPricingEngine logic for consistency
            var comboPricingEngine = new ComboPricingEngine(Mode, 5.0m); // Default max spread width
            return comboPricingEngine.CalculateInitialComboPrice(legs, comboQuote, orderDirection);
        }

        /// <summary>
        /// Calculates the next progressive price step for an existing combo order
        /// </summary>
        /// <param name="currentNetPrice">Current net limit price of the combo order</param>
        /// <param name="comboQuote">Current market quote for the combo</param>
        /// <param name="orderDirection">Overall direction of the combo</param>
        /// <param name="attemptNumber">Current attempt number (1-based)</param>
        /// <returns>Next progressive net price, or null if no more steps available</returns>
        public virtual decimal? CalculateNextComboPrice(decimal currentNetPrice, ComboQuote comboQuote, OrderDirection orderDirection, int attemptNumber)
        {
            if (comboQuote == null || !comboQuote.IsValid)
                return null;

            // Use the embedded ComboPricingEngine logic for consistency
            var comboPricingEngine = new ComboPricingEngine(Mode, 5.0m); // Default max spread width
            return comboPricingEngine.CalculateNextComboPrice(currentNetPrice, comboQuote, orderDirection, attemptNumber);
        }

        /// <summary>
        /// Validates if the combo pricing engine should attempt to improve the price
        /// </summary>
        /// <param name="comboQuote">Current combo market quote</param>
        /// <param name="orderDirection">Overall combo direction</param>
        /// <returns>True if smart combo pricing should be attempted</returns>
        public virtual bool ShouldAttemptComboPricing(ComboQuote comboQuote, OrderDirection orderDirection)
        {
            if (comboQuote == null || !comboQuote.IsValid)
                return false;

            // Use the embedded ComboPricingEngine logic for consistency
            var comboPricingEngine = new ComboPricingEngine(Mode, 5.0m); // Default max spread width
            return comboPricingEngine.ShouldAttemptComboPricing(comboQuote, orderDirection);
        }

        /// <summary>
        /// Calculates the price improvement benefit for a given attempt
        /// </summary>
        protected decimal CalculatePriceImprovement(Quote quote, OrderDirection orderDirection, int attemptNumber)
        {
            var aggressivePrice = orderDirection == OrderDirection.Buy ? quote.Ask : quote.Bid;
            var midPrice = quote.Price;
            
            // Calculate how much better mid-price is compared to aggressive price
            var maxImprovement = Math.Abs(aggressivePrice - midPrice);
            
            // Progressive improvement - less improvement with each step
            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
{
    /// <summary>
    /// Tracks the state of an order undergoing SmartPricing progression
    /// </summary>
    public class SmartOrderTracker
    {
        /// <summary>
        /// The QuantConnect OrderTicket being tracked
        /// </summary>
        public OrderTicket OrderTicket { get; }

        /// <summary>
        /// Direction of the order (Buy/Sell)
        /// </summary>
        public OrderDirection OrderDirection { get; }

        /// <summary>
        /// SmartPricing mode used for this order
        /// </summary>
        public SmartPricingMode PricingMode { get; }

        /// <summary>
        /// Current limit price of the order
        /// </summary>
        public decimal CurrentPrice { get; set; }

        /// <summary>
        /// Current attempt number (1-based)
        /// </summary>
        public int AttemptNumber { get; set; }

        /// <summary>
        /// Time when the order was first placed
        /// </summary>
        public DateTime StartTime { get; }

        /// <summary>
        /// Initial market quote when order was placed
        /// </summary>
        public Quote InitialQuote { get; }

        /// <summary>
        /// Most recent market quote
        /// </summary>
        public Quote CurrentQuote { get; private set; }

        /// <summary>
        /// Scheduled event for the next price update
        /// </summary>
        public ScheduledEvent ScheduledEvent { get; set; }

        /// <summary>
        /// Total quantity that has been filled
        /// </summary>
        public decimal FilledQuantity { get; private set; }

        /// <summary>
        /// Remaining quantity to be filled
        /// </summary>
        public decimal RemainingQuantity => Math.Abs(OrderTicket.Quantity) - FilledQuantity;

        /// <summary>
        /// Whether the order has been partially filled
        /// </summary>
        public bool IsPartiallyFilled => FilledQuantity > 0 && FilledQuantity < Math.Abs(OrderTicket.Quantity);

        /// <summary>
        /// History of price attempts for analysis
        /// </summary>
        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;

            // Initialize price history
            PriceHistory = new PricingAttempt[GetMaxAttempts(mode)];
            PriceHistory[0] = new PricingAttempt(1, CurrentPrice, initialQuote, StartTime);
        }

        /// <summary>
        /// Updates the order price and tracking information
        /// </summary>
        public void UpdatePrice(decimal newPrice, Quote newQuote)
        {
            CurrentPrice = newPrice;
            CurrentQuote = newQuote;
            AttemptNumber++;

            // Record this attempt in history
            if (AttemptNumber <= PriceHistory.Length)
            {
                PriceHistory[AttemptNumber - 1] = new PricingAttempt(AttemptNumber, newPrice, newQuote, DateTime.UtcNow);
            }
        }

        /// <summary>
        /// Updates tracking information when a partial fill occurs
        /// </summary>
        public void UpdatePartialFill(OrderEvent orderEvent)
        {
            if (orderEvent.Status == OrderStatus.PartiallyFilled)
            {
                FilledQuantity += Math.Abs(orderEvent.FillQuantity);
            }
        }

        /// <summary>
        /// Gets performance metrics for this order's pricing progression
        /// </summary>
        public SmartPricingMetrics GetMetrics()
        {
            var elapsed = DateTime.UtcNow - StartTime;
            var initialSpread = InitialQuote.Spread;
            var currentSpread = CurrentQuote?.Spread ?? initialSpread;
            
            // Calculate price improvement relative to initial aggressive price
            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
            };
        }

        /// <summary>
        /// Gets the limit price from an order ticket
        /// </summary>
        private static decimal GetLimitPrice(OrderTicket orderTicket)
        {
            // For now, assume the initial price was set correctly
            // In practice, this will be set properly by the execution model
            return 0; // Will be overridden by actual limit price from the order
        }

        /// <summary>
        /// Gets the maximum attempts for a pricing mode
        /// </summary>
        private static int GetMaxAttempts(SmartPricingMode mode)
        {
            return mode switch
            {
                SmartPricingMode.Fast => 3,
                SmartPricingMode.Normal => 4,
                SmartPricingMode.Patient => 5,
                _ => 1
            };
        }
    }

    /// <summary>
    /// Represents a single pricing attempt in the progression
    /// </summary>
    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;
        }
    }

    /// <summary>
    /// Performance metrics for SmartPricing orders
    /// </summary>
    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
{
    /// <summary>
    /// QC-First SmartPricing Execution Model that extends QuantConnect's IExecutionModel
    /// to improve fill rates on options spreads through intelligent limit order progression.
    /// 
    /// This execution model starts orders at mid-spread and progressively moves toward
    /// ask (for buys) or bid (for sells) over time to improve fill rates while maintaining
    /// good execution prices.
    /// </summary>
    public class SmartPricingExecutionModel : ExecutionModel
    {
        private readonly IAlgorithmContext _context;
        private readonly ISmartPricingEngine _pricingEngine;
        private readonly Dictionary<int, SmartOrderTracker> _activeOrders;
        private readonly HashSet<ScheduledEvent> _scheduledEvents;

        /// <summary>
        /// Initializes a new instance of the SmartPricingExecutionModel
        /// </summary>
        /// <param name="context">Algorithm context for logging and market data access</param>
        /// <param name="pricingEngine">Pricing engine for progressive pricing logic</param>
        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>();
        }

        /// <summary>
        /// Executes market orders immediately and starts progressive pricing for limit orders
        /// </summary>
        /// <param name="algorithm">The algorithm instance</param>
        /// <param name="targets">Portfolio targets to execute</param>
        public override void Execute(QCAlgorithm algorithm, IPortfolioTarget[] targets)
        {
            try
            {
                // Check if SmartPricing is enabled
                if (_pricingEngine.Mode == SmartPricingMode.Off)
                {
                    // Use standard execution model when SmartPricing is disabled
                    base.Execute(algorithm, targets);
                    return;
                }

                ((dynamic)_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;

                    // Get current market quote
                    var quote = GetCurrentQuote(security);
                    if (quote == null || !_pricingEngine.ShouldAttemptPricing(quote, quantity > 0 ? OrderDirection.Buy : OrderDirection.Sell))
                    {
                        // Fall back to market order if we can't get quote or shouldn't use smart pricing
                        algorithm.MarketOrder(target.Symbol, quantity, tag: "SmartPricing:Fallback");
                        continue;
                    }

                    // Calculate initial smart price
                    var orderDirection = quantity > 0 ? OrderDirection.Buy : OrderDirection.Sell;
                    var initialPrice = _pricingEngine.CalculateInitialPrice(quote, orderDirection);

                    // Place initial limit order
                    var orderTicket = algorithm.LimitOrder(target.Symbol, quantity, initialPrice, 
                        tag: $"SmartPricing:{_pricingEngine.Mode}:Initial");

                    if (orderTicket != null)
                    {
                        // Track this order for progressive pricing
                        var tracker = new SmartOrderTracker(orderTicket, quote, orderDirection, _pricingEngine.Mode, initialPrice);
                        _activeOrders[orderTicket.OrderId] = tracker;

                        // Schedule first pricing update
                        ScheduleNextPricingUpdate(algorithm, tracker);

                        ((dynamic)_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)
            {
                ((dynamic)_context.Logger).Error($"SmartPricing execution error: {ex.Message}");
                // Fall back to standard execution on error
                base.Execute(algorithm, targets);
            }
        }

        /// <summary>
        /// Handles order events to track fills and update order state
        /// </summary>
        /// <param name="algorithm">The algorithm instance</param>
        /// <param name="orderEvent">The order event</param>
        public override void OnOrderEvent(QCAlgorithm algorithm, OrderEvent orderEvent)
        {

            if (!_activeOrders.TryGetValue(orderEvent.OrderId, out var tracker))
                return;

            switch (orderEvent.Status)
            {
                case OrderStatus.Filled:
                    ((dynamic)_context.Logger).Debug($"SmartPricing: Order {orderEvent.OrderId} filled at ${orderEvent.FillPrice:F2} " +
                                                    $"after {tracker.AttemptNumber} attempts");
                    CleanupOrder(tracker);
                    break;

                case OrderStatus.PartiallyFilled:
                    ((dynamic)_context.Logger).Debug($"SmartPricing: Order {orderEvent.OrderId} partially filled " +
                                                    $"({orderEvent.FillQuantity}/{tracker.OrderTicket.Quantity})");
                    tracker.UpdatePartialFill(orderEvent);
                    break;

                case OrderStatus.Canceled:
                case OrderStatus.Invalid:
                    ((dynamic)_context.Logger).Warning($"SmartPricing: Order {orderEvent.OrderId} {orderEvent.Status}");
                    CleanupOrder(tracker);
                    break;
            }
        }

        /// <summary>
        /// Schedules the next pricing update for an order
        /// </summary>
        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;
        }

        /// <summary>
        /// Updates the price of an active order using progressive pricing
        /// </summary>
        private void UpdateOrderPrice(QCAlgorithm algorithm, SmartOrderTracker tracker)
        {
            try
            {
                // Remove the scheduled event
                if (tracker.ScheduledEvent != null)
                {
                    _scheduledEvents.Remove(tracker.ScheduledEvent);
                    tracker.ScheduledEvent = null;
                }

                // Check if order is still active
                if (!_activeOrders.ContainsKey(tracker.OrderTicket.OrderId) || 
                    tracker.OrderTicket.Status == OrderStatus.Filled)
                {
                    return;
                }

                // Get current market quote
                var security = algorithm.Securities[tracker.OrderTicket.Symbol];
                var currentQuote = GetCurrentQuote(security);
                
                if (currentQuote == null)
                {
                    ((dynamic)_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;
                }

                // Calculate next price
                var nextPrice = _pricingEngine.CalculateNextPrice(
                    tracker.CurrentPrice, currentQuote, tracker.OrderDirection, tracker.AttemptNumber + 1);

                if (nextPrice.HasValue)
                {
                    // Update order price
                    var updateResult = tracker.OrderTicket.Update(new UpdateOrderFields { LimitPrice = nextPrice.Value });
                    
                    if (updateResult.IsSuccess)
                    {
                        tracker.UpdatePrice(nextPrice.Value, currentQuote);
                        
                        ((dynamic)_context.Logger).Debug($"SmartPricing: Updated order {tracker.OrderTicket.OrderId} " +
                                                        $"to ${nextPrice.Value:F2} (attempt {tracker.AttemptNumber})");

                        // Schedule next update if we haven't reached max attempts
                        if (tracker.AttemptNumber < _pricingEngine.GetMaxAttempts())
                        {
                            ScheduleNextPricingUpdate(algorithm, tracker);
                        }
                        else
                        {
                            ((dynamic)_context.Logger).Debug($"SmartPricing: Order {tracker.OrderTicket.OrderId} reached max attempts, keeping final price");
                        }
                    }
                    else
                    {
                        ((dynamic)_context.Logger).Warning($"SmartPricing: Failed to update order {tracker.OrderTicket.OrderId}: {updateResult.ErrorMessage}");
                    }
                }
                else
                {
                    // No more price improvements available, keep current order
                    ((dynamic)_context.Logger).Debug($"SmartPricing: No more price improvements for order {tracker.OrderTicket.OrderId}");
                }
            }
            catch (Exception ex)
            {
                ((dynamic)_context.Logger).Error($"SmartPricing: Error updating order {tracker.OrderTicket.OrderId}: {ex.Message}");
            }
        }

        /// <summary>
        /// Gets the current market quote for a security
        /// </summary>
        private Quote GetCurrentQuote(Security security)
        {
            try
            {
                // Try to get the most recent quote - use Cache.GetData for QuoteBar
                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);
                }

                // Fall back to using last price if quote is not available
                var price = security.Price;
                if (price > 0)
                {
                    // Estimate spread as 0.5% of price (conservative estimate)
                    var estimatedSpread = price * 0.005m;
                    return new Quote(price - estimatedSpread / 2, price + estimatedSpread / 2);
                }

                return null;
            }
            catch (Exception ex)
            {
                ((dynamic)_context.Logger).Error($"SmartPricing: Error getting quote for {security.Symbol}: {ex.Message}");
                return null;
            }
        }

        /// <summary>
        /// Cleans up resources for a completed order
        /// </summary>
        private void CleanupOrder(SmartOrderTracker tracker)
        {
            _activeOrders.Remove(tracker.OrderTicket.OrderId);
            
            if (tracker.ScheduledEvent != null)
            {
                _scheduledEvents.Remove(tracker.ScheduledEvent);
            }
        }
    }

    /// <summary>
    /// Represents a market quote with bid and ask prices
    /// </summary>
    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
{
    /// <summary>
    /// Static utility class for handling option assignments using QuantConnect's native Portfolio system.
    /// Provides immediate liquidation of assigned shares to prevent margin crises.
    /// </summary>
    public static class AssignmentHandler
    {
        // Track processed assignments to prevent duplicate liquidations
        private static readonly HashSet<string> _processedAssignments = new HashSet<string>();
        /// <summary>
        /// Handle option assignment by immediately liquidating assigned underlying shares.
        /// Uses assignment event quantity and contract multiplier to compute shares (QC-native approach).
        /// Special handling for cash-settled index options (SPX).
        /// </summary>
        /// <param name="algorithm">The QC algorithm instance</param>
        /// <param name="assignmentEvent">The assignment order event from QC</param>
        public static void HandleAssignment(QCAlgorithm algorithm, OrderEvent assignmentEvent)
        {
            try
            {
                algorithm.Log($"ASSIGNMENT DETECTED: {assignmentEvent.Symbol} at {algorithm.Time}");
                
                // Idempotency guard: prevent duplicate processing of same assignment
                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);
                
                // Get the assigned option symbol
                var assignedSymbol = assignmentEvent.Symbol;
                
                // Check if this is a cash-settled index option (SPX)
                if (IsCashSettledIndexOption(assignedSymbol))
                {
                    algorithm.Log($"INDEX OPTION ASSIGNMENT: {assignedSymbol} (Cash-settled - no equity position created)");
                    HandleCashSettledAssignment(algorithm, assignmentEvent);
                    return;
                }
                
                // For option assignments, get the underlying symbol
                var underlyingSymbol = GetUnderlyingSymbol(assignedSymbol);
                
                if (underlyingSymbol == null)
                {
                    algorithm.Error($"Could not determine underlying symbol for assignment: {assignedSymbol}");
                    return;
                }
                
                // QC-NATIVE APPROACH: Compute shares from assignment event directly
                // This avoids relying on Portfolio holdings which may not be populated at midnight (assignment time)
                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);
                
                // CRITICAL FIX: Correct sign logic based on option right
                // Short CALL assignment creates SHORT stock position → BUY to flatten
                // Short PUT assignment creates LONG stock position → SELL to flatten
                int sharesToFlatten;
                
                // Try to use actual portfolio holdings if available (most accurate)
                if (algorithm.Portfolio.ContainsKey(underlyingSymbol) && 
                    algorithm.Portfolio[underlyingSymbol].Quantity != 0)
                {
                    // Flatten existing position by negating current holdings
                    sharesToFlatten = -(int)algorithm.Portfolio[underlyingSymbol].Quantity;
                    algorithm.Log($"Using portfolio holdings: {underlyingSymbol.Value} has {algorithm.Portfolio[underlyingSymbol].Quantity} shares");
                }
                else
                {
                    // Fallback: compute from option right
                    var optionRight = assignedSymbol.ID.OptionRight;
                    if (optionRight == OptionRight.Call)
                    {
                        // Short call assignment → we delivered shares → now short stock → BUY to cover
                        sharesToFlatten = (int)(contracts * contractMultiplier);
                    }
                    else // OptionRight.Put
                    {
                        // Short put assignment → we bought shares → now long stock → SELL to flatten
                        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}");
                
                // Execute immediate liquidation using computed quantity
                var orderTicket = algorithm.MarketOrder(underlyingSymbol, sharesToFlatten, tag: "Assignment Auto-Liquidation");
                
                algorithm.Log($"LIQUIDATING ASSIGNMENT: {underlyingSymbol.Value} - " +
                             $"Quantity: {sharesToFlatten}, Order ID: {orderTicket.OrderId}");
                
                // Log assignment impact for tracking
                LogAssignmentImpact(algorithm, assignmentEvent, underlyingSymbol, contractMultiplier);
            }
            catch (Exception ex)
            {
                algorithm.Error($"Error handling assignment: {ex.Message}");
                algorithm.Error($"Stack trace: {ex.StackTrace}");
            }
        }
        
        /// <summary>
        /// Check if the symbol represents a cash-settled index option (SPX, SPXW).
        /// These options settle in cash and don't create underlying equity positions.
        /// </summary>
        private static bool IsCashSettledIndexOption(Symbol symbol)
        {
            if (symbol?.SecurityType == SecurityType.IndexOption)
            {
                return true;
            }
            
            // SPXW weekly options are cash-settled but might not have IndexOption type
            var symbolStr = symbol?.Value?.ToUpperInvariant() ?? "";
            if (symbolStr.StartsWith("SPXW") || symbolStr.StartsWith("SPX"))
                return true;
            
            // Other known cash-settled index options
            if (symbolStr.StartsWith("VIX") || symbolStr.StartsWith("NDX") || symbolStr.StartsWith("RUT"))
                return true;
            
            return false;
        }
        
        /// <summary>
        /// Get the underlying symbol from an option symbol.
        /// Handles both equity options (SPY) and index options (SPXW -> SPX).
        /// </summary>
        private static Symbol GetUnderlyingSymbol(Symbol optionSymbol)
        {
            if (optionSymbol.SecurityType == SecurityType.Option || optionSymbol.SecurityType == SecurityType.IndexOption)
            {
                return optionSymbol.Underlying;
            }
            
            // If it's already an equity symbol (direct assignment), return as-is
            if (optionSymbol.SecurityType == SecurityType.Equity || optionSymbol.SecurityType == SecurityType.Index)
            {
                return optionSymbol;
            }
            
            return null;
        }
        
        /// <summary>
        /// Log assignment impact for performance tracking and analysis.
        /// Uses event-based calculation when holdings aren't available (e.g., at midnight assignment time).
        /// </summary>
        private static void LogAssignmentImpact(QCAlgorithm algorithm, OrderEvent assignmentEvent, 
            Symbol underlyingSymbol, decimal contractMultiplier)
        {
            try
            {
                var holding = algorithm.Portfolio[underlyingSymbol];
                decimal assignmentValue;
                
                // If holdings are populated, use them; otherwise compute from event
                if (holding.Invested && holding.HoldingsValue != 0)
                {
                    assignmentValue = Math.Abs(holding.HoldingsValue);
                }
                else
                {
                    // Compute from assignment event: contracts × multiplier × fill price
                    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}");
            }
        }
        
        /// <summary>
        /// Handle cash-settled index option assignments (SPX).
        /// These options settle in cash and don't create underlying positions.
        /// </summary>
        /// <param name="algorithm">The QC algorithm instance</param>
        /// <param name="assignmentEvent">The assignment order event from QC</param>
        private static void HandleCashSettledAssignment(QCAlgorithm algorithm, OrderEvent assignmentEvent)
        {
            try
            {
                // For cash-settled options, calculate the settlement value
                var optionSymbol = assignmentEvent.Symbol;
                var underlyingSymbol = GetUnderlyingSymbol(optionSymbol);
                
                if (underlyingSymbol == null)
                {
                    algorithm.Error($"Could not determine underlying symbol for cash-settled assignment: {optionSymbol}");
                    return;
                }
                
                // Get current underlying price for settlement calculation
                var underlyingPrice = algorithm.Securities.ContainsKey(underlyingSymbol) 
                    ? algorithm.Securities[underlyingSymbol].Price 
                    : 0m;
                
                // Log the cash settlement details
                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}");
                
                // Log assignment impact for cash-settled options
                LogCashSettledAssignmentImpact(algorithm, assignmentEvent, underlyingSymbol, underlyingPrice);
            }
            catch (Exception ex)
            {
                algorithm.Error($"Error handling cash-settled assignment: {ex.Message}");
            }
        }
        
        /// <summary>
        /// Log assignment impact for cash-settled index options.
        /// </summary>
        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}");
            }
        }
        
        /// <summary>
        /// Check for unexpected equity positions that might indicate unhandled assignments.
        /// This can be called periodically to monitor for assignment handling issues.
        /// </summary>
        public static void MonitorUnexpectedEquityPositions(QCAlgorithm algorithm)
        {
            try
            {
                var unexpectedEquities = 0;
                
                // Use QC's Portfolio.Values to check all positions
                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;

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
{
    /// <summary>
    /// Validates sufficient collateral for short option positions
    /// Prevents overselling calls against limited shares or puts against limited cash
    /// </summary>
    public class CollateralValidationRule : IPositionOverlapRule
    {
        private readonly IAlgorithmContext _context;
        private readonly object _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
            {
                // Only validate option positions
                if (proposedSymbol.SecurityType != SecurityType.Option)
                    return ValidationResult.Success();

                // Only validate short positions (negative quantity)
                if (quantity >= 0)
                    return ValidationResult.Success();

                // Skip validation for multi-leg strategies (spreads, Iron Condors, etc.)
                // These strategies manage their own risk through spread construction
                if (IsMultiLegStrategy(strategyTag))
                {
                    ((dynamic)_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)
            {
                ((dynamic)_logger).Error($"[{RuleName}] Error validating collateral: {ex.Message}");
                return ValidationResult.Error($"Collateral validation error: {ex.Message}");
            }
        }

        /// <summary>
        /// Validates sufficient shares for covered call positions
        /// </summary>
        private ValidationResult ValidateCallCollateral(
            Symbol underlying,
            decimal proposedQuantity,
            IEnumerable<KeyValuePair<Symbol, SecurityHolding>> existingPositions,
            int contractMultiplier)
        {
            try
            {
                // Get current stock holdings
                var stockHolding = _context.Algorithm.Portfolio[underlying];
                var availableShares = stockHolding?.Quantity ?? 0;

                // Calculate total short calls (existing + proposed)
                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;

                ((dynamic)_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)
            {
                ((dynamic)_logger).Warning($"[{RuleName}] Error in call validation: {ex.Message}");
                // Allow the trade if we can't validate (conservative but functional)
                return ValidationResult.Success();
            }
        }

        /// <summary>
        /// Validates sufficient cash for cash-secured put positions
        /// </summary>
        private ValidationResult ValidatePutCollateral(
            Symbol proposedSymbol,
            decimal proposedQuantity,
            IEnumerable<KeyValuePair<Symbol, SecurityHolding>> existingPositions,
            int contractMultiplier)
        {
            try
            {
                var underlying = proposedSymbol.Underlying;
                var strike = proposedSymbol.ID.StrikePrice;

                // Get available cash
                var availableCash = _context.Algorithm.Portfolio.Cash;

                // Calculate cash required for this put
                var cashRequiredForNewPut = Math.Abs(proposedQuantity) * strike * contractMultiplier;

                // Calculate cash already committed to existing cash-secured puts
                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;

                ((dynamic)_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)
            {
                ((dynamic)_logger).Warning($"[{RuleName}] Error in put validation: {ex.Message}");
                // Allow the trade if we can't validate
                return ValidationResult.Success();
            }
        }

        /// <summary>
        /// Gets total buying power reduction for all positions on an underlying
        /// </summary>
        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;
            }
        }

        /// <summary>
        /// Helper to get underlying symbol from any security type
        /// </summary>
        private Symbol GetPositionUnderlying(Symbol symbol)
        {
            return symbol.SecurityType == SecurityType.Option ? symbol.Underlying : symbol;
        }

        /// <summary>
        /// Determines if this is a multi-leg strategy based on current validation context
        /// Multi-leg strategies manage their own risk through spread construction
        /// </summary>
        private bool IsMultiLegStrategy(string strategyTag)
        {
            // Check for common multi-leg indicators in tag (keep generic terms only)
            if (!string.IsNullOrEmpty(strategyTag))
            {
                var tag = strategyTag.ToUpperInvariant();
                if (tag.Contains("COMBO") || tag.Contains("MULTI") || tag.Contains("SPREAD"))
                    return true;
            }
            
            // Try to determine from strategy configuration if available
            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)
            {
                ((dynamic)_logger).Debug($"[MULTI-LEG DETECTION] Could not read ComboOrderLegCount: {ex.Message}");
            }
            
            // Default to false for single-leg strategies
            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
    /// </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, lowest priority
        /// </summary>
        Debug = 3
    }
}
#region imports
    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;
#endregion

namespace CoreAlgo.Architecture.Core.Implementations
{
    /// <summary>
    /// Non-generic base class to hold shared static collections for all logger instances
    /// </summary>
    public static class SmartLoggerStore
    {
        // Shared static storage for ALL logger instances (thread-safe)
        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();

        // Throttling for high-frequency messages
        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;

        /// <summary>
        /// Get current collection counts for debugging
        /// </summary>
        public static (int dailyMessages, int messageGroups) GetCollectionCounts()
        {
            return (DailyMessages.Count, MessageGroups.Count);
        }

        /// <summary>
        /// Check if message should be throttled due to high frequency
        /// </summary>
        public static bool ShouldThrottleMessage(string baseHash, DateTime currentTime)
        {
            var throttleKey = $"throttle_{baseHash}";
            
            var info = MessageThrottle.GetOrAdd(throttleKey, _ => (currentTime, 0));
            
            // Reset count if window has expired
            if (currentTime - info.lastSeen > ThrottleWindow)
            {
                MessageThrottle.TryUpdate(throttleKey, (currentTime, 1), info);
                return false;
            }
            
            // Increment count
            var newCount = info.count + 1;
            MessageThrottle.TryUpdate(throttleKey, (currentTime, newCount), info);
            
            // Throttle if exceeded limit
            return newCount > MaxMessagesPerWindow;
        }

        /// <summary>
        /// Clean old throttle entries to prevent memory leaks
        /// </summary>
        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 _);
            }
        }

        /// <summary>
        /// Process and output accumulated daily logs with smart summarization
        /// </summary>
        public static void ProcessDailyLogs(QCAlgorithm algorithm)
        {   
            // Skip daily log processing entirely in live mode
            if (algorithm.LiveMode)
                return;
                
            lock (ProcessLock)
            {
                var currentDay = algorithm.Time.Date;
                
                // FIXED: Match Python centralalgorithm logic exactly
                // Always skip if already processed today (no conditions about message counts)
                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} ===");
                
                algorithm.Log("---------------------------------");
                algorithm.Log($"Daily Log Summary - {currentDay:yyyy-MM-dd}");
                algorithm.Log("---------------------------------");
                
                if (!DailyMessages.Any() && !MessageGroups.Any())
                {
                    algorithm.Log("*** NO SMART MESSAGES WERE COLLECTED ***");
                }
                else
                {
                    algorithm.Log($"Found {DailyMessages.Count} daily message groups and {MessageGroups.Count} statistical groups to process");
                }

                // Process regular messages from DailyMessages
                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}");
                        }
                    }
                }

                // Process grouped messages with statistical analysis
                foreach (var kvp in MessageGroups)
                {
                    var group = kvp.Value;
                    algorithm.Log(group.GetSummary());
                }

                algorithm.Log("");

                // Clear processed messages
                DailyMessages.Clear();
                MessageGroups.Clear();
                
                // Clear old throttle entries to prevent memory leaks
                CleanOldThrottleEntries(currentDay);
                
                LastProcessedDay = currentDay;
            }
        }
    }

    /// <summary>
    /// Represents a log message with smart hashing and pattern recognition capabilities
    /// </summary>
    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;
        }

        /// <summary>
        /// Exact hash for identical messages
        /// </summary>
        public string Hash
        {
            get
            {
                if (_hash == null)
                {
                    var content = $"{Level}|{ClassName}|{FunctionName}|{Message}";
                    _hash = ComputeHash(content);
                }
                return _hash;
            }
        }

        /// <summary>
        /// Pattern-based hash for messages with similar structure but different values
        /// </summary>
        public string BaseHash
        {
            get
            {
                if (_baseHash == null)
                {
                    try
                    {
                        // Normalize the message by replacing numeric values and common variables
                        var template = Regex.Replace(Message, @"[-+]?[0-9,]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?", "NUM");
                        template = Regex.Replace(template, @"\s+", " ");
                        template = template.ToLowerInvariant();
                        
                        // Normalize specific patterns like symbols and states
                        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
                    {
                        // Fallback to simpler hash if normalization fails
                        var content = $"{Level}|{ClassName}|{FunctionName}";
                        _baseHash = ComputeHash(content);
                    }
                }
                return _baseHash;
            }
        }

        /// <summary>
        /// Extract numeric value from message for statistical analysis
        /// </summary>
        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
            {
                // Ignore extraction errors
            }
            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();
            }
        }
    }

    /// <summary>
    /// Groups messages with similar patterns for statistical analysis
    /// </summary>
    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;
        }

        /// <summary>
        /// Generate statistical summary of grouped messages
        /// </summary>
        public string GetSummary()
        {
            if (Messages.Count == 1)
                return FormatMessage(Messages[0]);

            // Extract values and timestamps from sorted messages
            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]);

            // Calculate statistics
            var mean = values.Average();
            var min = values.Min();
            var max = values.Max();
            var count = values.Count;

            // Format time range
            var timeRange = $"{FirstTime:HH:mm:ss}-{LastTime:HH:mm:ss}";

            // Build summary with clear sections like Python version
            var sections = new List<string>
            {
                $"{timeRange} {Level} -> {ClassName}.{FunctionName}: {BaseMessage}",
                $"    Stats: mean={mean:F2}, min={min:F2}, max={max:F2}"
            };

            // Add trend analysis if we have enough data points
            if (count >= 2)
            {
                var trend = GetTrendAnalysis(values, timestamps);
                if (!string.IsNullOrEmpty(trend))
                    sections.Add($"    Trend:{trend}");
            }

            // Add distribution analysis if we have enough data points
            if (count >= 3)
            {
                var distribution = GetDistributionAnalysis(values);
                if (!string.IsNullOrEmpty(distribution))
                    sections.Add($"    {distribution}");
            }

            // Sample count
            sections.Add($"    Samples: {count}");

            return string.Join("\n", sections);
        }

        private string GetTrendAnalysis(List<double> values, List<DateTime> timestamps)
        {
            if (values.Count < 2) return "";

            try
            {
                // Find key changes (local maxima and minima) like Python version
                var keyChanges = new List<(DateTime time, double value)>();
                
                for (int i = 1; i < values.Count - 1; i++)
                {
                    // Look for local maxima and minima
                    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]));
                    }
                }

                // Limit to most significant changes
                if (keyChanges.Count > 4)
                {
                    // Sort by absolute change magnitude from first value
                    keyChanges = keyChanges.OrderByDescending(x => Math.Abs(x.value - values[0])).Take(4).ToList();
                    // Resort by time
                    keyChanges = keyChanges.OrderBy(x => x.time).ToList();
                }

                if (!keyChanges.Any()) return "";

                // Format changes with timestamps like Python
                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 all values are the same, return special format
                if (Math.Abs(minVal - maxVal) < 0.001)
                    return $"Distribution: [constant={minVal:F2}]";
                
                // Create 3 bins like Python version
                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]++;
                }

                // Format distribution
                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}";
        }
    }

    /// <summary>
    /// QuantConnect-specific logger implementation with smart deduplication
    /// </summary>
    public class QCLogger<T> : ILogger<T>
    {
        private readonly QCAlgorithm _algorithm;
        private readonly string _categoryName;
        private readonly int _currentLogLevel;
        private readonly bool _isLiveMode;
        private readonly bool _verboseMode;

        public QCLogger(QCAlgorithm algorithm, int logLevel = 3)
        {
            _algorithm = algorithm ?? throw new ArgumentNullException(nameof(algorithm));
            _categoryName = typeof(T).Name;
            _currentLogLevel = logLevel;
            _isLiveMode = algorithm.LiveMode;
            _verboseMode = bool.Parse(algorithm.GetParameter("VerboseMode", "false"));
        }

        public void LogInformation(string message, params object[] args)
        {
            var formattedMessage = FormatMessage(message, args);
            if (_isLiveMode)
            {
                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 (_isLiveMode)
            {
                var realTimeMessage = FormatRealTimeMessage(formattedMessage, _categoryName, GetCallingFunctionName(), LogLevel.Debug);
                _algorithm.Log(realTimeMessage);
                return;
            }
            StoreSmartMessage("DEBUG", formattedMessage);
        }

        public void LogWarning(string message, params object[] args)
        {
            var formattedMessage = FormatMessage(message, args);
            if (_isLiveMode)
            {
                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 (_isLiveMode)
            {
                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 (_isLiveMode)
            {
                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}");
        }

        /// <summary>
        /// Central logging method with level checking - implements context pattern like CentralAlgorithm
        /// </summary>
        /// <param name="message">Message to log</param>
        /// <param name="level">Log level for filtering</param>
        public void LogMessage(string message, LogLevel level = LogLevel.Debug)
        {
            // Level checking: only log if level is at or below current threshold
            if ((int)level > _currentLogLevel)
                return;

            // In live mode, output immediately for all levels
            if (_isLiveMode)
            {
                var realTimeMessage = FormatRealTimeMessage(message, _categoryName, GetCallingFunctionName(), level);
                _algorithm.Log(realTimeMessage);
                return;
            }

            // Route to appropriate logging method based on level
            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;
                default:
                    LogDebug(message);
                    break;
            }
        }

        /// <summary>
        /// Convenience method for error logging
        /// </summary>
        public void Error(string message) => LogMessage(message, LogLevel.Error);

        /// <summary>
        /// Convenience method for warning logging
        /// </summary>
        public void Warning(string message) => LogMessage(message, LogLevel.Warning);

        /// <summary>
        /// Convenience method for information logging
        /// </summary>
        public void Info(string message) => LogMessage(message, LogLevel.Info);

        /// <summary>
        /// Convenience method for debug logging
        /// </summary>
        public void Debug(string message) => LogMessage(message, LogLevel.Debug);

        /// <summary>
        /// Logs a message with explicit context information for better traceability
        /// Uses LogLevel-based output control: ERROR/WARNING immediate, INFO conditional, DEBUG batched
        /// </summary>
        public void LogWithContext(string message, string className, string methodName, LogLevel level = LogLevel.Info)
        {
            // Level checking: only log if level is at or below current threshold
            if ((int)level > _currentLogLevel)
                return;

            // Try dynamic context detection first (like Python logger)
            if (string.IsNullOrEmpty(className) || className == "SimpleBaseStrategy")
            {
                var (dynClass, dynMethod) = GetCallerContextDynamic();
                if (dynClass != "Unknown")
                {
                    className = dynClass;
                    methodName = dynMethod;
                }
            }

            // In live mode, output immediately for all levels and skip batching
            if (_isLiveMode)
            {
                var formattedMessage = FormatRealTimeMessage(message, className, methodName, level);
                _algorithm.Log(formattedMessage);
                return;
            }

            // LogLevel-based output decision
            bool shouldOutputImmediate = level <= LogLevel.Warning || // ERROR/WARNING always immediate
                                        (_verboseMode && level == LogLevel.Info) || // INFO if verbose
                                        (_isLiveMode && level == LogLevel.Info); // INFO if live

            if (shouldOutputImmediate)
            {
                var formattedMessage = FormatRealTimeMessage(message, className, methodName, level);
                _algorithm.Log(formattedMessage);
            }

            // Still batch DEBUG and non-verbose INFO for end-of-day summary
            if (!shouldOutputImmediate || level == LogLevel.Debug)
            {
                StoreSmartMessageWithContext(level.ToString().ToUpper(), message, className, methodName);
            }
        }

        /// <summary>
        /// Store message for smart processing with grouping for statistical analysis
        /// </summary>
        private void StoreSmartMessage(string level, string message)
        {
            // Skip storing in live mode to avoid memory accumulation
            if (_isLiveMode)
                return;
                
            var functionName = GetCallingFunctionName();
            var smartMessage = new SmartLogMessage(level, _categoryName, functionName, message, _algorithm.Time);
            
            // Apply throttling for high-frequency messages (especially DEBUG level)
            if (level == "DEBUG" && SmartLoggerStore.ShouldThrottleMessage(smartMessage.BaseHash, _algorithm.Time))
            {
                return; // Skip this message due to throttling
            }
            
            // Try to add to existing message group for messages with numeric values
            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
                    }
                }
            }
            
            // Store as regular message if no numeric value or grouping failed
            var messageList = SmartLoggerStore.DailyMessages.GetOrAdd(smartMessage.Hash, _ => new List<SmartLogMessage>());
            lock (messageList)
            {
                messageList.Add(smartMessage);
            }
        }

        /// <summary>
        /// Store message for smart processing with explicit context
        /// </summary>
        private void StoreSmartMessageWithContext(string level, string message, string className, string methodName)
        {
            // Skip storing in live mode to avoid memory accumulation
            if (_isLiveMode)
                return;
                
            var smartMessage = new SmartLogMessage(level, className, methodName, message, _algorithm.Time);
            
            // Apply throttling for high-frequency messages (especially DEBUG level)
            if (level == "DEBUG" && SmartLoggerStore.ShouldThrottleMessage(smartMessage.BaseHash, _algorithm.Time))
            {
                return; // Skip this message due to throttling
            }
            
            // Try to add to existing message group for messages with numeric values
            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
                    }
                }
            }
            
            // Store as regular message if no numeric value or grouping failed
            var messageList = SmartLoggerStore.DailyMessages.GetOrAdd(smartMessage.Hash, _ => new List<SmartLogMessage>());
            lock (messageList)
            {
                messageList.Add(smartMessage);
            }
        }

        /// <summary>
        /// Format message for real-time output
        /// </summary>
        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");
            
            // Format: "09:31:00 (15:24:33.123) [ORBTemplate.OnInitialize] INFO: Message"
            if (_isLiveMode)
            {
                return $"{timestamp} [{className}.{methodName}] {level.ToString().ToUpper()}: {message}";
            }
            else
            {
                // Include real time in backtest for debugging
                return $"{timestamp} (Real: {realTime}) [{className}.{methodName}] {level.ToString().ToUpper()}: {message}";
            }
        }


        private static string GetCallingFunctionName()
        {
            try
            {
                var stackTrace = new System.Diagnostics.StackTrace();
                // Skip current method, StoreSmartMessage, and LogXXX method
                var frame = stackTrace.GetFrame(3);
                return frame?.GetMethod()?.Name ?? "Unknown";
            }
            catch
            {
                return "Unknown";
            }
        }

        /// <summary>
        /// Better stack frame detection for caller context
        /// </summary>
        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;
                    
                    // Skip system and wrapper methods
                    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 it's SimpleBaseStrategy, try to get the actual derived class by looking deeper
                    if (typeName == "SimpleBaseStrategy")
                    {
                        continue;
                    }
                    
                    return (typeName, methodName);
                }
                
                return ("Unknown", "Unknown");
            }
            catch
            {
                return ("Unknown", "Unknown");
            }
        }

        /// <summary>
        /// Enhanced dynamic context detection inspired by Python logger
        /// Uses deeper stack inspection to find the actual calling strategy class
        /// </summary>
        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;
                    
                    // Skip wrapper/system methods
                    if (IsWrapperMethod(typeName, methodName))
                        continue;
                        
                    // For templates, get the actual derived class (highest priority)
                    if (typeName.EndsWith("Template") && !typeName.Contains("<"))
                        return (typeName, methodName);
                        
                    // Skip SimpleBaseStrategy to find the actual strategy
                    if (typeName != "SimpleBaseStrategy" && 
                        !typeName.Contains("Logger") &&
                        !typeName.Contains("Algorithm") &&
                        !typeName.Contains("System"))
                    {
                        return (typeName, methodName);
                    }
                }
                
                return ("Unknown", "Unknown");
            }
            catch
            {
                return ("Unknown", "Unknown");
            }
        }

        /// <summary>
        /// Check if a method should be skipped during stack trace inspection
        /// </summary>
        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
            {
                // Replace placeholders with argument indices for string.Format compatibility
                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
            {
                // Fallback to simple concatenation if formatting fails
                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
{
    /// <summary>
    /// Helper methods for SimpleBaseStrategy
    /// This partial class contains logging, configuration, option setup, and utility methods
    /// </summary>
    public partial class SimpleBaseStrategy
    {
        // ============================================================================
        // BASIC LOGGING AND UTILITY HELPERS
        // ============================================================================

        // QC Helper Methods - delegate to Algorithm instance
        protected void Log(string message) => ((dynamic)Logger).Info(message);
        protected void Error(string message) => ((dynamic)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);

        /// <summary>
        /// Helper method for debug logging using context pattern
        /// </summary>
        protected void Debug(string message) => ((dynamic)Logger).Debug(message);

        /// <summary>
        /// Get typed configuration parameter with QC fallback
        /// </summary>
        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
            {
                return defaultValue;
            }
        }

        // ============================================================================
        // SMART LOGGING WITH CONTEXT TRACKING
        // ============================================================================
        
        /// <summary>
        /// Helper method for information logging with improved context tracking
        /// </summary>
        protected void SmartLog(string message, 
            [CallerMemberName] string memberName = "", 
            [CallerFilePath] string sourceFilePath = "")
        {
            // Extract actual class name from file path (QuantConnect compatible)
            var className = ExtractClassNameFromPath(sourceFilePath);
            if (string.IsNullOrEmpty(className))
                className = this.GetType().Name;
                
            ((dynamic)Logger).LogWithContext(message, className, memberName, LogLevel.Info);
        }
        
        /// <summary>
        /// Helper method for warning logging with improved context tracking
        /// </summary>
        protected void SmartWarn(string message,
            [CallerMemberName] string memberName = "",
            [CallerFilePath] string sourceFilePath = "")
        {
            var className = ExtractClassNameFromPath(sourceFilePath);
            if (string.IsNullOrEmpty(className))
                className = this.GetType().Name;
                
            ((dynamic)Logger).LogWithContext(message, className, memberName, LogLevel.Warning);
        }
        
        /// <summary>
        /// Helper method for debug logging with improved context tracking
        /// </summary>
        protected void SmartDebug(string message,
            [CallerMemberName] string memberName = "",
            [CallerFilePath] string sourceFilePath = "")
        {
            var className = ExtractClassNameFromPath(sourceFilePath);
            if (string.IsNullOrEmpty(className))
                className = this.GetType().Name;
                
            ((dynamic)Logger).LogWithContext(message, className, memberName, LogLevel.Debug);
        }


        /// <summary>
        /// Helper method for error logging with improved context tracking
        /// </summary>
        protected void SmartError(string message,
            [CallerMemberName] string memberName = "",
            [CallerFilePath] string sourceFilePath = "")
        {
            var className = ExtractClassNameFromPath(sourceFilePath);
            if (string.IsNullOrEmpty(className))
                className = this.GetType().Name;
                
            ((dynamic)Logger).LogWithContext(message, className, memberName, LogLevel.Error);
        }

        /// <summary>
        /// Extract class name from file path using QuantConnect-compatible approach
        /// </summary>
        private string ExtractClassNameFromPath(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
                return "";
                
            try
            {
                // Extract filename from path manually (QuantConnect compatible)
                var lastSlash = Math.Max(filePath.LastIndexOf('/'), filePath.LastIndexOf('\\'));
                var fileName = lastSlash >= 0 ? filePath.Substring(lastSlash + 1) : filePath;
                
                // Remove .cs extension
                if (fileName.EndsWith(".cs"))
                    fileName = fileName.Substring(0, fileName.Length - 3);
                    
                return fileName;
            }
            catch
            {
                // Fallback to runtime type name
                return this.GetType().Name;
            }
        }

        // ============================================================================
        // COMMON OPTION SETUP HELPERS (Eliminates duplication across templates)
        // ============================================================================

        /// <summary>
        /// Set up underlying and options using AssetManager for consistent asset handling
        /// </summary>
        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);
        }

        /// <summary>
        /// Set up options filter with common parameters used across strategies
        /// </summary>
        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}");
            
            // Verify the options symbol exists in Securities
            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)}");
                
                // Clarify option resolution diagnostics to avoid GetHighestResolution confusion
                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}");
            }
        }

        /// <summary>
        /// Setup underlying and options with standard filter configuration
        /// Combines asset setup and filtering into one call to reduce template code
        /// </summary>
        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));
        }

        /// <summary>
        /// Override for custom pre-execution logic that should always run.
        /// </summary>
        protected virtual void OnPreExecuteAlways(Slice slice)
        {
            // Intentionally left blank. Derived strategies can override to prepare non-order state
            // such as computing time windows, refreshing mapped symbols, or resetting daily tracking.
        }

        // ============================================================================
        // UNIVERSE OPTIMIZATION HELPERS (For Large Universe Strategies)
        // ============================================================================

        /// <summary>
        /// Sets up optimized universe settings for large universe strategies.
        /// Enables async selection and configures performance optimizations.
        /// </summary>
        /// <param name="resolution">Universe data resolution</param>
        /// <param name="enableAsync">Enable asynchronous universe selection (recommended for large universes)</param>
        /// <param name="extendedHours">Enable extended market hours</param>
        protected void SetupAsyncUniverse(Resolution resolution = Resolution.Minute, 
            bool enableAsync = true, bool extendedHours = false)
        {
            UniverseOptimizer.SetupOptimizedUniverse(Algorithm, resolution, enableAsync, extendedHours);
        }

        /// <summary>
        /// Batch fetch historical data for multiple symbols efficiently.
        /// Processes symbols in chunks to avoid memory issues and timeouts.
        /// </summary>
        /// <param name="symbols">Symbols to fetch data for</param>
        /// <param name="days">Number of days of history</param>
        /// <param name="resolution">Data resolution</param>
        /// <param name="batchSize">Batch size for processing (default: 500)</param>
        /// <returns>Dictionary mapping Symbol to list of volumes</returns>
        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);
        }

        /// <summary>
        /// Calculate Average Daily Volume (ADV) for multiple symbols efficiently.
        /// Uses batch processing to handle large universes.
        /// </summary>
        /// <param name="symbols">Symbols to calculate ADV for</param>
        /// <param name="days">Number of days for ADV calculation</param>
        /// <returns>Dictionary mapping Symbol to ADV</returns>
        protected Dictionary<Symbol, decimal> CalculateBatchedADV(IEnumerable<Symbol> symbols, int days = 21)
        {
            return UniverseOptimizer.CalculateBatchedADV(Algorithm, symbols, days);
        }

        /// <summary>
        /// Calculate volume shock ratios for universe symbols efficiently.
        /// Compares current intraday volume to historical average.
        /// </summary>
        /// <param name="intradayVolumes">Current intraday volume data</param>
        /// <param name="symbols">Universe symbols to process</param>
        /// <param name="advDays">Days for ADV calculation</param>
        /// <returns>Dictionary mapping Symbol to shock ratio</returns>
        protected Dictionary<Symbol, decimal> CalculateVolumeShock(ConcurrentDictionary<Symbol, long> intradayVolumes,
            IEnumerable<Symbol> symbols, int advDays = 21)
        {
            return UniverseOptimizer.CalculateVolumeShock(Algorithm, intradayVolumes, symbols, advDays);
        }

        /// <summary>
        /// Clean up removed securities from tracking structures to prevent memory leaks.
        /// Call this from OnSecuritiesChanged for universe strategies.
        /// </summary>
        /// <param name="changes">Security changes from OnSecuritiesChanged</param>
        /// <param name="trackingDictionaries">Collection of dictionaries to clean up</param>
        protected void CleanupRemovedSecurities(SecurityChanges changes, params object[] trackingDictionaries)
        {
            UniverseOptimizer.CleanupRemovedSecurities(Algorithm, changes, trackingDictionaries);
        }

        /// <summary>
        /// Process large universe data in parallel for improved performance.
        /// Useful for computationally intensive selection logic.
        /// </summary>
        /// <typeparam name="TInput">Input data type</typeparam>
        /// <typeparam name="TResult">Result data type</typeparam>
        /// <param name="data">Input data collection</param>
        /// <param name="processor">Function to process each item</param>
        /// <param name="batchSize">Batch size for processing</param>
        /// <returns>Collection of results</returns>
        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 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
{
    /// <summary>
    /// Trade tracking and assignment handling methods for SimpleBaseStrategy
    /// This partial class contains all trade tracking functionality similar to Python Position.py
    /// </summary>
    public partial class SimpleBaseStrategy
    {
        // ============================================================================
        // TRADE TRACKING HELPERS (Simple trade data collection like Python Position.py)
        // ============================================================================

        /// <summary>
        /// Track a new working order (order submitted but not filled)
        /// </summary>
        protected void TrackWorkingOrder(OrderTicket ticket, string strategy = "")
        {
            if (ticket?.OrderId != null)
            {
                var orderId = ticket.OrderId.ToString();
                var symbol = ticket.Symbol?.Value ?? "Unknown";
                var strategyName = !string.IsNullOrEmpty(strategy) ? strategy : Name;
                
                TradeTracker.AddWorkingTrade(orderId, symbol, strategyName, ticket.Tag);
                Debug($"Tracking working order: {orderId} for {symbol}");
            }
        }
        
        /// <summary>
        /// Mark an order as filled (moved from working to open)
        /// </summary>
        public void TrackOrderFilled(OrderEvent orderEvent)
        {
            if (orderEvent?.OrderId != null)
            {
                var orderId = orderEvent.OrderId.ToString();
                TradeTracker.MarkTradeAsOpen(orderId, orderEvent.FillPrice, (int)orderEvent.FillQuantity);
                Debug($"Order filled: {orderId} at {orderEvent.FillPrice}");
                OnOrderFilled(orderEvent);
            }
        }
        
        /// <summary>
        /// Hook for derived strategies to handle order fill events immediately (event-time logic).
        /// Default implementation is no-op.
        /// </summary>
        /// <param name="orderEvent">The order event that was filled</param>
        protected virtual void OnOrderFilled(OrderEvent orderEvent) { }
        
        /// <summary>
        /// Mark a position as closed with P&L
        /// </summary>
        protected void TrackPositionClosed(string orderId, decimal closePrice, decimal pnl)
        {
            TradeTracker.MarkTradeAsClosed(orderId, closePrice, pnl);
            Debug($"Position closed: {orderId}, P&L: {pnl}");
        }
        
        /// <summary>
        /// Cancel a working order
        /// </summary>
        public void TrackOrderCancelled(string orderId)
        {
            TradeTracker.CancelWorkingTrade(orderId);
            Debug($"Order cancelled: {orderId}");
        }

        /// <summary>
        /// Handle option assignments using QuantConnect's native assignment detection.
        /// Automatically liquidates assigned underlying shares to prevent margin crises.
        /// </summary>
        /// <param name="assignmentEvent">Assignment order event from QuantConnect</param>
        public virtual void OnAssignmentOrderEvent(OrderEvent assignmentEvent)
        {
            try
            {
                SmartLog($"Assignment event received: {assignmentEvent.Symbol} at {Algorithm.Time}");
                
                // Use our QC-native assignment handler
                AssignmentHandler.HandleAssignment(Algorithm, assignmentEvent);
                
                // Allow derived strategies to add custom assignment logic
                OnAssignmentHandled(assignmentEvent);
            }
            catch (Exception ex)
            {
                SmartError($"Error in assignment handling: {ex.Message}");
                OnError(ex, ErrorSeverity.Error, true, "Assignment handling error");
            }
        }
        
        /// <summary>
        /// Called after assignment handling is complete. Override in derived strategies for custom logic.
        /// </summary>
        /// <param name="assignmentEvent">The processed assignment event</param>
        protected virtual void OnAssignmentHandled(OrderEvent assignmentEvent)
        {
            // Default implementation - derived strategies can override
            SmartLog($"Assignment handling completed for {assignmentEvent.Symbol}");
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Orders;
using QuantConnect.Securities;
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
{
    /// <summary>
    /// Trading-related methods for SimpleBaseStrategy
    /// This partial class contains all order management and smart trading functionality
    /// </summary>
    public partial class SimpleBaseStrategy
    {
        // Smart order methods that use SmartOrderManager when available and enabled
        protected OrderTicket MarketOrder(Symbol symbol, decimal quantity, string tag = "")
        {
            if (ShouldUseSmartOrderManager())
            {
                return SmartOrderManager.SmartMarketOrder(symbol, quantity, tag);
            }
            return Algorithm.MarketOrder(symbol, quantity, tag: tag);
        }
        
        protected OrderTicket LimitOrder(Symbol symbol, decimal quantity, decimal limitPrice, string tag = "") => Algorithm.LimitOrder(symbol, quantity, limitPrice, tag: tag);

        protected OrderTicket StopMarketOrder(Symbol symbol, decimal quantity, decimal stopPrice, string tag = "")
			=> Algorithm.StopMarketOrder(symbol, quantity, stopPrice, tag: tag);

        protected OrderTicket StopLimitOrder(Symbol symbol, decimal quantity, decimal stopPrice, decimal limitPrice, string tag = "")
			=> Algorithm.StopLimitOrder(symbol, quantity, stopPrice, limitPrice, tag: tag);
        
        protected List<OrderTicket> ComboMarketOrder(List<Leg> legs, int quantity, string tag = "")
		{
			if (ShouldUseSmartOrderManager())
			{
				return SmartOrderManager.SmartComboMarketOrder(legs, quantity, tag);
			}
			return Algorithm.ComboMarketOrder(legs, quantity, tag: tag);
		}
        
        // Helper method to determine if SmartOrderManager should be used
        private bool ShouldUseSmartOrderManager()
        {
            // Always route orders through SmartOrderManager when available.
            return SmartOrderManager != null;
        }
        
        // Helper method to determine if SmartPricing should be used
        private bool ShouldUseSmartPricing()
        {
            if (SmartOrderManager == null) return false;
            
            // Check if SmartPricingMode is enabled in config
            if (Config is StrategyConfig strategyConfig)
            {
                var smartPricingMode = strategyConfig.GetParameterValue("SmartPricingMode", "Off");
                return !string.Equals(smartPricingMode?.ToString(), "Off", StringComparison.OrdinalIgnoreCase);
            }
            
            return false;
        }
        
        // Helper method to check if overlap prevention is enabled
        private bool IsOverlapPreventionEnabled()
        {
            if (Config is StrategyConfig strategyConfig)
            {
                return strategyConfig.EnableOverlapPrevention;
            }
            return false;
        }
        
        // Setup SmartPricing engine based on configuration
        private void SetupSmartPricing()
        {
            if (SmartOrderManager == null) 
            {
                ((dynamic)Logger).Warning("SetupSmartPricing called but SmartOrderManager is null");
                return;
            }
            
            var smartPricingMode = "Off";
            
            // Try to get from Config first (if available)
            if (Config is StrategyConfig strategyConfig)
            {
                smartPricingMode = strategyConfig.GetParameterValue("SmartPricingMode", "Off")?.ToString() ?? "Off";
                ((dynamic)Logger).Info($"SmartPricing mode from Config: {smartPricingMode}");
            }
            else
            {
                // Fallback to direct parameter reading if Config not yet loaded
                smartPricingMode = Algorithm.GetParameter("SmartPricingMode", "Off");
                ((dynamic)Logger).Info($"SmartPricing mode from Algorithm.GetParameter: {smartPricingMode}");
            }
            
            if (!string.Equals(smartPricingMode, "Off", StringComparison.OrdinalIgnoreCase))
            {
                try
                {
                    ((dynamic)Logger).Info($"Creating SmartPricing engine for mode: {smartPricingMode}");
                    var pricingEngine = SmartPricingEngineFactory.Create(smartPricingMode);
                    SmartOrderManager.SetPricingEngine(pricingEngine);
                    ((dynamic)Logger).Info($"SmartPricing enabled with mode: {smartPricingMode}");
                }
                catch (Exception ex)
                {
                    ((dynamic)Logger).Error($"Failed to setup SmartPricing: {ex.Message}");
                }
            }
            else
            {
                ((dynamic)Logger).Info("SmartPricing disabled (mode is Off)");
            }
        }

        /// <summary>
        /// Helper method for creating option combo orders with better error handling
        /// </summary>
        protected OrderTicket SubmitComboOrder(List<Leg> legs, string tag = "")
        {
            try
            {
                // Validate margin before placing combo order
                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;
            }
        }

        /// <summary>
        /// Helper method for progressive pricing (simple retry logic)
        /// </summary>
        protected OrderTicket SubmitOrderWithRetry(Symbol symbol, decimal quantity, decimal? limitPrice = null, int maxRetries = 3)
        {
            // Check margin utilization before attempting order
            // Check if approaching margin call using QC's built-in calculations
            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
{
    /// <summary>
    /// Simplified base strategy that leverages QC's strengths while adding configuration and light extensions
    /// Implements context pattern for centralized logging and algorithm access
    /// </summary>
    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>();

        // Context pattern implementation - provides centralized access to algorithm and logger
        public QCAlgorithm Algorithm { get; private set; }
        public object Logger { get; private set; }
        
        // SmartPricing integration
        public SmartOrderManager SmartOrderManager { get; private set; }
        
        // Position overlap management
        protected PositionOverlapManager OverlapManager { get; private set; }
        
        // Trade tracking system (like Python setupbasestructure.py arrays)
        protected SimpleTradeTracker TradeTracker { get; private set; } = new SimpleTradeTracker();

        /// <inheritdoc/>
        public abstract string Name { get; }

        /// <inheritdoc/>
        public abstract string Description { get; }

        /// <inheritdoc/>
        public virtual string Version => "1.0.0";

        /// <inheritdoc/>
        public StrategyState State 
        { 
            get => _state;
            private set
            {
                if (_state != value)
                {
                    var previousState = _state;
                    _state = value;
                    StateChanged?.Invoke(this, new StrategyStateChangedEventArgs(previousState, value));
                }
            }
        }

        /// <inheritdoc/>
        public Dictionary<string, object> Parameters => _parameters;

        /// <inheritdoc/>
        public event EventHandler<StrategyStateChangedEventArgs> StateChanged;

        /// <inheritdoc/>
        public event EventHandler<StrategyErrorEventArgs> ErrorOccurred;

        /// <inheritdoc/>
        public virtual void Initialize(QCAlgorithm algorithm)
        {
            Algorithm = algorithm;
            
            // Logger will be injected via context pattern - no initialization here
            // Logger comes from Main.cs via SetContext method
            
            // Initialize calculators
            StrikeRangeCalculator = new StrikeRangeCalculator(algorithm);
            
            // Initialize SmartOrderManager if logger is available
            if (Logger != null)
            {
                // Create algorithm context adapter
                var context = new SimpleAlgorithmContext(algorithm, Logger);
                SmartOrderManager = new SmartOrderManager(algorithm, context);
                
                // Note: OverlapManager will be initialized later in OnConfigured() after Config is loaded
                
                // Set up pricing engine based on configuration
                SetupSmartPricing();
            }
            
            State = StrategyState.Initializing;
            
            OnInitialize();
            
            State = StrategyState.Ready;
        }

        /// <inheritdoc/>
        public virtual void Initialize(QCAlgorithm algorithm, Dictionary<string, object> parameters)
        {
            Algorithm = algorithm;
            _parameters = parameters ?? new Dictionary<string, object>();
            
            // Logger will be injected via context pattern - no initialization here
            // Logger comes from Main.cs via SetContext method
            
            // Initialize calculators
            StrikeRangeCalculator = new StrikeRangeCalculator(algorithm);
            
            State = StrategyState.Initializing;
            
            OnInitialize();
            
            State = StrategyState.Ready;
        }

        /// <summary>
        /// Set the context logger (injected from Main.cs)
        /// This implements the true context pattern where logger is created once in Main.cs
        /// </summary>
        public void SetContext(object logger)
        {
            Logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }
        
        /// <summary>
        /// Configure the strategy with typed configuration
        /// </summary>
        public virtual void Configure<T>() where T : StrategyConfig, new()
        {
            Config = new T();
            Config.LoadFromParameters(this); // Pass context instead of separate parameters
            
            // Higher-level validation before proceeding
            var validationErrors = Config.Validate();
            if (validationErrors.Length > 0)
            {
                var errorMessage = $"Configuration validation failed:\n{string.Join("\n", validationErrors)}";
                ((dynamic)Logger).Error(errorMessage);
                throw new InvalidOperationException(errorMessage);
            }
            
            ((dynamic)Logger).Debug($"Configuration validation passed for {typeof(T).Name}");
            
            // Initialize entry/exit restrictions with the loaded config
            EntryRestrictions = new EntryRestrictions(Config, Algorithm);
            ExitRestrictions = new ExitRestrictions(Config, Algorithm);
            
            OnConfigured();
        }

        /// <summary>
        /// Called during strategy initialization - override to set up securities, indicators, etc.
        /// </summary>
        public abstract void OnInitialize();

        // Core methods moved to partial class files:
        // - Trading methods: SimpleBaseStrategy.Trading.cs
        // - Tracking methods: SimpleBaseStrategy.Tracking.cs  
        // - Helper methods: SimpleBaseStrategy.Helpers.cs
        protected SecurityPortfolioManager Portfolio => Algorithm.Portfolio;
        protected SecurityManager Securities => Algorithm.Securities;
        protected DateTime Time => Algorithm.Time;
        
        /// <summary>
        /// Called after configuration is loaded
        /// </summary>
        protected virtual void OnConfigured() 
        { 
            // Initialize position overlap manager now that Config is available
            if (Config != null && Config.EnableOverlapPrevention && SmartOrderManager != null && Logger != null)
            {
                var context = new SimpleAlgorithmContext(Algorithm, Logger);
                OverlapManager = new PositionOverlapManager(context);
                SmartOrderManager.SetOverlapManager(OverlapManager);
                
                ((dynamic)Logger).Info($"[{Name}] Position overlap prevention enabled (Mode: {Config.OverlapPreventionMode})");
            }
        }
        
        /// <summary>
        /// Ensures SmartPricing is initialized if needed (called from Main.cs after full initialization)
        /// </summary>
        public void EnsureSmartPricingInitialized()
        {
            ((dynamic)Logger).Info($"EnsureSmartPricingInitialized called. SmartOrderManager null: {SmartOrderManager == null}, Logger null: {Logger == null}, Algorithm null: {Algorithm == null}");
            
            if (SmartOrderManager == null && Logger != null && Algorithm != null)
            {
                ((dynamic)Logger).Info("Creating SmartOrderManager...");
                
                // Create algorithm context adapter
                var context = new SimpleAlgorithmContext(Algorithm, Logger);
                SmartOrderManager = new SmartOrderManager(Algorithm, context);
                
                // Set up pricing engine based on configuration
                SetupSmartPricing();
                
                ((dynamic)Logger).Info("SmartOrderManager initialized after strategy setup");
            }
            else
            {
                ((dynamic)Logger).Warning($"SmartOrderManager initialization skipped - already exists: {SmartOrderManager != null}");
            }
        }

        /// <summary>
        /// Centralized validation to determine if the strategy should execute trades.
        /// Checks common conditions like trading hours, position limits, margin utilization.
        /// Templates can override OnShouldExecuteTrade for strategy-specific validations.
        /// </summary>
        /// <param name="slice">Current market data slice</param>
        /// <param name="blockReason">Reason why trading is blocked (if returning false)</param>
        /// <returns>True if strategy should proceed with trade execution, false otherwise</returns>
        protected virtual bool ShouldExecuteTrade(Slice slice, out string blockReason)
        {
            blockReason = "";
            
            // Skip validation if config not loaded yet
            if (Config == null) return true;
            
            // 1. Trading Hours Check (if configured)
            // Use TimeSpan.Zero to disable this check
            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;
                }
            }
            
            // 2. Position Limit Check (if MaxPositions > 0)
            // Use 0 or negative values to disable this check
            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;
                }
            }
            
            // 3. Margin Utilization Check (configurable threshold)
            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;
                }
            }

            // 4. Central Entry Window Check (optional)
            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;
                }
            }
            
            // 4. Available Cash Check (ensure minimum cash reserves)
            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;
                }
            }
            
            // 5. Call strategy-specific validation hook
            if (!OnShouldExecuteTrade(slice, out var customReason))
            {
                blockReason = customReason;
                return false;
            }
            
            return true;
        }
        
        /// <summary>
        /// Strategy-specific validation hook. Override in templates for custom validations
        /// like daily trade limits, loss limits, or other strategy-specific conditions.
        /// </summary>
        /// <param name="slice">Current market data slice</param>
        /// <param name="blockReason">Reason why trading should be blocked</param>
        /// <returns>True if strategy-specific conditions allow trading</returns>
        protected virtual bool OnShouldExecuteTrade(Slice slice, out string blockReason)
        {
            blockReason = "";
            return true;
        }

        /// <summary>
        /// Check exit conditions for all current positions using ExitRestrictions.
        /// This is called even when new trades are blocked to ensure proper exits.
        /// </summary>
        /// <param name="slice">Current market data slice</param>
        protected virtual void CheckExitConditions(Slice slice)
        {
            if (ExitRestrictions == null) return;
            
            // Check all invested positions for exit conditions
            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}");
                    
                    // Call exit signal handler - templates can override for custom exit logic
                    OnExitSignal(position.Key, reason);
                }
            }
        }
        
        /// <summary>
        /// Handle exit signal for a position. Override in templates for custom exit logic.
        /// Default implementation liquidates the position immediately.
        /// </summary>
        /// <param name="symbol">Symbol to exit</param>
        /// <param name="reason">Reason for the exit</param>
        protected virtual void OnExitSignal(Symbol symbol, string reason)
        {
            try
            {
                // Default behavior - liquidate immediately
                var ticket = Algorithm.Liquidate(symbol: symbol, tag: 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}");
            }
        }

        /// <inheritdoc/>
        public virtual void Execute(Slice slice)
        {
            if (State == StrategyState.Ready)
                State = StrategyState.Running;

            if (State != StrategyState.Running)
            {
                return;
            }

            try
            {
                // Allow strategies to prepare required state before gating (no order placement here)
                OnPreExecuteAlways(slice);
                // Update positions using QC's native Portfolio
                // QC's Portfolio updates automatically
                
                // 1. Always check exit conditions first (even if new trades are blocked)
                CheckExitConditions(slice);
                
                // 2. Check if new trades should be executed
                if (!ShouldExecuteTrade(slice, out var blockReason))
                {
                    // Log blocking reason periodically to avoid spam
                    // Only log on the first minute of each hour to reduce noise
                    if (slice.Time.Minute == 0 && slice.Time.Second == 0)
                    {
                        SmartLog($"[TRADING BLOCKED] {blockReason}");
                    }
                    return;
                }
                
                // 3. Execute strategy-specific logic if validation passes
                OnExecute(slice);
            }
            catch (Exception ex)
            {
                ((dynamic)Logger).Error($"Error during strategy execution: {ex.Message}");
                ((dynamic)Logger).Error($"Stack trace: {ex.StackTrace}");
                OnError(ex, ErrorSeverity.Error, true, "Error during strategy execution");
                throw;
            }
        }

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

        /// <inheritdoc/>
        public virtual bool Validate()
        {
            try
            {
                return OnValidate();
            }
            catch (Exception ex)
            {
                OnError(ex, ErrorSeverity.Warning, true, "Error during strategy validation");
                return false;
            }
        }

        /// <inheritdoc/>
        public virtual Dictionary<string, double> GetPerformanceMetrics()
        {
            var metrics = new Dictionary<string, double>();
            
            try
            {
                // Use QC's native portfolio metrics
                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;
                
                // Add custom metrics from derived strategies
                OnGetPerformanceMetrics(metrics);
            }
            catch (Exception ex)
            {
                OnError(ex, ErrorSeverity.Warning, true, "Error getting performance metrics");
            }

            return metrics;
        }

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

        /// <summary>
        /// Strategy-specific execution logic
        /// </summary>
        protected abstract void OnExecute(Slice slice);

        /// <summary>
        /// Override for custom shutdown logic
        /// </summary>
        protected virtual void OnShutdown() 
        { 
            // Export trade tracking data to logs (like Python main.py export)
            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}");
            }
        }

        /// <summary>
        /// Override for custom validation logic
        /// </summary>
        protected virtual bool OnValidate() => true;

        /// <summary>
        /// Override to add custom performance metrics
        /// </summary>
        protected virtual void OnGetPerformanceMetrics(Dictionary<string, double> metrics) { }

        /// <summary>
        /// Override for custom reset logic
        /// </summary>
        protected virtual void OnReset() { }

        /// <summary>
        /// Handle security changes from universe selection.
        /// Override in templates that use dynamic universe selection.
        /// </summary>
        /// <param name="changes">Security changes from QuantConnect</param>
        public virtual void OnSecuritiesChanged(SecurityChanges changes)
        {
            // Default implementation - do nothing
            // Strategies using universe selection can override
        }

        // Additional helper methods moved to SimpleBaseStrategy.Helpers.cs

        // Trade tracking methods moved to SimpleBaseStrategy.Tracking.cs

        // Option setup helpers moved to SimpleBaseStrategy.Helpers.cs

        // Universe optimization helpers moved to SimpleBaseStrategy.Helpers.cs
    }
}
using System;
using System.Collections.Generic;
using System.Linq;

namespace CoreAlgo.Architecture.Core.Implementations
{
    /// <summary>
    /// Simple trade tracking system that maintains lists of trades in different states
    /// Similar to the Python setupbasestructure.py arrays for position management
    /// </summary>
    public class SimpleTradeTracker
    {
        /// <summary>
        /// All trades ever created (complete history)
        /// </summary>
        public List<TradeRecord> AllTrades { get; set; } = new List<TradeRecord>();
        
        /// <summary>
        /// Currently open trades (filled and active)
        /// </summary>
        public List<TradeRecord> OpenTrades { get; set; } = new List<TradeRecord>();
        
        /// <summary>
        /// Working trades (submitted but not yet filled)
        /// </summary>
        public List<TradeRecord> WorkingTrades { get; set; } = new List<TradeRecord>();
        
        /// <summary>
        /// Closed trades (completed positions)
        /// </summary>
        public List<TradeRecord> ClosedTrades { get; set; } = new List<TradeRecord>();
        
        /// <summary>
        /// Adds a new working trade
        /// </summary>
        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);
        }
        
        /// <summary>
        /// Moves a trade from working to open when it gets filled
        /// </summary>
        public void MarkTradeAsOpen(string orderId, decimal fillPrice, int fillQuantity)
        {
            var trade = WorkingTrades.FirstOrDefault(t => t.OrderId == orderId);
            if (trade != null)
            {
                trade.MarkAsOpen(fillPrice, fillQuantity);
                WorkingTrades.Remove(trade);
                OpenTrades.Add(trade);
            }
        }
        
        /// <summary>
        /// Moves a trade from open to closed when position is closed
        /// </summary>
        public void MarkTradeAsClosed(string orderId, decimal closePrice, decimal pnl)
        {
            var trade = OpenTrades.FirstOrDefault(t => t.OrderId == orderId);
            if (trade != null)
            {
                trade.MarkAsClosed(closePrice, pnl);
                OpenTrades.Remove(trade);
                ClosedTrades.Add(trade);
            }
        }
        
        /// <summary>
        /// Removes a working trade (cancelled order)
        /// </summary>
        public void CancelWorkingTrade(string orderId)
        {
            var trade = WorkingTrades.FirstOrDefault(t => t.OrderId == orderId);
            if (trade != null)
            {
                WorkingTrades.Remove(trade);
                // Keep in AllTrades for audit trail but mark as cancelled
                trade.Status = "Cancelled";
            }
        }
        
        /// <summary>
        /// Exports all trades to QC logs (CSV format in logs instead of file)
        /// QuantConnect doesn't allow file operations, so we use logging instead
        /// </summary>
        public void ExportToLogs(Action<string> logAction)
        {
            try
            {
                // Log header
                logAction?.Invoke($"=== TRADE EXPORT === {TradeRecord.CsvHeader()}");
                
                // Log all trades
                foreach (var trade in AllTrades.OrderBy(t => t.OpenTime))
                {
                    logAction?.Invoke($"TRADE_DATA: {trade.ToCsv()}");
                }
                
                logAction?.Invoke("=== END TRADE EXPORT ===");
            }
            catch (Exception ex)
            {
                // Fail silently - don't break algorithm execution
                logAction?.Invoke($"Failed to export trades to logs: {ex.Message}");
            }
        }
        
        /// <summary>
        /// Gets summary statistics for logging
        /// </summary>
        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}";
        }
        
        /// <summary>
        /// Clears all trade data (for testing)
        /// </summary>
        public void Clear()
        {
            AllTrades.Clear();
            WorkingTrades.Clear();
            OpenTrades.Clear();
            ClosedTrades.Clear();
        }
    }
}
#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>
    /// Event arguments for strategy state changes
    /// </summary>
    public class StrategyStateChangedEventArgs : EventArgs
    {
        /// <summary>
        /// Gets the previous state
        /// </summary>
        public StrategyState PreviousState { get; }

        /// <summary>
        /// Gets the new state
        /// </summary>
        public StrategyState NewState { get; }

        /// <summary>
        /// Gets the timestamp of the state change
        /// </summary>
        public DateTime Timestamp { get; }

        /// <summary>
        /// Gets any additional message about the state change
        /// </summary>
        public string Message { get; }

        /// <summary>
        /// Creates a new instance of StrategyStateChangedEventArgs
        /// </summary>
        public StrategyStateChangedEventArgs(StrategyState previousState, StrategyState newState, string message = null)
        {
            PreviousState = previousState;
            NewState = newState;
            Timestamp = DateTime.UtcNow;
            Message = message;
        }
    }

    /// <summary>
    /// Event arguments for strategy errors
    /// </summary>
    public class StrategyErrorEventArgs : EventArgs
    {
        /// <summary>
        /// Gets the error that occurred
        /// </summary>
        public Exception Error { get; }

        /// <summary>
        /// Gets the error severity
        /// </summary>
        public ErrorSeverity Severity { get; }

        /// <summary>
        /// Gets the timestamp of the error
        /// </summary>
        public DateTime Timestamp { get; }

        /// <summary>
        /// Gets whether the strategy can continue
        /// </summary>
        public bool CanContinue { get; }

        /// <summary>
        /// Gets additional context about the error
        /// </summary>
        public string Context { get; }

        /// <summary>
        /// Creates a new instance of StrategyErrorEventArgs
        /// </summary>
        public StrategyErrorEventArgs(Exception error, ErrorSeverity severity, bool canContinue, string context = null)
        {
            Error = error;
            Severity = severity;
            CanContinue = canContinue;
            Context = context;
            Timestamp = DateTime.UtcNow;
        }
    }

    /// <summary>
    /// Error severity levels
    /// </summary>
    public enum ErrorSeverity
    {
        /// <summary>
        /// Informational message
        /// </summary>
        Info,

        /// <summary>
        /// Warning that doesn't affect execution
        /// </summary>
        Warning,

        /// <summary>
        /// Error that may affect execution
        /// </summary>
        Error,

        /// <summary>
        /// Critical error that stops execution
        /// </summary>
        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
{
    /// <summary>
    /// Prevents overlapping strike prices that could create margin model conflicts
    /// Especially important for Iron Condors and complex multi-leg strategies
    /// </summary>
    public class StrikeOverlapRule : IPositionOverlapRule
    {
        private readonly IAlgorithmContext _context;
        private readonly object _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
            {
                // Only validate option positions
                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;

                // Get existing option positions on same underlying
                var existingOptions = existingPositions
                    .Where(p => p.Value.Invested &&
                               p.Key.SecurityType == SecurityType.Option &&
                               p.Key.Underlying == underlying)
                    .ToList();

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

                // Only check for exact duplicates - allow all other combinations
                return ValidateExactDuplicates(
                    proposedStrike, 
                    proposedExpiry,
                    proposedRight,
                    quantity,
                    existingOptions);
            }
            catch (System.Exception ex)
            {
                ((dynamic)_logger).Error($"[{RuleName}] Error validating strike overlaps: {ex.Message}");
                return ValidationResult.Error($"Strike validation error: {ex.Message}");
            }
        }

        /// <summary>
        /// Validates for exact duplicate positions only
        /// </summary>
        private ValidationResult ValidateExactDuplicates(
            decimal proposedStrike,
            System.DateTime proposedExpiry,
            OptionRight proposedRight,
            decimal quantity,
            List<KeyValuePair<Symbol, SecurityHolding>> existingOptions)
        {
            // Check for exact strike/expiry/right matches
            var exactMatch = existingOptions.FirstOrDefault(p => 
                p.Key.ID.StrikePrice == proposedStrike &&
                p.Key.ID.Date == proposedExpiry &&
                p.Key.ID.OptionRight == proposedRight);

            if (exactMatch.Key != null)
            {
                // Allow position modifications (different quantities)
                if (IsPositionModification(quantity, exactMatch.Value.Quantity))
                {
                    ((dynamic)_logger).Debug($"[{RuleName}] Allowing position modification for {proposedStrike} {proposedRight} {proposedExpiry:yyyy-MM-dd}");
                    return ValidationResult.Success();
                }

                // Block exact duplicates (same quantity, same position)
                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();
        }


        /// <summary>
        /// Determines if this is a position modification rather than a new position
        /// </summary>
        private bool IsPositionModification(decimal proposedQuantity, decimal existingQuantity)
        {
            // Allow all position modifications:
            // - Same sign = position increase (e.g., double the position)
            // - Different sign = position reduction/close (e.g., sell 50%)
            // - Different quantity = any modification is allowed
            return Math.Abs(proposedQuantity) != Math.Abs(existingQuantity);
        }
    }
}
using System;

namespace CoreAlgo.Architecture.Core.Implementations
{
    /// <summary>
    /// Simple trade record for tracking individual trades through their lifecycle
    /// </summary>
    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
        public decimal PnL { get; set; }
        public string OrderTag { get; set; } = string.Empty;
        
        /// <summary>
        /// Creates a new trade record
        /// </summary>
        public TradeRecord(string orderId, string symbol, string strategy = "", string orderTag = "")
        {
            OrderId = orderId;
            Symbol = symbol;
            Strategy = strategy;
            OrderTag = orderTag;
            OpenTime = DateTime.UtcNow;
        }
        
        /// <summary>
        /// Marks trade as open with fill details
        /// </summary>
        public void MarkAsOpen(decimal fillPrice, int fillQuantity)
        {
            Status = "Open";
            OpenPrice = fillPrice;
            Quantity = fillQuantity;
        }
        
        /// <summary>
        /// Marks trade as closed with close details
        /// </summary>
        public void MarkAsClosed(decimal closePrice, decimal pnl)
        {
            Status = "Closed";
            CloseTime = DateTime.UtcNow;
            ClosePrice = closePrice;
            PnL = pnl;
        }
        
        /// <summary>
        /// Returns CSV header
        /// </summary>
        public static string CsvHeader()
        {
            return "OrderId,Symbol,Strategy,OrderTag,Status,OpenTime,CloseTime,OpenPrice,ClosePrice,Quantity,PnL";
        }
        
        /// <summary>
        /// Returns trade data as CSV row
        /// </summary>
        public string ToCsv()
        {
            return $"{OrderId},{Symbol},{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;
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 object _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
                
                ((dynamic)_logger).Debug($"[{RuleName}] Allowed: {proposedUnderlying.Value} has {optionPositions} options, {stockPositions} stock positions");
                return ValidationResult.Success();
            }
            catch (System.Exception ex)
            {
                ((dynamic)_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
        }

    }
}
#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 result of a validation operation
    /// </summary>
    public class ValidationResult
    {
        /// <summary>
        /// Gets whether the validation passed
        /// </summary>
        public bool IsValid { get; private set; }

        /// <summary>
        /// Gets the validation errors
        /// </summary>
        public List<ValidationError> Errors { get; private set; }

        /// <summary>
        /// Gets the validation warnings
        /// </summary>
        public List<ValidationWarning> Warnings { get; private set; }

        /// <summary>
        /// Gets the first error or warning message
        /// </summary>
        public string Message
        {
            get
            {
                if (Errors?.Any() == true)
                    return Errors.First().Message;
                if (Warnings?.Any() == true)
                    return Warnings.First().Message;
                return string.Empty;
            }
        }

        /// <summary>
        /// Creates a successful validation result
        /// </summary>
        public static ValidationResult Success()
        {
            return new ValidationResult
            {
                IsValid = true,
                Errors = new List<ValidationError>(),
                Warnings = new List<ValidationWarning>()
            };
        }

        /// <summary>
        /// Creates a failed validation result with errors
        /// </summary>
        public static ValidationResult Failure(params ValidationError[] errors)
        {
            return new ValidationResult
            {
                IsValid = false,
                Errors = new List<ValidationError>(errors),
                Warnings = new List<ValidationWarning>()
            };
        }

        /// <summary>
        /// Creates a blocked validation result with message
        /// </summary>
        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;
        }

        /// <summary>
        /// Creates an error validation result with message
        /// </summary>
        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;
        }

        /// <summary>
        /// Creates a warning validation result with message
        /// </summary>
        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;
        }

        /// <summary>
        /// Adds an error to the validation result
        /// </summary>
        public void AddError(string code, string message, string field = null)
        {
            IsValid = false;
            Errors.Add(new ValidationError { Code = code, Message = message, Field = field });
        }

        /// <summary>
        /// Adds a warning to the validation result
        /// </summary>
        public void AddWarning(string code, string message, string field = null)
        {
            Warnings.Add(new ValidationWarning { Code = code, Message = message, Field = field });
        }

        /// <summary>
        /// Merges another validation result into this one
        /// </summary>
        public void Merge(ValidationResult other)
        {
            if (!other.IsValid)
            {
                IsValid = false;
            }
            Errors.AddRange(other.Errors);
            Warnings.AddRange(other.Warnings);
        }

        /// <summary>
        /// Creates a new instance of ValidationResult
        /// </summary>
        public ValidationResult()
        {
            Errors = new List<ValidationError>();
            Warnings = new List<ValidationWarning>();
            IsValid = true;
        }
    }

    /// <summary>
    /// Represents a validation error
    /// </summary>
    public class ValidationError
    {
        /// <summary>
        /// Gets or sets the error code
        /// </summary>
        public string Code { get; set; }

        /// <summary>
        /// Gets or sets the error message
        /// </summary>
        public string Message { get; set; }

        /// <summary>
        /// Gets or sets the field that caused the error
        /// </summary>
        public string Field { get; set; }
    }

    /// <summary>
    /// Represents a validation warning
    /// </summary>
    public class ValidationWarning
    {
        /// <summary>
        /// Gets or sets the warning code
        /// </summary>
        public string Code { get; set; }

        /// <summary>
        /// Gets or sets the warning message
        /// </summary>
        public string Message { get; set; }

        /// <summary>
        /// Gets or sets the field that caused the warning
        /// </summary>
        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.
        /// Uses object type to avoid circular dependency with Main.cs CoreAlgo class
        /// </summary>
        object 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 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);
    }
}
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; }
    }
}
#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;
    using CoreAlgo.Architecture.Core.Implementations;
#endregion

namespace CoreAlgo.Architecture.Core.Interfaces
{
    /// <summary>
    /// Base interface for all trading strategies
    /// </summary>
    public interface IStrategy
    {
        /// <summary>
        /// Gets the unique name of the strategy
        /// </summary>
        string Name { get; }

        /// <summary>
        /// Gets the description of the strategy
        /// </summary>
        string Description { get; }

        /// <summary>
        /// Gets the version of the strategy
        /// </summary>
        string Version { get; }

        /// <summary>
        /// Gets the current state of the strategy
        /// </summary>
        StrategyState State { get; }

        /// <summary>
        /// Gets the parameters for this strategy
        /// </summary>
        Dictionary<string, object> Parameters { get; }

        /// <summary>
        /// Initializes the strategy with the given algorithm instance
        /// </summary>
        /// <param name="algorithm">The QCAlgorithm instance</param>
        void Initialize(QCAlgorithm algorithm);

        /// <summary>
        /// Initializes the strategy with parameters
        /// </summary>
        /// <param name="algorithm">The QCAlgorithm instance</param>
        /// <param name="parameters">Strategy-specific parameters</param>
        void Initialize(QCAlgorithm algorithm, Dictionary<string, object> parameters);

        /// <summary>
        /// Executes the strategy logic for the given data slice
        /// </summary>
        /// <param name="slice">The current data slice</param>
        void Execute(Slice slice);

        /// <summary>
        /// Called when the strategy is being shut down
        /// </summary>
        void Shutdown();

        /// <summary>
        /// Validates the strategy configuration
        /// </summary>
        /// <returns>True if the strategy is valid</returns>
        bool Validate();

        /// <summary>
        /// Gets performance metrics for the strategy
        /// </summary>
        /// <returns>Dictionary of performance metrics</returns>
        Dictionary<string, double> GetPerformanceMetrics();

        /// <summary>
        /// Resets the strategy to its initial state
        /// </summary>
        void Reset();

        /// <summary>
        /// Event raised when the strategy state changes
        /// </summary>
        event EventHandler<StrategyStateChangedEventArgs> StateChanged;

        /// <summary>
        /// Event raised when the strategy encounters an error
        /// </summary>
        event EventHandler<StrategyErrorEventArgs> ErrorOccurred;
    }
}
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.Collections.Generic;
using CoreAlgo.Architecture.Core.Attributes;

namespace CoreAlgo.Architecture.Core.Models
{
    /// <summary>
    /// Configuration for Calendar Spread Forward Factor strategy.
    /// Trades long call calendar spreads when front-month IV exceeds forward IV by threshold.
    /// Based on term structure mispricing edge: FF = (IVnear / ForwardVol) - 1
    /// where ForwardVol is the implied forward volatility between near and far maturities.
    /// </summary>
    public class CalendarSpreadFFConfig : StrategyConfig
    {
        // Universe Selection (use 'new' to explicitly hide base properties)
        [StrategyParameter("Use dynamic universe selection instead of manual symbols", true)]
        public new bool UseUniverseSelection { get; set; } = true;

        [StrategyParameter("Maximum number of candidates in universe", 50)]
        public int UniverseMaxCandidates { get; set; } = 100;

        [StrategyParameter("Minimum 20-day average dollar volume for liquidity", 50_000_000)]
        public decimal MinDollarVolume { get; set; } = 50_000_000m;

        [StrategyParameter("Minimum market cap for stability", 5_000_000_000)]
        public decimal MinMarketCap { get; set; } = 5_000_000_000m;

        [StrategyParameter("Minimum stock price to avoid penny stocks", 10)]
        public decimal PriceFloor { get; set; } = 10m;

        [StrategyParameter("Minutes between trades on same symbol", 15)]
        public int MinutesBetweenTrades { get; set; } = 15;

        // Forward Factor (FF) Threshold
        [StrategyParameter("Forward Factor threshold: (IVnear / ForwardVol) - 1 >= this value", 0.20)]
        public decimal FFThreshold { get; set; } = 0.20m;

        // Note: DTE pair is now fixed at 60-90 with ±5 day buffer per dataset requirements
        // This aligns with research showing 60-90 DTE gave best Sharpe and CAGR

        // Exit Management
        [StrategyParameter("Minutes before front expiry to exit position", 15)]
        public int ExitMinutesBeforeFrontExpiry { get; set; } = 15;

        // Options Trading
        [StrategyParameter("Trade options (must be true for calendar spreads)", true)]
        public bool TradeOptions { get; set; } = true;

        // Debug Logging
        [StrategyParameter("Enable verbose debug logging", true)]
        public bool DebugLogging { get; set; } = false;

        // Portfolio Kill-Switch (prevents PV<=0)
        [StrategyParameter("Maximum drawdown before kill-switch (0.4 = 40%)", 0.40)]
        public decimal MaxDrawdownPct { get; set; } = 0.40m;

        [StrategyParameter("Hard stop portfolio value (absolute floor)", 25000)]
        public decimal HardStopValue { get; set; } = 25000m;

        public CalendarSpreadFFConfig()
        {
            // Defaults aligned with plan (SPX weekly options)
            // UnderlyingSymbol = "SPX";
            // Symbols = new[] { "SPX" };

            // Override base class defaults for this strategy
            UseUniverseSelection = true; // This strategy is all about universe selection

            // Disable regular-hours gating (we enter at night)
            TradingStartTime = TimeSpan.Zero;
            TradingEndTime = TimeSpan.Zero;

            StartDate = new DateTime(2012, 1, 1);
			EndDate = new DateTime(2013, 1, 1);

            // Set high base cap; enforce spread-level caps in template
            MaxPositions = 1000; // Disable position limit

            // Disable generic profit/stop logic (hold-to-expiration behavior)
            ProfitTarget = 0m;
            StopLoss = 0m;

            UnderlyingResolution = "Hour";
            
            // Tighten default allocation to reduce risk
            AllocationPerPosition = 0.02m; // Reduced from 0.04 to 0.02
            AccountSize = 1000000m;
        }

        public string GetConfigurationSummary()
        {
            var lines = new List<string>
            {
                "=== Calendar Spread FF Configuration ===",
                $"Universe: {(UseUniverseSelection ? $"Dynamic (top {UniverseMaxCandidates} by volume)" : $"Manual")}",
                $"Liquidity Filters: ${MinDollarVolume:N0} dollar volume, ${MinMarketCap:N0} market cap, ${PriceFloor} min price",
                $"Position Sizing: {AllocationPerPosition:P1} allocation per position, max {MaxPositions} positions",
                $"FF Threshold: {FFThreshold:P0} (front IV must exceed forward IV by this %)",
                $"DTE Pair: 60-90 (±1 day buffer) - optimal per research",
                $"Liquidity Gate: 20-day avg volume ≥ 10,000 or OpenInterest ≥ 5,000",
                $"Exit: {ExitMinutesBeforeFrontExpiry} minutes before front expiry",
                $"Safety: Max drawdown {MaxDrawdownPct:P0}, hard stop ${HardStopValue:N0}",
                $"Resolution: Hour (underlyings), Minute (2 option contracts per position)",
                $"Debug Logging: {DebugLogging}"
            };

            return string.Join("\n", lines);
        }
    }
}

using System;
using System.Linq;
using QuantConnect;
using CoreAlgo.Architecture.Core.Attributes;

namespace CoreAlgo.Architecture.Core.Models
{
    /// <summary>
    /// Base configuration class for futures trading strategies.
    /// Provides common parameters for futures contract management, rollover handling,
    /// and continuous contract settings.
    /// </summary>
    public abstract class FuturesConfig : StrategyConfig
    {
        /// <summary>
        /// The futures symbol to trade (e.g., "ES", "GC", "CL")
        /// </summary>
        [StrategyParameter("Futures symbol", "ES")]
        public string FutureSymbol { get; set; } = "ES";

        /// <summary>
        /// Number of days before contract expiration to initiate rollover
        /// Only relevant for manual contract management (not continuous contracts)
        /// </summary>
        [StrategyParameter("Rollover days to expiry", 5)]
        public int ContractRolloverDays { get; set; } = 5;

        /// <summary>
        /// Whether to use continuous contracts (recommended for backtesting)
        /// When true, QuantConnect automatically handles contract rollovers
        /// </summary>
        [StrategyParameter("Use continuous contracts", true)]
        public bool UseContinuousContract { get; set; } = true;

        /// <summary>
        /// Contract depth offset (0 = front month, 1 = next month, etc.)
        /// Used when adding the futures contract
        /// </summary>
        [StrategyParameter("Contract depth offset", 0)]
        public int ContractDepthOffset { get; set; } = 0;

        /// <summary>
        /// Position sizing multiplier relative to account equity
        /// For futures, this is combined with contract multiplier
        /// </summary>
        [StrategyParameter("Position size multiplier", 1.0)]
        public decimal PositionSizeMultiplier { get; set; } = 1.0m;

        /// <summary>
        /// Maximum number of contracts to trade (risk management)
        /// </summary>
        [StrategyParameter("Maximum contracts per trade", 5)]
        public int MaxContractsPerTrade { get; set; } = 5;

        /// <summary>
        /// Data resolution for futures data subscription
        /// </summary>
        [StrategyParameter("Data resolution", "Minute")]
        public string DataResolution { get; set; } = "Minute";

        /// <summary>
        /// Parse the data resolution string to Resolution enum
        /// </summary>
        /// <returns>Resolution enum value</returns>
        public Resolution GetDataResolution()
        {
            if (Enum.TryParse<Resolution>(DataResolution, true, out var resolution))
            {
                return resolution;
            }
            return Resolution.Minute; // Default fallback
        }

        /// <summary>
        /// Get the futures category based on the symbol
        /// Useful for strategy-specific logic
        /// </summary>
        /// <returns>Futures category string</returns>
        public string GetFuturesCategory()
        {
            var symbol = FutureSymbol?.ToUpperInvariant();
            
            // Equity Index Futures
            if (new[] { "ES", "NQ", "YM", "RTY", "EMD", "NKD" }.Contains(symbol))
                return "equity";
                
            // Energy Futures
            if (new[] { "CL", "NG", "RB", "HO", "BZ" }.Contains(symbol))
                return "energy";
                
            // Metal Futures
            if (new[] { "GC", "SI", "HG", "PA", "PL" }.Contains(symbol))
                return "metals";
                
            // Agricultural Futures
            if (new[] { "ZC", "ZS", "ZW", "ZM", "ZL", "KC", "CT", "SB", "CC", "OJ" }.Contains(symbol))
                return "agricultural";
                
            // Bond/Interest Rate Futures
            if (new[] { "ZB", "ZN", "ZF", "TU", "UB", "ED", "SR1", "SR3" }.Contains(symbol))
                return "bonds";
                
            // Currency Futures
            if (new[] { "6E", "6J", "6B", "6S", "6C", "6A", "6N", "6M", "E7", "J7" }.Contains(symbol))
                return "currency";
                
            // Volatility Futures
            if (new[] { "VX" }.Contains(symbol))
                return "volatility";
                
            // Crypto Futures
            if (new[] { "BTC", "ETH" }.Contains(symbol))
                return "crypto";
                
            return "unknown";
        }

        /// <summary>
        /// Optimization parameters for futures trading strategies.
        /// Focuses on core futures parameters and contract management
        /// </summary>
        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
{
    /// <summary>
    /// Enhanced strategy configuration for multi-asset options trading.
    /// Extends StrategyConfig with asset-specific parameter adaptation and validation.
    /// </summary>
    public class MultiAssetConfig : StrategyConfig
    {
        /// <summary>
        /// Enable automatic asset-specific parameter adaptation
        /// When true, delta ranges, position limits, and strike widths are automatically
        /// adjusted based on the underlying asset characteristics
        /// </summary>
        [StrategyParameter("EnableAssetAdaptation", true)]
        public bool EnableAssetAdaptation { get; set; } = true;

        /// <summary>
        /// Maximum number of different assets to trade simultaneously
        /// Helps control correlation risk when trading multiple underlyings
        /// </summary>
        [StrategyParameter("MaxAssets", 3)]
        public int MaxAssets { get; set; } = 3;

        /// <summary>
        /// Strike width multiplier for option selection
        /// Base value that gets adjusted per asset (e.g., SPX gets wider, AAPL gets narrower)
        /// </summary>
        [StrategyParameter("BaseStrikeWidth", 0.05)]
        public decimal BaseStrikeWidth { get; set; } = 0.05m; // 5% base strike width

        /// <summary>
        /// Whether to use equal allocation across all assets or asset-specific allocation
        /// </summary>
        [StrategyParameter("UseEqualAllocation", false)]
        public bool UseEqualAllocation { get; set; } = false;

        /// <summary>
        /// Maximum correlation threshold between assets (0.0 to 1.0)
        /// Prevents trading highly correlated assets simultaneously
        /// </summary>
        [StrategyParameter("MaxCorrelation", 0.7)]
        public decimal MaxCorrelation { get; set; } = 0.7m;

        /// <summary>
        /// Minimum asset price for inclusion (helps filter penny stocks)
        /// </summary>
        [StrategyParameter("MinAssetPrice", 10.0)]
        public decimal MinAssetPrice { get; set; } = 10.0m;

        /// <summary>
        /// Load parameters and apply asset-specific adaptations
        /// </summary>
        public override void LoadFromParameters(IAlgorithmContext context)
        {
            // Load base parameters first
            base.LoadFromParameters(context);

            // Apply asset-specific adaptations if enabled
            if (EnableAssetAdaptation && Symbols != null && Symbols.Length > 0)
            {
                ApplyAssetAdaptations(context);
            }

            // Validate multi-asset specific constraints
            ValidateMultiAssetConstraints(context);
        }

        /// <summary>
        /// Apply asset-specific parameter adaptations based on the selected symbols
        /// </summary>
        private void ApplyAssetAdaptations(IAlgorithmContext context)
        {
            ((dynamic)context.Logger).Info("MultiAssetConfig: Applying asset-specific adaptations");

            // For multi-asset strategies, we'll adapt based on the first (primary) symbol
            // or calculate weighted averages for portfolio-level parameters
            var primarySymbol = Symbols[0];
            
            // Get asset-specific delta targets
            var (adaptedDeltaMin, adaptedDeltaMax) = MultiAssetHelper.GetAssetDeltaTargets(
                primarySymbol, EntryDeltaMin, EntryDeltaMax);
                
            EntryDeltaMin = adaptedDeltaMin;
            EntryDeltaMax = adaptedDeltaMax;

            // Get asset-specific position limits
            var (minPositions, maxPositions, recommendedAllocation) = MultiAssetHelper.GetAssetPositionLimits(
                primarySymbol, StartingCash);

            // Update max positions if not using equal allocation
            if (!UseEqualAllocation)
            {
                MaxPositions = Math.Min(MaxPositions, maxPositions);
                AllocationPerPosition = recommendedAllocation;
            }

            ((dynamic)context.Logger).Info($"MultiAssetConfig: Adapted for {primarySymbol} - " +
                               $"Delta: {EntryDeltaMin:F2}-{EntryDeltaMax:F2}, " +
                               $"MaxPos: {MaxPositions}, " +
                               $"Allocation: {AllocationPerPosition:P1}");

            // Log asset profiles for all symbols
            foreach (var symbol in Symbols)
            {
                var profile = MultiAssetHelper.GetAssetProfile(symbol);
                if (profile != null)
                {
                    ((dynamic)context.Logger).Info($"MultiAssetConfig: {symbol} profile - {profile}");
                }
                else
                {
                    ((dynamic)context.Logger).Info($"MultiAssetConfig: {symbol} - using default profile (unknown asset)");
                }
            }
        }

        /// <summary>
        /// Validate multi-asset specific constraints
        /// </summary>
        private void ValidateMultiAssetConstraints(IAlgorithmContext context)
        {
            // Check asset count limit
            if (Symbols.Length > MaxAssets)
            {
                ((dynamic)context.Logger).Error($"MultiAssetConfig: Too many assets ({Symbols.Length}), maximum allowed is {MaxAssets}");
            }

            // Check for valid options support
            var unsupportedSymbols = Symbols.Where(symbol => !MultiAssetHelper.HasLiquidOptions(symbol)).ToList();
            if (unsupportedSymbols.Any())
            {
                ((dynamic)context.Logger).Warning($"MultiAssetConfig: Warning - symbols may have limited options liquidity: {string.Join(", ", unsupportedSymbols)}");
            }

            // Log configuration summary
            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)
            {
                ((dynamic)context.Logger).Info(message);
            }
        }

        /// <summary>
        /// Get asset-specific strike width for a symbol
        /// </summary>
        public decimal GetStrikeWidthForAsset(string symbol)
        {
            return MultiAssetHelper.GetAssetStrikeWidth(symbol, BaseStrikeWidth);
        }

        /// <summary>
        /// Get position limits for a specific asset
        /// </summary>
        public (int MinPositions, int MaxPositions, decimal RecommendedAllocation) GetPositionLimitsForAsset(string symbol)
        {
            return MultiAssetHelper.GetAssetPositionLimits(symbol, StartingCash);
        }

        /// <summary>
        /// Get delta targets for a specific asset
        /// </summary>
        public (decimal DeltaMin, decimal DeltaMax) GetDeltaTargetsForAsset(string symbol)
        {
            return MultiAssetHelper.GetAssetDeltaTargets(symbol, EntryDeltaMin, EntryDeltaMax);
        }

        /// <summary>
        /// Validate the multi-asset configuration
        /// </summary>
        public override string[] Validate()
        {
            var errors = base.Validate().ToList();

            // Multi-asset specific validations
            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");
            }

            // Asset-specific validation against account size and margin requirements
            if (Symbols != null && Symbols.Length > 0)
            {
                foreach (var symbol in Symbols)
                {
                    var profile = MultiAssetHelper.GetAssetProfile(symbol);
                    if (profile != null)
                    {
                        // Check if account size meets minimum requirements for this asset
                        if (AccountSize < profile.MinAccountSize)
                        {
                            errors.Add($"Account size ${AccountSize:F0} below minimum ${profile.MinAccountSize:F0} required for {symbol}");
                        }
                        
                        // Check if allocation is reasonable for this asset type
                        if (AssetManager.IsIndex(symbol) && AllocationPerPosition > 0.1m)
                        {
                            errors.Add($"Allocation {AllocationPerPosition:P0} too high for index option {symbol} (recommended max 10%)");
                        }
                    }
                    else
                    {
                        // Warning for unknown assets
                        errors.Add($"Unknown asset {symbol} - option liquidity not verified");
                    }
                }
            }

            return errors.ToArray();
        }

        /// <summary>
        /// Key parameters for Multi-Asset strategy optimization
        /// Focuses on asset selection, adaptation, and allocation management
        /// </summary>
        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
{
    /// <summary>
    /// Base class for all strategy configurations with QC GetParameter() integration
    /// </summary>
    public abstract class StrategyConfig
    {
        /// <summary>
        /// Underlying symbol to trade (e.g., "SPY", "QQQ", "AAPL")
        /// </summary>
        public string UnderlyingSymbol { get; set; } = "SPY";

        /// <summary>
        /// Strategy start date
        /// Configurable via StartDate parameter in config.json
        /// </summary>
        [StrategyParameter("StartDate", "2023-01-01")]
        public DateTime StartDate { get; set; } = new DateTime(2023, 1, 1);

        /// <summary>
        /// Strategy end date
        /// Configurable via EndDate parameter in config.json
        /// </summary>
        [StrategyParameter("EndDate", "2023-12-31")]
        public DateTime EndDate { get; set; } = new DateTime(2023, 12, 31);

        /// <summary>
        /// Starting cash amount
        /// </summary>
        public decimal StartingCash { get; set; } = 100000m;

        /// <summary>
        /// Account size for position sizing and margin calculations (configurable via parameters)
        /// </summary>
        [StrategyParameter("AccountSize", 100000)]
        public virtual decimal AccountSize { get; set; } = 100000m;

        // ============================================================================
        // POSITION OVERLAP PREVENTION CONFIGURATION (Task 19)
        // ============================================================================

        /// <summary>
        /// Enable position overlap prevention system
        /// </summary>
        [StrategyParameter("EnableOverlapPrevention", true)]
        public bool EnableOverlapPrevention { get; set; } = true;

        /// <summary>
        /// Position overlap prevention mode: Block, Warn, or Log
        /// Block: Prevents orders that would create overlaps
        /// Warn: Allows orders but logs warnings
        /// Log: Only logs overlap detection for analysis
        /// </summary>
        [StrategyParameter("OverlapPreventionMode", "Block")]
        public string OverlapPreventionMode { get; set; } = "Block";

        /// <summary>
        /// Allow multiple strategies on same underlying (advanced users only)
        /// </summary>
        [StrategyParameter("AllowMultiStrategyUnderlying", false)]
        public bool AllowSameUnderlyingMultiStrategy { get; set; } = false;

        /// <summary>
        /// Maximum number of active positions per underlying asset
        /// </summary>
        [StrategyParameter("MaxPositionsPerUnderlying", 1)]
        public int MaxPositionsPerUnderlying { get; set; } = 1;

        /// <summary>
        /// Maximum number of active combo orders per underlying asset
        /// Combo orders are multi-leg strategies like Iron Condors, Spreads, etc.
        /// </summary>
        [StrategyParameter("MaxPositionsPerCombo", 2)]
        public int MaxPositionsPerCombo { get; set; } = 2;

        /// <summary>
        /// Expected leg count for this strategy's combo orders
        /// Used for structural analysis: 2=Spread, 4=Iron Condor, etc.
        /// Set to 0 for single-leg strategies or variable leg counts
        /// </summary>
        [StrategyParameter("ComboOrderLegCount", 0)]
        public int ComboOrderLegCount { get; set; } = 0;

        /// <summary>
        /// Minimum strike distance for options overlap detection (in dollars)
        /// </summary>
        [StrategyParameter("MinimumStrikeDistance", 5.0)]
        public decimal MinimumStrikeDistance { get; set; } = 5.0m;

        // ============================================================================
        // ENHANCED STRATEGY PARAMETERS (Added for Task 2)
        // ============================================================================

        /// <summary>
        /// Array of symbols to trade (supports multi-asset strategies)
        /// Default to single SPY for backward compatibility
        /// </summary>
        [StrategyParameter("Symbols", "SPY")]
        public string[] Symbols { get; set; } = new[] { "SPY" };

        /// <summary>
        /// Enable universe selection instead of manual symbol list
        /// </summary>
        [StrategyParameter("UseUniverseSelection", false)]
        public bool UseUniverseSelection { get; set; } = false;

        /// <summary>
        /// Minimum delta for option entry (for options strategies)
        /// </summary>
        [StrategyParameter("EntryDeltaMin", 0.25)]
        public decimal EntryDeltaMin { get; set; } = 0.25m;

        /// <summary>
        /// Maximum delta for option entry (for options strategies)
        /// </summary>
        [StrategyParameter("EntryDeltaMax", 0.35)]
        public decimal EntryDeltaMax { get; set; } = 0.35m;

        /// <summary>
        /// Delta threshold for option exit (for options strategies)
        /// </summary>
        [StrategyParameter("ExitDelta", 0.10)]
        public decimal ExitDelta { get; set; } = 0.10m;

        /// <summary>
        /// Allocation percentage per position (e.g., 0.1 = 10% of portfolio per position)
        /// </summary>
        [StrategyParameter("AllocationPerPosition", 0.1)]
        public decimal AllocationPerPosition { get; set; } = 0.1m;

        /// <summary>
        /// Maximum number of concurrent positions
        /// </summary>
        [StrategyParameter("MaxPositions", 5)]
        public int MaxPositions { get; set; } = 5;

        /// <summary>
        /// Trading start time (market hours restriction)
        /// </summary>
        [StrategyParameter("TradingStartTime", "09:30:00")]
        public TimeSpan TradingStartTime { get; set; } = new TimeSpan(9, 30, 0);

        /// <summary>
        /// Trading end time (market hours restriction)
        /// </summary>
        [StrategyParameter("TradingEndTime", "15:30:00")]
        public TimeSpan TradingEndTime { get; set; } = new TimeSpan(15, 30, 0);

        /// <summary>
        /// Enable a central entry time window. When enabled, entries are only allowed between EntryWindowStart and EntryWindowEnd
        /// </summary>
        [StrategyParameter("UseEntryTimeWindow", false)]
        public bool UseEntryTimeWindow { get; set; } = false;

        /// <summary>
        /// Earliest time-of-day to allow new entries (ET). Ignored when UseEntryTimeWindow is false
        /// </summary>
        [StrategyParameter("EntryWindowStart", "00:00:00")]
        public TimeSpan EntryWindowStart { get; set; } = TimeSpan.Zero;

        /// <summary>
        /// Latest time-of-day to allow new entries (ET). Ignored when UseEntryTimeWindow is false
        /// </summary>
        [StrategyParameter("EntryWindowEnd", "23:59:59")]
        public TimeSpan EntryWindowEnd { get; set; } = new TimeSpan(23, 59, 59);

        /// <summary>
        /// Profit target as percentage (e.g., 0.5 = 50% profit target)
        /// Set to 0 to disable profit target checks (for strategies using custom exits)
        /// </summary>
        [StrategyParameter("ProfitTarget", 0.5)]
        public decimal ProfitTarget { get; set; } = 0.5m;

        /// <summary>
        /// Stop loss as percentage (e.g., -0.5 = 50% stop loss)
        /// Set to 0 to disable stop loss checks (for strategies using custom exits)
        /// </summary>
        [StrategyParameter("StopLoss", -0.5)]
        public decimal StopLoss { get; set; } = -0.5m;

        /// <summary>
        /// Maximum days to hold a position
        /// </summary>
        [StrategyParameter("MaxDaysInTrade", 30)]
        public int MaxDaysInTrade { get; set; } = 30;

        /// <summary>
        /// Minimum implied volatility for entry (options strategies)
        /// </summary>
        [StrategyParameter("MinImpliedVolatility", 0.15)]
        public decimal MinImpliedVolatility { get; set; } = 0.15m;

        // ============================================================================
        // SMARTPRICING EXECUTION PARAMETERS (Added for Task 12)
        // ============================================================================

        /// <summary>
        /// SmartPricing execution mode for improved fill rates on options spreads
        /// Supported values: "Normal", "Fast", "Patient", "Off"
        /// </summary>
        [StrategyParameter("SmartPricingMode", "Normal")]
        public string SmartPricingMode { get; set; } = "Normal";

        /// <summary>
        /// Maximum acceptable net spread width for combo orders before using market orders
        /// Prevents smart pricing on combos with excessive spread costs
        /// </summary>
        [StrategyParameter("ComboMaxNetSpreadWidth", 5.0)]
        public decimal ComboMaxNetSpreadWidth { get; set; } = 5.0m;

        /// <summary>
        /// SmartPricing mode specifically for combo orders (can be different from single-leg)
        /// If not specified, uses the main SmartPricingMode. Supported values: "Normal", "Fast", "Patient", "Off"
        /// </summary>
        [StrategyParameter("ComboSmartPricingMode", "")]
        public string ComboSmartPricingMode { get; set; } = "";

        /// <summary>
        /// Enable smart pricing for combo orders (multi-leg strategies)
        /// When false, combo orders use basic market execution regardless of SmartPricingMode
        /// </summary>
        [StrategyParameter("EnableComboSmartPricing", true)]
        public bool EnableComboSmartPricing { get; set; } = true;

        // ============================================================================
        // OPTIMIZATION PARAMETERS CONFIGURATION
        // ============================================================================

        /// <summary>
        /// Parameters to include in config.json for QuantConnect optimization
        /// These are the key parameters that should be exposed for backtesting optimization
        /// All other StrategyParameter attributes remain available for internal configuration
        /// </summary>
        /// <summary>
        /// Preferred underlying data resolution for strategies that need to downsample equities
        /// Defaults to Minute to preserve existing behaviour
        /// </summary>
        [StrategyParameter("DataResolution", "Minute")]
        public string UnderlyingResolution { get; set; } = "Minute";

        /// <summary>
        /// Parse the UnderlyingResolution string into Resolution enum (fallback Minute)
        /// </summary>
        public virtual Resolution GetUnderlyingResolution()
        {
            return UnderlyingResolution?.ToUpperInvariant() switch
            {
                "SECOND" => Resolution.Second,
                "MINUTE" => Resolution.Minute,
                "HOUR" => Resolution.Hour,
                "DAILY" => Resolution.Daily,
                _ => Resolution.Minute
            };
        }

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

        /// <summary>
        /// Load parameters from QC's GetParameter() method using context pattern
        /// </summary>
        public virtual void LoadFromParameters(IAlgorithmContext context)
        {
            var properties = GetType().GetProperties();
            
            foreach (var property in properties)
            {
                var attribute = property.GetCustomAttribute<StrategyParameterAttribute>();
                if (attribute != null)
                {
                    try
                    {
                        var parameterName = attribute.Name;
                        var defaultValue = property.GetValue(this)?.ToString() ?? attribute.DefaultValue?.ToString() ?? "";
                        
                        var parameterValue = context.Algorithm.GetParameter(parameterName, defaultValue);
                        
                        // Use context logger for debugging
                        ((dynamic)context.Logger).Debug($"Raw parameter {parameterName} = '{parameterValue}' (Type: {parameterValue?.GetType()?.Name ?? "null"})");
                        
                        // Convert the parameter value to the property type
                        var convertedValue = ConvertParameterValue(parameterValue, property.PropertyType);
                        property.SetValue(this, convertedValue);
                        
                        // Enhanced logging for arrays
                        if (convertedValue is string[] arrayValue)
                        {
                            ((dynamic)context.Logger).Debug($"Loaded parameter {parameterName} = [{string.Join(", ", arrayValue)}] (Array Length: {arrayValue.Length})");
                        }
                        else
                        {
                            ((dynamic)context.Logger).Debug($"Loaded parameter {parameterName} = {convertedValue} (Converted Type: {convertedValue?.GetType()?.Name ?? "null"})");
                        }
                    }
                    catch (Exception ex)
                    {
                        ((dynamic)context.Logger).Error($"Failed to load parameter {attribute.Name}: {ex.Message}");
                    }
                }
            }
        }

        /// <summary>
        /// Convert parameter value to the target type
        /// </summary>
        private object ConvertParameterValue(string value, Type targetType)
        {
            if (targetType == typeof(string))
                return value;
                
            if (targetType == typeof(int))
                return int.Parse(value);
                
            if (targetType == typeof(decimal))
                return decimal.Parse(value);
                
            if (targetType == typeof(bool))
                return bool.Parse(value);
                
            if (targetType == typeof(DateTime))
                return DateTime.Parse(value);
                
            if (targetType == typeof(TimeSpan))
                return TimeSpan.Parse(value);
                
            // Handle string arrays (for Symbols parameter)
            if (targetType == typeof(string[]))
            {
                // Handle various input formats that QuantConnect might send
                if (string.IsNullOrWhiteSpace(value))
                {
                    return new string[] { "SPY" }; // Default fallback
                }
                
                // Handle already-parsed arrays (QuantConnect might pass this way)
                if (value.StartsWith("System.String[]"))
                {
                    // QC passes arrays as "System.String[]" string - extract from property if available
                    return new string[] { "SPY" }; // Fallback, will be overridden by template logic
                }
                
                // Handle comma-separated string (our expected format)
                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
            }
                
            // For other types, try generic conversion
            return Convert.ChangeType(value, targetType);
        }

        /// <summary>
        /// Validate the configuration
        /// </summary>
        public virtual string[] Validate()
        {
            var errors = new List<string>();
            
            // Generic validations that apply to all strategies
            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})");
                
            // Skip validation if profit target is disabled (0 means disabled)
            if (ProfitTarget != 0 && ProfitTarget <= 0)
                errors.Add($"ProfitTarget must be positive (current: {ProfitTarget:P1}) or 0 to disable");
                
            // Skip validation if stop loss is disabled (0 means disabled)
            if (StopLoss != 0 && StopLoss >= 0)
                errors.Add($"StopLoss must be negative (current: {StopLoss:P1}) or 0 to disable");

            // Validate SmartPricing mode
            if (!CoreAlgo.Architecture.Core.Execution.SmartPricingEngineFactory.IsValidMode(SmartPricingMode))
                errors.Add($"Invalid SmartPricingMode '{SmartPricingMode}' (valid: Normal, Fast, Patient, Off)");

            // Entry window validation
            if (UseEntryTimeWindow)
            {
                if (EntryWindowStart > EntryWindowEnd)
                {
                    errors.Add($"EntryWindowStart {EntryWindowStart:hh\\:mm} must be <= EntryWindowEnd {EntryWindowEnd:hh\\:mm}");
                }
            }
            
            return errors.ToArray();
        }

        /// <summary>
        /// Gets a parameter value by name with a default fallback
        /// </summary>
        public object GetParameterValue(string parameterName, object defaultValue)
        {
            var property = GetType().GetProperty(parameterName);
            if (property != null)
            {
                return property.GetValue(this);
            }
            return defaultValue;
        }

        /// <summary>
        /// Creates a SmartPricing engine based on the current configuration
        /// </summary>
        /// <returns>Configured SmartPricing engine or null if disabled</returns>
        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 System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Securities;
using CoreAlgo.Architecture.Core.Interfaces;

namespace CoreAlgo.Architecture.Core.Services
{
    /// <summary>
    /// Cross-asset risk management system that extends QuantConnect's native risk capabilities.
    /// Provides portfolio-level risk aggregation, concentration limits, and multi-asset risk monitoring.
    /// 
    /// Design Philosophy: Extend QC's excellent risk framework rather than replace it.
    /// Integrates with QC's Portfolio, margin calculations, and position tracking.
    /// </summary>
    public class CoreAlgoRiskManager
    {
        private readonly IAlgorithmContext _context;
        
        // Risk thresholds - configurable via strategy parameters
        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;
        }

        /// <summary>
        /// Validates if a new position can be safely added to the portfolio.
        /// Integrates with QC's native margin and position tracking.
        /// </summary>
        /// <param name="symbol">Symbol to validate</param>
        /// <param name="quantity">Proposed quantity</param>
        /// <param name="estimatedPrice">Estimated fill price</param>
        /// <returns>True if position is within risk limits</returns>
        public bool ValidateNewPosition(string symbol, decimal quantity, decimal estimatedPrice)
        {
            try
            {
                var algorithm = _context.Algorithm;
                var portfolio = algorithm.Portfolio;
                
                // 1. Check portfolio-level margin utilization (extends QC's native margin tracking)
                var currentMarginUtilization = GetPortfolioMarginUtilization();
                if (currentMarginUtilization > _maxPortfolioMarginUtilization)
                {
                    ((dynamic)_context.Logger).Warning($"Risk: Portfolio margin utilization too high: {currentMarginUtilization:P1} > {_maxPortfolioMarginUtilization:P1}");
                    return false;
                }

                // 2. Check asset concentration limits
                var proposedConcentration = CalculateAssetConcentrationAfterTrade(symbol, quantity, estimatedPrice);
                if (proposedConcentration > _maxAssetConcentration)
                {
                    ((dynamic)_context.Logger).Warning($"Risk: Asset concentration would exceed limit: {proposedConcentration:P1} > {_maxAssetConcentration:P1} for {symbol}");
                    return false;
                }

                // 3. Check if we have too many positions (basic diversification)
                var totalOptionPositions = portfolio.Values.Count(x => x.Invested && x.Symbol.SecurityType == SecurityType.Option);
                if (totalOptionPositions >= 20) // Reasonable upper limit for option strategies
                {
                    ((dynamic)_context.Logger).Warning($"Risk: Too many option positions: {totalOptionPositions} >= 20");
                    return false;
                }

                return true;
            }
            catch (Exception ex)
            {
                ((dynamic)_context.Logger).Error($"Risk validation error for {symbol}: {ex.Message}");
                return false; // Fail safe - reject if we can't validate
            }
        }

        /// <summary>
        /// Gets comprehensive portfolio risk metrics using QC's native data.
        /// </summary>
        public PortfolioRiskMetrics GetPortfolioRisk()
        {
            var algorithm = _context.Algorithm;
            var portfolio = algorithm.Portfolio;
            
            return new PortfolioRiskMetrics
            {
                // QC Native metrics
                TotalPortfolioValue = portfolio.TotalPortfolioValue,
                TotalMarginUsed = portfolio.TotalMarginUsed,
                MarginRemaining = portfolio.MarginRemaining,
                Cash = portfolio.Cash,
                TotalUnrealizedProfit = portfolio.TotalUnrealizedProfit,
                
                // CoreAlgo calculated metrics
                MarginUtilization = GetPortfolioMarginUtilization(),
                AssetConcentrations = GetAssetConcentrations(),
                OptionPositionCount = portfolio.Values.Count(x => x.Invested && x.Symbol.SecurityType == SecurityType.Option),
                LargestAssetExposure = GetLargestAssetExposure(),
                
                // Risk alerts
                RiskAlerts = GenerateRiskAlerts()
            };
        }

        /// <summary>
        /// Calculates portfolio margin utilization using QC's native margin tracking.
        /// </summary>
        private decimal GetPortfolioMarginUtilization()
        {
            var portfolio = _context.Algorithm.Portfolio;
            
            if (portfolio.TotalPortfolioValue <= 0)
                return 0;
                
            return portfolio.TotalMarginUsed / portfolio.TotalPortfolioValue;
        }

        /// <summary>
        /// Calculates concentration by underlying asset across all positions.
        /// </summary>
        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;

            // Group by underlying asset for options
            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;
        }

        /// <summary>
        /// Calculates what asset concentration would be after a proposed trade.
        /// </summary>
        private decimal CalculateAssetConcentrationAfterTrade(string symbol, decimal quantity, decimal price)
        {
            var concentrations = GetAssetConcentrations();
            var portfolio = _context.Algorithm.Portfolio;
            var totalValue = portfolio.TotalPortfolioValue;
            
            // Calculate additional value from proposed trade
            var additionalValue = Math.Abs(quantity * price);
            var newTotalValue = totalValue + additionalValue;
            
            // Get underlying symbol for options
            var underlyingSymbol = symbol; // Assume equity by default
            // TODO: Extract underlying symbol for options when we add option support
            
            var currentAssetValue = concentrations.ContainsKey(underlyingSymbol) 
                ? concentrations[underlyingSymbol] * totalValue 
                : 0;
                
            var newAssetValue = currentAssetValue + additionalValue;
            
            return newTotalValue > 0 ? newAssetValue / newTotalValue : 0;
        }

        /// <summary>
        /// Gets the largest single asset exposure as percentage of portfolio.
        /// </summary>
        private decimal GetLargestAssetExposure()
        {
            var concentrations = GetAssetConcentrations();
            return concentrations.Values.DefaultIfEmpty(0).Max();
        }

        /// <summary>
        /// Generates active risk alerts based on current portfolio state.
        /// </summary>
        private List<RiskAlert> GenerateRiskAlerts()
        {
            var alerts = new List<RiskAlert>();
            
            // Margin utilization alert
            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
                });
            }

            // Asset concentration alerts
            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;
        }
    }

    /// <summary>
    /// Portfolio risk metrics combining QC native data with CoreAlgo calculations.
    /// </summary>
    public class PortfolioRiskMetrics
    {
        // QC Native Portfolio metrics
        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; }
        
        // CoreAlgo calculated risk metrics
        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; }
        
        // Risk monitoring
        public List<RiskAlert> RiskAlerts { get; set; } = new List<RiskAlert>();
    }

    /// <summary>
    /// Risk alert for portfolio monitoring.
    /// </summary>
    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 QuantConnect;
using QuantConnect.Securities;
using CoreAlgo.Architecture.Core.Interfaces;

namespace CoreAlgo.Architecture.Core.Services
{
    /// <summary>
    /// TODO: INTEGRATE LATER - Advanced portfolio reporting for multi-asset strategies.
    /// 
    /// CURRENT STATUS: Built but not integrated with templates yet.
    /// REASON: Avoiding complexity in templates while building foundation for future reporting needs.
    /// 
    /// FUTURE INTEGRATION PLAN:
    /// 1. Add periodic portfolio reporting to Main.cs OnEndOfDay
    /// 2. Integrate with risk monitoring dashboard
    /// 3. Add performance analytics and attribution reporting
    /// 4. Connect with correlation analysis for risk attribution
    /// 
    /// This component extends QuantConnect's native portfolio metrics with multi-asset
    /// specific reporting, risk attribution, and performance analytics.
    /// </summary>
    public class PortfolioReporter
    {
        private readonly IAlgorithmContext _context;
        private readonly CoreAlgoRiskManager _riskManager;
        
        // TODO: FUTURE USE - Add correlation calculator when ready for integration
        // private readonly CorrelationCalculator _correlationCalculator;

        public PortfolioReporter(IAlgorithmContext context, CoreAlgoRiskManager riskManager)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
            _riskManager = riskManager ?? throw new ArgumentNullException(nameof(riskManager));
        }

        /// <summary>
        /// TODO: FUTURE USE - Generates comprehensive portfolio risk report.
        /// Combines QC native metrics with CoreAlgo risk analysis.
        /// </summary>
        public PortfolioRiskReport GenerateRiskReport()
        {
            try
            {
                var portfolio = _context.Algorithm.Portfolio;
                var riskMetrics = _riskManager.GetPortfolioRisk();
                
                var report = new PortfolioRiskReport
                {
                    Timestamp = _context.Algorithm.Time,
                    
                    // Portfolio overview
                    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
                    },
                    
                    // Asset breakdown
                    AssetBreakdown = GenerateAssetBreakdown(),
                    
                    // Risk metrics
                    RiskMetrics = riskMetrics,
                    
                    // Position summary
                    PositionSummary = GeneratePositionSummary(),
                    
                    // Performance metrics (TODO: Add more sophisticated metrics)
                    PerformanceMetrics = GeneratePerformanceMetrics()
                };

                ((dynamic)_context.Logger).Debug($"Portfolio risk report generated: {report.AssetBreakdown.Count} assets, {report.PositionSummary.TotalPositions} positions");
                
                return report;
            }
            catch (Exception ex)
            {
                ((dynamic)_context.Logger).Error($"Error generating portfolio risk report: {ex.Message}");
                return new PortfolioRiskReport { Timestamp = _context.Algorithm.Time };
            }
        }

        /// <summary>
        /// TODO: FUTURE USE - Gets key portfolio metrics for dashboard display.
        /// Designed for periodic monitoring and alert systems.
        /// </summary>
        public Dictionary<string, object> GetDashboardMetrics()
        {
            var portfolio = _context.Algorithm.Portfolio;
            var riskMetrics = _riskManager.GetPortfolioRisk();
            
            return new Dictionary<string, object>
            {
                // Key performance indicators
                ["TotalValue"] = portfolio.TotalPortfolioValue,
                ["DailyPnL"] = portfolio.TotalUnrealizedProfit, // TODO: Calculate actual daily P&L
                ["MarginUtilization"] = riskMetrics.MarginUtilization,
                ["CashBalance"] = portfolio.Cash,
                
                // Risk indicators
                ["ActivePositions"] = riskMetrics.OptionPositionCount,
                ["LargestExposure"] = riskMetrics.LargestAssetExposure,
                ["RiskAlerts"] = riskMetrics.RiskAlerts.Count,
                
                // Asset diversification
                ["UniqueAssets"] = riskMetrics.AssetConcentrations.Count,
                ["TopAssetConcentration"] = riskMetrics.AssetConcentrations.Values.DefaultIfEmpty(0).Max(),
                
                // Status indicators
                ["LastUpdate"] = _context.Algorithm.Time,
                ["PortfolioHealth"] = CalculatePortfolioHealthScore(riskMetrics)
            };
        }

        /// <summary>
        /// TODO: FUTURE USE - Generates asset-level breakdown of portfolio.
        /// Groups positions by underlying asset for multi-asset analysis.
        /// </summary>
        private List<AssetBreakdown> GenerateAssetBreakdown()
        {
            var portfolio = _context.Algorithm.Portfolio;
            var breakdown = new List<AssetBreakdown>();
            
            // Group positions by underlying asset
            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,
                    
                    // Position details
                    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();
        }

        /// <summary>
        /// TODO: FUTURE USE - Generates summary of all positions.
        /// </summary>
        private PositionSummary GeneratePositionSummary()
        {
            var portfolio = _context.Algorithm.Portfolio;
            var investedPositions = portfolio.Values.Where(x => x.Invested).ToList();
            
            return 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))
            };
        }

        /// <summary>
        /// TODO: FUTURE USE - Generates performance metrics.
        /// Placeholder for more sophisticated performance analytics.
        /// </summary>
        private PerformanceMetrics GeneratePerformanceMetrics()
        {
            var portfolio = _context.Algorithm.Portfolio;
            
            // TODO: Implement proper performance calculations
            // - Sharpe ratio, Sortino ratio, maximum drawdown
            // - Risk-adjusted returns, alpha, beta
            // - Asset-specific performance attribution
            
            return new PerformanceMetrics
            {
                TotalReturn = portfolio.TotalProfit,
                UnrealizedReturn = portfolio.TotalUnrealizedProfit,
                RealizedReturn = portfolio.TotalProfit - portfolio.TotalUnrealizedProfit,
                
                // Placeholder metrics - TODO: Implement proper calculations
                WinRate = CalculateWinRate(),
                AverageWin = 0, // TODO: Calculate from closed positions
                AverageLoss = 0, // TODO: Calculate from closed positions
                
                // TODO: Add time-weighted returns, risk metrics, benchmarking
            };
        }

        /// <summary>
        /// Calculates a simple portfolio health score (0-100).
        /// TODO: FUTURE USE - Enhance with more sophisticated scoring.
        /// </summary>
        private int CalculatePortfolioHealthScore(PortfolioRiskMetrics riskMetrics)
        {
            var score = 100;
            
            // Penalize high margin utilization
            if (riskMetrics.MarginUtilization > 0.8m) score -= 30;
            else if (riskMetrics.MarginUtilization > 0.6m) score -= 15;
            
            // Penalize high concentration
            if (riskMetrics.LargestAssetExposure > 0.5m) score -= 25;
            else if (riskMetrics.LargestAssetExposure > 0.3m) score -= 10;
            
            // Penalize too many risk alerts
            score -= riskMetrics.RiskAlerts.Count * 5;
            
            return Math.Max(0, Math.Min(100, score));
        }

        /// <summary>
        /// Gets the underlying symbol for an asset (handles options).
        /// </summary>
        private string GetUnderlyingSymbol(Symbol symbol)
        {
            return symbol.SecurityType == SecurityType.Option ? 
                symbol.Underlying.Value : symbol.Value;
        }

        /// <summary>
        /// Calculates win rate from current unrealized positions.
        /// TODO: Enhance to use actual trade history.
        /// </summary>
        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;
        }
    }

    #region Report Data Models

    /// <summary>
    /// TODO: FUTURE USE - Comprehensive portfolio risk report.
    /// </summary>
    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 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 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; }
    }

    #endregion
}
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
{
    /// <summary>
    /// Manages position overlap detection and prevention across all strategies
    /// Leverages QuantConnect's native Portfolio for real-time position tracking
    /// </summary>
    public class PositionOverlapManager
    {
        private readonly IAlgorithmContext _context;
        private readonly List<IPositionOverlapRule> _rules;
        private readonly object _logger;

        public PositionOverlapManager(IAlgorithmContext context)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
            _logger = context.Logger;
            _rules = new List<IPositionOverlapRule>();
            
            // Initialize with built-in rules
            InitializeBuiltInRules();
        }

        /// <summary>
        /// Validates whether a new combo order (multi-leg strategy) can be opened without creating dangerous overlaps
        /// </summary>
        /// <param name="legs">List of legs in the combo order</param>
        /// <param name="quantity">Combo order quantity</param>
        /// <param name="strategyTag">Strategy identifier for validation</param>
        /// <returns>ValidationResult indicating if combo order is allowed</returns>
        public ValidationResult ValidateComboOrder(List<QuantConnect.Orders.Leg> legs, int quantity, string strategyTag = "")
        {
            try
            {
                // For combo orders, validate the strategy as a whole unit rather than individual legs
                // This preserves ComboMarketOrder atomicity and prevents partial execution issues
                
                ((dynamic)_logger).Debug($"[COMBO VALIDATION] Validating combo order: {legs.Count} legs, qty:{quantity}, strategy:{strategyTag}");
                
                // Get current portfolio positions using QC's native Portfolio API
                var existingPositions = _context.Algorithm.Portfolio
                    .Where(p => p.Value.Invested)
                    .ToList();
                
                // Apply combo-specific validation logic based on strategy type
                return ValidateComboStrategy(legs, quantity, existingPositions, strategyTag);
            }
            catch (Exception ex)
            {
                var errorMsg = $"Error in combo order validation: {ex.Message}";
                ((dynamic)_logger).Error(errorMsg);
                return ValidationResult.Error(errorMsg);
            }
        }

        /// <summary>
        /// Validates whether a new position can be opened without creating dangerous overlaps
        /// </summary>
        /// <param name="symbol">Symbol to validate</param>
        /// <param name="quantity">Proposed quantity</param>
        /// <param name="strategyTag">Strategy identifier for logging</param>
        /// <returns>ValidationResult indicating if position is allowed</returns>
        public ValidationResult ValidateNewPosition(Symbol symbol, decimal quantity, string strategyTag = "")
        {
            try
            {
                // Get current portfolio positions using QC's native Portfolio API
                var existingPositions = _context.Algorithm.Portfolio
                    .Where(p => p.Value.Invested)
                    .ToList();

                ((dynamic)_logger).Debug($"[OVERLAP CHECK] Validating {symbol} qty:{quantity} strategy:{strategyTag}");
                ((dynamic)_logger).Debug($"[OVERLAP CHECK] Existing positions: {existingPositions.Count}");

                // Apply all registered rules
                foreach (var rule in _rules)
                {
                    var result = rule.Validate(symbol, quantity, existingPositions, strategyTag);
                    if (!result.IsValid)
                    {
                        ((dynamic)_logger).Warning($"[OVERLAP BLOCKED] {rule.GetType().Name}: {result.Message}");
                        return result;
                    }
                }

                ((dynamic)_logger).Debug($"[OVERLAP ALLOWED] Position validated successfully");
                return ValidationResult.Success();
            }
            catch (Exception ex)
            {
                var errorMsg = $"Error in position overlap validation: {ex.Message}";
                ((dynamic)_logger).Error(errorMsg);
                return ValidationResult.Error(errorMsg);
            }
        }

        /// <summary>
        /// Validates a combo strategy as an atomic unit using structural analysis (QC-First approach)
        /// </summary>
        private ValidationResult ValidateComboStrategy(List<QuantConnect.Orders.Leg> legs, int quantity, 
            List<KeyValuePair<Symbol, SecurityHolding>> existingPositions, string strategyTag)
        {
            // Analyze order structure instead of relying on strategy names
            var orderStructure = AnalyzeOrderStructure(legs);
            
            ((dynamic)_logger).Debug($"[COMBO STRATEGY] Analyzed structure: {orderStructure.LegCount} legs, {orderStructure.PutCount} puts, {orderStructure.CallCount} calls");
            
            // Use structural analysis for validation
            if (orderStructure.LegCount == 4 && orderStructure.PutCount == 2 && orderStructure.CallCount == 2)
            {
                // 4-leg combo with 2 puts + 2 calls = Iron Condor pattern
                return ValidateFourLegCombo(legs, quantity, existingPositions, orderStructure);
            }
            else if (orderStructure.LegCount == 2)
            {
                // 2-leg combo = Spread pattern
                return ValidateTwoLegCombo(legs, quantity, existingPositions, orderStructure);
            }
            else
            {
                // Other combo patterns - use conservative validation
                return ValidateGenericCombo(legs, quantity, existingPositions, orderStructure);
            }
        }
        
        /// <summary>
        /// Validates 4-leg combo orders (Iron Condor pattern) using configuration-driven limits
        /// </summary>
        private ValidationResult ValidateFourLegCombo(List<QuantConnect.Orders.Leg> legs, int quantity,
            List<KeyValuePair<Symbol, SecurityHolding>> existingPositions, OrderStructureAnalysis structure)
        {
            // Get the underlying from the first leg
            var underlying = legs.First().Symbol.Underlying;
            var expiry = legs.First().Symbol.ID.Date;
            
            // Count existing 4-leg combos on same underlying and expiry
            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();
            
            // Get limit from strategy config (default to 1 for 4-leg combos if not specified)
            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})");
            }
            
            ((dynamic)_logger).Debug($"[4-LEG COMBO] Validation passed: {activeFourLegCombos}/{maxComboPositions} positions");
            return ValidationResult.Success();
        }
        
        /// <summary>
        /// Validates 2-leg combo orders (Spread pattern) using configuration-driven limits
        /// </summary>
        private ValidationResult ValidateTwoLegCombo(List<QuantConnect.Orders.Leg> legs, int quantity,
            List<KeyValuePair<Symbol, SecurityHolding>> existingPositions, OrderStructureAnalysis structure)
        {
            var underlying = legs.First().Symbol.Underlying;
            
            // Count existing 2-leg combos on same 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();
                
            // Get limit from strategy config (more permissive for 2-leg combos)
            var maxComboPositions = GetComboPositionLimit(2);
                
            if (activeTwoLegCombos >= maxComboPositions)
            {
                return ValidationResult.Blocked(
                    $"2-leg combo limit exceeded: {activeTwoLegCombos} active positions on {underlying.Value} (max: {maxComboPositions})");
            }
            
            ((dynamic)_logger).Debug($"[2-LEG COMBO] Validation passed: {activeTwoLegCombos}/{maxComboPositions} positions");
            return ValidationResult.Success();
        }
        
        /// <summary>
        /// Validates generic combo orders with conservative portfolio-level limits
        /// </summary>
        private ValidationResult ValidateGenericCombo(List<QuantConnect.Orders.Leg> legs, int quantity,
            List<KeyValuePair<Symbol, SecurityHolding>> existingPositions, OrderStructureAnalysis structure)
        {
            // Conservative validation for unknown combo patterns
            // Focus on portfolio-level risk management
            
            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();
                
            // Use configuration-driven limit with conservative fallback
            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})");
            }
            
            ((dynamic)_logger).Debug($"[GENERIC COMBO] Validation passed: {activeComboPositions}/{maxComboPositions} positions");
            return ValidationResult.Success();
        }
        
        /// <summary>
        /// Analyzes combo order structure without relying on strategy names
        /// </summary>
        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)
            };
            
            // Analyze strike relationships for pattern detection
            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;
        }
        
        /// <summary>
        /// Gets combo position limit from strategy configuration
        /// </summary>
        private int GetComboPositionLimit(int legCount)
        {
            try
            {
                // Try to get MaxPositionsPerCombo from current strategy configuration
                var algorithm = _context.Algorithm;
                if (algorithm != null)
                {
                    // Access strategy config through algorithm if available
                    var maxComboPositions = algorithm.GetParameter("MaxPositionsPerCombo");
                    if (int.TryParse(maxComboPositions, out var configLimit))
                    {
                        return configLimit;
                    }
                }
            }
            catch (Exception ex)
            {
                ((dynamic)_logger).Debug($"[CONFIG] Could not read MaxPositionsPerCombo: {ex.Message}");
            }
            
            // Fallback limits based on leg count complexity
            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
            };
        }

        /// <summary>
        /// Structure analysis result for combo orders
        /// </summary>
        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; }
        }
        
        /// <summary>
        /// Adds a custom overlap rule to the validation pipeline
        /// </summary>
        public void AddRule(IPositionOverlapRule rule)
        {
            if (rule == null) throw new ArgumentNullException(nameof(rule));
            _rules.Add(rule);
            ((dynamic)_logger).Debug($"[OVERLAP MANAGER] Added rule: {rule.GetType().Name}");
        }

        /// <summary>
        /// Removes a rule from the validation pipeline
        /// </summary>
        public void RemoveRule<T>() where T : IPositionOverlapRule
        {
            var removed = _rules.RemoveAll(r => r is T);
            if (removed > 0)
            {
                ((dynamic)_logger).Debug($"[OVERLAP MANAGER] Removed {removed} rule(s) of type: {typeof(T).Name}");
            }
        }

        /// <summary>
        /// Gets summary of current overlap prevention configuration
        /// </summary>
        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;
        }

        /// <summary>
        /// Initialize built-in overlap prevention rules
        /// </summary>
        private void InitializeBuiltInRules()
        {
            // Add core overlap prevention rules
            AddRule(new UnderlyingConflictRule(_context));
            AddRule(new CollateralValidationRule(_context));
            AddRule(new StrikeOverlapRule(_context));
            
            ((dynamic)_logger).Info($"[OVERLAP MANAGER] Initialized with {_rules.Count} built-in rules");
        }

        /// <summary>
        /// Gets all positions for a specific underlying symbol
        /// </summary>
        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();
        }

        /// <summary>
        /// Checks if there are any active positions for a specific underlying
        /// </summary>
        public bool HasActivePositions(Symbol underlying)
        {
            return GetPositionsForUnderlying(underlying).Any();
        }

        /// <summary>
        /// Gets count of active option positions for an underlying
        /// </summary>
        public int GetActiveOptionPositionCount(Symbol underlying)
        {
            return _context.Algorithm.Portfolio.Values
                .Count(h => h.Invested && 
                           h.Symbol.SecurityType == SecurityType.Option &&
                           h.Symbol.Underlying == underlying);
        }

        /// <summary>
        /// Calculates total margin requirement for all positions on an underlying
        /// </summary>
        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)
            {
                ((dynamic)_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 object Logger { get; }

        public SimpleAlgorithmContext(QCAlgorithm algorithm, object 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;

namespace CoreAlgo.Architecture.Core.Services
{
    /// <summary>
    /// Manages smart orders with progressive pricing without requiring Algorithm Framework
    /// Intercepts order placement and applies progressive limit pricing to improve fill rates
    /// </summary>
    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>();
        }

        /// <summary>
        /// Sets the pricing engine for smart order execution
        /// </summary>
        public void SetPricingEngine(ISmartPricingEngine pricingEngine)
        {
            _pricingEngine = pricingEngine;
        }

        /// <summary>
        /// Sets the position overlap manager for order validation
        /// </summary>
        public void SetOverlapManager(PositionOverlapManager overlapManager)
        {
            _overlapManager = overlapManager;
        }

        /// <summary>
        /// Places a smart market order that starts as a limit order at mid-spread
        /// and progressively moves toward market price
        /// </summary>
        public OrderTicket SmartMarketOrder(Symbol symbol, decimal quantity, string tag = "")
        {
            // Validate position overlap before placing order
            if (_overlapManager != null)
            {
                var validation = _overlapManager.ValidateNewPosition(symbol, quantity, tag);
                if (!validation.IsValid)
                {
                    ((dynamic)_context.Logger).Warning($"[OVERLAP PREVENTION] Order blocked: {validation.Message}");
                    
                    // Return null ticket to indicate order was blocked
                    // Strategies should check for null returns and handle appropriately
                    return null;
                }
            }

            if (_pricingEngine == null)
            {
                // Fall back to regular market order if no pricing engine
                return _algorithm.MarketOrder(symbol, quantity, tag: tag);
            }

            try
            {
                var security = _algorithm.Securities[symbol];
                var quote = GetCurrentQuote(security);
                
                if (quote == null || quote.Spread > 10m) // Skip if spread too wide
                {
                    ((dynamic)_context.Logger).Debug($"SmartOrder: Using market order for {symbol} due to wide spread or no quote");
                    return _algorithm.MarketOrder(symbol, quantity, tag: tag);
                }

                // Calculate initial limit price at mid-spread
                var direction = quantity > 0 ? CoreAlgo.Architecture.Core.Execution.OrderDirection.Buy : CoreAlgo.Architecture.Core.Execution.OrderDirection.Sell;
                var initialPrice = _pricingEngine.CalculateInitialPrice(quote, direction);
                
                // Place initial limit order
                var ticket = _algorithm.LimitOrder(symbol, quantity, initialPrice, tag: tag + " [Smart]");
                
                if (ticket == null || ticket.Status == OrderStatus.Invalid)
                {
                    ((dynamic)_context.Logger).Error($"SmartOrder: Failed to place initial limit order for {symbol}");
                    return _algorithm.MarketOrder(symbol, quantity, tag: tag);
                }

                // Create tracker for progressive pricing
                var tracker = new SmartOrderTracker(ticket, quote, direction, SmartPricingMode.Normal, initialPrice);

                _activeOrders[ticket.OrderId] = tracker;
                
                ((dynamic)_context.Logger).Info($"SmartOrder: Placed initial limit order for {symbol} at ${initialPrice:F2} " +
                                              $"(Mid: ${quote.Price:F2}, Spread: ${quote.Spread:F2})");

                // Schedule first price update
                ScheduleNextPricingUpdate(tracker);
                
                return ticket;
            }
            catch (Exception ex)
            {
                ((dynamic)_context.Logger).Error($"SmartOrder error: {ex.Message}");
                // Fall back to market order on any error
                return _algorithm.MarketOrder(symbol, quantity, tag: tag);
            }
        }

        /// <summary>
        /// Places a smart combo limit order for multi-leg options with progressive net pricing
        /// Uses QC's native ComboLimitOrder with intelligent net price calculation and updates
        /// </summary>
        public List<OrderTicket> SmartComboMarketOrder(List<Leg> legs, int quantity, string tag = "")
        {
            // Validate combo order as atomic unit (QC-First approach)
            if (_overlapManager != null)
            {
                var validation = _overlapManager.ValidateComboOrder(legs, quantity, tag);
                if (!validation.IsValid)
                {
                    ((dynamic)_context.Logger).Warning($"[COMBO ORDER BLOCKED] {validation.Message}");
                    return new List<OrderTicket>(); // Return empty list to indicate order was blocked
                }
                
                ((dynamic)_context.Logger).Debug($"[COMBO ORDER APPROVED] {legs.Count}-leg order validated for {tag}");
            }

            // If no pricing engine, fall back to basic combo market order
            if (_pricingEngine == null || _pricingEngine.Mode == SmartPricingMode.Off)
            {
                ((dynamic)_context.Logger).Debug($"SmartCombo: Using basic combo market order (no smart pricing)");
                return _algorithm.ComboMarketOrder(legs, quantity, tag: tag);
            }

            try
            {
                // Get current market quotes for the combo
                var comboQuote = ComboQuote.FromSecurities(legs, _algorithm.Securities);
                if (comboQuote == null)
                {
                    ((dynamic)_context.Logger).Warning($"SmartCombo: No valid quotes available for combo, using market order");
                    return _algorithm.ComboMarketOrder(legs, quantity, tag: tag);
                }

                // Determine combo direction (buy = net debit, sell = net credit)
                var comboDirection = ComboPricingEngine.DetermineComboDirection(legs);

                // Check if we should attempt smart pricing
                if (!_pricingEngine.ShouldAttemptComboPricing(comboQuote, comboDirection))
                {
                    ((dynamic)_context.Logger).Debug($"SmartCombo: Conditions not suitable for smart pricing, using market order");
                    return _algorithm.ComboMarketOrder(legs, quantity, tag: tag);
                }

                // Calculate initial net limit price
                var initialNetPrice = _pricingEngine.CalculateInitialComboPrice(legs, comboQuote, comboDirection);
                if (!initialNetPrice.HasValue)
                {
                    ((dynamic)_context.Logger).Debug($"SmartCombo: Could not calculate initial price, using market order");
                    return _algorithm.ComboMarketOrder(legs, quantity, tag: tag);
                }

                // Place initial combo limit order with calculated net price
                var comboTickets = _algorithm.ComboLimitOrder(legs, quantity, initialNetPrice.Value, tag: tag + " [SmartCombo]");
                
                if (comboTickets == null || comboTickets.Count == 0)
                {
                    ((dynamic)_context.Logger).Error($"SmartCombo: Failed to place combo limit order, trying market order fallback");
                    return _algorithm.ComboMarketOrder(legs, quantity, tag: tag);
                }

                // Create combo tracker for progressive pricing
                var comboTracker = new ComboOrderTracker(comboTickets, legs, comboQuote, 
                    comboDirection, _pricingEngine.Mode, initialNetPrice.Value);

                // Track using primary order ID
                _activeComboOrders[comboTracker.PrimaryOrderId] = comboTracker;

                ((dynamic)_context.Logger).Info($"SmartCombo: Placed {legs.Count}-leg combo limit order " +
                                              $"at net price ${initialNetPrice.Value:F2} " +
                                              $"(NetBid: ${comboQuote.NetBid:F2}, NetAsk: ${comboQuote.NetAsk:F2}, " +
                                              $"NetMid: ${comboQuote.NetMid:F2}, Direction: {comboDirection})");

                // Schedule first pricing update
                ScheduleNextComboPricingUpdate(comboTracker);

                return comboTickets;
            }
            catch (Exception ex)
            {
                ((dynamic)_context.Logger).Error($"SmartCombo error: {ex.Message}");
                // Fall back to basic combo market order on any error
                return _algorithm.ComboMarketOrder(legs, quantity, tag: tag);
            }
        }

        /// <summary>
        /// Handles order events to track fills and update order state
        /// </summary>
        public void OnOrderEvent(OrderEvent orderEvent)
        {
            // Handle single-leg orders
            if (_activeOrders.TryGetValue(orderEvent.OrderId, out var tracker))
            {
                switch (orderEvent.Status)
                {
                    case OrderStatus.Filled:
                        ((dynamic)_context.Logger).Info($"SmartOrder: Order {orderEvent.OrderId} filled at ${orderEvent.FillPrice:F2} " +
                                                      $"after {tracker.AttemptNumber} attempts");
                        CleanupOrder(tracker);
                        break;

                    case OrderStatus.PartiallyFilled:
                        ((dynamic)_context.Logger).Debug($"SmartOrder: Order {orderEvent.OrderId} partially filled " +
                                                       $"({orderEvent.FillQuantity}/{tracker.OrderTicket.Quantity})");
                        tracker.UpdatePartialFill(orderEvent);
                        break;

                    case OrderStatus.Canceled:
                    case OrderStatus.Invalid:
                        ((dynamic)_context.Logger).Warning($"SmartOrder: Order {orderEvent.OrderId} {orderEvent.Status}");
                        CleanupOrder(tracker);
                        break;
                }
                return;
            }

            // Handle combo orders - check if this order event belongs to any tracked combo
            foreach (var comboTracker in _activeComboOrders.Values)
            {
                var matchingTicket = comboTracker.ComboTickets.FirstOrDefault(t => t.OrderId == orderEvent.OrderId);
                if (matchingTicket != null)
                {
                    HandleComboOrderEvent(comboTracker, orderEvent, matchingTicket);
                    return;
                }
            }
        }

        /// <summary>
        /// Handles order events for combo orders
        /// </summary>
        private void HandleComboOrderEvent(ComboOrderTracker comboTracker, OrderEvent orderEvent, OrderTicket matchingTicket)
        {
            switch (orderEvent.Status)
            {
                case OrderStatus.Filled:
                    ((dynamic)_context.Logger).Debug($"SmartCombo: Leg {orderEvent.OrderId} of combo {comboTracker.PrimaryOrderId} " +
                                                   $"filled at ${orderEvent.FillPrice:F2}");
                    
                    // Check if entire combo is now filled
                    if (comboTracker.IsCompletelyFilled)
                    {
                        ((dynamic)_context.Logger).Info($"SmartCombo: Combo order {comboTracker.PrimaryOrderId} completely filled " +
                                                      $"after {comboTracker.AttemptNumber} pricing attempts");
                        CleanupComboOrder(comboTracker);
                    }
                    break;

                case OrderStatus.PartiallyFilled:
                    ((dynamic)_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:
                    ((dynamic)_context.Logger).Warning($"SmartCombo: Leg {orderEvent.OrderId} of combo {comboTracker.PrimaryOrderId} {orderEvent.Status}");
                    
                    // If any leg fails, the entire combo fails
                    ((dynamic)_context.Logger).Warning($"SmartCombo: Combo order {comboTracker.PrimaryOrderId} failed due to leg {orderEvent.OrderId}");
                    CleanupComboOrder(comboTracker);
                    break;
            }
        }

        /// <summary>
        /// Updates the price of an active order using progressive pricing
        /// </summary>
        private void UpdateOrderPrice(SmartOrderTracker tracker)
        {
            try
            {
                // Remove the scheduled event
                if (tracker.ScheduledEvent != null)
                {
                    _scheduledEvents.Remove(tracker.ScheduledEvent);
                    tracker.ScheduledEvent = null;
                }

                // Check if order is still active
                if (!_activeOrders.ContainsKey(tracker.OrderTicket.OrderId) || 
                    tracker.OrderTicket.Status == OrderStatus.Filled)
                {
                    return;
                }

                // Check if we've exceeded max attempts
                if (tracker.AttemptNumber >= _pricingEngine.GetMaxAttempts())
                {
                    ((dynamic)_context.Logger).Info($"SmartOrder: Max attempts reached for order {tracker.OrderTicket.OrderId}, " +
                                                  "converting to market order");
                    
                    // Cancel limit order and place market order
                    tracker.OrderTicket.Cancel("Max pricing attempts reached");
                    
                    // Place market order for remaining quantity
                    var remainingQty = tracker.OrderTicket.Quantity - tracker.OrderTicket.QuantityFilled;
                    if (remainingQty != 0)
                    {
                        _algorithm.MarketOrder(tracker.OrderTicket.Symbol, remainingQty, 
                            tag: tracker.OrderTicket.Tag + " [Smart-Market]");
                    }
                    
                    CleanupOrder(tracker);
                    return;
                }

                // Get current market quote
                var security = _algorithm.Securities[tracker.OrderTicket.Symbol];
                var currentQuote = GetCurrentQuote(security);
                
                if (currentQuote == null)
                {
                    ((dynamic)_context.Logger).Warning($"SmartOrder: No quote available for {tracker.OrderTicket.Symbol}");
                    // Keep the order but don't update - try again next time
                    ScheduleNextPricingUpdate(tracker);
                    return;
                }

                // Calculate next price
                tracker.AttemptNumber++;
                var nextPrice = _pricingEngine.CalculateNextPrice(
                    tracker.CurrentPrice, currentQuote, tracker.OrderDirection, tracker.AttemptNumber);

                if (nextPrice.HasValue && Math.Abs(nextPrice.Value - tracker.CurrentPrice) > 0.01m)
                {
                    // Update order price
                    var updateFields = new UpdateOrderFields { LimitPrice = nextPrice.Value };
                    var response = tracker.OrderTicket.Update(updateFields);
                    
                    if (response.IsSuccess)
                    {
                        tracker.CurrentPrice = nextPrice.Value;
                        ((dynamic)_context.Logger).Debug($"SmartOrder: Updated order {tracker.OrderTicket.OrderId} " +
                                                       $"price to ${nextPrice.Value:F2} (attempt {tracker.AttemptNumber})");
                    }
                    else
                    {
                        ((dynamic)_context.Logger).Warning($"SmartOrder: Failed to update order price: {response.ErrorMessage}");
                    }
                }

                // Schedule next update
                ScheduleNextPricingUpdate(tracker);
            }
            catch (Exception ex)
            {
                ((dynamic)_context.Logger).Error($"SmartOrder update error: {ex.Message}");
                CleanupOrder(tracker);
            }
        }

        /// <summary>
        /// Schedules the next pricing update for an order
        /// </summary>
        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;
        }

        /// <summary>
        /// Cleans up an order and removes associated scheduled events
        /// </summary>
        private void CleanupOrder(SmartOrderTracker tracker)
        {
            _activeOrders.Remove(tracker.OrderTicket.OrderId);
            
            if (tracker.ScheduledEvent != null)
            {
                _algorithm.Schedule.Remove(tracker.ScheduledEvent);
                _scheduledEvents.Remove(tracker.ScheduledEvent);
            }
        }

        /// <summary>
        /// Gets the current quote for a security
        /// </summary>
        private Quote GetCurrentQuote(Security security)
        {
            if (security.BidPrice == 0 || security.AskPrice == 0)
                return null;

            return new Quote(security.BidPrice, security.AskPrice);
        }

        /// <summary>
        /// Schedules the next pricing update for a combo order
        /// </summary>
        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;
        }

        /// <summary>
        /// Updates the net price of an active combo order using progressive pricing
        /// </summary>
        private void UpdateComboOrderPrice(ComboOrderTracker tracker)
        {
            try
            {
                // Remove the scheduled event
                if (tracker.ScheduledEvent != null)
                {
                    _scheduledEvents.Remove(tracker.ScheduledEvent);
                    tracker.ScheduledEvent = null;
                }

                // Check if combo is still active
                if (!_activeComboOrders.ContainsKey(tracker.PrimaryOrderId) || tracker.IsCompletelyFilled)
                {
                    return;
                }

                // Check if we should continue pricing
                var maxAttempts = _pricingEngine.GetMaxAttempts();
                var maxRuntime = TimeSpan.FromMinutes(5); // Max 5 minutes for combo orders

                if (!tracker.ShouldContinuePricing(maxAttempts, maxRuntime))
                {
                    ((dynamic)_context.Logger).Debug($"SmartCombo: Stopping progressive pricing for combo {tracker.PrimaryOrderId} " +
                                                   $"(attempts: {tracker.AttemptNumber}/{maxAttempts}, runtime: {tracker.GetRuntime().TotalSeconds:F0}s)");
                    CleanupComboOrder(tracker);
                    return;
                }

                // Get current combo quote
                var currentComboQuote = ComboQuote.FromSecurities(tracker.Legs, _algorithm.Securities);
                if (currentComboQuote == null)
                {
                    ((dynamic)_context.Logger).Warning($"SmartCombo: No quotes available for combo {tracker.PrimaryOrderId}, stopping updates");
                    CleanupComboOrder(tracker);
                    return;
                }

                // Calculate next net price
                var nextNetPrice = _pricingEngine.CalculateNextComboPrice(
                    tracker.CurrentNetPrice, currentComboQuote, tracker.ComboDirection, tracker.AttemptNumber + 1);

                if (nextNetPrice.HasValue)
                {
                    // Update all combo order tickets with the new net price
                    // QC handles the individual leg price distribution automatically
                    var updateSuccess = true;
                    foreach (var ticket in tracker.ComboTickets)
                    {
                        if (ticket.Status == OrderStatus.Submitted || ticket.Status == OrderStatus.PartiallyFilled)
                        {
                            // Note: For combo orders, we update the primary ticket's limit price
                            // QC automatically adjusts the other legs proportionally
                            var result = ticket.Update(new UpdateOrderFields { LimitPrice = nextNetPrice.Value });
                            if (!result.IsSuccess)
                            {
                                ((dynamic)_context.Logger).Warning($"SmartCombo: Failed to update combo ticket {ticket.OrderId}: {result.ErrorMessage}");
                                updateSuccess = false;
                            }
                        }
                    }

                    if (updateSuccess)
                    {
                        tracker.UpdateNetPrice(nextNetPrice.Value, currentComboQuote);
                        
                        ((dynamic)_context.Logger).Debug($"SmartCombo: Updated combo {tracker.PrimaryOrderId} " +
                                                       $"to net price ${nextNetPrice.Value:F2} (attempt {tracker.AttemptNumber}) " +
                                                       $"NetMid: ${currentComboQuote.NetMid:F2}");

                        // Schedule next update if we haven't reached max attempts
                        if (tracker.AttemptNumber < maxAttempts)
                        {
                            ScheduleNextComboPricingUpdate(tracker);
                        }
                        else
                        {
                            ((dynamic)_context.Logger).Debug($"SmartCombo: Reached max attempts for combo {tracker.PrimaryOrderId}");
                        }
                    }
                    else
                    {
                        ((dynamic)_context.Logger).Warning($"SmartCombo: Failed to update combo order, stopping progressive pricing");
                        CleanupComboOrder(tracker);
                    }
                }
                else
                {
                    ((dynamic)_context.Logger).Debug($"SmartCombo: No more price improvements for combo {tracker.PrimaryOrderId}");
                    CleanupComboOrder(tracker);
                }
            }
            catch (Exception ex)
            {
                ((dynamic)_context.Logger).Error($"SmartCombo update error for combo {tracker.PrimaryOrderId}: {ex.Message}");
                CleanupComboOrder(tracker);
            }
        }

        /// <summary>
        /// Cleans up a combo order and removes associated scheduled events
        /// </summary>
        private void CleanupComboOrder(ComboOrderTracker tracker)
        {
            _activeComboOrders.Remove(tracker.PrimaryOrderId);
            
            if (tracker.ScheduledEvent != null)
            {
                _algorithm.Schedule.Remove(tracker.ScheduledEvent);
                _scheduledEvents.Remove(tracker.ScheduledEvent);
            }
        }

        /// <summary>
        /// Gets the mode for the pricing engine (for logging)
        /// </summary>
        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 QuantConnect;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities;
using QuantConnect.Securities.Option;
using QuantConnect.Orders;
using CoreAlgo.Architecture.Core.Implementations;
using CoreAlgo.Architecture.Core.Models;
using CoreAlgo.Architecture.Core.Execution;
using CoreAlgo.Architecture.Core.Helpers;
using CoreAlgo.Architecture.QC.Helpers;

namespace CoreAlgo.Architecture.Core.Templates
{
    /// <summary>
    /// Calendar Spread Forward Factor (FF) strategy.
    /// Exploits term structure mispricing when front-month IV exceeds forward IV.
    /// 
    /// Edge: FF = (IVnear / ForwardVol) - 1
    ///       where ForwardVol = sqrt((TV2 - TV1) / (T2 - T1))
    ///       and TV = IV^2 * T (total variance)
    /// Entry: FF >= threshold (default 20%)
    /// Structure: Long ATM call calendar (sell near, buy far)
    /// Exit: 15 minutes before front expiry
    /// 
    /// Based on research showing calendar spreads profit from IV term structure dislocations,
    /// especially during periods when front-month demand bids up near-term options.
    /// </summary>
    public class CalendarSpreadFFTemplate : SimpleBaseStrategy
    {
        private CalendarSpreadFFConfig _config;
        private List<Symbol> _activeSymbols = new List<Symbol>();
        private Dictionary<Symbol, DateTime> _lastTradeTime = new Dictionary<Symbol, DateTime>();
        private List<CalendarPosition> _activeCalendars = new List<CalendarPosition>();
        private Dictionary<Symbol, PendingCalendarSelection> _pendingSelections = new Dictionary<Symbol, PendingCalendarSelection>();

        // Daily tracking
        private DateTime _lastTradingDay = DateTime.MinValue;
        private DateTime _lastDailyScan = DateTime.MinValue;
        
        // Portfolio safety
        private decimal _initialPortfolioValue = 0m;
        
        // Assignment tracking
        private int _assignmentsHandled = 0;
        private decimal _assignmentPnL = 0m;

        public override string Name => "Calendar Spread FF";

        public override string Description =>
            "Calendar spread strategy using Forward Factor (term structure IV mispricing) to identify entry opportunities. " +
            "Trades long call calendars when front IV materially exceeds forward IV.";

        public override void OnInitialize()
        {
            SmartLog("CalendarSpreadFFTemplate.OnInitialize() starting...");

            // Load configuration
            Configure<CalendarSpreadFFConfig>();
            _config = (CalendarSpreadFFConfig)Config;
            SmartLog("Calendar Spread FF configuration loaded successfully");

            // Track initial portfolio value for kill-switch
            _initialPortfolioValue = Portfolio.TotalPortfolioValue;
            SmartLog($"Initial portfolio value: ${_initialPortfolioValue:N2}");

            // Log strategy parameters
            SmartLog(_config.GetConfigurationSummary());

            // Strategy may request a specific underlying resolution via config
            var underlyingResolution = _config.GetUnderlyingResolution();
            // Individual option contracts will be added at Minute resolution
            Algorithm.UniverseSettings.Resolution = underlyingResolution;
            Algorithm.UniverseSettings.Asynchronous = true;
            Algorithm.UniverseSettings.MinimumTimeInUniverse = TimeSpan.Zero;
            SmartLog("UniverseSettings: Hour resolution, asynchronous enabled");

            // Set up universe or manual symbols
            if (_config.UseUniverseSelection)
            {
                SmartLog("Setting up dynamic coarse universe for calendar spreads...");

                Algorithm.AddUniverse(coarse =>
                {
                    var minDollarVol = _config.MinDollarVolume;
                    var minPrice = _config.PriceFloor;

                    var filtered = coarse
                        .Where(c => c.HasFundamentalData)
                        .Where(c => c.DollarVolume >= (double)minDollarVol)
                        .Where(c => c.Price >= minPrice)
                        .OrderByDescending(c => c.DollarVolume)
                        .Take(_config.UniverseMaxCandidates)
                        .Select(c => c.Symbol);

                    return filtered;
                });

                SmartLog($"Universe configured: top {_config.UniverseMaxCandidates} by dollar volume");
            }
            else
            {
                SmartLog("Using manual symbol list...");

                foreach (var symbolStr in _config.Symbols)
                {
                    if (string.IsNullOrEmpty(symbolStr)) continue;

                    SmartLog($"Adding underlying asset: {symbolStr}");
                    var equity = Algorithm.AddEquity(symbolStr, underlyingResolution, dataNormalizationMode: DataNormalizationMode.Raw);
                    _activeSymbols.Add(equity.Symbol);
                    SmartLog($"Added {symbolStr} at {underlyingResolution} resolution with RAW normalization");
                }
            }

            // Schedule daily scan at market open (9:35 AM ET = 5 min after market open)
            // Use absolute time to avoid issues with empty Securities during universe selection
            Algorithm.Schedule.On(
                Algorithm.DateRules.EveryDay(),
                Algorithm.TimeRules.At(9, 35),
                DailyScanAndSelect);

            SmartLog($"Calendar Spread FF initialized with {_activeSymbols.Count} initial symbols");
            SmartLog("Daily scan scheduled at 9:35 AM ET (5 minutes after market open)");
            SmartLog("Ready to trade calendar spreads on term structure dislocations!");
        }

        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
            // Handle added securities from universe selection
            foreach (var security in changes.AddedSecurities)
            {
                if (security.Type != SecurityType.Equity) continue;

                if (!_activeSymbols.Contains(security.Symbol))
                {
                    _activeSymbols.Add(security.Symbol);
                    SmartDebug($"Added to universe: {security.Symbol}");
                    
                    // Force RAW normalization for option trading compatibility via subscription configs
                    security.Subscriptions.SetDataNormalizationMode(DataNormalizationMode.Raw);
                }
            }

            // Handle removed securities
            foreach (var security in changes.RemovedSecurities)
            {
                if (security.Type != SecurityType.Equity) continue;

                if (_activeSymbols.Contains(security.Symbol))
                {
                    // Keep symbol if we have active calendar positions
                    var hasActivePosition = _activeCalendars.Any(c => c.Underlying == security.Symbol);
                    if (hasActivePosition)
                    {
                        SmartLog($"Keeping {security.Symbol} in tracking (has active calendar position)");
                        continue;
                    }

                    _activeSymbols.Remove(security.Symbol);
                    _lastTradeTime.Remove(security.Symbol);
                    _pendingSelections.Remove(security.Symbol);
                    
                    // Remove any option contracts for this underlying
                    CleanupOptionsForUnderlying(security.Symbol);
                    
                    SmartDebug($"Removed from universe: {security.Symbol}");
                }
            }

            if (_config.DebugLogging)
            {
                SmartLog($"Universe updated: {changes.AddedSecurities.Count()} added, {changes.RemovedSecurities.Count()} removed. " +
                         $"Tracking {_activeSymbols.Count} symbols with {_activeCalendars.Count} active calendars.");
            }
        }

        protected override void OnPreExecuteAlways(Slice slice)
        {
            // Reset daily tracking at market open
            if (slice.Time.Date > _lastTradingDay)
            {
                _lastTradingDay = slice.Time.Date;
                SmartLog($"New trading day: {slice.Time.Date:yyyy-MM-dd}");
            }

            // PORTFOLIO KILL-SWITCH: Prevent PV <= 0 catastrophe
            var currentPV = Portfolio.TotalPortfolioValue;
            var drawdownThreshold = _initialPortfolioValue * (1m - _config.MaxDrawdownPct);
            var killSwitchThreshold = Math.Max(_config.HardStopValue, drawdownThreshold);
            
            if (currentPV <= killSwitchThreshold)
            {
                var drawdownPct = (_initialPortfolioValue - currentPV) / _initialPortfolioValue;
                SmartError($"KILL-SWITCH TRIGGERED: PV=${currentPV:N2} <= threshold=${killSwitchThreshold:N2} (drawdown {drawdownPct:P1})");
                SmartError($"Initial PV: ${_initialPortfolioValue:N2}, Current PV: ${currentPV:N2}");
                
                // Liquidate all positions immediately
                Algorithm.Liquidate();
                SmartError("All positions liquidated by kill-switch");
                
                // Quit the algorithm
                Algorithm.Quit($"Kill-switch: PV dropped to ${currentPV:N2} (threshold ${killSwitchThreshold:N2})");
            }

            // No order placement here - just state preparation
        }

        protected override bool OnShouldExecuteTrade(Slice slice, out string blockReason)
        {
            blockReason = "";

            // Base class already handles trading hours, position limits, margin checks
            // Check if we have any pending selections ready to place
            if (_pendingSelections.Count == 0)
            {
                blockReason = "No pending calendar selections from daily scan";
                if (_config.DebugLogging && slice.Time.Minute == 0)
                    SmartDebug($"[FF-DEBUG] OnShouldExecuteTrade: BLOCKED - No pending selections (activeCalendars={_activeCalendars.Count})");
                return false;
            }

            if (_config.DebugLogging)
                SmartDebug($"[FF-DEBUG] OnShouldExecuteTrade: READY - {_pendingSelections.Count} pending, {_activeCalendars.Count} active, available slots={_config.MaxPositions - _activeCalendars.Count}");
            return true;
        }

        protected override void OnExecute(Slice slice)
        {
            SmartDebug($"[FF-DEBUG] OnExecute called at {slice.Time:HH:mm:ss} - _pendingSelections.Count={_pendingSelections.Count}, _activeCalendars.Count={_activeCalendars.Count}");

            // Calculate available position slots
            var availableSlots = _config.MaxPositions - _activeCalendars.Count;
            if (availableSlots <= 0)
            {
                SmartDebug("[FF-DEBUG] Max calendar positions reached");
                return;
            }

            // Process pending selections from daily scan
            var candidatesToPlace = _pendingSelections
                .Where(kvp => !_activeCalendars.Any(c => c.Underlying == kvp.Key))
                .OrderByDescending(kvp => kvp.Value.FF)
                .Take(availableSlots)
                .ToList();

            SmartDebug($"[FF-DEBUG] candidatesToPlace.Count={candidatesToPlace.Count} (filtered from {_pendingSelections.Count} pending, taking up to {availableSlots} slots)");
            if (candidatesToPlace.Count > 0)
            {
                SmartDebug($"[FF-DEBUG] Candidates to place: {string.Join(", ", candidatesToPlace.Select(kvp => $"{kvp.Key} FF={kvp.Value.FF:P2}"))}");
            }

            if (candidatesToPlace.Count == 0)
            {
                if (_config.DebugLogging)
                    SmartDebug("No valid pending selections to place");
                return;
            }

            var ordersPlaced = 0;
            foreach (var kvp in candidatesToPlace)
            {
                var underlying = kvp.Key;
                var pending = kvp.Value;

                // Check and force RAW normalization before adding option contracts
                if (Algorithm.Securities.ContainsKey(underlying))
                {
                    var eq = Algorithm.Securities[underlying];
                    var mode = eq.Subscriptions.FirstOrDefault()?.DataNormalizationMode;
                    SmartDebug($"[FF-DEBUG] {underlying}: DataNormalizationMode={mode}");
                    
                    if (mode != DataNormalizationMode.Raw)
                    {
                        SmartWarn($"Forcing RAW normalization on {underlying} (was {mode})");
                        eq.Subscriptions.SetDataNormalizationMode(DataNormalizationMode.Raw);
                        var newMode = eq.Subscriptions.FirstOrDefault()?.DataNormalizationMode;
                        SmartDebug($"[FF-DEBUG] {underlying}: DataNormalizationMode now={newMode}");
                    }
                }
                else
                {
                    SmartWarn($"[FF-DEBUG] {underlying}: Not found in Algorithm.Securities!");
                }

                // Subscribe to the two option contracts
                var nearOption = Algorithm.AddOptionContract(pending.NearSymbol, Resolution.Minute);
                var farOption = Algorithm.AddOptionContract(pending.FarSymbol, Resolution.Minute);

                SmartWarn($"Subscribed to contracts: {pending.NearSymbol} and {pending.FarSymbol}");

                // Place the calendar spread
                var legs = new List<Leg>
                {
                    Leg.Create(pending.NearSymbol, -1), // Sell near
                    Leg.Create(pending.FarSymbol, 1)    // Buy far
                };

                var quantity = CalculateQuantity(legs, underlying);
                if (quantity <= 0)
                {
                    SmartWarn($"Invalid quantity for {underlying}: {quantity}");
                    _pendingSelections.Remove(underlying);
                    continue;
                }

                var tag = $"FF Entry {pending.FF:P1} N{pending.NearDte}F{pending.FarDte} ${pending.Strike:F0}";
                var tickets = ComboMarketOrder(legs, quantity, tag: tag);

                if (tickets != null && tickets.Count > 0 && tickets.All(t => t.Status != OrderStatus.Invalid))
                {
                    var position = new CalendarPosition
                    {
                        Underlying = underlying,
                        NearLeg = pending.NearSymbol,
                        FarLeg = pending.FarSymbol,
                        NearExpiry = pending.NearExpiry,
                        FarExpiry = pending.FarExpiry,
                        Strike = pending.Strike,
                        Quantity = quantity,
                        EntryTime = slice.Time,
                        EntryFF = pending.FF,
                        Tickets = tickets
                    };

                    _activeCalendars.Add(position);
                    _lastTradeTime[underlying] = slice.Time;
                    _pendingSelections.Remove(underlying);

                    ExitRestrictions.RecordPositionEntry(underlying, slice.Time);

                    SmartWarn($"[SUCCESS] Calendar spread entry: {underlying.Value} FF={pending.FF:P2} " +
                             $"Strike=${pending.Strike:F2} NearDTE={pending.NearDte} FarDTE={pending.FarDte} Qty={quantity}");
                    
                    ordersPlaced++;
                }
                else
                {
                    SmartError($"[FAILED] Calendar spread order for {underlying}: invalid tickets");
                    _pendingSelections.Remove(underlying);
                }
            }

            if (ordersPlaced > 0)
            {
                SmartWarn($"Placed {ordersPlaced} calendar spread(s) from pending selections");
            }
        }

        // Daily scan to find calendar opportunities using daily option chain data
        private void DailyScanAndSelect()
        {
            if (Algorithm.Time.Date <= _lastDailyScan)
            {
                if (_config.DebugLogging)
                    SmartDebug("Daily scan already completed today");
                return;
            }

            _lastDailyScan = Algorithm.Time.Date;
            SmartDebug($"=== DAILY CALENDAR SCAN at {Algorithm.Time:yyyy-MM-dd HH:mm:ss} ===");

            // Clear old pending selections
            _pendingSelections.Clear();
            SmartDebug($"[FF-DEBUG] Cleared _pendingSelections, starting fresh scan");

            var scannedCount = 0;
            var qualifiedCount = 0;

            foreach (var underlying in _activeSymbols)
            {
                // Skip if already have calendar on this symbol
                if (_activeCalendars.Any(c => c.Underlying == underlying))
                    continue;

                scannedCount++;

                var selection = BuildCalendarCandidateFromDaily(underlying, Algorithm.Time);
                if (selection != null && selection.FF >= _config.FFThreshold)
                {
                    _pendingSelections[underlying] = selection;
                    qualifiedCount++;

                    SmartWarn($"✅ QUALIFIED: {underlying.Value} FF={selection.FF:P2} N{selection.NearDte}F{selection.FarDte} " +
                             $"Strike=${selection.Strike:F2}");
                } else if (selection != null) {
                    SmartWarn($"❌ NOT QUALIFIED: {underlying.Value} FF={selection.FF:P2} N{selection.NearDte}F{selection.FarDte} " +
                             $"Strike=${selection.Strike:F2}");
                }
            }

            SmartWarn($"Daily scan complete: {scannedCount} symbols scanned, {qualifiedCount} qualified for entry");
            SmartDebug($"[FF-DEBUG] _pendingSelections now has {_pendingSelections.Count} entries: " +
                     $"{string.Join(", ", _pendingSelections.Select(kvp => $"{kvp.Key} FF={kvp.Value.FF:P2}"))}");
        }

        // Build calendar candidate from daily OptionUniverse data
        private PendingCalendarSelection BuildCalendarCandidateFromDaily(Symbol underlying, DateTime currentTime)
        {
            var security = Algorithm.Securities[underlying];
            var underlyingPrice = security.Price;

            if (underlyingPrice <= 0)
            {
                if (_config.DebugLogging)
                    SmartDebug($"[FF-DEBUG] {underlying}: Invalid price ${underlyingPrice}");
                return null;
            }

            // Get daily option chain
            var canonical = Symbol.CreateCanonicalOption(underlying);
            var chain = Algorithm.OptionChain(canonical);

            if (chain == null || !chain.Any())
            {
                if (_config.DebugLogging)
                    SmartDebug($"[FF-DEBUG] {underlying}: No option chain data");
                return null;
            }

                // Inspect expiries and strikes available prior to filtering
                if (_config.DebugLogging)
                {
                    var expiryDump = chain
                        .Select(c => (expiry: c.Expiry.Date, dte: (int)(c.Expiry.Date - currentTime.Date).TotalDays))
                        .Distinct()
                        .OrderBy(x => x.dte)
                        .Take(15)
                        .Select(x => $"{x.expiry:yyyy-MM-dd} (DTE {x.dte})");

                    var strikeDump = chain.Select(c => c.Strike).Distinct().OrderBy(s => s).Take(15);

                    SmartDebug($"[FF-DEBUG] {underlying}: expiries snapshot -> {string.Join(", ", expiryDump)}");
                    SmartDebug($"[FF-DEBUG] {underlying}: strikes snapshot -> {string.Join(", ", strikeDump.Select(s => s.ToString("F2")))}");
                }

                // Find ATM strike
                var strikes = chain.Select(c => c.Strike).Distinct().OrderBy(s => s).ToList();
                var atmStrike = strikes.OrderBy(s => Math.Abs(s - underlyingPrice)).FirstOrDefault();

                if (atmStrike <= 0)
                {
                    if (_config.DebugLogging)
                        SmartDebug($"[FF-DEBUG] {underlying}: No valid ATM strike");
                    return null;
                }

                // Get ATM calls with valid IV using strike range instead of exact match
                // This allows finding contracts across different expirations even if exact ATM strike isn't available
                var allStrikes = chain.Select(c => c.Strike).Distinct().OrderBy(s => s).ToList();
                var atmIndex = allStrikes.IndexOf(atmStrike);
                
                // Allow strikes within ±2 levels of ATM to ensure we find contracts at both 60 and 90 DTE
                const int strikeBuffer = 2;
                var minStrike = allStrikes[Math.Max(0, atmIndex - strikeBuffer)];
                var maxStrike = allStrikes[Math.Min(allStrikes.Count - 1, atmIndex + strikeBuffer)];
                
                var atmCalls = chain
                    .Where(c => c.Right == OptionRight.Call)
                    .Where(c => c.Strike >= minStrike && c.Strike <= maxStrike)
                    .Where(c => c.ImpliedVolatility > 0)
                    .OrderBy(c => Math.Abs(c.Strike - atmStrike))  // Prefer closest to true ATM
                    .ToList();

                if (atmCalls.Count < 2)
                {
                    if (_config.DebugLogging)
                    {
                        SmartDebug($"[FF-DEBUG] {underlying}: Need 2+ ATM calls in range [{minStrike:F2}, {maxStrike:F2}], found {atmCalls.Count}");

                        var byExpiry = atmCalls
                            .GroupBy(c => c.Expiry.Date)
                            .Select(g => $"{g.Key:yyyy-MM-dd}: {string.Join(", ", g.Select(c => c.Symbol.Value))}")
                            .Take(10);

                        SmartDebug($"[FF-DEBUG] {underlying}: ATM call symbols -> {string.Join("; ", byExpiry)}");
                    }
                    return null;
                }

                // Apply 60-90 DTE pair with ±5 day buffer
                const int targetNearDte = 60;
                const int targetFarDte = 90;
                const int dteBuffer = 1;

                var nearExpiry = atmCalls
                    .Where(c => (c.Expiry.Date - currentTime.Date).TotalDays >= targetNearDte - dteBuffer)
                    .Where(c => (c.Expiry.Date - currentTime.Date).TotalDays <= targetNearDte + dteBuffer)
                    .OrderBy(c => Math.Abs((c.Expiry.Date - currentTime.Date.AddDays(targetNearDte)).TotalDays))
                    .FirstOrDefault();

                var farExpiry = atmCalls
                    .Where(c => (c.Expiry.Date - currentTime.Date).TotalDays >= targetFarDte - dteBuffer)
                    .Where(c => (c.Expiry.Date - currentTime.Date).TotalDays <= targetFarDte + dteBuffer)
                    .Where(c => c.Expiry > nearExpiry?.Expiry)
                    .OrderBy(c => Math.Abs((c.Expiry.Date - currentTime.Date.AddDays(targetFarDte)).TotalDays))
                    .FirstOrDefault();

                if (nearExpiry == null || farExpiry == null)
                {
                    if (_config.DebugLogging)
                        SmartDebug($"[FF-DEBUG] {underlying}: No valid 60-90 DTE pair");
                    return null;
                }

                // Check liquidity: 20-day avg volume or OpenInterest
                // if (!MeetsLiquidityRequirement(nearExpiry.Symbol) || !MeetsLiquidityRequirement(farExpiry.Symbol))
                // {
                //     if (_config.DebugLogging)
                //         SmartLog($"[FF-DEBUG] {underlying}: Failed liquidity check");
                //     return null;
                // }

                // Calculate FF using forward volatility
                var nearDte = (int)(nearExpiry.Expiry.Date - currentTime.Date).TotalDays;
                var farDte = (int)(farExpiry.Expiry.Date - currentTime.Date).TotalDays;
                var nearIv = (decimal)nearExpiry.ImpliedVolatility;
                var farIv = (decimal)farExpiry.ImpliedVolatility;

                if (!VolatilityMath.TryComputeForwardVolatility(nearIv, nearDte, farIv, farDte, out var forwardVol))
                {
                    if (_config.DebugLogging)
                        SmartDebug($"[FF-DEBUG] {underlying}: Invalid forward volatility (nearDTE={nearDte}, farDTE={farDte}, nearIV={nearIv:F4}, farIV={farIv:F4})");
                    return null;
                }

                var ff = (nearIv / forwardVol) - 1m;

                if (_config.DebugLogging)
                {
                    SmartDebug($"[FF-DEBUG] {underlying}: nearIV={nearIv:F4}, farIV={farIv:F4}, forwardVol={forwardVol:F4}, FF={ff:P2}");
                }

            return new PendingCalendarSelection
            {
                NearSymbol = nearExpiry.Symbol,
                FarSymbol = farExpiry.Symbol,
                Strike = atmStrike,
                FF = ff,
                NearExpiry = nearExpiry.Expiry.Date,
                FarExpiry = farExpiry.Expiry.Date,
                NearDte = nearDte,
                FarDte = farDte
            };
        }

        // Check if contract meets liquidity requirement (20-day avg volume ≥ 10,000 or OI ≥ 5,000)
        private bool MeetsLiquidityRequirement(Symbol optionSymbol)
        {
            // Get 20 days of history for average volume calculation
            var history = Algorithm.History<OptionUniverse>(optionSymbol, 20, Resolution.Daily);
            
            if (history.Any())
            {
                var avgVolume = history.Average(x => x.Volume);
                if (avgVolume >= 10000)
                    return true;
            }

            // Fallback: check current OpenInterest
            var currentChain = Algorithm.OptionChain(Symbol.CreateCanonicalOption(optionSymbol.Underlying));
            var contract = currentChain.FirstOrDefault(c => c.Symbol == optionSymbol);
            
            if (contract != null && contract.OpenInterest >= 5000)
                return true;

            return false;
        }

        private void CleanupOptionsForUnderlying(Symbol underlying)
        {
            // Remove any active calendar positions for this underlying
            var positionsToRemove = _activeCalendars.Where(c => c.Underlying == underlying).ToList();
            foreach (var position in positionsToRemove)
            {
                Algorithm.RemoveOptionContract(position.NearLeg);
                Algorithm.RemoveOptionContract(position.FarLeg);
                _activeCalendars.Remove(position);
            }
        }

        private int CalculateQuantity(List<Leg> legs, Symbol underlying)
        {
            var budget = Portfolio.TotalPortfolioValue * _config.AllocationPerPosition;

            // Get combo quote to estimate cost
            var comboQuote = ComboQuote.FromSecurities(legs, Algorithm.Securities);
            if (comboQuote == null || comboQuote.NetMid <= 0)
            {
                SmartWarn($"Invalid combo quote for {underlying}, skipping position");
                return 0;
            }

            // Calendar is typically a net debit (buy far > sell near premium)
            // Each contract = 100 shares for equity options
            var costPerContract = Math.Abs(comboQuote.NetMid) * 100m;

            var quantity = (int)Math.Floor(budget / costPerContract);
            
            // Fallback: always place at least 1 contract if budget allows any position
            if (quantity < 1)
                quantity = 1;

            if (_config.DebugLogging)
            {
                SmartDebug($"[SIZING] {underlying}: budget=${budget:F2}, cost/contract=${costPerContract:F2}, quantity={quantity}");
            }

            return quantity;
        }

        protected override void CheckExitConditions(Slice slice)
        {
            var positionsToExit = new List<CalendarPosition>();

            // Check each active calendar for exit conditions
            foreach (var position in _activeCalendars.ToList())
            {
                // Primary exit: approaching front expiry
                // Compute the last tradable close before expiry using exchange hours
                // For Saturday expiries, this will be Friday 16:00 ET; for early closes, it respects actual close time
                var lastTradableClose = GetLastTradableCloseBeforeExpiry(position.NearLeg, position.NearExpiry);
                var minutesUntilClose = (lastTradableClose - slice.Time).TotalMinutes;

                if (minutesUntilClose <= _config.ExitMinutesBeforeFrontExpiry && minutesUntilClose >= -60)
                {
                    positionsToExit.Add(position);
                    SmartWarn($"Front expiry exit trigger: {position.Underlying} " +
                             $"(minutes until last tradable close: {minutesUntilClose:F0}, close time: {lastTradableClose:yyyy-MM-dd HH:mm:ss})");
                }
            }

            // Exit positions
            foreach (var position in positionsToExit)
            {
                ExitCalendarSpread(position, slice.Time, "Front Expiry");
            }
        }

        /// <summary>
        /// Compute the last tradable close before option expiry using exchange hours.
        /// For Saturday expiries (most options), returns Friday 16:00 ET.
        /// Respects early closes (e.g., day before Thanksgiving) by checking market hours.
        /// </summary>
        private DateTime GetLastTradableCloseBeforeExpiry(Symbol optionSymbol, DateTime expiryDate)
        {
            // Get the option security to access exchange hours
            if (!Algorithm.Securities.ContainsKey(optionSymbol))
            {
                // Fallback: assume standard Friday 16:00 for Saturday expiries
                if (expiryDate.DayOfWeek == DayOfWeek.Saturday)
                {
                    return expiryDate.Date.AddDays(-1).AddHours(16);
                }
                return expiryDate.Date.AddHours(16);
            }

            var security = Algorithm.Securities[optionSymbol];
            var exchangeHours = security.Exchange.Hours;

            // Find the last market close before expiry by walking backwards from expiry date
            var checkDate = expiryDate.Date;
            for (int i = 0; i <= 7; i++)  // Check up to 7 days back (handles long weekends)
            {
                var marketHours = exchangeHours.GetMarketHours(checkDate);
                if (!marketHours.IsClosedAllDay)
                {
                    // Found the last trading day - use standard 16:00 ET close
                    // (Early closes would need custom handling per exchange calendar)
                    var lastClose = checkDate.AddHours(16);
                    
                    if (_config.DebugLogging)
                    {
                        SmartDebug($"[EXIT-TIME] {optionSymbol}: expiry={expiryDate:yyyy-MM-dd}, last tradable close={lastClose:yyyy-MM-dd HH:mm:ss}");
                    }
                    
                    return lastClose;
                }
                
                checkDate = checkDate.AddDays(-1);
            }

            // Fallback if we couldn't find market hours (shouldn't happen)
            if (expiryDate.DayOfWeek == DayOfWeek.Saturday)
            {
                return expiryDate.Date.AddDays(-1).AddHours(16);
            }
            return expiryDate.Date.AddHours(16);
        }

        private void ExitCalendarSpread(CalendarPosition position, DateTime exitTime, string reason)
        {
            // Check if legs are tradable
            var nearTradable = Algorithm.Securities.ContainsKey(position.NearLeg) && 
                               Algorithm.Securities[position.NearLeg].IsTradable;
            var farTradable = Algorithm.Securities.ContainsKey(position.FarLeg) && 
                              Algorithm.Securities[position.FarLeg].IsTradable;

            List<OrderTicket> tickets = null;
            var holdingDays = (exitTime - position.EntryTime).TotalDays;

            if (nearTradable && farTradable)
            {
                // Build reverse legs to close position
                // We sold near, so buy it back; we bought far, so sell it
                var legs = new List<Leg>
                {
                    Leg.Create(position.NearLeg, 1),  // Buy back near
                    Leg.Create(position.FarLeg, -1)   // Sell far
                };

                var tag = $"FF Exit {reason}";
                tickets = ComboMarketOrder(legs, position.Quantity, tag: tag);

                if (tickets != null && tickets.Count > 0 && tickets.All(t => t.Status != OrderStatus.Invalid))
                {
                    SmartWarn($"[SUCCESS] Calendar spread exit: {position.Underlying} Reason={reason} " +
                             $"EntryFF={position.EntryFF:P2} HoldDays={holdingDays:F1} Strike=${position.Strike:F2}");
                }
                else
                {
                    SmartError($"[FAILED] Calendar spread exit for {position.Underlying}: invalid tickets");
                }
            }
            else
            {
                // Degraded exit: one or both legs are non-tradable
                SmartWarn($"[DEGRADED EXIT] {position.Underlying}: nearTradable={nearTradable}, farTradable={farTradable}");

                if (nearTradable)
                {
                    // Buy back the near leg only
                    var ticket = Algorithm.MarketOrder(position.NearLeg, position.Quantity, tag: $"FF Exit Near {reason}");
                    SmartWarn($"[PARTIAL] Closed near leg {position.NearLeg} x{position.Quantity}");
                }

                if (farTradable)
                {
                    // Sell the far leg only
                    var ticket = Algorithm.MarketOrder(position.FarLeg, -position.Quantity, tag: $"FF Exit Far {reason}");
                    SmartWarn($"[PARTIAL] Closed far leg {position.FarLeg} x{position.Quantity}");
                }

                if (!nearTradable && !farTradable)
                {
                    SmartError($"[FAILED] Both legs non-tradable for {position.Underlying} - cleaning up tracking only");
                }
                
                SmartWarn($"[DEGRADED SUCCESS] {position.Underlying} Reason={reason} " +
                         $"EntryFF={position.EntryFF:P2} HoldDays={holdingDays:F1} Strike=${position.Strike:F2}");
            }

            // Remove from active tracking
            _activeCalendars.Remove(position);
            ExitRestrictions.ClearPositionEntry(position.Underlying);
            
            // Unsubscribe from option contracts (safe to call even if non-tradable)
            try
            {
                Algorithm.RemoveOptionContract(position.NearLeg);
                Algorithm.RemoveOptionContract(position.FarLeg);
                
                if (_config.DebugLogging)
                    SmartDebug($"Removed option contracts: {position.NearLeg}, {position.FarLeg}");
            }
            catch (Exception ex)
            {
                SmartDebug($"Could not remove option contracts: {ex.Message}");
            }
        }

        protected override void OnGetPerformanceMetrics(Dictionary<string, double> metrics)
        {
            base.OnGetPerformanceMetrics(metrics);

            metrics["ActiveCalendars"] = _activeCalendars.Count;
            metrics["TrackedSymbols"] = _activeSymbols.Count;
            metrics["AssignmentsHandled"] = _assignmentsHandled;
            metrics["AssignmentPnL"] = (double)_assignmentPnL;

            if (_activeCalendars.Count > 0)
            {
                metrics["AverageEntryFF"] = (double)_activeCalendars.Average(c => c.EntryFF);
            }
        }

        /// <summary>
        /// Handle early assignment of the near (short) leg in a calendar spread.
        /// Close the far (long) leg and clean up calendar position tracking.
        /// Base class already handles equity liquidation via AssignmentHandler.
        /// </summary>
        protected override void OnAssignmentHandled(OrderEvent assignmentEvent)
        {
            SmartDebug($"[ASSIGNMENT] Calendar assignment detected: {assignmentEvent.Symbol}");
            
            // Find the matching calendar position
            var position = _activeCalendars.FirstOrDefault(c => c.NearLeg == assignmentEvent.Symbol);
            if (position == null)
            {
                var underlyingSymbol = assignmentEvent.Symbol.Underlying;
                position = _activeCalendars.FirstOrDefault(c => c.Underlying == underlyingSymbol);
            }
            
            if (position == null)
            {
                SmartWarn($"[ASSIGNMENT] No calendar position found for {assignmentEvent.Symbol}");
                return;
            }
            
            var pvBefore = Portfolio.TotalPortfolioValue;
            
            // Close the far (long) leg - we bought it, so sell it now
            var farTicket = Algorithm.MarketOrder(position.FarLeg, -position.Quantity, tag: "FF Assignment - Close Far Leg");
            SmartWarn($"[ASSIGNMENT] Closed far leg {position.FarLeg} x{position.Quantity}");
            
            // Track metrics
            var pvAfter = Portfolio.TotalPortfolioValue;
            _assignmentPnL += (pvAfter - pvBefore);
            _assignmentsHandled++;
            
            // Clean up tracking
            _activeCalendars.Remove(position);
            ExitRestrictions.ClearPositionEntry(position.Underlying);
            Algorithm.RemoveOptionContract(position.NearLeg);
            Algorithm.RemoveOptionContract(position.FarLeg);
            
            var holdDays = (Algorithm.Time - position.EntryTime).TotalDays;
            SmartWarn($"[ASSIGNMENT] {position.Underlying}: FF={position.EntryFF:P2}, Days={holdDays:F1}, " +
                        $"Total assignments={_assignmentsHandled}, PnL=${_assignmentPnL:F2}");
        }

        private class PendingCalendarSelection
        {
            public Symbol NearSymbol { get; set; }
            public Symbol FarSymbol { get; set; }
            public decimal Strike { get; set; }
            public decimal FF { get; set; }
            public DateTime NearExpiry { get; set; }
            public DateTime FarExpiry { get; set; }
            public int NearDte { get; set; }
            public int FarDte { get; set; }
        }

        private class CalendarPosition
        {
            public Symbol Underlying { get; set; }
            public Symbol NearLeg { get; set; }
            public Symbol FarLeg { get; set; }
            public DateTime NearExpiry { get; set; }
            public DateTime FarExpiry { get; set; }
            public decimal Strike { get; set; }
            public int Quantity { get; set; }
            public DateTime EntryTime { get; set; }
            public decimal EntryFF { get; set; }
            public List<OrderTicket> Tickets { 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
{
    /// <summary>
    /// Simple static helper for asset type detection and appropriate QC method calls.
    /// Handles the complexity of different asset types (equity, index, future) so strategies
    /// can switch between SPY/SPX/QQQ/ES with just a configuration change.
    /// </summary>
    public static class AssetManager
    {
        /// <summary>
        /// Cash indices that require AddIndex() instead of AddEquity()
        /// These are the QuantConnect supported cash indices
        /// </summary>
        private static readonly HashSet<string> CashIndices = new HashSet<string>
        {
            "SPX", "VIX", "NDX", "RUT", "DJX"
        };

        /// <summary>
        /// Future symbols that require AddFuture() instead of AddEquity()
        /// Expanded to include major futures across all asset classes
        /// </summary>
        private static readonly HashSet<string> Futures = new HashSet<string>
        {
            // Equity Index Futures
            "ES", "NQ", "YM", "RTY", "EMD", "NKD",
            
            // Energy Futures
            "CL", "NG", "RB", "HO", "BZ",
            
            // Metal Futures  
            "GC", "SI", "HG", "PA", "PL",
            
            // Agricultural Futures
            "ZC", "ZS", "ZW", "ZM", "ZL", "KC", "CT", "SB", "CC", "OJ",
            
            // Interest Rate & Bond Futures
            "ZB", "ZN", "ZF", "TU", "UB", "ED", "SR1", "SR3",
            
            // Currency Futures
            "6E", "6J", "6B", "6S", "6C", "6A", "6N", "6M", "E7", "J7",
            
            // Volatility Futures
            "VX",
            
            // Crypto Futures (if supported)
            "BTC", "ETH"
        };

        /// <summary>
        /// Add an asset to the algorithm using the appropriate QC method based on asset type.
        /// Automatically detects whether to use AddEquity(), AddIndex(), or AddFuture().
        /// For futures, supports continuous contracts with proper data normalization.
        /// </summary>
        /// <param name="context">The algorithm context providing access to algorithm and logger</param>
        /// <param name="symbol">The symbol to add (e.g., "SPY", "SPX", "ES")</param>
        /// <param name="resolution">Data resolution (default: Minute)</param>
        /// <param name="useContinuousContract">For futures: use continuous contract (default: true)</param>
        /// <param name="contractDepthOffset">For futures: contract depth (0=front month, 1=next month, default: 0)</param>
        /// <returns>The Security object for the added asset</returns>
        /// <exception cref="ArgumentNullException">If context or symbol is null</exception>
        /// <exception cref="ArgumentException">If symbol is empty or whitespace</exception>
        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;
                
                // Detect asset type and call appropriate QC method
                if (CashIndices.Contains(upperSymbol))
                {
                    ((dynamic)context.Logger).Info($"AssetManager: Adding index {upperSymbol} using AddIndex()");
                    addedSecurity = context.Algorithm.AddIndex(upperSymbol, resolution);
                }
                else if (Futures.Contains(upperSymbol))
                {
                    ((dynamic)context.Logger).Info($"AssetManager: Adding future {upperSymbol} using AddFuture() with continuous contract: {useContinuousContract}, extendedMarketHours: {extendedMarketHours}");
                    
                    if (useContinuousContract)
                    {
                        // Add future with continuous contract support
                        addedSecurity = context.Algorithm.AddFuture(upperSymbol, resolution, 
                            extendedMarketHours: extendedMarketHours,
                            dataMappingMode: DataMappingMode.OpenInterest,
                            dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
                            contractDepthOffset: contractDepthOffset);
                        
                        ((dynamic)context.Logger).Info($"AssetManager: Continuous contract configured - DataMapping: OpenInterest, DataNormalization: BackwardsRatio, Depth: {contractDepthOffset}");
                    }
                    else
                    {
                        // Simple future addition without continuous contract
                        addedSecurity = context.Algorithm.AddFuture(upperSymbol, resolution, extendedMarketHours: extendedMarketHours);
                    }
                }
                else
                {
                    ((dynamic)context.Logger).Info($"AssetManager: Adding equity {upperSymbol} using AddEquity()");
                    addedSecurity = context.Algorithm.AddEquity(upperSymbol, resolution);
                }

                // Verify the security was successfully added and log data availability
                if (addedSecurity != null)
                {
                    var isInSecurities = context.Algorithm.Securities.ContainsKey(addedSecurity.Symbol);
                    ((dynamic)context.Logger).Info($"AssetManager: [SUCCESS] {upperSymbol} successfully added to Securities collection: {isInSecurities}");
                    ((dynamic)context.Logger).Info($"AssetManager: Security details - Type: {addedSecurity.Type}, Resolution: {addedSecurity.Subscriptions.GetHighestResolution()}, Exchange: {addedSecurity.Exchange}");
                    
                    // Check if security has current price data (indicates data subscription is working)
                    var hasPrice = addedSecurity.Price > 0;
                    var priceStatus = hasPrice ? $"${addedSecurity.Price:F2}" : "No price data yet";
                    ((dynamic)context.Logger).Info($"AssetManager: Price data status: {priceStatus}");
                    
                    if (!hasPrice)
                    {
                        ((dynamic)context.Logger).Info($"AssetManager: [WARNING] No price data yet for {upperSymbol} - this is normal during initialization, data should arrive during backtest");
                    }
                }
                else
                {
                    ((dynamic)context.Logger).Error($"AssetManager: [FAILED] Failed to add {upperSymbol} - returned null security");
                }

                return addedSecurity;
            }
            catch (Exception ex)
            {
                ((dynamic)context.Logger).Error($"AssetManager: Failed to add asset {upperSymbol}: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// Add an options chain for the underlying asset using the appropriate QC method.
        /// Handles special cases like SPX->SPXW mapping automatically.
        /// </summary>
        /// <param name="context">The algorithm context providing access to algorithm and logger</param>
        /// <param name="underlying">The underlying security</param>
        /// <param name="resolution">Data resolution (default: Minute)</param>
        /// <returns>The Symbol for the options chain</returns>
        /// <exception cref="ArgumentNullException">If context or underlying is null</exception>
        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)
                {
                    // Special case for SPX: use SPXW (weekly) options
                    if (symbol == "SPX")
                    {
                        ((dynamic)context.Logger).Info($"AssetManager: Adding SPX index options using SPXW with {resolution} resolution");
                        // Pre-add diagnostics for universe and requested resolution
                        ((dynamic)context.Logger).Info($"AssetManager: [UNIVERSE] UniverseSettings.Resolution={context.Algorithm.UniverseSettings.Resolution}, FillForward={context.Algorithm.UniverseSettings.FillForward}, ExtendedMktHours={context.Algorithm.UniverseSettings.ExtendedMarketHours}");
                        
                        // Force minute resolution explicitly for intraday option trading
                        var requestedResolution = resolution == Resolution.Daily ? Resolution.Minute : resolution;
                        if (requestedResolution != resolution)
                        {
                            ((dynamic)context.Logger).Info($"AssetManager: [RESOLUTION OVERRIDE] Changing from {resolution} to {requestedResolution} for intraday option data");
                        }
                        
                        optionSymbol = context.Algorithm.AddIndexOption(underlying.Symbol, "SPXW", requestedResolution).Symbol;
                        ((dynamic)context.Logger).Info($"AssetManager: [RESOLUTION VERIFICATION] Requested: {requestedResolution}");
                    }
                    else
                    {
                        // Other indices use standard index options
                        ((dynamic)context.Logger).Info($"AssetManager: Adding index options for {symbol} with {resolution} resolution");
                        // Pre-add diagnostics for universe and requested resolution
                        ((dynamic)context.Logger).Info($"AssetManager: [UNIVERSE] UniverseSettings.Resolution={context.Algorithm.UniverseSettings.Resolution}, FillForward={context.Algorithm.UniverseSettings.FillForward}, ExtendedMktHours={context.Algorithm.UniverseSettings.ExtendedMarketHours}");
                        
                        // Force minute resolution explicitly for intraday option trading
                        var requestedResolution = resolution == Resolution.Daily ? Resolution.Minute : resolution;
                        if (requestedResolution != resolution)
                        {
                            ((dynamic)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);
                        ((dynamic)context.Logger).Info($"AssetManager: [RESOLUTION VERIFICATION] Requested: {requestedResolution}");
                    }
                }
                else if (underlying.Type == SecurityType.Future)
                {
                    ((dynamic)context.Logger).Info($"AssetManager: Adding future options for {symbol}");
                    context.Algorithm.AddFutureOption(underlying.Symbol);
                    optionSymbol = Symbol.CreateCanonicalOption(underlying.Symbol);
                }
                else
                {
                    // Equity options
                    ((dynamic)context.Logger).Info($"AssetManager: Adding equity options for {symbol}");
                    optionSymbol = context.Algorithm.AddOption(symbol, resolution).Symbol;
                }

                // [DEBUG] COMPREHENSIVE OPTIONS CHAIN VERIFICATION
                if (optionSymbol != null)
                {
                    ((dynamic)context.Logger).Info($"AssetManager: [SUCCESS] Options chain created for {symbol}");
                    ((dynamic)context.Logger).Info($"AssetManager: [DEBUG] DETAILED Option Symbol Analysis:");
                    ((dynamic)context.Logger).Info($"   optionSymbol: {optionSymbol}");
                    ((dynamic)context.Logger).Info($"   optionSymbol.Value: {optionSymbol.Value}");
                    ((dynamic)context.Logger).Info($"   optionSymbol.SecurityType: {optionSymbol.SecurityType}");
                    ((dynamic)context.Logger).Info($"   optionSymbol.ID: {optionSymbol.ID}");
                    ((dynamic)context.Logger).Info($"   optionSymbol.HasCanonical: {optionSymbol.HasCanonical()}");
                    if (optionSymbol.HasCanonical())
                    {
                        ((dynamic)context.Logger).Info($"   optionSymbol.Canonical: {optionSymbol.Canonical}");
                    }
                    
                    // Check if option symbol is in Securities collection
                    var isInSecurities = context.Algorithm.Securities.ContainsKey(optionSymbol);
                    ((dynamic)context.Logger).Info($"AssetManager: Option chain in Securities collection: {isInSecurities}");
                    
                    // [DEBUG] VERIFY OPTION SECURITY DETAILS
                    if (isInSecurities)
                    {
                        var optionSecurity = context.Algorithm.Securities[optionSymbol];
                        
                        // Enumerate and log all subscriptions with detail
                        var subscriptionResolutions = new System.Collections.Generic.List<Resolution>();
                        foreach (var sub in optionSecurity.Subscriptions)
                        {
                            subscriptionResolutions.Add(sub.Resolution);
                            ((dynamic)context.Logger).Info($"AssetManager: [SUB] DataType={sub.Type?.Name}, Resolution={sub.Resolution}, TickType={sub.TickType}");
                        }
                        var distinctRes = subscriptionResolutions.Distinct().OrderBy(x => x).ToList();
                        
                        ((dynamic)context.Logger).Info($"AssetManager: [DEBUG] Option Security Details:");
                        ((dynamic)context.Logger).Info($"   Type: {optionSecurity.Type}");
                        ((dynamic)context.Logger).Info($"   Subscriptions: {string.Join(", ", distinctRes)}");
                        ((dynamic)context.Logger).Info($"   Exchange: {optionSecurity.Exchange}");
                        ((dynamic)context.Logger).Info($"   IsMarketOpen: {optionSecurity.Exchange.ExchangeOpen}");
                        
                        // Check if we have minute-level subscriptions for intraday trading
                        var hasMinuteData = distinctRes.Contains(Resolution.Minute) || 
                                          distinctRes.Contains(Resolution.Second);
                        
                        if (!hasMinuteData && distinctRes.All(r => r == Resolution.Daily))
                        {
                            ((dynamic)context.Logger).Warning($"AssetManager: [RESOLUTION WARNING] Only Daily resolution subscriptions found");
                            ((dynamic)context.Logger).Warning($"AssetManager: [RESOLUTION WARNING] Intraday option chains may not be available");
                            ((dynamic)context.Logger).Warning($"AssetManager: [HINT] If Minute was requested, ensure UniverseSettings.Resolution=Minute before adding options.");
                        }
                        else
                        {
                            ((dynamic)context.Logger).Info($"AssetManager: [RESOLUTION SUCCESS] Intraday subscriptions available");
                        }
                    }
                    
                    // [DEBUG] LOG ALL SECURITIES THAT CONTAIN OPTION-RELATED SYMBOLS
                    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())
                    {
                        ((dynamic)context.Logger).Info($"AssetManager: [DEBUG] Option-related securities in collection ({optionRelatedSecurities.Count}):");
                        foreach (var sec in optionRelatedSecurities)
                        {
                            ((dynamic)context.Logger).Info($"   {sec} (Type: {sec.SecurityType})");
                        }
                    }
                    else
                    {
                        ((dynamic)context.Logger).Info($"AssetManager: [ERROR] No option-related securities found in collection");
                    }
                    
                    // Additional verification for option chain subscription
                    ((dynamic)context.Logger).Info($"AssetManager: Option chain ready for filtering and data feed");
                    // Note: The following line previously suggested LOCAL data; remove misleading data-source implication
                    // ((dynamic)context.Logger).Info($"AssetManager: [DEBUG] Data Source: LOCAL - Using generated option data for testing");
                    ((dynamic)context.Logger).Info($"AssetManager: [TARGET] Symbol to use for slice.OptionChains access: {optionSymbol}");
                }
                else
                {
                    ((dynamic)context.Logger).Error($"AssetManager: [FAILED] Failed to create options chain for {symbol} - returned null symbol");
                }

                return optionSymbol;
            }
            catch (Exception ex)
            {
                ((dynamic)context.Logger).Error($"AssetManager: Failed to add options chain for {symbol}: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// Check if a symbol is a supported cash index
        /// </summary>
        /// <param name="symbol">The symbol to check</param>
        /// <returns>True if the symbol is a cash index</returns>
        public static bool IsIndex(string symbol)
        {
            if (string.IsNullOrWhiteSpace(symbol))
                return false;
            
            return CashIndices.Contains(symbol.ToUpperInvariant());
        }

        /// <summary>
        /// Check if a symbol is a supported future
        /// </summary>
        /// <param name="symbol">The symbol to check</param>
        /// <returns>True if the symbol is a future</returns>
        public static bool IsFuture(string symbol)
        {
            if (string.IsNullOrWhiteSpace(symbol))
                return false;
            
            return Futures.Contains(symbol.ToUpperInvariant());
        }

        /// <summary>
        /// Check if a symbol is treated as an equity (default case)
        /// </summary>
        /// <param name="symbol">The symbol to check</param>
        /// <returns>True if the symbol is treated as an equity</returns>
        public static bool IsEquity(string symbol)
        {
            if (string.IsNullOrWhiteSpace(symbol))
                return false;
            
            return !IsIndex(symbol) && !IsFuture(symbol);
        }

        /// <summary>
        /// Get the asset type for a symbol
        /// </summary>
        /// <param name="symbol">The symbol to check</param>
        /// <returns>Asset type as string: "INDEX", "FUTURE", or "EQUITY"</returns>
        public static string GetAssetType(string symbol)
        {
            if (IsIndex(symbol)) return "INDEX";
            if (IsFuture(symbol)) return "FUTURE";
            return "EQUITY";
        }

        /// <summary>
        /// Get a list of all supported cash indices
        /// </summary>
        /// <returns>Array of supported cash index symbols</returns>
        public static string[] GetSupportedIndices()
        {
            return new string[CashIndices.Count];
        }

        /// <summary>
        /// Get a list of all supported futures
        /// </summary>
        /// <returns>Array of supported future symbols</returns>
        public static string[] GetSupportedFutures()
        {
            return Futures.ToArray();
        }

        /// <summary>
        /// Get futures by category for easier strategy configuration
        /// </summary>
        /// <param name="category">Category: "equity", "energy", "metals", "agricultural", "bonds", "currency", "volatility", "crypto"</param>
        /// <returns>Array of futures symbols in the specified category</returns>
        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];
            }
        }

        /// <summary>
        /// Check if a futures contract rollover is approaching based on days to expiration
        /// </summary>
        /// <param name="context">Algorithm context</param>
        /// <param name="futureSymbol">Future symbol to check</param>
        /// <param name="rolloverDays">Number of days before expiration to trigger rollover (default: 5)</param>
        /// <returns>True if rollover is needed</returns>
        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
            {
                // For continuous contracts, QC handles rollover automatically
                // This method is for manual contract management if needed
                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
{
    /// <summary>
    /// Static helper for multi-asset specific parameters and calculations.
    /// Provides asset-specific defaults for strike ranges, position sizing, and option filtering
    /// to enable easy switching between SPX, QQQ, AAPL, etc. with optimal parameters for each.
    /// </summary>
    public static class MultiAssetHelper
    {
        /// <summary>
        /// Asset-specific volatility characteristics for position sizing and strike selection
        /// </summary>
        private static readonly Dictionary<string, AssetProfile> AssetProfiles = new Dictionary<string, AssetProfile>
        {
            // Major Indices - Higher volatility, wider strikes, high margin requirements
            ["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 },
            
            // ETFs - Moderate volatility, lower margin requirements
            ["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 },
            
            // Individual Stocks - Variable volatility, moderate margin requirements
            ["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 },
            
            // Futures - High volatility, fewer positions, high margin requirements
            ["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 },
        };

        /// <summary>
        /// Add multiple assets with options chains to the algorithm.
        /// Uses AssetManager for each symbol and returns configured securities.
        /// </summary>
        /// <param name="context">The algorithm context providing access to algorithm and logger</param>
        /// <param name="symbols">Array of symbols to add</param>
        /// <param name="resolution">Data resolution</param>
        /// <returns>Dictionary mapping symbols to their Security and options Symbol</returns>
        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
                {
                    // Add the underlying asset
                    var security = AssetManager.AddAsset(context, symbol, resolution);
                    
                    // Add options chain
                    var optionsSymbol = AssetManager.AddOptionsChain(context, security, resolution);
                    
                    result[symbol.ToUpperInvariant()] = (security, optionsSymbol);
                    
                    ((dynamic)context.Logger).Debug($"MultiAssetHelper: Successfully added {symbol} with options chain");
                }
                catch (Exception ex)
                {
                    ((dynamic)context.Logger).Error($"MultiAssetHelper: Failed to add {symbol}: {ex.Message}");
                    throw;
                }
            }

            return result;
        }

        /// <summary>
        /// Get asset-specific strike width for option selection.
        /// Returns wider ranges for higher volatility assets.
        /// </summary>
        /// <param name="symbol">The underlying symbol</param>
        /// <param name="baseStrikeWidth">Base strike width (in dollar terms or percentage)</param>
        /// <returns>Adjusted strike width for the asset</returns>
        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;
            }

            // Default for unknown symbols
            return baseStrikeWidth;
        }

        /// <summary>
        /// Get asset-specific position sizing limits.
        /// Different assets have different optimal position counts based on liquidity and volatility.
        /// </summary>
        /// <param name="symbol">The underlying symbol</param>
        /// <param name="totalPortfolioValue">Total portfolio value for percentage-based sizing</param>
        /// <returns>Recommended min and max position sizes</returns>
        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))
            {
                // Calculate recommended allocation based on volatility
                // Higher volatility = smaller allocation per position
                var recommendedAllocation = Math.Max(0.05m, Math.Min(0.2m, 0.15m / profile.TypicalVolatility));
                
                return (profile.MinPosition, profile.MaxPosition, recommendedAllocation);
            }

            // Default for unknown symbols
            return (1, 5, 0.1m);
        }

        /// <summary>
        /// Get asset-specific delta targets for option selection.
        /// Adjusts delta ranges based on typical volatility characteristics.
        /// </summary>
        /// <param name="symbol">The underlying symbol</param>
        /// <param name="baseDeltaMin">Base minimum delta</param>
        /// <param name="baseDeltaMax">Base maximum delta</param>
        /// <returns>Adjusted delta range for the asset</returns>
        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))
            {
                // For higher volatility assets, use slightly tighter delta ranges
                if (profile.TypicalVolatility > 0.4m)
                {
                    // High vol assets: tighter delta range
                    var adjustment = 0.05m;
                    return (baseDeltaMin + adjustment, baseDeltaMax - adjustment);
                }
                else if (profile.TypicalVolatility < 0.2m)
                {
                    // Low vol assets: wider delta range
                    var adjustment = 0.03m;
                    return (Math.Max(0.05m, baseDeltaMin - adjustment), Math.Min(0.45m, baseDeltaMax + adjustment));
                }
            }

            // Default unchanged
            return (baseDeltaMin, baseDeltaMax);
        }

        /// <summary>
        /// Check if symbol has options available and is suitable for options strategies
        /// </summary>
        /// <param name="symbol">The symbol to check</param>
        /// <returns>True if symbol is known to have liquid options</returns>
        public static bool HasLiquidOptions(string symbol)
        {
            if (string.IsNullOrWhiteSpace(symbol))
                return false;

            var upperSymbol = symbol.ToUpperInvariant();
            return AssetProfiles.ContainsKey(upperSymbol);
        }

        /// <summary>
        /// Get all supported symbols for multi-asset strategies
        /// </summary>
        /// <returns>Array of all supported symbols</returns>
        public static string[] GetSupportedSymbols()
        {
            var result = new string[AssetProfiles.Count];
            AssetProfiles.Keys.CopyTo(result, 0);
            return result;
        }

        /// <summary>
        /// Get asset profile information for debugging/logging
        /// </summary>
        /// <param name="symbol">The symbol to look up</param>
        /// <returns>Asset profile or null if not found</returns>
        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;
        }

        /// <summary>
        /// Get asset-specific strike increment for rounding option strikes.
        /// Different assets have different strike intervals (SPX=5, SPY=1, etc.)
        /// </summary>
        /// <param name="symbol">The underlying symbol</param>
        /// <returns>Strike increment for rounding</returns>
        public static decimal GetStrikeIncrement(string symbol)
        {
            if (string.IsNullOrWhiteSpace(symbol))
                return 1m;

            var upperSymbol = symbol.ToUpperInvariant();
            
            // Asset-specific strike increments
            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
            }
        }
    }

    /// <summary>
    /// Asset-specific profile containing volatility and position characteristics
    /// </summary>
    public class AssetProfile
    {
        /// <summary>
        /// Typical implied volatility for the asset (used for position sizing)
        /// </summary>
        public decimal TypicalVolatility { get; set; }

        /// <summary>
        /// Multiplier for strike width selection (1.0 = default, >1.0 = wider strikes)
        /// </summary>
        public decimal StrikeWidthMultiplier { get; set; }

        /// <summary>
        /// Minimum recommended number of positions for this asset
        /// </summary>
        public int MinPosition { get; set; }

        /// <summary>
        /// Maximum recommended number of positions for this asset
        /// </summary>
        public int MaxPosition { get; set; }

        /// <summary>
        /// Estimated margin requirement as multiplier of underlying price (e.g., 1.7 = 170% of underlying)
        /// </summary>
        public decimal EstimatedMarginMultiplier { get; set; } = 0.3m;

        /// <summary>
        /// Minimum account size recommended for trading this asset's options
        /// </summary>
        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
{
    /// <summary>
    /// Simple extension methods for order retry logic and enhanced order management.
    /// Provides minimal retry functionality without complex order management overhead.
    /// </summary>
    public static class OrderExtensions
    {
        /// <summary>
        /// Submit an order with automatic retry logic on failures.
        /// Retries failed orders with configurable attempts and delays.
        /// </summary>
        /// <param name="algorithm">The QC algorithm instance</param>
        /// <param name="request">The order request to submit</param>
        /// <param name="maxRetries">Maximum number of retry attempts (default: 3)</param>
        /// <param name="retryDelay">Delay between retry attempts (default: 1 second)</param>
        /// <returns>The OrderTicket from the successful submission, or null if all retries failed</returns>
        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}");
                    
                    // Submit the order using QC's standard method
                    if (request.OrderType == OrderType.Market)
                    {
                        ticket = algorithm.MarketOrder(request.Symbol, request.Quantity, tag: request.Tag);
                    }
                    else if (request.OrderType == OrderType.Limit)
                    {
                        ticket = algorithm.LimitOrder(request.Symbol, request.Quantity, request.LimitPrice, tag: request.Tag);
                    }
                    else
                    {
                        // For other order types, try the basic Submit method
                        ticket = algorithm.MarketOrder(request.Symbol, request.Quantity, tag: request.Tag);
                    }
                    
                    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}");
                    
                    // Don't retry on the last attempt
                    if (attempt <= maxRetries)
                    {
                        algorithm.Debug($"OrderExtensions: Waiting {retryDelay.TotalSeconds} seconds before retry");
                        Thread.Sleep(retryDelay);
                    }
                }
            }

            // All attempts failed
            algorithm.Error($"OrderExtensions: Failed to submit order after {maxRetries + 1} attempts. Last error: {lastException?.Message}");
            return null;
        }

        /// <summary>
        /// Submit a market order with retry logic.
        /// Convenience method for the most common order type.
        /// </summary>
        /// <param name="algorithm">The QC algorithm instance</param>
        /// <param name="symbol">The symbol to trade</param>
        /// <param name="quantity">The quantity to trade</param>
        /// <param name="maxRetries">Maximum number of retry attempts (default: 3)</param>
        /// <param name="retryDelay">Delay between retry attempts (default: 1 second)</param>
        /// <returns>The OrderTicket from successful submission, or null if failed</returns>
        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);
        }

        /// <summary>
        /// Submit a limit order with retry logic.
        /// Convenience method for limit orders.
        /// </summary>
        /// <param name="algorithm">The QC algorithm instance</param>
        /// <param name="symbol">The symbol to trade</param>
        /// <param name="quantity">The quantity to trade</param>
        /// <param name="limitPrice">The limit price</param>
        /// <param name="maxRetries">Maximum number of retry attempts (default: 3)</param>
        /// <param name="retryDelay">Delay between retry attempts (default: 1 second)</param>
        /// <returns>The OrderTicket from successful submission, or null if failed</returns>
        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);
        }

        /// <summary>
        /// Check if an order ticket represents a successful order.
        /// Provides simple success/failure checking.
        /// </summary>
        /// <param name="ticket">The order ticket to check</param>
        /// <returns>True if the order was successful, false otherwise</returns>
        public static bool WasSuccessful(this OrderTicket ticket)
        {
            if (ticket == null)
                return false;

            // Check order status for success indicators
            var status = ticket.Status;
            return status == OrderStatus.Filled || 
                   status == OrderStatus.PartiallyFilled || 
                   status == OrderStatus.Submitted;
        }

        /// <summary>
        /// Check if an order ticket represents a failed order.
        /// Provides simple failure checking.
        /// </summary>
        /// <param name="ticket">The order ticket to check</param>
        /// <returns>True if the order failed, false otherwise</returns>
        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;
        }

        /// <summary>
        /// Get a human-readable description of the order status.
        /// Useful for logging and debugging.
        /// </summary>
        /// <param name="ticket">The order ticket to describe</param>
        /// <returns>A descriptive string of the order status</returns>
        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}"
            };
        }

        /// <summary>
        /// Submit a Market-On-Close order with basic error handling and logging.
        /// </summary>
        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);
                    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;
        }

        /// <summary>
        /// Submit a Market-On-Open order with basic error handling and logging.
        /// </summary>
        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);
                    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
{
    /// <summary>
    /// Simple static helper for position size calculations.
    /// Provides percentage-based and fixed sizing methods for different asset types.
    /// Handles the complexity of options contract multipliers and different security types.
    /// </summary>
    public static class PositionSizer
    {
        /// <summary>
        /// Standard options contract multiplier (100 shares per contract)
        /// </summary>
        public const int StandardOptionsMultiplier = 100;

        /// <summary>
        /// Calculate position quantity based on percentage allocation of portfolio.
        /// Works for stocks, futures, and other direct securities.
        /// </summary>
        /// <param name="portfolio">The algorithm's portfolio manager</param>
        /// <param name="allocationPercent">Percentage of portfolio to allocate (e.g., 0.1 = 10%)</param>
        /// <param name="price">Current price of the security</param>
        /// <returns>Quantity to purchase (number of shares/contracts)</returns>
        /// <exception cref="ArgumentNullException">If portfolio is null</exception>
        /// <exception cref="ArgumentException">If allocation or price is invalid</exception>
        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));

            // Calculate allocation amount from total portfolio value
            var totalValue = portfolio.TotalPortfolioValue;
            var allocationAmount = totalValue * allocationPercent;
            
            // Calculate quantity based on price
            var quantity = (int)Math.Floor(allocationAmount / price);
            
            return Math.Max(0, quantity);
        }

        /// <summary>
        /// Calculate position quantity for options contracts.
        /// Accounts for the contract multiplier (typically 100 shares per contract).
        /// </summary>
        /// <param name="portfolio">The algorithm's portfolio manager</param>
        /// <param name="allocationPercent">Percentage of portfolio to allocate (e.g., 0.1 = 10%)</param>
        /// <param name="premium">Premium price per contract</param>
        /// <param name="multiplier">Contract multiplier (default: 100 for standard options)</param>
        /// <returns>Number of options contracts to purchase</returns>
        /// <exception cref="ArgumentNullException">If portfolio is null</exception>
        /// <exception cref="ArgumentException">If allocation, premium, or multiplier is invalid</exception>
        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));

            // Calculate allocation amount from total portfolio value
            var totalValue = portfolio.TotalPortfolioValue;
            var allocationAmount = totalValue * allocationPercent;
            
            // Calculate cost per contract (premium * multiplier)
            var costPerContract = premium * multiplier;
            
            // Calculate number of contracts
            var contracts = (int)Math.Floor(allocationAmount / costPerContract);
            
            return Math.Max(0, contracts);
        }

        /// <summary>
        /// Calculate position quantity using a fixed dollar amount.
        /// Alternative to percentage-based sizing.
        /// </summary>
        /// <param name="dollarAmount">Fixed dollar amount to invest</param>
        /// <param name="price">Current price of the security</param>
        /// <returns>Quantity to purchase</returns>
        /// <exception cref="ArgumentException">If dollar amount or price is invalid</exception>
        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);
        }

        /// <summary>
        /// Calculate options quantity using a fixed dollar amount.
        /// </summary>
        /// <param name="dollarAmount">Fixed dollar amount to invest</param>
        /// <param name="premium">Premium price per contract</param>
        /// <param name="multiplier">Contract multiplier (default: 100 for standard options)</param>
        /// <returns>Number of options contracts to purchase</returns>
        /// <exception cref="ArgumentException">If parameters are invalid</exception>
        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));

            // Calculate cost per contract
            var costPerContract = premium * multiplier;
            
            // Calculate number of contracts
            var contracts = (int)Math.Floor(dollarAmount / costPerContract);
            
            return Math.Max(0, contracts);
        }

        /// <summary>
        /// Calculate the maximum safe position size to avoid overcommitting portfolio.
        /// Includes a safety buffer to account for price movements and fees.
        /// </summary>
        /// <param name="portfolio">The algorithm's portfolio manager</param>
        /// <param name="allocationPercent">Desired percentage allocation</param>
        /// <param name="price">Current price of the security</param>
        /// <param name="safetyBuffer">Safety buffer as percentage (e.g., 0.05 = 5% buffer)</param>
        /// <returns>Safe quantity to purchase</returns>
        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));

            // Adjust allocation for safety buffer
            var adjustedAllocation = allocationPercent * (1 - safetyBuffer);
            
            return CalculateQuantity(portfolio, adjustedAllocation, price);
        }

        /// <summary>
        /// Calculate safe options quantity with buffer.
        /// </summary>
        /// <param name="portfolio">The algorithm's portfolio manager</param>
        /// <param name="allocationPercent">Desired percentage allocation</param>
        /// <param name="premium">Premium price per contract</param>
        /// <param name="safetyBuffer">Safety buffer as percentage (default: 5%)</param>
        /// <param name="multiplier">Contract multiplier (default: 100)</param>
        /// <returns>Safe number of options contracts to purchase</returns>
        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));

            // Adjust allocation for safety buffer
            var adjustedAllocation = allocationPercent * (1 - safetyBuffer);
            
            return CalculateOptionsQuantity(portfolio, adjustedAllocation, premium, multiplier);
        }

        /// <summary>
        /// Get the effective buying power for position sizing.
        /// Accounts for existing positions and available cash.
        /// </summary>
        /// <param name="portfolio">The algorithm's portfolio manager</param>
        /// <returns>Available buying power for new positions</returns>
        public static decimal GetAvailableBuyingPower(SecurityPortfolioManager portfolio)
        {
            if (portfolio == null)
                throw new ArgumentNullException(nameof(portfolio));

            // Use QC's available cash as buying power
            return portfolio.Cash;
        }

        /// <summary>
        /// Calculate position value for risk management.
        /// Useful for tracking total exposure.
        /// </summary>
        /// <param name="quantity">Number of shares/contracts</param>
        /// <param name="price">Current price</param>
        /// <param name="multiplier">Contract multiplier (1 for stocks, 100 for options)</param>
        /// <returns>Total position value</returns>
        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 CoreAlgo.Architecture.Core.Models;
using QuantConnect;
using QuantConnect.Algorithm;
using QuantConnect.Data.Market;
using QuantConnect.Securities.Option;

namespace CoreAlgo.Architecture.QC.Helpers
{
    /// <summary>
    /// Calculates optimal strike ranges based on delta targets and market conditions
    /// </summary>
    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;
        }

        /// <summary>
        /// Represents delta targets for option strategies
        /// </summary>
        public class DeltaTargets
        {
            public decimal ShortPut { get; set; }
            public decimal LongPut { get; set; }
            public decimal ShortCall { get; set; }
            public decimal LongCall { get; set; }
        }

        /// <summary>
        /// Represents selected strikes for a strategy
        /// </summary>
        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; }
        }

        /// <summary>
        /// Gets asset-specific delta targets adjusted for current volatility
        /// </summary>
        public DeltaTargets GetAssetSpecificDeltas(string symbol, decimal currentVolatility)
        {
            // Get base delta targets from MultiAssetHelper
            var (deltaMin, deltaMax) = MultiAssetHelper.GetAssetDeltaTargets(symbol, 0.15m, 0.25m);
            
            // Adjust deltas based on volatility regime
            var adjustment = currentVolatility > _highVolThreshold ? _volAdjustment : 0m;
            
            return new DeltaTargets
            {
                // In high volatility, move strikes further OTM
                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)
            };
        }

        /// <summary>
        /// Calculates optimal strike range for the given option chain
        /// </summary>
        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 };

            // Separate puts and calls
            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();

            // Find strikes closest to target deltas
            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);

            // Validate and adjust strikes
            return ValidateAndAdjustStrikes(strikes, chain.Symbol.Underlying.Value);
        }

        /// <summary>
        /// Gets the at-the-money strike price
        /// </summary>
        public decimal GetATMStrike(OptionChain chain)
        {
            var underlyingPrice = chain.Underlying.Price;
            
            // Find the strike closest to the underlying price
            var atmStrike = chain
                .Select(x => x.Strike)
                .Distinct()
                .OrderBy(strike => Math.Abs(strike - underlyingPrice))
                .FirstOrDefault();

            return atmStrike > 0 ? atmStrike : underlyingPrice;
        }

        /// <summary>
        /// Finds the strike price closest to the target delta
        /// </summary>
        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)
            {
                // Skip if Greeks aren't available
                if (contract.Greeks?.Delta == null) continue;

                var deltaDiff = Math.Abs(contract.Greeks.Delta - targetDelta);
                if (deltaDiff < bestDeltaDiff)
                {
                    bestDeltaDiff = deltaDiff;
                    bestContract = contract;
                }
            }

            // If no contract with Greeks found, use strike selection based on distance from ATM
            if (bestContract == null)
            {
                return EstimateStrikeByDelta(contracts, targetDelta, atmStrike);
            }

            return bestContract.Strike;
        }

        /// <summary>
        /// Estimates strike when Greeks aren't available
        /// </summary>
        private decimal EstimateStrikeByDelta(List<OptionContract> contracts, decimal targetDelta, decimal atmStrike)
        {
            var isPut = contracts.FirstOrDefault()?.Right == OptionRight.Put;
            var absTargetDelta = Math.Abs(targetDelta);

            // Rough approximation: 0.50 delta at ATM, decreases as we move OTM
            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);

            // Find closest available strike
            return contracts
                .Select(x => x.Strike)
                .OrderBy(strike => Math.Abs(strike - targetStrike))
                .FirstOrDefault();
        }

        /// <summary>
        /// Validates and adjusts strikes to ensure proper spread structure
        /// </summary>
        private StrikeRange ValidateAndAdjustStrikes(StrikeRange strikes, string symbol)
        {
            var increment = MultiAssetHelper.GetStrikeIncrement(symbol);
            var minSpreadWidth = increment * 2; // Minimum 2 strikes apart

            // Ensure put spreads are valid
            if (strikes.ShortPutStrike - strikes.LongPutStrike < minSpreadWidth)
            {
                strikes.LongPutStrike = strikes.ShortPutStrike - minSpreadWidth;
            }

            // Ensure call spreads are valid
            if (strikes.LongCallStrike - strikes.ShortCallStrike < minSpreadWidth)
            {
                strikes.LongCallStrike = strikes.ShortCallStrike + minSpreadWidth;
            }

            // Round strikes to proper increments
            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;
        }

        /// <summary>
        /// Rounds a strike price to the nearest valid increment
        /// </summary>
        private decimal RoundToIncrement(decimal strike, decimal increment)
        {
            return Math.Round(strike / increment) * increment;
        }

        /// <summary>
        /// Validates strike spacing using structural analysis instead of strategy names
        /// </summary>
        public bool ValidateStrikeSpacing(StrikeRange strikes)
        {
            // Detect strategy pattern from strike structure
            var hasLongStrikes = strikes.LongPutStrike > 0 || strikes.LongCallStrike > 0;
            var hasShortStrikes = strikes.ShortPutStrike > 0 || strikes.ShortCallStrike > 0;
            var hasBothPutAndCall = strikes.ShortPutStrike > 0 && strikes.ShortCallStrike > 0;
            
            // 4-strike pattern (Iron Condor/Butterfly structure)
            if (hasLongStrikes && hasShortStrikes && hasBothPutAndCall && 
                strikes.LongPutStrike > 0 && strikes.LongCallStrike > 0)
            {
                // Validate 4-leg structure: Long Put < Short Put < ATM < Short Call < Long Call
                return strikes.LongPutStrike < strikes.ShortPutStrike &&
                       strikes.ShortPutStrike < strikes.ATMStrike &&
                       strikes.ATMStrike < strikes.ShortCallStrike &&
                       strikes.ShortCallStrike < strikes.LongCallStrike;
            }
            
            // 2-strike pattern with both puts and calls (Strangle/Straddle structure)
            if (hasShortStrikes && hasBothPutAndCall && !hasLongStrikes)
            {
                // Validate short strikes are on opposite sides of ATM
                return strikes.ShortPutStrike < strikes.ATMStrike &&
                       strikes.ShortCallStrike > strikes.ATMStrike;
            }
            
            // Single-leg or spread patterns - basic validation
            return ValidateBasicStrikeOrder(strikes);
        }
        
        /// <summary>
        /// Validates basic strike ordering for any strategy pattern
        /// </summary>
        private bool ValidateBasicStrikeOrder(StrikeRange strikes)
        {
            // Ensure put strikes are below ATM and call strikes are above ATM
            bool validPutSide = strikes.ShortPutStrike <= 0 || strikes.ShortPutStrike < strikes.ATMStrike;
            bool validCallSide = strikes.ShortCallStrike <= 0 || strikes.ShortCallStrike > strikes.ATMStrike;
            
            // Ensure long strikes are outside short strikes if both exist
            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;
        }

        /// <summary>
        /// Gets strike selection statistics for reporting
        /// </summary>
        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
{
    /// <summary>
    /// Helper class for optimizing large universe strategies.
    /// Provides efficient batch processing, memory management, and QC-native patterns.
    /// </summary>
    public static class UniverseOptimizer
    {
        /// <summary>
        /// Fetches historical data in batches to avoid memory issues with large universes.
        /// Processes symbols in chunks to prevent timeouts and optimize performance.
        /// </summary>
        /// <param name="algorithm">The QCAlgorithm instance</param>
        /// <param name="symbols">List of symbols to fetch data for</param>
        /// <param name="days">Number of days of history</param>
        /// <param name="resolution">Data resolution</param>
        /// <param name="batchSize">Number of symbols to process per batch (default: 500)</param>
        /// <returns>Dictionary mapping Symbol to list of TradeBar volumes</returns>
        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();
            
            // Process in batches to avoid memory issues and timeouts
            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);
        }

        /// <summary>
        /// Calculates Average Daily Volume (ADV) for multiple symbols efficiently.
        /// Uses batch processing to handle large universes without memory issues.
        /// </summary>
        /// <param name="algorithm">The QCAlgorithm instance</param>
        /// <param name="symbols">Symbols to calculate ADV for</param>
        /// <param name="days">Number of days for ADV calculation</param>
        /// <returns>Dictionary mapping Symbol to ADV value</returns>
        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;
        }

        /// <summary>
        /// Optimized volume shock calculation for large universes.
        /// Compares current intraday volume to historical average efficiently.
        /// </summary>
        /// <param name="algorithm">The QCAlgorithm instance</param>
        /// <param name="intradayVolumes">Current intraday volume data</param>
        /// <param name="symbols">Universe symbols to process</param>
        /// <param name="advDays">Days for ADV calculation</param>
        /// <returns>Dictionary mapping Symbol to volume shock ratio</returns>
        public static Dictionary<Symbol, decimal> CalculateVolumeShock(
            QCAlgorithm algorithm,
            ConcurrentDictionary<Symbol, long> intradayVolumes,
            IEnumerable<Symbol> symbols,
            int advDays = 21)
        {
            // Get ADV for all symbols in batch
            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;
        }

        /// <summary>
        /// Efficiently cleans up removed securities from tracking structures.
        /// Prevents memory leaks in large universe strategies.
        /// </summary>
        /// <param name="algorithm">The QCAlgorithm instance</param>
        /// <param name="changes">Security changes from OnSecuritiesChanged</param>
        /// <param name="trackingDictionaries">Collection of dictionaries to clean up</param>
        public static void CleanupRemovedSecurities(
            QCAlgorithm algorithm,
            SecurityChanges changes,
            params object[] trackingDictionaries)
        {
            foreach (var removed in changes.RemovedSecurities)
            {
                // Liquidate any positions in removed securities
                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");
                }
                
                // Clean up tracking dictionaries
                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;
                    }
                }
            }
        }

        /// <summary>
        /// Sets up optimized universe settings for large universe strategies.
        /// Configures async selection and other performance optimizations.
        /// </summary>
        /// <param name="algorithm">The QCAlgorithm instance</param>
        /// <param name="resolution">Universe data resolution</param>
        /// <param name="enableAsync">Enable asynchronous universe selection</param>
        /// <param name="extendedHours">Enable extended market hours</param>
        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}");
        }

        /// <summary>
        /// Processes a large universe in parallel for improved performance.
        /// Useful for computationally intensive universe selection logic.
        /// </summary>
        /// <typeparam name="TInput">Input data type</typeparam>
        /// <typeparam name="TResult">Result data type</typeparam>
        /// <param name="data">Input data collection</param>
        /// <param name="processor">Function to process each item</param>
        /// <param name="batchSize">Batch size for processing</param>
        /// <returns>Collection of results</returns>
        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
                    {
                        // Log error but continue processing
                    }
                }
            });
            
            return results;
        }
    }

    /// <summary>
    /// Extension methods for universe optimization
    /// </summary>
    public static class UniverseExtensions
    {
        /// <summary>
        /// Batches an enumerable into chunks of specified size
        /// </summary>
        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);
                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");
                }
                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);
            
            // Route to strategy's SmartOrderManager if available
            if (_strategy is SimpleBaseStrategy baseStrategy && baseStrategy.SmartOrderManager != null)
            {
                try
                {
                    baseStrategy.SmartOrderManager.OnOrderEvent(orderEvent);
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex, $"Error processing order event {orderEvent.OrderId}");
                }
            }
            
            // Track order events for trade tracking system (like Python position tracking)
            if (_strategy is SimpleBaseStrategy strategy)
            {
                try
                {
                    // Track filled orders
                    if (orderEvent.Status == OrderStatus.Filled)
                    {
                        strategy.TrackOrderFilled(orderEvent);
                    }
                    // Track cancelled orders
                    else if (orderEvent.Status == OrderStatus.Canceled)
                    {
                        strategy.TrackOrderCancelled(orderEvent.OrderId.ToString());
                    }
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex, $"Error tracking order event {orderEvent.OrderId}");
                }
            }
        }
        
        /// <summary>
        /// Called at the end of each trading day for each symbol
        /// Always process batched logs (DEBUG and non-verbose INFO)
        /// Even in LiveMode we want daily summaries of DEBUG messages
        /// </summary>
        public override void OnEndOfDay(Symbol symbol)
        {
            // Debug message to verify OnEndOfDay is called
            Logger?.Debug($"OnEndOfDay called for symbol {symbol}");
            
            // Always process batched logs for daily summary
            // This includes DEBUG logs (always batched) and INFO logs (when not verbose)
            // Even in LiveMode, we want the DEBUG daily summary
            SmartLoggerStore.ProcessDailyLogs(this);
        }

        public override void OnEndOfAlgorithm()
        {
            // DEBUG: Immediate verification that OnEndOfAlgorithm() is called
            Log("=== DEBUG: OnEndOfAlgorithm() CALLED - IMMEDIATE CONFIRMATION ===");
            
            // 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?.Info($"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?.Debug($"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?.Debug($"Initializer RAW set: {security.Symbol} => {mode}");
            
            var lastPrices = GetLastKnownPrices(security);
            if (lastPrices != null && lastPrices.Any())
            {
                security.SetMarketPrice(lastPrices.First());
            }
        }

    }
}