Overall Statistics
Total Orders
18
Average Win
5.10%
Average Loss
-0.71%
Compounding Annual Return
30.783%
Drawdown
12.400%
Expectancy
3.110
Start Equity
1000000
End Equity
1389110.4
Net Profit
38.911%
Sharpe Ratio
0.799
Sortino Ratio
0.382
Probabilistic Sharpe Ratio
46.326%
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
7.22
Alpha
0.138
Beta
0.391
Annual Standard Deviation
0.223
Annual Variance
0.05
Information Ratio
0.33
Tracking Error
0.228
Treynor Ratio
0.455
Total Fees
$544.60
Estimated Strategy Capacity
$5000000.00
Lowest Capacity Asset
ES YLZ9Z7LFSJOK|ES YLZ9Z50BJE2P
Portfolio Turnover
20.89%
#region imports
    using Newtonsoft.Json;
    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.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.Interfaces;
    using QuantConnect.Securities.Volatility;
    using QuantConnect.Storage;
    using QuantConnect.Statistics;
    using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
    using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
#endregion

namespace QuantConnect.Algorithm.CSharp
{
    public class FutureOptionExampleAlgorithm : QCAlgorithm
    {
        private Future _underlying;

        public override void Initialize()
        {
            SetStartDate(2023, 7, 1);
            SetCash(1000000);
            // Subscribe the underlying since the updated price is needed for filtering
            _underlying = AddFuture(Futures.Indices.SP500EMini,
                extendedMarketHours: true,
                dataMappingMode: DataMappingMode.OpenInterest,
                dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
                contractDepthOffset: 0);
            // Filter the underlying continuous Futures to narrow the FOP spectrum
            _underlying.SetFilter(0, 182);
            // Filter for the current-week-expiring calls to formulate a covered call that expires at the end of week
            AddFutureOption(_underlying.Symbol, (u) => u.IncludeWeeklys().CallsOnly().Expiration(0, 5));
        }

        public override void OnData(Slice slice)
        {
            // Create canonical symbol for the mapped future contract, since option chains are mapped by canonical symbol
            var symbol = QuantConnect.Symbol.CreateCanonicalOption(_underlying.Mapped);

            // Get option chain data for the mapped future, as both the underlying and FOP have the highest liquidity among all other contracts
            if (!Portfolio.Invested && 
                slice.OptionChains.TryGetValue(symbol, out var chain))
            {
                // Obtain the ATM call that expires at the end of week, such that both underlying and the FOP expires the same time
                var expiry = chain.Max(x => x.Expiry);
                var atmCall = chain.Where(x => x.Expiry == expiry)
                    .OrderBy(x => Math.Abs(x.Strike - x.UnderlyingLastPrice))
                    .First();

                // Use abstraction method to order a covered call to avoid manual error
                var optionStrategy = OptionStrategies.CoveredCall(symbol, atmCall.Strike, expiry);
                Buy(optionStrategy, 1);
            }
        }

        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
            foreach (var security in changes.AddedSecurities)
            {
                if (security.Type == SecurityType.FutureOption)
                {
                    // Historical data
                    var history = History(security.Symbol, 10, Resolution.Minute);
                    Debug($"We got {history.Count()} from our history request for {security.Symbol}");
                }
            }
        }
    }
}