Created with Highcharts 12.1.2EquityJan 2019Jan…Jul 2019Jan 2020Jul 2020Jan 2021Jul 2021Jan 2022Jul 2022Jan 2023Jul 2023Jan 2024Jul 2024Jan 2025Jul 202501M-40-20001-505020M40M05M10M02550
Overall Statistics
Total Orders
1413
Average Win
5.13%
Average Loss
-0.55%
Compounding Annual Return
81.292%
Drawdown
35.300%
Expectancy
3.087
Start Equity
20000
End Equity
820470.92
Net Profit
4002.355%
Sharpe Ratio
0.776
Sortino Ratio
5.757
Probabilistic Sharpe Ratio
0.002%
Loss Rate
61%
Win Rate
39%
Profit-Loss Ratio
9.38
Alpha
1.633
Beta
0.921
Annual Standard Deviation
2.261
Annual Variance
5.113
Information Ratio
0.72
Tracking Error
2.254
Treynor Ratio
1.905
Total Fees
$32202.57
Estimated Strategy Capacity
$3000.00
Lowest Capacity Asset
MNQ YQYHC5L1GPA9
Portfolio Turnover
15.43%
#region imports
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using QuantConnect.Data.Market;
    using Microsoft.ML;
    using Microsoft.ML.Data;
    using Microsoft.ML.FastTree;
#endregion

namespace QuantConnect.Algorithm.CSharp
{
    public class PreTradeData
    {
        // Z-Score Features
        public float CurrentZScore;             // Current z-score value
        public float ZScoreChange1Day;          // 1-day change in z-score
        public float ZScoreChange3Day;          // 3-day change in z-score
        public float ZScoreAbsValue;            // Absolute value of z-score
        public float ZScoreVolatility;          // Volatility of z-score (std dev of recent z-scores)
        
        // Mean Reversion Features
        public float PriceToSMA;                // Price relative to SMA (ratio)
        public float DaysAboveThreshold;        // How many days z-score has been beyond threshold
        public float MeanReversionSpeed;        // Average daily change when reverting to mean
        
        // Price Action Features
        public float SignalBarHigh;
        public float SignalBarLow;
        public float SignalBarClose;
        public float SignalBarOpen;
        public float SignalBarVolume;
        public float DailyTrueRange;            // ATR-style volatility measure
        public float PriceVolatility;           // Standard deviation of recent prices
        
        // Trade Setup Features
        public bool IsLongPosition;             // Direction of the trade
        public bool IsHighConviction;           // Whether it's a high conviction trade
        public float ExpectedProfit;            // Estimated profit based on z-score extremity
        public float StopLossPercent;           // Stop loss percentage used
        
        // Time Features
        public int DayOfWeek;                   // 0-6 for day of week
        public int HourOfDay;                   // 0-23 for hour of day
        public float DaysToExpiration;          // Days until contract expiration
        
        // Instrument Features
        public string Instrument;               // The futures instrument (e.g., "MES", "MNQ")
        public float HistoricalWinRate;         // Historical win rate for this instrument
        
        // Outcome
        public bool Win;                        // Whether the trade was profitable
        public float ReturnPercent;             // Actual return percentage
        public string ExitReason;               // Reason for exit (time, z-score, stop-loss)
        
        // Default parameterless constructor needed for ML.NET
        public PreTradeData() { }
        
        // Constructor for creating from trade data
        public PreTradeData(
            float currentZScore, 
            float zScoreChange1Day,
            float priceToSMA, 
            TradeBar signalBar,
            bool isLongPosition,
            bool isHighConviction,
            string instrument,
            float historicalWinRate,
            float daysToExpiration)
        {
            // Set core z-score features
            CurrentZScore = currentZScore;
            ZScoreChange1Day = zScoreChange1Day;
            ZScoreAbsValue = Math.Abs(currentZScore);
            PriceToSMA = priceToSMA;
            
            // Set price bar data
            SignalBarHigh = (float)signalBar.High;
            SignalBarLow = (float)signalBar.Low;
            SignalBarClose = (float)signalBar.Close;
            SignalBarOpen = (float)signalBar.Open;
            SignalBarVolume = (float)signalBar.Volume;
            
            // Set trade setup features
            IsLongPosition = isLongPosition;
            IsHighConviction = isHighConviction;
            ExpectedProfit = isHighConviction ? 0.03f : 0.02f; // Higher for high conviction
            StopLossPercent = isHighConviction ? 0.03f : 0.02f;
            
            // Set time features
            DateTime time = signalBar.Time;
            DayOfWeek = (int)time.DayOfWeek;
            HourOfDay = time.Hour;
            DaysToExpiration = daysToExpiration;
            
            // Set instrument features
            Instrument = instrument;
            HistoricalWinRate = historicalWinRate;
            
            // Initialize outcome (will be set later)
            Win = false;
            ReturnPercent = 0;
            ExitReason = "";
        }
        
        // Additional constructor with more z-score history
        public PreTradeData(
            float[] recentZScores, // Array of z-scores [current, 1 day ago, 2 days ago, 3 days ago]
            float priceToSMA,
            float[] recentPrices, // Array of recent prices for volatility calculation
            TradeBar signalBar,
            bool isLongPosition,
            bool isHighConviction,
            float daysAboveThreshold,
            string instrument,
            float historicalWinRate,
            float daysToExpiration)
            : this(recentZScores[0], recentZScores[0] - recentZScores[1], priceToSMA, signalBar, 
                  isLongPosition, isHighConviction, instrument, historicalWinRate, daysToExpiration)
        {
            // Calculate additional z-score features
            if (recentZScores.Length >= 4)
            {
                ZScoreChange3Day = recentZScores[0] - recentZScores[3];
                
                // Calculate z-score volatility
                float sum = 0;
                float mean = recentZScores.Average();
                for (int i = 0; i < recentZScores.Length; i++)
                {
                    sum += (recentZScores[i] - mean) * (recentZScores[i] - mean);
                }
                ZScoreVolatility = (float)Math.Sqrt(sum / recentZScores.Length);
            }
            
            // Calculate price volatility
            if (recentPrices.Length > 1)
            {
                float sum = 0;
                float mean = recentPrices.Average();
                for (int i = 0; i < recentPrices.Length; i++)
                {
                    sum += (recentPrices[i] - mean) * (recentPrices[i] - mean);
                }
                PriceVolatility = (float)Math.Sqrt(sum / recentPrices.Length);
                
                // Calculate daily true range (simplified)
                float highestHigh = recentPrices.Max();
                float lowestLow = recentPrices.Min();
                DailyTrueRange = highestHigh - lowestLow;
            }
            
            // Set days above threshold
            DaysAboveThreshold = daysAboveThreshold;
            
            // Calculate mean reversion speed
            if (recentZScores.Length > 1 && Math.Abs(recentZScores[0]) < Math.Abs(recentZScores[1]))
            {
                MeanReversionSpeed = Math.Abs(recentZScores[0] - recentZScores[1]);
            }
            else
            {
                MeanReversionSpeed = 0;
            }
        }
        
        // Method to set the win/loss outcome
        public void SetOutcome(bool isWin, float returnPercent, string exitReason)
        {
            Win = isWin;
            ReturnPercent = returnPercent;
            ExitReason = exitReason;
        }
        
        // For debugging - creates a readable string of the trade data
        public override string ToString()
        {
            return $"Instrument:{Instrument}, ZScore:{CurrentZScore:F2}, IsLong:{IsLongPosition}, " + 
                   $"HighConv:{IsHighConviction}, Win:{Win}, Return:{ReturnPercent:P2}, Exit:{ExitReason}";
        }
    }

    /// <summary>
    /// Simplified ML.NET utility for predicting expected trade returns
    /// Uses FastTree regression to predict actual return percentage
    /// </summary>
    public static class ReturnPredictor
    {
        /// <summary>
        /// Class for holding return predictions from ML.NET
        /// </summary>
        public class ReturnPrediction
        {
            [ColumnName("Score")]
            public float PredictedReturn { get; set; }
            
            // For feature importance and logging
            public float[] FeatureContributions { get; set; }
        }
        
        /// <summary>
        /// Trade decision including expected return and position sizing
        /// </summary>
        public class TradeDecision
        {
            public bool ShouldTake { get; set; }
            public float ExpectedReturn { get; set; }
            public float RecommendedSize { get; set; }
            public string Reason { get; set; }
            
            public override string ToString() => 
                $"Take Trade: {ShouldTake}, Expected Return: {ExpectedReturn:P2}, " +
                $"Size: {RecommendedSize:F2}x, Reason: {Reason}";
        }
        
        /// <summary>
        /// Trains a FastTree regression model to predict trade returns
        /// </summary>
        public static PredictionEngine<PreTradeData, ReturnPrediction> TrainModel(List<PreTradeData> historicalTrades)
        {
            // Validate training data
            if (historicalTrades == null || historicalTrades.Count < 40)
            {
                Console.WriteLine($"WARNING: Insufficient data ({historicalTrades?.Count ?? 0} trades). Need at least 40 samples.");
                return null;
            }
            
            // Create ML context with fixed seed for reproducibility
            var mlContext = new MLContext(42);
            
            // Print basic data summary
            var avgReturn = historicalTrades.Average(t => t.ReturnPercent) * 100;
            var winRate = historicalTrades.Count(t => t.Win) / (float)historicalTrades.Count * 100;
            Console.WriteLine($"Training with {historicalTrades.Count} trades. " +
                             $"Avg Return: {avgReturn:F2}%, Win Rate: {winRate:F2}%");
            
            // Shuffle and load data
            var shuffledData = historicalTrades.OrderBy(x => Guid.NewGuid()).ToList();
            var trainingData = mlContext.Data.LoadFromEnumerable(shuffledData);
            
            // Define feature columns for the model
            var featureColumns = new[] 
            { 
                // Most important features for return prediction
                nameof(PreTradeData.CurrentZScore),
                nameof(PreTradeData.ZScoreAbsValue),
                nameof(PreTradeData.ZScoreChange1Day),
                nameof(PreTradeData.PriceToSMA),
                nameof(PreTradeData.IsLongPosition),
                nameof(PreTradeData.IsHighConviction),
                nameof(PreTradeData.DayOfWeek),
                nameof(PreTradeData.HistoricalWinRate)
            };
            
            // Create a simple pipeline with FastTree regression
            var pipeline = mlContext.Transforms.Categorical.OneHotEncoding(
                    outputColumnName: "InstrumentEncoded", 
                    inputColumnName: nameof(PreTradeData.Instrument))
                .Append(mlContext.Transforms.Concatenate("Features", 
                    new[] { "InstrumentEncoded" }.Concat(featureColumns).ToArray()))
                .Append(mlContext.Transforms.NormalizeMinMax("Features"))
                .Append(mlContext.Transforms.CopyColumns("Label", nameof(PreTradeData.ReturnPercent)))
                .Append(mlContext.Regression.Trainers.FastTree(
                    numberOfLeaves: 20,
                    numberOfTrees: 200,
                    minimumExampleCountPerLeaf: 3));
            
            // Train model
            Console.WriteLine("Training FastTree regression model...");
            var model = pipeline.Fit(trainingData);
            
            // Create prediction engine
            var predictor = mlContext.Model.CreatePredictionEngine<PreTradeData, ReturnPrediction>(model);
            Console.WriteLine("Model training complete");
            
            return predictor;
        }
        
        /// <summary>
        /// Makes a trade decision based on predicted return and strategy parameters
        /// </summary>
        public static TradeDecision EvaluateTrade(
            PredictionEngine<PreTradeData, ReturnPrediction> predictor,
            PreTradeData setup,
            float minReturnThreshold = 0.01f)  // Minimum 1% return threshold
        {
            if (predictor == null || setup == null)
            {
                return new TradeDecision { 
                    ShouldTake = false, 
                    ExpectedReturn = 0,
                    RecommendedSize = 0,
                    Reason = "Invalid model or trade setup" 
                };
            }
            
            try
            {
                // Get return prediction
                var prediction = predictor.Predict(setup);
                float expectedReturn = prediction.PredictedReturn;
                
                // Base decision
                bool takeBasedOnReturn = expectedReturn >= minReturnThreshold;
                
                // Adjust threshold based on special conditions
                if (setup.IsHighConviction)
                {
                    // Lower threshold for high conviction trades
                    minReturnThreshold *= 0.8f;
                }
                
                if (setup.DayOfWeek == 5) // Friday
                {
                    // Higher threshold on Friday to avoid weekend risk
                    minReturnThreshold *= 1.25f;
                }
                
                // Consider extreme Z-scores
                bool extremeZScore = Math.Abs(setup.CurrentZScore) >= 2.5;
                
                // Make final decision
                bool shouldTake = takeBasedOnReturn || (extremeZScore && expectedReturn > 0);
                
                // Determine position size multiplier (0.5x to 2.0x)
                float sizeMultiplier = 1.0f;
                
                if (expectedReturn >= 0.03f) sizeMultiplier = 1.5f;      // 3%+ expected return
                else if (expectedReturn >= 0.05f) sizeMultiplier = 2.0f; // 5%+ expected return
                else if (expectedReturn < 0.015f) sizeMultiplier = 0.75f; // <1.5% expected return
                
                // For high conviction extreme z-scores, use larger size
                if (extremeZScore && setup.IsHighConviction)
                {
                    sizeMultiplier = Math.Max(sizeMultiplier, 1.5f);
                }
                
                // Create decision object
                string reason = shouldTake 
                    ? $"Expected return {expectedReturn:P2} exceeds {minReturnThreshold:P2} threshold"
                    : $"Expected return {expectedReturn:P2} below {minReturnThreshold:P2} threshold";
                    
                if (extremeZScore && shouldTake)
                {
                    reason += " and extreme Z-score detected";
                }
                
                // Log decision
                Console.WriteLine($"Trade evaluation for {setup.Instrument}: " +
                                 $"Z-Score={setup.CurrentZScore:F2}, " +
                                 $"Expected Return={expectedReturn:P2}, " +
                                 $"Take={shouldTake}, Size={sizeMultiplier:F1}x");
                
                return new TradeDecision {
                    ShouldTake = shouldTake,
                    ExpectedReturn = expectedReturn,
                    RecommendedSize = sizeMultiplier,
                    Reason = reason
                };
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error evaluating trade: {ex.Message}");
                return new TradeDecision { 
                    ShouldTake = false, 
                    ExpectedReturn = 0,
                    RecommendedSize = 0,
                    Reason = $"Error: {ex.Message}" 
                };
            }
        }
        
        /// <summary>
        /// Integrates with the mean reversion strategy to filter trades
        /// </summary>
        public static decimal GetOptimizedPositionSize(
            PredictionEngine<PreTradeData, ReturnPrediction> predictor,
            PreTradeData setup,
            decimal baseQuantity,
            float minReturnThreshold = 0.01f)
        {
            // Get trade evaluation
            var decision = EvaluateTrade(predictor, setup, minReturnThreshold);
            
            if (!decision.ShouldTake)
            {
                return 0; // Skip trade
            }
            
            // Scale position size based on recommendation
            decimal adjustedQuantity = baseQuantity * (decimal)decision.RecommendedSize;
            
            // Ensure quantity is at least 1 but not too large
            int maxSize = setup.IsHighConviction ? 10 : 5;
            adjustedQuantity = Math.Max(1, Math.Min(adjustedQuantity, maxSize));
            
            return Math.Floor(adjustedQuantity); // Round down to whole contracts
        }
    }
}
#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 QuantConnect.Algorithm.CSharp
{
    /// <summary>
    /// Revised Multi-Futures Mean Reversion Algorithm with Z-Score Thresholds
    /// Preserving the core edge while slightly increasing trade frequency
    /// </summary>
    public class MeanReversionFuturesAlgorithm : QCAlgorithm
    {
        // Futures objects to properly handle continuous contracts and mapping
        private readonly Dictionary<string, Future> _futures = new();
        
        // Z-score settings - preserve the original robust parameters
        private readonly int _lookbackPeriod = 50;      // Original lookback period
        private readonly int _holdingPeriodDays = 5;    // Original hold time 
        private readonly int _maxSize = 10;
        
        // Keep the original strict thresholds that were working well
        private double _entryZScoreThresholdLong = -1.5;   // Original threshold that works
        private double _entryZScoreThresholdShort = 1.5;   // Original threshold that works
        private readonly double _exitZScoreThresholdLong = 0.0; // Original exit threshold
        private readonly double _exitZScoreThresholdShort = 0.0; // Original exit threshold

        // Very extreme thresholds for "high conviction" trades
        private double _highConvictionZScoreLong = -2;   // More extreme z-score for high conviction entries
        private double _highConvictionZScoreShort = 2;   // More extreme z-score for high conviction entries
        
        // Risk management
        private decimal _riskPerTrade = 0.0025m;  // 0.25% risk per trade
        private readonly decimal _maxPortfolioRisk = 0.025m; // 2.5% maximum overall risk
        
        // Track trade information
        private Dictionary<Symbol, DateTime> _tradeOpenTime = new();
        private Dictionary<Symbol, List<bool>> _recentTradeResults = new();
        private Dictionary<Symbol, Dictionary<string, object>> _instrumentStats = new();
        
        // Z-Score indicators for each continuous contract
        private Dictionary<Symbol, SimpleMovingAverage> _smas = new();
        private Dictionary<Symbol, StandardDeviation> _stdDevs = new();
        
        // Add a slightly larger set of futures to increase opportunities
        private readonly List<string> _tickersToTrade = new List<string>{"MNQ", "MES", "MYM", "MGC", "MBT", "MCL", "M2K"};
        
        public override void Initialize()
        {
            SetStartDate(2019, 1, 1); // Start 3 years ago
            SetEndDate(2025, 4, 30);   // End at current date
            SetCash(20000);           // Starting capital
            SetBenchmark("QQQ");
            SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
            SetRiskManagement(new MaximumDrawdownPercentPerSecurity(0.1m));
            // Add all the futures we want to trade
            foreach (var ticker in _tickersToTrade)
            {
                AddFuture(ticker);
            }
            
            // Schedule rebalancing twice per day - morning and afternoon
            // This slightly increases trade frequency without being excessive
            Schedule.On(DateRules.EveryDay(), TimeRules.Every(TimeSpan.FromHours(3)), Rebalance);
            
            // Schedule monthly parameter optimization
            Schedule.On(DateRules.MonthStart(), TimeRules.At(9, 30), OptimizeParameters);
        }
        
        public override void OnMarginCallWarning()
        {
            Debug("Warning: Close to margin call");
        }
        
        private void AddFuture(string ticker)
        {
            try
            {
                // Add the continuous future contract with proper settings
                var future = AddFuture(
                    ticker, 
                    Resolution.Hour, 
                    extendedMarketHours: true,
                    dataMappingMode: DataMappingMode.OpenInterest,
                    dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
                    contractDepthOffset: 0
                );
                
                // Original filter worked well - stick with it but slightly expanded
                future.SetFilter(futureFilterUniverse => 
                    futureFilterUniverse.Expiration(0, 120)  // Slightly wider range than original
                );
                
                // Store the Future object for easy access
                _futures[ticker] = future;
                
                // Create indicators for the continuous contract
                _smas[future.Symbol] = SMA(future.Symbol, _lookbackPeriod, Resolution.Daily);
                _stdDevs[future.Symbol] = STD(future.Symbol, _lookbackPeriod, Resolution.Daily);
                
                // Initialize trade tracking
                _recentTradeResults[future.Symbol] = new List<bool>();
                _instrumentStats[future.Symbol] = new Dictionary<string, object>
                {
                    { "TotalTrades", 0 },
                    { "WinningTrades", 0 },
                    { "TotalPnl", 0m }
                };
                
                // Warm up the indicators
                WarmUpIndicator(future.Symbol, _smas[future.Symbol]);
                WarmUpIndicator(future.Symbol, _stdDevs[future.Symbol]);
                
                Log($"Added {ticker} future contract: {future.Symbol}. Current mapping: {future.Mapped}");
            }
            catch (Exception e)
            {
                Log($"Error adding {ticker}: {e.Message}");
            }
        }
        
        public override void OnData(Slice data)
        {
            // Handle existing positions
            ManageExistingPositions();
        }
        
        private double CalculateZScore(Symbol symbol)
        {
            // Use the properly warmed up indicators for z-score calculation
            if (!_smas[symbol].IsReady || !_stdDevs[symbol].IsReady)
            {
                return 0;
            }
            
            decimal mean = _smas[symbol].Current.Value;
            decimal stdDev = _stdDevs[symbol].Current.Value;
            
            if (stdDev == 0) return 0;
            
            // Get the current price of the continuous contract
            decimal currentPrice = Securities[symbol].Price;
            
            // Calculate z-score
            double zScore = (double)((currentPrice - mean) / stdDev);
            
            return zScore;
        }
        
        // Calculate daily volatility from standard deviation indicator
        private double CalculateVolatility(Symbol symbol)
        {
            if (!_stdDevs[symbol].IsReady) return 0;
            
            // Convert standard deviation to annualized volatility
            double dailyStdDev = (double)_stdDevs[symbol].Current.Value / (double)_smas[symbol].Current.Value;
            
            return dailyStdDev;
        }
        
        private void Rebalance()
        {
            // Count current open positions
            var openPositions = Portfolio.Securities.Count(pair => pair.Value.Invested);
            var availablePositions = Math.Max(0, (int)(_maxPortfolioRisk / _riskPerTrade) - openPositions);
            
            if (availablePositions <= 0)
            {
                return;
            }
            
            // Dictionary to store trade candidates
            var tradeCandidates = new Dictionary<Symbol, Tuple<double, bool, bool>>();  // symbol -> (score, isLong, isHighConviction)
            
            foreach (var kvp in _futures)
            {
                string ticker = kvp.Key;
                Future future = kvp.Value;
                Symbol continuousSymbol = future.Symbol;
                
                // Skip if we already have a position in this future
                if (Portfolio[future.Mapped].Invested) continue;
                
                // Skip if indicators aren't ready
                if (!_smas[continuousSymbol].IsReady || !_stdDevs[continuousSymbol].IsReady)
                {
                    continue;
                }
                
                // Calculate z-score
                var zScore = CalculateZScore(continuousSymbol);
                
                // Determine if this is a valid trade candidate
                bool isLongCandidate = zScore <= _entryZScoreThresholdLong;
                bool isShortCandidate = zScore >= _entryZScoreThresholdShort;
                
                // Check for high conviction trades
                bool isHighConvictionLong = zScore <= _highConvictionZScoreLong;
                bool isHighConvictionShort = zScore >= _highConvictionZScoreShort;
                bool isHighConviction = isHighConvictionLong || isHighConvictionShort;
                
                if (isLongCandidate || isShortCandidate)
                {
                    bool isLong = isLongCandidate;
                    
                    // Calculate volatility - lower volatility assets are preferred
                    double volatility = CalculateVolatility(continuousSymbol);
                    
                    // Basic score - absolute z-score value, higher is better
                    double score = Math.Abs(zScore);
                    
                    // Adjust score for volatility - prefer lower volatility assets
                    if (volatility > 0)
                    {
                        score *= (1 / volatility);
                    }
                    
                    // Adjust score for win rate
                    double winRate = GetDynamicWinRate(continuousSymbol);
                    score *= (winRate / 0.5);  // Normalize around 1.0
                    
                    tradeCandidates.Add(continuousSymbol, Tuple.Create(score, isLong, isHighConviction));
                }
            }
            
            // Take the best candidates based on score
            var selectedCandidates = tradeCandidates
                .OrderByDescending(pair => pair.Value.Item1)
                .Take(availablePositions)
                .ToList();
            
            // Execute trades for selected candidates
            foreach (var candidate in selectedCandidates)
            {
                var continuousSymbol = candidate.Key;
                var isLong = candidate.Value.Item2;
                var isHighConviction = candidate.Value.Item3;
                var zScore = CalculateZScore(continuousSymbol);
                
                // Get the actual tradable contract using the Mapped property
                var mappedSymbol = _futures.First(f => f.Value.Symbol == continuousSymbol).Value.Mapped;
                
                // Set stop-loss based on conviction
                decimal stopLossPct = isHighConviction ? 0.03m : 0.02m;  // More room for high conviction trades
                
                // Calculate position size - use slightly larger size for high conviction
                decimal quantity = CalculatePositionSize(mappedSymbol, stopLossPct);
                if (isHighConviction) quantity = Math.Min(quantity * 1.5m, 10);  // Up to 50% larger for high conviction
                
                if (quantity > 0)
                {
                    try
                    {
                        if (isLong)
                        {
                            var orderTicket = MarketOrder(mappedSymbol, quantity);
                            Log($"LONG {mappedSymbol}: Z-Score={zScore:F2}, Qty={quantity}, HighConviction={isHighConviction}, OrderId={orderTicket.OrderId}");
                        }
                        else
                        {
                            var orderTicket = MarketOrder(mappedSymbol, -quantity);
                            Log($"SHORT {mappedSymbol}: Z-Score={zScore:F2}, Qty={quantity}, HighConviction={isHighConviction}, OrderId={orderTicket.OrderId}");
                        }
                        
                        // Track trade open time
                        _tradeOpenTime[mappedSymbol] = Time;
                        
                        // Increment total trades counter
                        _instrumentStats[continuousSymbol]["TotalTrades"] = (int)_instrumentStats[continuousSymbol]["TotalTrades"] + 1;
                    }
                    catch (Exception e)
                    {
                        Log($"Error placing order for {mappedSymbol}: {e.Message}");
                    }
                }
            }
        }
        
        private void ManageExistingPositions()
        {
            foreach (var kvp in _futures)
            {
                string ticker = kvp.Key;
                Future future = kvp.Value;
                Symbol mappedSymbol = future.Mapped;
                Symbol continuousSymbol = future.Symbol;
                
                if (Portfolio[mappedSymbol].Invested && _tradeOpenTime.ContainsKey(mappedSymbol))
                {
                    var position = Portfolio[mappedSymbol];
                    var zScore = CalculateZScore(continuousSymbol);
                    var holdingDays = (Time - _tradeOpenTime[mappedSymbol]).TotalDays;
                    
                    // Original exit conditions - these worked well
                    bool timeExitCondition = holdingDays >= _holdingPeriodDays;
                    bool zScoreExitCondition = false;
                    
                    // Stop loss as an additional safety measure
                    bool stopLossCondition = position.UnrealizedProfitPercent <= -0.03m; // 3% stop loss
                    
                    if (position.IsLong)
                    {
                        zScoreExitCondition = zScore >= _exitZScoreThresholdLong;
                    }
                    else
                    {
                        zScoreExitCondition = zScore <= _exitZScoreThresholdShort;
                    }
                    
                    // Exit position if any condition is met
                    if (timeExitCondition || zScoreExitCondition || stopLossCondition)
                    {
                        try
                        {
                            // Liquidate returns a list of order tickets
                            var orderTickets = Liquidate(mappedSymbol);
                            string orderIds = string.Join(",", orderTickets.Select(t => t.OrderId));
                            
                            // Calculate trade result
                            bool isWin = position.UnrealizedProfitPercent > 0;
                            _recentTradeResults[continuousSymbol].Add(isWin);
                            
                            // Keep only last 20 trades for win rate calculation
                            if (_recentTradeResults[continuousSymbol].Count > 20)
                            {
                                _recentTradeResults[continuousSymbol].RemoveAt(0);
                            }
                            
                            // Update instrument statistics
                            if (isWin)
                            {
                                _instrumentStats[continuousSymbol]["WinningTrades"] = (int)_instrumentStats[continuousSymbol]["WinningTrades"] + 1;
                            }
                            
                            // Calculate realized PnL for this trade
                            decimal tradePnl = position.LastTradeProfit;
                            _instrumentStats[continuousSymbol]["TotalPnl"] = (decimal)_instrumentStats[continuousSymbol]["TotalPnl"] + tradePnl;
                            
                            string exitReason = timeExitCondition ? "time" : (zScoreExitCondition ? "z-score" : "stop-loss");
                            Log($"EXIT {mappedSymbol}: Reason={exitReason}, Z-Score={zScore:F2}, PnL={tradePnl:C}, Win={isWin}, OrderIds={orderIds}");
                            
                            // Remove from tracking
                            _tradeOpenTime.Remove(mappedSymbol);
                        }
                        catch (Exception e)
                        {
                            Log($"Error liquidating position for {mappedSymbol}: {e.Message}");
                        }
                    }
                }
            }
        }
        
        private void OptimizeParameters()
        {
            // Calculate overall win rate across all instruments
            int totalTrades = 0;
            int totalWins = 0;
            
            foreach (var kvp in _recentTradeResults)
            {
                totalTrades += kvp.Value.Count;
                totalWins += kvp.Value.Count(win => win);
            }
            
            if (totalTrades >= 10)
            {
                double overallWinRate = (double)totalWins / totalTrades;
                
                // Adjust thresholds based on historical performance - but keep the core edge intact
                if (overallWinRate < 0.5)  // Underperforming strategy
                {
                    // Make entry more conservative (require more extreme z-scores)
                    _entryZScoreThresholdLong = -2.2;
                    _entryZScoreThresholdShort = 2.2;
                    _highConvictionZScoreLong = -2.7;
                    _highConvictionZScoreShort = 2.7;
                    
                    // Reduce risk per trade
                    _riskPerTrade = Math.Max(0.007m, _riskPerTrade * 0.9m);
                }
                else if (overallWinRate > 0.7)  // Performing even better than expected
                {
                    // Can be very slightly more aggressive on entries
                    _entryZScoreThresholdLong = -1.9;
                    _entryZScoreThresholdShort = 1.9;
                    _highConvictionZScoreLong = -2.4;
                    _highConvictionZScoreShort = 2.4;
                    
                    // Increase risk slightly for consistently good performance
                    _riskPerTrade = Math.Min(0.012m, _riskPerTrade * 1.05m);
                }
                else  // Standard good performance - maintain the edge
                {
                    // Default settings
                    _entryZScoreThresholdLong = -2.0;
                    _entryZScoreThresholdShort = 2.0;
                    _highConvictionZScoreLong = -2.5;
                    _highConvictionZScoreShort = 2.5;
                }
                
                Log($"Optimized parameters: WinRate={overallWinRate:P2}, EntryLong={_entryZScoreThresholdLong:F1}, EntryShort={_entryZScoreThresholdShort:F1}, RiskPerTrade={_riskPerTrade:P2}");
            }
        }
        
        private double GetDynamicWinRate(Symbol symbol)
        {
            // Return actual win rate if we have enough data
            if (_recentTradeResults[symbol].Count >= 5)
            {
                return _recentTradeResults[symbol].Count(win => win) / (double)_recentTradeResults[symbol].Count;
            }
            
            // Otherwise return a default value
            return 0.5;
        }
        
        private decimal CalculatePositionSize(Symbol symbol, decimal stopLossPct)
        {
            if (stopLossPct == 0) return 0;
            
            var security = Securities[symbol];
            var price = security.Price;
            
            if (price == 0)
            {
                return 0;
            }
            
            // Calculate dollar risk amount
            decimal riskAmount = Portfolio.TotalPortfolioValue * _riskPerTrade;
            
            // Calculate position size based on risk per trade
            decimal positionValue = riskAmount / stopLossPct;
            
            // Calculate quantity - handle potential zero contract multiplier
            decimal contractMultiplier = security.SymbolProperties.ContractMultiplier;
            if (contractMultiplier == 0)
            {
                contractMultiplier = 1;
            }
            
            decimal contractValue = price * contractMultiplier;
            if (contractValue == 0)
            {
                return 0;
            }
            
            decimal quantity = Math.Floor(positionValue / contractValue);
            
            // Ensure quantity is at least 1 but not too large
            quantity = Math.Max(1, Math.Min(quantity, _maxSize));  // Cap at 10 contracts for safety
            
            return quantity;
        }
        
        public override void OnOrderEvent(OrderEvent orderEvent)
        {
            // Log all order events for debugging
            Log($"Order {orderEvent.OrderId}: {orderEvent.Status} - {orderEvent.FillQuantity} @ {orderEvent.FillPrice:C} Value: {orderEvent.FillPrice * orderEvent.FillQuantity}");
        }
        
        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
            // Handle securities being added or removed from the universe
            foreach (var security in changes.RemovedSecurities)
            {
                // Liquidate positions in securities that are removed from the universe
                // This handles futures contracts that are expiring
                if (Portfolio[security.Symbol].Invested)
                {
                    Log($"Security removed from universe: {security.Symbol}. Liquidating position.");
                    Liquidate(security.Symbol);
                }
            }
        }
        
        public override void OnEndOfAlgorithm()
        {
            Log("Strategy Performance Summary:");
            
            foreach (var kvp in _futures)
            {
                var ticker = kvp.Key;
                var future = kvp.Value;
                var continuousSymbol = future.Symbol;
                
                var totalTrades = (int)_instrumentStats[continuousSymbol]["TotalTrades"];
                if (totalTrades > 0)
                {
                    var winningTrades = (int)_instrumentStats[continuousSymbol]["WinningTrades"];
                    var totalPnl = (decimal)_instrumentStats[continuousSymbol]["TotalPnl"];
                    
                    var winRate = (double)winningTrades / totalTrades;
                    var avgPnlPerTrade = totalTrades > 0 ? totalPnl / totalTrades : 0;
                    
                    Log($"{ticker}: Trades={totalTrades}, WinRate={winRate:P2}, AvgPnL={avgPnlPerTrade:C}, TotalPnL={totalPnl:C}");
                }
                else
                {
                    Log($"{ticker}: No trades executed");
                }
            }
        }
    }
}