Overall Statistics
Total Trades
116
Average Win
40.86%
Average Loss
-27.86%
Compounding Annual Return
0.510%
Drawdown
46.100%
Expectancy
0.233
Net Profit
3.050%
Sharpe Ratio
0.118
Probabilistic Sharpe Ratio
0.946%
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
1.47
Alpha
-0.013
Beta
0.302
Annual Standard Deviation
0.184
Annual Variance
0.034
Information Ratio
-0.442
Tracking Error
0.212
Treynor Ratio
0.072
Total Fees
$746.00
using QuantConnect.Data;
using QuantConnect.Data.Custom.CBOE; 
using QuantConnect.Data.Custom;
using QuantConnect.Indicators;
using System.Collections.Generic;
using System;
using System.Linq;
using System.Drawing;
using QuantConnect.Securities;
using QuantConnect.Securities.Equity;
using QuantConnect.Securities.Option;
using QuantConnect.Data.Market;
using QuantConnect.Orders;
namespace QuantConnect.Algorithm.CSharp
{
    public class PutCreditSpread : QCAlgorithm
    {
    	public class CustomBuyingPowerModel : BuyingPowerModel
        {
            public override GetMaximumOrderQuantityResult GetMaximumOrderQuantityForTargetBuyingPower(
                GetMaximumOrderQuantityForTargetBuyingPowerParameters parameters)
            {
                var quantity = base.GetMaximumOrderQuantityForTargetBuyingPower(parameters).Quantity;
                quantity = Math.Floor(quantity / 100) * 100;
                return new GetMaximumOrderQuantityResult(quantity);
            }
            public override HasSufficientBuyingPowerForOrderResult HasSufficientBuyingPowerForOrder(
                HasSufficientBuyingPowerForOrderParameters parameters)
            {
                return new HasSufficientBuyingPowerForOrderResult(true);
            }
        }
        private Security _vix;
        private Equity _spx;
        private Option _spxOption;
        private OptionContract _shortPut;
        private OptionContract _longPut;
        private RelativeStrengthIndex _vixRSI;
        private RelativeStrengthIndex _vixRSILag;
        private bool _inPosition = false;
        private bool _crossOver = false;
        private decimal _openPortfolioValue;
        private decimal _netCredit;
        private DateTime _expiry;
        private DateTime _exitDate;
        private DateTime _waitDate;
        public override void Initialize()
        {
            //SetStartDate(2019, 2, 1);
            //SetEndDate(2020, 2, 24);
            SetStartDate(2015, 1, 1);
            //SetEndDate(2018, 11, 15);
            SetCash(10000);             //Set Strategy Cash
			// Add securities
            _vix = AddData<CBOE>("VIX");
            _spx = AddEquity("SPY", Resolution.Minute);
            _vixRSI = new RelativeStrengthIndex(10);
            _vixRSILag = new RelativeStrengthIndex(10);
            // Charting
            var stockPlot = new Chart("Trade Plot");
            var mainRsi = new Series("RSI", SeriesType.Line, "", Color.Aqua);
            var Ob = new Series("Over Bought", SeriesType.Line, "", Color.Navy);
            var Os = new Series("Over Sold", SeriesType.Line, "", Color.Navy);
            var Om = new Series("Mid", SeriesType.Line, "", Color.Navy);
            var sell = new Series("Sell", SeriesType.Scatter, "", Color.Red);
            var buy = new Series("Buy", SeriesType.Scatter, "", Color.Green);
            stockPlot.AddSeries(mainRsi);
            stockPlot.AddSeries(Ob);
            stockPlot.AddSeries(Os);
            stockPlot.AddSeries(Om);
            stockPlot.AddSeries(sell);
            stockPlot.AddSeries(buy);
            AddChart(stockPlot);
			// Add Options
            _spxOption = AddOption("SPY", Resolution.Minute);
            _spxOption.PriceModel = OptionPriceModels.CrankNicolsonFD();
            _spxOption.SetBuyingPowerModel(new CustomBuyingPowerModel());
            // Set our custom filter for this option chain
            _spxOption.SetFilter(universe => from symbol in universe
                                                          .OnlyApplyFilterAtMarketOpen()
                                                          .PutsOnly()
                                                          .Strikes(-30, 0)
                                                          .Expiration(TimeSpan.FromDays(20), TimeSpan.FromDays(45))
                                             select symbol);
            // Use the underlying equity as the benchmark
            SetBenchmark("SPY");
            SetWarmUp(TimeSpan.FromDays(30));
        	_waitDate = Time.Date;
        }
        public override void OnData(Slice data)
        {
        	// Check entry once a day
            if (Time.Hour == 10 && Time.Minute == 30)
            {
                if (IsWarmingUp)
                    return;
				// Update RSI
                _vixRSI.Update(Time, _vix.Close);
                _vixRSILag.Update(Time, _vix.Open);
                // Update the Lagging RSI to match current
                // check if we have a crossOver
                if (_vixRSILag <=59 && _vixRSI >=60 && _vixRSI < 99) {
                	_crossOver = true;
                } 
                
                Plot("Trade Plot", "RSI", _vixRSI);
                Plot("Trade Plot", "Over Bought", 80);
                Plot("Trade Plot", "Over Sold", 20);    
                Plot("Trade Plot", "Mid", 50);

                if (_crossOver == true && !_inPosition && _waitDate <= Time.Date)
                {
                    EnterPosition(data);
                }
                // reset our cross over
                _crossOver = false;

            }
			// Always check the exit
            if (_inPosition)
                CheckExit(data);
        }
        private void EnterPosition(Slice data)
        {
            // Can't invest 100% due to margin. Can increase leverage or lower our invest percent.
            var investPercent = .9m;
            // Delta for short puts
            var shortDelta = -.20m;
            // Delta for long put
            var longDelta = -.10m;
			// Helper variables
            _shortPut = null;
            _longPut = null;
            var deltaShortDiff = 100m;
            var deltaLongDiff = 100m;
            var w1 = Time.AddDays(25).Date;
            var w2 = Time.AddDays(45).Date;
			// Loop through chain to find target options
            OptionChain chain;
            if (data.OptionChains.TryGetValue(_spxOption.Symbol, out chain))
            {
                // Find short put contract
                foreach (var contract in chain.Contracts.Values)
                {   
                    if (!(contract.Expiry.Date > w1 && contract.Expiry.Date < w2))
                        continue;
                    // Calculate the difference between the contract Delta and our short target Delta
                    var shortDiff = shortDelta - contract.Greeks.Delta;
                    // Check to see if this is the closest delta
                    if (shortDiff < deltaShortDiff && shortDiff > 0)
                    {
                        deltaShortDiff = shortDiff;
                        _shortPut = contract;
                    }
                    // Calculate the difference between the contract Delta and our long target Delta
                    var longDiff = longDelta - contract.Greeks.Delta;
                    // Check to see if this is the closest delta
                    if (longDiff < deltaLongDiff && longDiff > 0)
                    {
                        deltaLongDiff = longDiff;
                        _longPut = contract;
                    }
                }
                if (_shortPut == null || _longPut == null)
                {
                    Debug("Could not find strikes near our target Delta");
                    return;
                }
                if (_shortPut.Strike == _longPut.Strike)
                {
                    Debug("Strikes of long and short were equivalent, not trade made.");
                }
                Debug($"Short Delta:{_shortPut.Greeks.Delta} Long Delta:{_longPut.Greeks.Delta}");
                Debug($"Short Expiry Date: {_shortPut.Expiry} Long Expiry Date: {_longPut.Expiry}");
				// Calculate qty of both legs
                var margin = Portfolio.GetBuyingPower(_shortPut.Symbol, OrderDirection.Sell);
                var qty = margin * investPercent / ((_shortPut.BidPrice + _longPut.AskPrice) * 100);
               	Debug($"Buying Power:{margin}");
                Debug($"Short Qty:{qty:F0} @ {_shortPut.BidPrice:F2} @ ${_shortPut.Strike}");
                Debug($"Long  Qty:{qty:F0} @ {_longPut.AskPrice} @ ${_longPut.Strike}");
                Debug($"Underlying: {_shortPut.UnderlyingLastPrice}");
                if (_shortPut.Expiry.Date != _longPut.Expiry.Date) 
                {
                	Debug("Expiry dates don't match!");
                }
                if (qty < 1)
                {
                    Debug("Not enough cash to buy.");
                }
                else
                {
                	// Buy legs and store net credit
                    var longOrder = Buy(_longPut.Symbol, Math.Floor(qty));
                    Debug($"Long Put: S:{_longPut.Strike} Q:{longOrder.QuantityFilled:F0} @ {longOrder.AverageFillPrice:F2} ");
                    var shortOrder = Sell(_shortPut.Symbol, Math.Floor(qty));
					_netCredit = (Math.Abs(shortOrder.AverageFillPrice) - Math.Abs(longOrder.AverageFillPrice)) * Math.Abs(longOrder.QuantityFilled) * 100;
                    Debug($"Short Put: S:{_shortPut.Strike} Q:{shortOrder.QuantityFilled:F0} @ {shortOrder.AverageFillPrice:F2} ");
                    _inPosition = true;
                    // plot where we bought on the RSI
                    Plot("Trade Plot", "Buy", _vixRSI);
                    _expiry = _longPut.Expiry.Date;
                    // Get last Trading day before expiration date
                    _exitDate = TradingCalendar.GetTradingDays(_expiry.AddDays(-7), _expiry.AddDays(-1)).Last().Date.Date;
                    _openPortfolioValue = Portfolio.TotalPortfolioValue;
                    Debug($"Exit at {_netCredit*.7m:F0} profit, netcredit was: {_netCredit} or on {_exitDate}. ");
                }
            }
        }
        private void ExitPosition()
        {
            Liquidate();
            _inPosition = false;
            Plot("Trade Plot", "Sell", _vixRSI);
        }
        private void CheckExit(Slice data)
        {
            var change = Portfolio.TotalPortfolioValue - _openPortfolioValue;
            // Exit day before expiration -- THIS SHOULD NEVER HAPPEN
            if (Time.Date == _exitDate)
            {
				Debug($"_openPortfolioValue: {_openPortfolioValue}, TotalPortfolioValue: {Portfolio.TotalPortfolioValue}");
            	Debug($"change: {change} _netCredit: {_netCredit}, ratio: {change/_netCredit}");
				Debug($"Date: {Time.ToShortDateString()} Price: {_spx.Price:F2} Strike: {_shortPut.Strike:F2}  - Exiting before expiration");
				_waitDate = Time.Date;
				ExitPosition();
            }
           	// Check if we have 70% of max profit (net credit)
            if (change / _netCredit > .7m) {
				Debug($"_openPortfolioValue: {_openPortfolioValue}");
            	Debug($"change: {change} _netCredit: {_netCredit}, ratio: {change/_netCredit}");
                Debug($"Date: {Time.ToShortDateString()} Price: {_spx.Price:F2} Strike: {_shortPut.Strike}  - Exiting due to hitting 70% profit");
				_waitDate = Time.Date;
                ExitPosition();
            }
			
            if(change / _netCredit < -0.99m || _spx.Price < _shortPut.Strike) {
				Debug($"_openPortfolioValue: {_openPortfolioValue}");
            	Debug($"change: {change} _netCredit: {_netCredit}, ratio: {change/_netCredit}");
        		Debug($"Date: {Time.ToShortDateString()} Price: {_spx.Price:F2} Strike: {_shortPut.Strike} - LOSS!");
    			_waitDate = Time.Date.AddDays(30);
                ExitPosition();
        	}
        }
    }
}