Overall Statistics
Total Trades
26
Average Win
6.04%
Average Loss
-3.42%
Compounding Annual Return
7.013%
Drawdown
63.900%
Expectancy
-0.171
Net Profit
17.809%
Sharpe Ratio
0.458
Probabilistic Sharpe Ratio
15.015%
Loss Rate
70%
Win Rate
30%
Profit-Loss Ratio
1.76
Alpha
-0.017
Beta
0.397
Annual Standard Deviation
0.122
Annual Variance
0.015
Information Ratio
-0.842
Tracking Error
0.151
Treynor Ratio
0.141
Total Fees
$32.00
Estimated Strategy Capacity
$32000000.00
Lowest Capacity Asset
AAPL R735QTJ8XC9X
using System;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using System.Collections.Generic;
using QuantConnect.Securities.Option.StrategyMatcher;
using QuantConnect.Orders;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Positions;
using QuantConnect.Securities.Equity;

namespace QuantConnect.Algorithm.CSharp.S89
{
    /// <summary>
    /// Regression algorithm exercising an equity Call Calendar Spread option strategy and asserting it's being detected by Lean and works as expected
    /// </summary>
    public class OptionEquityCallCalendarSpreadRegressionAlgorithmS89 : OptionEquityBaseStrategyRegressionAlgorithmS89
    {

        /// <summary>
        /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
        /// </summary>
        /// <param name="slice">Slice object keyed by symbol containing the stock data</param>
        public override void OnData(Slice slice)
        {
            if (equity != null)
            {
                this.RemoveSecurity(equity.Symbol);
                equity = null;
            }

            foreach (var kvp in ActiveSecurities)
            {
                if (kvp.Value.Type != SecurityType.Option)
                    this.RemoveSecurity(kvp.Value.Symbol);
            }

            if (!Portfolio.Invested)
            {
                OptionChain chain;
                if (IsMarketOpen(_optionSymbol) && slice.OptionChains.TryGetValue(_optionSymbol, out chain))
                {
                    var contracts = chain
                        .Where(contract => contract.Right == OptionRight.Call)
                        .OrderBy(x => x.Expiry)
                        .ThenBy(x => x.Strike)
                        .ToList();

                    var shortCall = contracts.FirstOrDefault();
                    var longCall = contracts.FirstOrDefault(contract => contract.Expiry > shortCall.Expiry && contract.Strike == shortCall.Strike);
                    if (shortCall == null || longCall == null) return;

                    var initialMargin = Portfolio.MarginRemaining;

                    MarketOrder(shortCall.Symbol, -10);
                    MarketOrder(longCall.Symbol, +10);
                }
            }
        }


    }
    /// <summary>
    /// Base class for equity option strategy regression algorithms which holds some basic shared setup logic
    /// </summary>
    public abstract class OptionEquityBaseStrategyRegressionAlgorithmS89 : QCAlgorithm
    {
        protected Equity equity;
        protected decimal _paidFees;
        protected Symbol _optionSymbol;

        public override void Initialize()
        {
            SetStartDate(2019, 01, 24);
            SetEndDate(2021, 06, 24);
            SetCash(200000);

            equity = AddEquity("AAPL");//, leverage: 4
            var option = AddOption(equity.Symbol);
            _optionSymbol = option.Symbol;

            // set our strike/expiry filter for this option chain
            option.SetFilter(u => u.Strikes(-1, +1)
                                   // Expiration method accepts TimeSpan objects or integer for days.
                                   // The following statements yield the same filtering criteria
                                   .Expiration(28, 180)
                                   .CallsOnly()
                                   //.IncludeWeeklys()
                                   );
        }

        protected decimal GetPriceSpreadDifference(params Symbol[] symbols)
        {
            var spreadPaid = 0m;
            foreach (var symbol in symbols)
            {
                var security = Securities[symbol];
                var actualQuantity = security.Holdings.AbsoluteQuantity;
                var spread = 0m;
                if (security.Holdings.IsLong)
                {
                    if (security.AskPrice != 0)
                    {
                        spread = security.Price - security.AskPrice;
                    }
                }
                else if (security.BidPrice != 0)
                {
                    spread = security.BidPrice - security.Price;
                }
                spreadPaid += spread * actualQuantity * security.SymbolProperties.ContractMultiplier;
            }

            return spreadPaid;
        }

        /// <summary>
        /// Order fill event handler. On an order fill update the resulting information is passed to this method.
        /// </summary>
        /// <param name="orderEvent">Order event details containing details of the evemts</param>
        /// <remarks>This method can be called asynchronously and so should only be used by seasoned C# experts. Ensure you use proper locks on thread-unsafe objects</remarks>
        public override void OnOrderEvent(OrderEvent orderEvent)
        {
            if (orderEvent.Status == OrderStatus.Filled)
            {
                _paidFees += orderEvent.OrderFee.Value.Amount;
                if (orderEvent.Symbol.SecurityType.IsOption())
                {
                    var security = Securities[orderEvent.Symbol];
                    var premiumPaid = orderEvent.Quantity * orderEvent.FillPrice * security.SymbolProperties.ContractMultiplier;
                    Log($"{orderEvent}. Premium paid: {premiumPaid}");
                    return;
                }
            }
            Log($"{orderEvent}");
        }
    }
}