Overall Statistics
Total Trades
15
Average Win
80.25%
Average Loss
-7.30%
Compounding Annual Return
149.942%
Drawdown
82.400%
Expectancy
1.998
Net Profit
149.942%
Sharpe Ratio
4.454
Probabilistic Sharpe Ratio
64.335%
Loss Rate
75%
Win Rate
25%
Profit-Loss Ratio
10.99
Alpha
4.29
Beta
10.8
Annual Standard Deviation
1.569
Annual Variance
2.461
Information Ratio
4.539
Tracking Error
1.484
Treynor Ratio
0.647
Total Fees
$333.00
using QuantConnect.Securities.Future;
using QuantConnect.Data.Market;

namespace QuantConnect.Algorithm.CSharp
{
    public class BuyAndHoldFuturesTemplate : QCAlgorithm
    {
        private Future _future;
        private FuturesContract _activeContract;
        private bool _isInitializedForFutures = false;
        private int _DaysBeforeExpiryToRollover = 1;

        public override void Initialize()
        {
            SetStartDate(2019, 1, 1);
            SetEndDate(2020, 1, 1);
            SetCash(100000);

            _future = AddFuture(Futures.Indices.SP500EMini, Resolution.Minute);
            _future.SetFilter(0, 600);
            //_future.SetFilter(x => x.FrontMonth());

            // optional, helps reduce margin calls
            Settings.FreePortfolioValuePercentage = 0.3m;
        }

        override public void OnData(Slice slice)
        {
            // for QC API reasons, futures are best initialized in the OnData method
            if (!_isInitializedForFutures)
            {
                InitializeForFutures(slice);
                return;
            }

            // indicator (alpha) generating logic goes here
            // ...
        }

        private void InitializeForFutures(Slice slice)
        {
            SetActiveContractAndScheduleRollover(slice);

            SetHoldings(_activeContract.Symbol, 1);
            Log($"New contract purchased: {_activeContract.Symbol.Value}");

            _isInitializedForFutures = true;
        }

        #region FUTURES ROLLOVER OVERHEAD
        private void SetActiveContractAndScheduleRollover(Slice slice)
        {
            if (slice.FutureChains.Count != 1)
            {
                Log($"ERROR - No contracts in var {nameof(slice.FutureChains)}");
                return;
            }

            var esMiniChain = slice.FutureChains.First();
            var recentContracts = esMiniChain.Value
                .Where(x => (x.Expiry - Time.Date).TotalDays > _DaysBeforeExpiryToRollover)
                .OrderBy(x => (x.Expiry - Time.Date).TotalDays)
                .ToList();

            if (recentContracts.Count == 0)
            {
                _activeContract = null;
                Log($"ERROR - No contracts in var {nameof(recentContracts)}, no active contract assigned, no scheduled rollover. Liquidating portfolio.");
                
                Liquidate();
                Log($"Portfolio liquidated.");

                return;
            }
            Log($"list of sorted recent contracts: {string.Join(", ", recentContracts.Select(x => x.Symbol.Value).ToList())}");

            var frontContract = recentContracts.First();
            _activeContract = frontContract;
            
            Log($"New active contract set to {_activeContract.Symbol.Value}");

            ScheduleRollover(slice);
        }

        private void ScheduleRollover(Slice slice)
        {
            // hack to pass Slice to Rollover callback, see https://social.msdn.microsoft.com/Forums/en-US/fd7dcd64-5ec3-4951-81ab-2ca4f7dc06b2/
            Action rolloverCallback = () => { Rollover(slice); };

            Log($"Contract {_activeContract.Symbol.Value} has rollover scheduled for: {_activeContract.Expiry.AddDays(-1 * _DaysBeforeExpiryToRollover).ToShortDateString()}");
            
            Schedule.On(DateRules.On(_activeContract.Expiry.AddDays(-1 * _DaysBeforeExpiryToRollover)),
                TimeRules.AfterMarketOpen(_activeContract.Symbol),
                rolloverCallback);
        }

        private void Rollover(Slice slice)
        {
            SetActiveContractAndScheduleRollover(slice);
            if (Portfolio.Invested)
            {
                Liquidate();
                Log($"Portfolio liquidated.");

                SetHoldings(_activeContract.Symbol, 1);
                Log($"New contract purchased: {_activeContract.Symbol.Value}");
            }
        }
        #endregion
    }
}