Overall Statistics
Total Trades
2
Average Win
0%
Average Loss
0%
Compounding Annual Return
-0.028%
Drawdown
0.000%
Expectancy
0
Net Profit
-0.005%
Sharpe Ratio
-1.713
Probabilistic Sharpe Ratio
11.234%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
-0.201
Tracking Error
0.202
Treynor Ratio
3.946
Total Fees
$2.00
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Data.Custom.CBOE;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Securities.Option;


namespace QuantConnect.Algorithm.CSharp
{
    public class VentralCalibratedProcessor : QCAlgorithm
    {
		private Symbol _equitySymbol;
		private Symbol _optionSymbol;
		private QuantConnect.Indicators.RelativeStrengthIndex _rsi;
		
		public decimal MinDeltaLimit = 0.70M;
    	public decimal MaxTradeSize = 0.25M;
    	
    	private OptionContract _longCall;
    	public int DaysBeforeExp;
		
        public override void Initialize()
        {
            SetStartDate(2018, 1, 1);      //Set Start Date
            SetEndDate(2018, 3, 1);        //Set End Date
            SetCash(500000);                 //Set Strategy Cash
            
            SetSecurityInitializer((security) => security.MarginModel = new CustomBuyingPowerModel());
            
            var equity = AddEquity("SPY", Resolution.Minute);
            var option = AddOption("SPY", Resolution.Minute);
            
            this.Symbol(option.Symbol);
            _equitySymbol = equity.Symbol;
			_optionSymbol = option.Symbol;
			
			// 14 day RSI
			_rsi = this.RSI(equity.Symbol, 14);

			// Filter by 12 strikes and expiration 5 to 10 days
			option.SetFilter(universe => universe.IncludeWeeklys().Strikes(-15, 15).Expiration(5, 10));
			option.PriceModel = OptionPriceModels.CrankNicolsonFD();
			UniverseSettings.Leverage = 4;
			
			// Adding this to reproduce GH issue #2314
            SetWarmup(TimeSpan.FromMinutes(1));
            
            // use the underlying equity as the benchmark
            SetBenchmark(_equitySymbol);
            
            this.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);

            //Schedule.On(DateRules.EveryDay("SPY"), TimeRules.Every(TimeSpan.FromMinutes(5)), () =>{
	        	//Debug("Specific Time: Fired at : " + Time);
			//});
            
        }

        /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
        /// Slice object keyed by symbol containing the stock data
        public override void OnData(Slice slice)
        {
        	if(this.IsWarmingUp)
        		return;
        	
        	if (IsMarketOpen(_equitySymbol) == false)
        	{
        		return;
        	}
			
            if (!Portfolio.Invested)
			{

            	OptionChain chain;
                if (slice.OptionChains.TryGetValue(_optionSymbol, out chain))
                {

                	// Find options between delta ranges closest to expiry date
                	var itmCallDebitSpread = from option in chain
                           where Math.Abs(option.Greeks.Delta) <= MinDeltaLimit &&
                    			 option.Right ==  OptionRight.Call
            			   orderby option.Expiry ascending, option.Strike
                           select option;
                           
                    //foreach(var option in itmCallDebitSpread.ToList()){
                    	//Debug(string.Format("Strike: {0} Expiration: {1} DTE: {2}", option.Strike, option.Expiry, (option.Expiry - Time).Days));
                    //}
                           
					// Get the spread call options
					var longCall = itmCallDebitSpread.ToList()[0];
	                var shortCall = itmCallDebitSpread.ToList()[1];
	                
	                _longCall = longCall;
	                
            		// Get debit for potential trade
	                var spreadDebit = (longCall.AskPrice - shortCall.AskPrice) * 100;
					
	                int numOfSpreadsToBuy = 0;
	                bool canAffordToBuy = true;
	               
	                // Only allow trade size of percentage of portfolio
           			var availableCash = Math.Floor(Portfolio.Cash * MaxTradeSize);
               			
	                while(canAffordToBuy){
                   	
                   		numOfSpreadsToBuy++;

               			// If we can afford to sell, increment amount
           				if((availableCash - (numOfSpreadsToBuy * spreadDebit)) < 0){
           					numOfSpreadsToBuy--;
       						canAffordToBuy = false;
       					}
                    }
                    
                    
					// Sell the spreads
					if(_rsi < 100)
					{
						//Debug("Spread Debit: $" + Math.Abs(numOfSpreadsToBuy * spreadDebit));
						//Debug("Short Call Delta: " + shortCall.Greeks.Delta.ToString());
						//Debug("Long Call Delta: " + longCall.Greeks.Delta.ToString());
						//Debug(string.Format("Strikes - Long: {0} / Short: {1}", longCall.Strike, shortCall.Strike));
						//Debug(string.Format("Expiry Date ({0}): {1}", (longCall.Expiry - Time).Days, longCall.Expiry));

                		// Buy the spreads (We use a loop for margin reasons)
                		for(int i = 1; i<= numOfSpreadsToBuy; i++){
                			
        					
                			
                		}
                		
                		Buy(longCall.Symbol, 1);
            			Sell(shortCall.Symbol, 1);
					}

                }
            }
            else{
                	
            	// Close positions based on stop-loss
            	//foreach(var security in Securities){
            		//if(security.Value.Holdings.UnrealizedProfitPercent < -0.30M){
            			
            			//Debug("Stop Loss - Profit Percent (P/L): " + (security.Value.Holdings.UnrealizedProfitPercent * 100) + "%");
            			//Liquidate(_optionSymbol);
            			
            		//}
            	//}
            	
            	// Close positions before expiration to prevent assignment
				if(_longCall != null){
					if((_longCall.Expiry - Time).Days <= DaysBeforeExp){
						//Liquidate(_optionSymbol);
						//Debug("Closed options contracts early.");
					}
				}
            	
            }
        }
        
        /// <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)
        {
        	//Log(orderEvent.ToString());
        	
        	var order = Transactions.GetOrderById(orderEvent.OrderId);
        	if (order.Type == OrderType.OptionExercise){
            	Log(string.Format("{0}: {1}: {2}", Time, order.Type, orderEvent));
            	Liquidate();
        	}
        }
        
        public override void OnAssignmentOrderEvent(OrderEvent orderEvent){
        	
        	Log(orderEvent.ToString());
        	Liquidate();
        }

    }
    
    public class CustomBuyingPowerModel : BuyingPowerModel 
    {
    	public override HasSufficientBuyingPowerForOrderResult HasSufficientBuyingPowerForOrder(HasSufficientBuyingPowerForOrderParameters parameters)
        {
            return new HasSufficientBuyingPowerForOrderResult(true);
        }
    }
 
}