Overall Statistics
Total Trades
66
Average Win
0.35%
Average Loss
-0.37%
Compounding Annual Return
6.809%
Drawdown
1.100%
Expectancy
0.135
Net Profit
1.582%
Sharpe Ratio
1.755
Probabilistic Sharpe Ratio
66.343%
Loss Rate
42%
Win Rate
58%
Profit-Loss Ratio
0.94
Alpha
0.049
Beta
0.103
Annual Standard Deviation
0.038
Annual Variance
0.001
Information Ratio
-0.935
Tracking Error
0.116
Treynor Ratio
0.649
Total Fees
$52.00
using System;
using System.Diagnostics;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities.Option;

namespace QuantConnect.Algorithm.CSharp
{
    public class BasicTemplateOptionsHistoryAlgorithm : QCAlgorithm
    {
        private decimal deltaStart = 0.8m;
        private decimal deltaEnd = 0.9m;
        private decimal deltaAdjust = 0.4m;
        private bool isShort = false;
        private List<PositionGroup> Underlying;
        private DateTime currentDay = DateTime.Now;
        private decimal cash = 40000;

        private PositionProvider positionProvider;
        private DateTime startDate = new DateTime(2016, 6, 1);
        private DateTime endDate = new DateTime(2016,8, 28);
        private int liquidateBefore = 1;
        private int optionsFrom = 3;
        private int optionsTo = 6;

        private int daysPass = 0;
        public override void Initialize()
        {
            SetCash(cash);
            SetStartDate(startDate);
            SetEndDate(endDate);
            positionProvider = new PositionProvider(Portfolio);

            var s2 = new PositionGroup();

            var option2 = AddOption("MSFT");
            var eq2 = AddEquity("MSFT");
            s2.OptionSymbol = option2.Symbol;
            s2.UnderlyingSymbol = eq2.Symbol;
            positionProvider.PositionList.Add(s2);

            //var s3 = new PositionGroup();
            //var option3 = AddOption("FB");
            //var eq3 = AddEquity("FB");
            //s3.OptionSymbol = option3.Symbol;
            //s3.UnderlyingSymbol = eq3.Symbol;
            //positionProvider.PositionList.Add(s3);

            //var s4 = new PositionGroup();
            //var option4 = AddOption("JNJ");
            //var eq4 = AddEquity("JNJ");
            //s4.OptionSymbol = option4.Symbol;
            //s4.UnderlyingSymbol = eq4.Symbol;
            //positionProvider.PositionList.Add(s4);

            //var s5 = new PositionGroup();
            //var option5 = AddOption("PG");
            //var eq5 = AddEquity("PG");
            //s5.OptionSymbol = option5.Symbol;
            //s5.UnderlyingSymbol = eq5.Symbol;
            //positionProvider.PositionList.Add(s5);

            //var s6 = new PositionGroup();
            //var option6 = AddOption("V");
            //var eq6 = AddEquity("V");
            //s6.OptionSymbol = option6.Symbol;
            //s6.UnderlyingSymbol = eq6.Symbol;
            //positionProvider.PositionList.Add(s6);

            //var s7 = new PositionGroup();
            //var option7 = AddOption("INTC");
            //var eq7 = AddEquity("INTC");
            //s7.OptionSymbol = option7.Symbol;
            //s7.UnderlyingSymbol = eq7.Symbol;
            //positionProvider.PositionList.Add(s7);

            //var s1 = new PositionGroup();
            //var option1 = AddOption("AAPL");
            //var eq1 = AddEquity("AAPL");
            //s1.OptionSymbol = option1.Symbol;
            //s1.UnderlyingSymbol = eq1.Symbol;
            //positionProvider.PositionList.Add(s1);

            //var s8 = new PositionGroup();
            //var option8 = AddOption("JPM");
            //var eq8 = AddEquity("JPM");
            //s8.OptionSymbol = option8.Symbol;
            //s8.UnderlyingSymbol = eq8.Symbol;
            //positionProvider.PositionList.Add(s8);


            //option1.PriceModel = OptionPriceModels.CrankNicolsonFD();
            //option1.SetFilter(universe => from symbol in universe
            //                    .IncludeWeeklys()
            //                    .Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
            //                              select symbol);
            option2.PriceModel = OptionPriceModels.CrankNicolsonFD();
            option2.SetFilter(universe => from symbol in universe
                                .IncludeWeeklys()
                                .Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
                                          select symbol);
            //option3.PriceModel = OptionPriceModels.CrankNicolsonFD();
            //option3.SetFilter(universe => from symbol in universe
            //                    .IncludeWeeklys()
            //                    .Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
            //                              select symbol);
            //option4.PriceModel = OptionPriceModels.CrankNicolsonFD();
            //option4.SetFilter(universe => from symbol in universe
            //                    .IncludeWeeklys()
            //                    .Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
            //                              select symbol);
            //option5.PriceModel = OptionPriceModels.CrankNicolsonFD();
            //option5.SetFilter(universe => from symbol in universe
            //                    .IncludeWeeklys()
            //                    .Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
            //                              select symbol);
            //option6.PriceModel = OptionPriceModels.CrankNicolsonFD();
            //option6.SetFilter(universe => from symbol in universe
            //                    .IncludeWeeklys()
            //                    .Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
            //                              select symbol);
            //option7.PriceModel = OptionPriceModels.CrankNicolsonFD();
            //option7.SetFilter(universe => from symbol in universe
            //                    .IncludeWeeklys()
            //                    .Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
            //                              select symbol);

            //option8.PriceModel = OptionPriceModels.CrankNicolsonFD();
            //option8.SetFilter(universe => from symbol in universe
            //                    .IncludeWeeklys()
            //                    .Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
            //                              select symbol);


            SetWarmUp(TimeSpan.FromDays(7));
            SetBenchmark("SPY");

        }

        public override void OnData(Slice slice)
        {
            if (IsWarmingUp) return;
            if (currentDay.Date != slice.Time.Date)
            {
                currentDay = slice.Time;
                positionProvider.CollectData(slice, deltaStart, deltaEnd);
                //LogHolding(slice);
                daysPass++;
                if (daysPass % 10 == 0)
                {
                    LogHolding(slice);
                }
            }
            else
            {
                return;

            }

            Stopwatch stopWatch = new Stopwatch();
            //stopWatch.Start();
            foreach (var pg in positionProvider.PositionList.Where(x => !x.Deleted))
            {
                var businessDays = TradingCalendar.GetTradingDays(startDate.Date, endDate.Date).Where(x => x.BusinessDay);
                // Log(" expiry : " + pg.Expiry.Date + "    slice:" + slice.Time.AddDays(-liquidateBefore).Date);
                if ( false && pg.Expiry.Date == slice.Time.AddDays(liquidateBefore).Date)
                {
                    Log("Expiring");
                    foreach (var p in pg.Positions)
                    {
                        if (p.SecurityType == SecurityType.Equity)
                        {
                            Liquidate(pg.UnderlyingSymbol);
                        }
                        else
                        {
                            Liquidate(p.Symbol);

                        }
                    }

                    pg.Deleted = true;

                }
            }

            if (slice.Time.Year == 2010)
            {
                return;
            }

            OrderHighestIv(slice);

            OptionChain chain;
            foreach (var pos in positionProvider.PositionList)
            {
                if (slice.OptionChains.TryGetValue(pos.OptionSymbol, out chain))
                {

                    var q = Portfolio[pos.UnderlyingSymbol].Quantity;
                    var positionDelta = -((pos.Delta * GetVolume() * 100) + q);
                    var reverseDelta = q + (pos.Delta * GetVolume() * 100);

                    //Debug ("delta:"+pg.Delta + " reverseDelta:"+ reverseDelta + " position:"+ q);
                    if (positionDelta > 40 * GetVolume() || positionDelta < -40 * GetVolume())
                    {

                        var k = pos.Positions.Where(x => x.SecurityType == SecurityType.Equity).FirstOrDefault();
                        if (k != null)
                        {
                            k.Quantity = k.Quantity - (int)(Math.Ceiling(pos.Delta) * GetVolume());
                        }
                        else
                        {
                            Position p = new Position();
                            p.Symbol = pos.UnderlyingSymbol;
                            p.Quantity = (int)-(Math.Ceiling(pos.Delta) * GetVolume());
                            p.SecurityType = SecurityType.Equity;
                            pos.Positions.Add(p);
                        }

                        //Log("PositionDelta :" + positionDelta + " Delta:" + pg.Delta);
                        //Debug("Delta:" + pg.Delta);
                        if (pos.Delta != 0)
                        {
                            //   Log("Starting adjustment" + positionDelta);
                           // var tik = MarketOrder(pos.UnderlyingSymbol, -reverseDelta);
                        }
                        else
                        {
                        //    Liquidate(pos.UnderlyingSymbol);

                        }


                    }

                }
            }

            GetPositionProfitLoss(slice);
        }

        public int GetPositionProfitLoss(Slice slice)
        {
            Debug("profit and loss");
            foreach (var p in Portfolio.Where(x => x.Value.Invested))
            {
                PositionGroup pg = null;
                foreach (var pr in positionProvider.PositionList)
                {
                    var poss = pr.Positions.Where(x => x.Symbol == p.Key && x.SecurityType == SecurityType.Option);
                    if (poss != null)
                    {
                        pg = pr;
                        break;
                    }
                }

                var premiumCollected = 0m;
                var currentPremium = 0m;

                OptionChain chain;
                if (slice.OptionChains.TryGetValue(pg.OptionSymbol, out chain))
                {
                    foreach (var pu in pg.Positions.Where(b => b.SecurityType == SecurityType.Option))
                    {
                        var option = Portfolio.Where(x => x.Key == pu.Symbol).FirstOrDefault();
                        if (option.Value != null)
                        {
                            premiumCollected = pg.PremiumCollected + option.Value.AveragePrice;
                            var uu = chain.Where(x => x.Symbol == pu.Symbol).FirstOrDefault();
                            if (uu != null)
                            {
                                currentPremium = currentPremium + ((uu.BidPrice + uu.AskPrice) / 2);
                            }
                        }
                    }
                }

				Log("premium collected: "+ premiumCollected + " currentPremium:" + currentPremium );
                pg.PremiumCollected = premiumCollected;
                pg.CurrentPremium = currentPremium;



                if (pg.CurrentPremium > pg.PremiumCollected * 3 || pg.CurrentPremium < pg.PremiumCollected * 0.5m)
                {
                	Log("liquidate stop loss take profit");
                    foreach (var pos in pg.Positions)
                    {
                        Liquidate(pos.Symbol);
                    }
                }
            }





            return 0;
        }


        public bool IsMonday(Slice slice)
        {
            if (slice.Time.DayOfWeek == DayOfWeek.Monday && slice.Time.Hour == 9 && slice.Time.Minute == 31)
            {
                return true;
            }

            return false;
        }

        public int GetVolume()
        {
            return 1;
        }

        public override void OnAssignmentOrderEvent(OrderEvent e)
        {
            Log("assignment , liquidating--------------------------");
            foreach (var pg in positionProvider.PositionList)
            {
                pg.Deleted = true;
            }

            Liquidate();
        }

        public override void OnOrderEvent(OrderEvent e)
        {
            if (e.FillQuantity == 0)
            {
                return;

            }
        }

        public OptionContract OrderHighestIv(Slice slice)
        {
            //LogHolding(slice);
            OptionChain chain;

            //	Debug ("Count:" + positionProvider.SymbolList.Count());
            foreach (var pg in positionProvider.PositionList)
            {
                var toClearup = false;
                foreach (var poss in pg.Positions.Where(x => x.SecurityType == SecurityType.Option))
                {
                    var portfolioExist = Portfolio.Where(x => x.Value.Invested && x.Key == poss.Symbol);
                    if (portfolioExist == null)
                    {
                        toClearup = true;
                        break;
                    }
                }

                if (toClearup)
                {
                	Log("liquidate missing leg");
                    foreach (var poss in pg.Positions)
                    {
                        Liquidate(poss.Symbol);
                    }

                    continue;
                }
            

            if (slice.OptionChains.TryGetValue(pg.OptionSymbol, out chain))
            {

                var pp = chain.Where(x => x.Greeks.Delta < (decimal)-deltaStart && x.Greeks.Delta > (decimal)-deltaEnd && x.Right == OptionRight.Put).OrderByDescending(x => (x.BidPrice + x.AskPrice) / 2).FirstOrDefault();
                if (pp != null)
                {

                   	LogOption(pp);
                    var cc = chain.Where(x => x.Greeks.Delta > (decimal)deltaStart && x.Greeks.Delta < (decimal)deltaEnd && x.Right == OptionRight.Call && x.Expiry.Date == pp.Expiry.Date).OrderByDescending(x => (x.BidPrice + x.AskPrice) / 2).FirstOrDefault();

                    if (cc != null)
                    {

                        //	LogOption(pp);
                        //	LogOption(cc);
                      //  if (pg.IvRank > 70)
                      //    {

                        var ticket = MarketOrder(cc.Symbol, -GetVolume());
                        var ticket2 = MarketOrder(pp.Symbol, -GetVolume());
                        pg.Positions = new List<Position>();
                        pg.Tickets.Add(ticket);
                        pg.Tickets.Add(ticket2);
                        pg.Delta = cc.Greeks.Delta + pp.Greeks.Delta;
                        pg.Expiry = cc.Symbol.ID.Date;


                        Log("CALL: BID:" + cc.BidPrice + " ASK: " + cc.AskPrice);
                        Log("CALL: Strike:" + cc.Strike + " UNDERLYING: " + chain.Underlying.Price);

                         Log("PUT: BID:" + pp.BidPrice + " ASK: " + pp.AskPrice);
                        Log("PUT: Strike:" + pp.Strike + " UNDERLYING: " + chain.Underlying.Price);
                        var expiry = pg.Expiry;
                        var businessDays = TradingCalendar.GetTradingDays(startDate, endDate).Where(x => x.BusinessDay);
                        if (!businessDays.Select(x => x.Date).Contains(pg.Expiry))
                        {
                            var u = businessDays.Where(x => x.Date < pg.Expiry).OrderByDescending(x => x.Date).FirstOrDefault();
                            pg.Expiry = u.Date;
                        }

                        var p = new Position();
                        p.Symbol = cc.Symbol;
                        p.Quantity = GetVolume();
                        p.SecurityType = SecurityType.Option;

                        var c = new Position();
                        c.Symbol = pp.Symbol;
                        c.Quantity = GetVolume();
                        c.SecurityType = SecurityType.Option;
                        pg.Positions.Add(c);
                        pg.Positions.Add(p);

                     //       }
                    }
                }
            }
        }


            return null;
        }

    public void LogOption(OptionContract pp)
    {
        Log("-----------------");
        Log("Symbol" + pp.Symbol);
        Log("Expiry:" + pp.Expiry);
        Log("Strike:" + pp.Strike);
        Log("Delta:" + pp.Greeks.Delta);
        Log("Underlying:" + pp.UnderlyingLastPrice);
        Log("MId Price:" + (pp.BidPrice + pp.AskPrice) / 2);
    }

    public void LogHolding(Slice slice)
    {
        Debug("---------HOLDING " + slice.Time.Date);

        OptionChain chain;

        foreach (var h in Portfolio.Where(x => x.Value.Invested))
        {

            Log("Symbol:" + h.Key);
            Log("Quantity" + h.Value.Quantity);
            Log("Expiry:" + h.Key.ID.Date);
            Log("Price:" + h.Value.Price);
        }

        Debug("----Holding END------------");
    }

}
}
namespace QuantConnect {
    public class Position
    {
    	public 	Symbol Symbol {get; set;}
    	
    	public double AvgPrice {get;set;}
    	
    	public int Quantity {get; set;}
    	
    	public SecurityType SecurityType {get;set;}
    }
}
namespace QuantConnect {
	public class IVRank
	{
		public IVRank(decimal iv , DateTime date){
			this.IV = iv;
			this.Date = date;
		}
		
		public decimal IV {get; set; }

		public DateTime Date {get; set; }
	}
}
namespace QuantConnect {
	
	public class PositionGroup {
		public List<Position> Positions = new List<Position>();

		public decimal Delta;
		
		public Symbol OptionSymbol;
	
		public Symbol UnderlyingSymbol;
		
		public decimal PremiumCollected;
		
		public decimal CurrentPremium;
		
		public decimal AdjustmentProfit;
		
		public decimal AdjustmentQuantity;
		
		public DateTime Expiry ;
		
		public List<OrderTicket> Tickets = new List<OrderTicket>();
		
		public bool Deleted = false;
		
		public decimal IvRank;

		public List<decimal> iv = new List<decimal>();
	}
}
namespace QuantConnect
{
    using System;
    using System.Linq;
    using QuantConnect.Data;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Securities.Option;


    public class PositionProvider
    {
        public List<PositionGroup> PositionList = new List<PositionGroup>();


        private SecurityPortfolioManager portfolioManager;

        public PositionProvider(SecurityPortfolioManager portfolio)
        {
            this.portfolioManager = portfolio;
        }

        public void AddCombo(List<Position> positions)
        {
            var pg = new PositionGroup();
            pg.Positions.AddRange(positions);
            PositionList.Add(pg);
        }

        public void CollectData(Slice slice, decimal deltaStart, decimal deltaEnd)
        {
            foreach (var pg in PositionList)
            {
                OptionChain chain;
  
                if (slice.OptionChains.TryGetValue(pg.OptionSymbol, out chain))
                {

                        var positions = pg.Positions;
                        var options = chain.Where(x => pg.Positions.Where(b => b.SecurityType == SecurityType.Option).Select(y => y.Symbol.ID).Contains(x.Symbol.ID));
                        pg.Delta = options.Sum(x => x.Greeks.Delta);
                        var symbol = pg.Positions.Where(b => b.SecurityType == SecurityType.Equity).FirstOrDefault();
                        if (symbol == null)
                        {
                            pg.AdjustmentProfit = 0;
                            pg.AdjustmentQuantity = 0;
                        }
                        else
                        {
                        
                            var ticks = pg.Tickets.Where(x => x.Symbol == symbol.Symbol);
                            pg.AdjustmentProfit = ticks.Sum(fill => fill.QuantityFilled * fill.AverageFillPrice);
                            pg.AdjustmentQuantity = ticks.Sum(fill => fill.QuantityFilled);
                        }

                        var sumQuantity = pg.Tickets.Sum(fill => fill.QuantityFilled);
                        var pgOptions = pg.Positions.Where(x=>x.SecurityType == SecurityType.Option);
                        if (sumQuantity != 0)
                        {
                            pg.PremiumCollected = pg.AdjustmentProfit / sumQuantity;
                        }
                        else
                        {
                            pg.PremiumCollected = 0;
                        }
                 
                    var put = chain.Where(x => x.Greeks.Delta < (decimal)-deltaStart && x.Greeks.Delta > (decimal)-deltaEnd && x.Right == OptionRight.Put).FirstOrDefault();
                    if (put != null)
                    {
                        pg.iv.Add(put.ImpliedVolatility);
                        GetIv(pg, put);
                    }
                }

            }

        }

        public decimal GetDeltaQuantity(PositionGroup pg)
        {
            var symbol = pg.Positions.Where(b => b.SecurityType == SecurityType.Equity).FirstOrDefault();
            if (symbol == null)
            {
                return 0;
            }
            return pg.Tickets.Where(x => x.Symbol == symbol.Symbol).Sum(fill => fill.QuantityFilled);
        }

        public void GetIv(PositionGroup symbolData, OptionContract option)
        {
            var low = symbolData.iv.Where(x => x < option.ImpliedVolatility).Count();
            var count = symbolData.iv.Count();
            if (count > 90)
            {
                symbolData.iv = symbolData.iv.Skip(count - 90).ToList();
            }
            if (count < 10)
            {
                symbolData.IvRank = 0;
            }

            symbolData.IvRank = ((decimal)low / (decimal)symbolData.iv.Count()) * 100;
        }
    }

}