Overall Statistics
Total Trades
18
Average Win
0.14%
Average Loss
-0.16%
Compounding Annual Return
2.766%
Drawdown
0.200%
Expectancy
0.472
Net Profit
0.688%
Sharpe Ratio
2.195
Probabilistic Sharpe Ratio
80.776%
Loss Rate
22%
Win Rate
78%
Profit-Loss Ratio
0.89
Alpha
0.021
Beta
-0.006
Annual Standard Deviation
0.009
Annual Variance
0
Information Ratio
-2.757
Tracking Error
0.08
Treynor Ratio
-3.483
Total Fees
$0.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
ZC XUBT0M6O6LNP
Portfolio Turnover
5.56%
#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.Execution;
    using QuantConnect.Algorithm.Framework.Risk;
    using QuantConnect.Parameters;
    using QuantConnect.Benchmarks;
    using QuantConnect.Brokerages;
    using QuantConnect.Util;
    using QuantConnect.Interfaces;
    using QuantConnect.Algorithm;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Consolidators;
    using QuantConnect.Data.Custom;
    using QuantConnect.DataSource;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Notifications;
    using QuantConnect.Orders;
    using QuantConnect.Orders.Fees;
    using QuantConnect.Orders.Fills;
    using QuantConnect.Orders.Slippage;
    using QuantConnect.Scheduling;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Equity;
    using QuantConnect.Securities.Future;
    using QuantConnect.Securities.Option;
    using QuantConnect.Securities.Forex;
    using QuantConnect.Securities.Crypto;   
    using QuantConnect.Securities.Interfaces;
    using QuantConnect.Storage;
    using QuantConnect.Data.Custom.AlphaStreams;
    using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
    using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
#endregion
namespace QuantConnect.Algorithm.CSharp
{
    public class YourAlgorithm : QCAlgorithm
    {
        DateTime startDate = new DateTime(2021, 6, 1);
        DateTime endDate = new DateTime(2021, 8, 31);
        //SetWarmUp(5, Resolution.Tick);
        //SetWarmUp(TimeSpan.FromDays(5), Resolution.Tick);
        Future future_contract;
        
        bool isWarmedup = false;
        
        List<string> futuresContracts = new List<string> { Futures.Grains.Corn };
        Dictionary<string, Future> futureSecurities = new Dictionary<string, Future>();

        //Dictionary<string, TimeSpan> customCloseTimes = new Dictionary<string, TimeSpan> { { Symbols.FutureGrainsCorn, TimeSpan.Zero } };
        //Dictionary<string, (decimal, decimal)> futuresVolatility = new Dictionary<string, (decimal, decimal)> { {Symbols.FutureGrainsCorn, (0.0074m, 0.0106m)} };

        decimal lastValidBidSize = 0m, lastValidAskSize = 0m;
        public static decimal lastValidAskPrice = 0m;
        public static decimal lastValidBidPrice = 0m;
        decimal priceMagnifier;
        decimal contractMultiplier;
        int minute_period = 120;
        decimal lowest_low = 0m;
        decimal highest_high = 0m;
        bool isFirstTick = true;

        LinkedList<decimal> rollingPrices = new LinkedList<decimal>();
        LinkedList<decimal> rollingVolumes = new LinkedList<decimal>();
        LinkedList<DateTime> rollingTimeStamps = new LinkedList<DateTime>();
        decimal averageVolUpdate = 0m;

        DateTime longCooldown = DateTime.MinValue; 
        DateTime shortCooldown = DateTime.MinValue;

        DateTime startTimeRollingWindow = DateTime.MinValue;
        bool volume_box_ready = false;
        bool totalVolumeLogged = false;
        bool volumeBoxReadyLogged = false;
        DateTime lastCalculated = DateTime.MinValue;
        DateTime lastCheckedDate = DateTime.MinValue;
        DateTime loopStart = DateTime.MinValue; 
        DateTime exchangeOpenTime = DateTime.MinValue; // Add this line

        DateTime exchangeCloseTime = DateTime.MinValue;

        decimal maxPrice = decimal.MinValue;
        decimal minPrice = decimal.MaxValue;
        decimal maxVolumeBoxPrice = decimal.MaxValue;
        decimal minVolumeBoxPrice = decimal.MinValue;
        decimal totalVolume = 0;
        decimal currentTotalVolume = 0;
        bool printonce = false;
        List<decimal> volatility = new List<decimal>();
        Dictionary<int, DateTime> positionCreationTimes = new Dictionary<int, DateTime>();
        Dictionary<int, decimal> positionPnLs = new Dictionary<int, decimal>();
        decimal contractMultiplier2 = 5000.0m;
        decimal contractMultiplier3 = 5000.0m;
        bool isFirstOrderOfTheDay = false;
        decimal highestLastValidAskPrice = 0m;
        
        public override void Initialize()
        {
            SetStartDate(startDate);
            SetEndDate(endDate);
            SetWarmUp(TimeSpan.FromDays(5), Resolution.Tick);

            future_contract = AddFuture(Futures.Grains.Corn, Resolution.Tick, extendedMarketHours: false, dataNormalizationMode: DataNormalizationMode.Raw, dataMappingMode: DataMappingMode.OpenInterest, contractDepthOffset: 0);

            SetSecurityInitializer(CustomSecurityInitializer);
            contractMultiplier = Securities[future_contract.Symbol].SymbolProperties.ContractMultiplier;
            //contractMultiplier2 = Securities[future_contract.Symbol].SymbolProperties.ContractMultiplier;
            //contractMultiplier3 = Securities[future_contract.Symbol].SymbolProperties.ContractMultiplier;
            //future_contract.SetFillModel(new BestBidAskFillModel());
                        
            //foreach (var contract in futuresContracts)
            //{
            //    var future = AddFuture(contract, Resolution.Tick, extendedMarketHours: false, dataNormalizationMode: DataNormalizationMode.Raw, dataMappingMode: DataMappingMode.OpenInterest, contractDepthOffset: 0);
                        
                // Set the same fill model for the contract future as you did for the continuous future
            //    future.SetFillModel(new BestBidAskFillModel());
                        
                //futureSecurities[future.Symbol] = future;
            //}
        }

        public override void OnData(Slice data)
        {
            DateTime sliceDate = Time.Date; 
            DateTime targetDate = new DateTime(2021, 8, 2);
            //foreach (var symbol in futureSecurities.Keys)
            if (sliceDate > DateTime.MinValue)
            {
                //if (!data.Ticks.ContainsKey(future_contract.Symbol)) continue;
                //if (!data.Ticks.ContainsKey(symbol)) continue;

                if (sliceDate != lastCheckedDate) // Assuming lastCheckedDate is a DateTime variable declared at class level to track the last checked date
                {
                    ResetVariables();
                    var future = future_contract;
                    var exchangeHours = future.Exchange.Hours;
                    var exchangeOpenTimeSpan = exchangeHours.MarketHours[sliceDate.DayOfWeek].GetMarketOpen(TimeSpan.Zero, false);
                    // Get exchange close time
                    var exchangeCloseTimeSpan = exchangeHours.MarketHours[sliceDate.DayOfWeek].GetMarketClose(TimeSpan.Zero, false);
                    exchangeOpenTime = sliceDate.Date + exchangeOpenTimeSpan.GetValueOrDefault();
                    exchangeCloseTime = sliceDate.Date + exchangeCloseTimeSpan.GetValueOrDefault();
                    loopStart = exchangeOpenTime;
                    lastCheckedDate = sliceDate;
                    //var contractMultiplier2 = Securities[future.Symbol].SymbolProperties.ContractMultiplier;
                    
                    
                    //var contractMultiplier3 = 5000.0m;
                    //Debug($"contact multi {contractMultiplier}");
                    //Debug($"contact multi {contractMultiplier2}");
                    //Debug($"contact multi {contractMultiplier3}");

                }
                
                foreach (var ticks in data.Ticks.Values)
                {
                    foreach (var tick in ticks)
                    {
                        if (tick.Suspicious) continue;
                        
                        if (tick.TickType == TickType.Quote)
                        {
                        
                            if (tick.BidPrice != 0) lastValidBidPrice = tick.BidPrice;
                            if (tick.AskPrice != 0) lastValidAskPrice = tick.AskPrice;

                            if (tick.BidSize != 0) lastValidBidSize = tick.BidSize;
                            if (tick.AskSize != 0) lastValidAskSize = tick.AskSize;

                            if (lastValidAskPrice > highestLastValidAskPrice && isFirstOrderOfTheDay == true && sliceDate == targetDate)
                            {
                                highestLastValidAskPrice = lastValidAskPrice;
                                //Debug($"new highest {highestLastValidAskPrice}");
                            }

                            foreach (var holding in Portfolio.Values)
                                {
                                    if (holding.Invested)
                                    {
                                        var positionId = holding.Symbol.GetHashCode();
                                        //var pnl = holding.UnrealizedProfitPercent;
                                        //positionPnLs[positionId] = pnl;
                                        decimal pnl;
                                        if(holding.Quantity > 0)
                                        {
                                            pnl = ((lastValidBidPrice - holding.AveragePrice) / holding.AveragePrice);
                                        }
                                        else
                                        {
                                            pnl = ((lastValidAskPrice - holding.AveragePrice) / holding.AveragePrice) *-1;
                                        }

                                        // Close position based on PnL conditions
                                        if (pnl >= 0.0028m )
                                        {
                                            var qty = -Portfolio[holding.Symbol].Quantity;
                                            
                                            if(qty > 0)
                                            {
                                                MarketOrder(holding.Symbol, qty, false, $"0.28% take profit hit ask price: {lastValidAskPrice}, entry {holding.AveragePrice}, profit {((lastValidAskPrice - holding.AveragePrice) *-1)}");
                                            }
                                            else
                                            {
                                                MarketOrder(holding.Symbol, qty, false, $"0.28% take profit hit bid price: {lastValidBidPrice}, entry {holding.AveragePrice}, profit {(lastValidBidPrice - holding.AveragePrice) * contractMultiplier2}");
                                            }
                                            positionPnLs.Remove(positionId);
                                            positionCreationTimes.Remove(positionId);
                                        }
                                        if (pnl <= -0.006m)
                                        {
                                            var qty = -Portfolio[holding.Symbol].Quantity;
                                            if(qty > 0)
                                            {
                                                MarketOrder(holding.Symbol, qty, false, $"0.6% stop loss hit ask price: {lastValidAskPrice}, entry {holding.AveragePrice}, profit {((lastValidAskPrice - holding.AveragePrice) *-1) * contractMultiplier2}");
                                            }
                                            else
                                            {
                                                MarketOrder(holding.Symbol, qty, false, $"0.6% stop loss hit bid price: {lastValidBidPrice}, entry {holding.AveragePrice}, profit {((lastValidBidPrice - holding.AveragePrice) * contractMultiplier2)}");
                                            }
                                            
                                            positionPnLs.Remove(positionId);
                                            positionCreationTimes.Remove(positionId);
                                        }

                                        // Close position if it's been open for 2 hours or more
                                        if (positionCreationTimes.ContainsKey(positionId) && Time - positionCreationTimes[positionId] >= TimeSpan.FromHours(2))
                                        {
                                            var qty = -Portfolio[holding.Symbol].Quantity;
                                            //MarketOrder(holding.Symbol, qty, false, $"Time limit reached, pnl {pnl}");
                                            if(qty > 0)
                                            {
                                                MarketOrder(holding.Symbol, qty, false, $"Time limit reached ask price: {lastValidAskPrice}, entry {holding.AveragePrice}, profit {((lastValidAskPrice - holding.AveragePrice) *-1) * contractMultiplier2}");
                                            }
                                            else
                                            {
                                                MarketOrder(holding.Symbol, qty, false, $"Time limit reached bid price: {lastValidBidPrice}, entry {holding.AveragePrice}, profit {(lastValidBidPrice - holding.AveragePrice) * contractMultiplier2}");
                                            }
                                            positionPnLs.Remove(positionId);
                                            positionCreationTimes.Remove(positionId);
                                        }
                                    }
                                }

                        }
                        if (tick.TickType == TickType.Trade)

                        {
                            if (isWarmedup)
                            {  
                                DateTime cutOffTime = tick.Time.AddMinutes(-30);
                                while (rollingTimeStamps.Count > 0 && rollingTimeStamps.First.Value <= cutOffTime)
                                {
                                    rollingPrices.RemoveFirst();
                                    rollingVolumes.RemoveFirst();
                                    rollingTimeStamps.RemoveFirst();
                                }
                                rollingPrices.AddLast(tick.Price);
                                rollingVolumes.AddLast(tick.Quantity);
                                rollingTimeStamps.AddLast(tick.Time);

                                if ((tick.Time - lastCalculated).TotalSeconds >= 5)
                                {
                                    maxPrice = rollingPrices.Max();
                                    minPrice = rollingPrices.Min();
                                    currentTotalVolume = rollingVolumes.Sum();

                                    if(minPrice == 0)
                                    {
                                        continue;
                                    }

                                    if ((maxPrice - minPrice) / minPrice  <= (averageVolUpdate * 0.66m) && tick.Time >= exchangeOpenTime.AddMinutes(30))
                                    {
                                        volume_box_ready = true;
                                        maxVolumeBoxPrice = maxPrice;
                                        minVolumeBoxPrice = minPrice;
                                        //Debug($"volume_box_ready");
                                    }

                                    lastCalculated = tick.Time;
                                }

                                totalVolume = currentTotalVolume;

                                if (volume_box_ready && tick.Time < exchangeCloseTime.AddMinutes(-30) )
                                {
                                    if (tick.Price > maxVolumeBoxPrice * (1.000m + (averageVolUpdate * 0.66m)) && tick.Time > longCooldown.AddMinutes(5))
                                    {
                                        decimal averageVolatility = volatility.Any() ? volatility.Average() : 0m;
                                        
                                        //Debug($"order long ");
                                        //currentContract = self.Securities[continuousContract.Mapped]
                                        //MarketOrder(futureSecurities[symbol].Symbol, 1, false, "");
                                        MarketOrder(future_contract.Mapped, 1, true, $"ask price: {lastValidAskPrice} bid price: {lastValidBidPrice}");
                                        isFirstOrderOfTheDay = true;
                                    
                                        longCooldown = tick.Time;
                                                
                                        volume_box_ready = false;
                                        maxVolumeBoxPrice = decimal.MaxValue;
                                        minVolumeBoxPrice = decimal.MinValue;
                                    }

                                    else if (tick.Price < minVolumeBoxPrice * (1.0m - (averageVolUpdate * 0.66m)) && tick.Time > shortCooldown.AddMinutes(5))
                                    {
                                        decimal averageVolatility = volatility.Any() ? volatility.Average() : 0;
                

                                        //MarketOrder(future_contract.Mapped, -1, true, $"ask price: {lastValidAskPrice} bid price: {lastValidBidPrice}");

                                        //MarketOrder(futureSecurities[symbolKey].Symbol, -1, false, "", false);
                                        shortCooldown = tick.Time;
                                        //Console.WriteLine($"order short: {tick.Time}, price: {tick.Price}");
                                        volume_box_ready = false;
                                        maxVolumeBoxPrice = decimal.MaxValue;
                                        minVolumeBoxPrice = decimal.MinValue;
                                    }
                                } //end order logic

                                

                                // Close all positions 15 minutes before market close
                                if (tick.Time >= exchangeCloseTime.AddMinutes(-15))
                                {
                                    foreach (var holding in Portfolio.Values)
                                    {
                                        if (holding.Invested)
                                        {
                                            var qty = -holding.Quantity;
                                            //MarketOrder(holding.Symbol, qty, false, "Market close");
                                            if(qty > 0)
                                            {
                                                MarketOrder(holding.Symbol, qty, false, $"market closing ask price: {lastValidAskPrice}, entry {holding.AveragePrice}, profit {((lastValidAskPrice - holding.AveragePrice) *-1) * contractMultiplier2}");
                                            }
                                            else
                                            {
                                                MarketOrder(holding.Symbol, qty, false, $"market closing bid price: {lastValidBidPrice}, entry {holding.AveragePrice}, profit {(lastValidBidPrice - holding.AveragePrice) * contractMultiplier2}");
                                            }
                                        }
                                    }
                                    positionPnLs.Clear();
                                    positionCreationTimes.Clear();
                                }

                            }

                            if (isFirstTick && lastValidBidPrice != 0m && lastValidAskPrice != 0m) 
                            {
                                lowest_low = lastValidBidPrice;
                                highest_high = lastValidAskPrice;
                                loopStart = tick.Time;
                                isFirstTick = false;
                            }

                            // Compare and update lowest_low and highest_high
                            if (tick.Price < lowest_low) 
                            {
                                lowest_low = tick.Price;
                            }
                            if (tick.Price > highest_high) 
                            {
                                highest_high = tick.Price;
                            }

                            // Check if an hour has passed since loop start, if yes calculate the volatility and reset
                            // start vol
                            if ((tick.Time - loopStart).TotalHours >= 1)
                            {
                                //Console.WriteLine($"lowest_low {lowest_low}");
                                //Debug($"tick time {tick.Time} market open {exchangeOpenTime}, best bid {lastValidBidPrice}, best ask {lastValidAskPrice}, is first tick {isFirstTick}");
                                decimal price_diff_percentage = ((highest_high - lowest_low) / lowest_low);

                                // Manage volatility list size
                                if (volatility.Count >= 20)
                                {
                                    volatility.RemoveAt(0);
                                }
                                volatility.Add(price_diff_percentage);
                                //string volatilityCsv = string.Join(",", volatility);
                                //Console.WriteLine(volatilityCsv);

                                // Reset lowest_low and highest_high for the next hour
                                lowest_low = lastValidBidPrice;
                                highest_high = lastValidAskPrice;
                                loopStart = tick.Time;
                                averageVolUpdate = volatility.Average();
                                //Console.WriteLine($"volatility: {price_diff_percentage}");
                            }
                        }
                    }
                }
            }
        }

        public override void OnWarmupFinished()
        {
            Log("Algorithm Ready");
            isWarmedup = true;
        }

        public override void OnOrderEvent(OrderEvent orderEvent)
        {
            if (orderEvent.Status == OrderStatus.Filled)
            {
                var positionId = Securities[orderEvent.Symbol].Holdings.Symbol.GetHashCode();
                positionCreationTimes[positionId] = Time;
            }
        }

        private void CustomSecurityInitializer(Security security)
        {
            security.SetFeeModel(new ConstantFeeModel(0.0m));
            security.SetSlippageModel(new ConstantSlippageModel(0.0m));
            security.SetFillModel(new BestBidAskFillModel());
            // Add other models as necessary
        }

        private void ResetVariables()
        {
            lastValidBidPrice = 0m;
            lastValidAskPrice = 0m;
            lastValidBidSize = 0m;
            lastValidAskSize = 0m;
            priceMagnifier = default; 
            contractMultiplier = default; 
            lowest_low = 0m; 
            highest_high = 0m; 
            isFirstTick = true; 

            rollingPrices.Clear(); 
            rollingVolumes.Clear(); 
            rollingTimeStamps.Clear(); 
            averageVolUpdate = 0m;

            longCooldown = DateTime.MinValue; 
            shortCooldown = DateTime.MinValue;

            startTimeRollingWindow = DateTime.MinValue; 
            volume_box_ready = false; 
            totalVolumeLogged = false; 
            volumeBoxReadyLogged = false; 
            lastCalculated = DateTime.MinValue; 
            loopStart = DateTime.MinValue; 
            exchangeOpenTime = DateTime.MinValue;
            exchangeCloseTime = DateTime.MinValue; 

            maxPrice = decimal.MinValue; 
            minPrice = decimal.MaxValue; 
            maxVolumeBoxPrice = decimal.MaxValue; 
            minVolumeBoxPrice = decimal.MinValue; 
            totalVolume = 0; 
            currentTotalVolume = 0; 

            positionCreationTimes.Clear();
            positionPnLs.Clear();
        }

    public class BestBidAskFillModel : ImmediateFillModel
    {
        public override OrderEvent MarketFill(Security asset, MarketOrder order)
        {
            var fill = base.MarketFill(asset, order);
            var lastData = asset.GetLastData();
            var tick = lastData as Tick;

            if (tick != null)
            {
                if (order.Direction == OrderDirection.Buy)
                {
                    fill.FillPrice = YourAlgorithm.lastValidAskPrice != 0 ? YourAlgorithm.lastValidAskPrice : fill.FillPrice;
                }
                else
                {
                    fill.FillPrice = YourAlgorithm.lastValidBidPrice != 0 ? YourAlgorithm.lastValidBidPrice : fill.FillPrice;
                }
            }

            return fill;
        }
    }


    
    }
}

Notebook too long to render.