Overall Statistics
Total Trades
222
Average Win
5.53%
Average Loss
-1.38%
Compounding Annual Return
22.861%
Drawdown
26.200%
Expectancy
1.279
Net Profit
550.250%
Sharpe Ratio
0.977
Loss Rate
54%
Win Rate
46%
Profit-Loss Ratio
4.00
Alpha
0.11
Beta
0.889
Annual Standard Deviation
0.194
Annual Variance
0.038
Information Ratio
0.654
Tracking Error
0.153
Treynor Ratio
0.214
Total Fees
$1689.75
/*
 * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
 * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

using System;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;

namespace QuantConnect.Securities.Option
{
    /// <summary>
    /// Represents a simple option margining model.
    /// </summary>
    /// <remarks>
    /// Options are not traded on margin. Margin requirements exist though for those portfolios with short positions.
    /// Current implementation covers only single long/naked short option positions.
    /// </remarks>
    public class CustomOptionMarginModel : SecurityMarginModel
    {
        // initial margin
        private const decimal OptionMarginRequirement = 1;
        private const decimal NakedPositionMarginRequirement = 0.0000001m;
        private const decimal NakedPositionMarginRequirementOtm = 0.0000002m;

        /// <summary>
        /// Initializes a new instance of the <see cref="OptionMarginModel"/>
        /// </summary>
        /// <param name="requiredFreeBuyingPowerPercent">The percentage used to determine the required unused buying power for the account.</param>
        public CustomOptionMarginModel(decimal requiredFreeBuyingPowerPercent = 0)
        {
            RequiredFreeBuyingPowerPercent = requiredFreeBuyingPowerPercent;
        }

        /// <summary>
        /// Gets the current leverage of the security
        /// </summary>
        /// <param name="security">The security to get leverage for</param>
        /// <returns>The current leverage in the security</returns>
        public override decimal GetLeverage(Security security)
        {
            // Options are not traded on margin
            return 1;
        }

        /// <summary>
        /// Sets the leverage for the applicable securities, i.e, options.
        /// </summary>
        /// <param name="security"></param>
        /// <param name="leverage">The new leverage</param>
        public override void SetLeverage(Security security, decimal leverage)
        {
            // Options are leveraged products and different leverage cannot be set by user.
            throw new InvalidOperationException("Options are leveraged products and different leverage cannot be set by user");
        }

        /// <summary>
        /// Gets the total margin required to execute the specified order in units of the account currency including fees
        /// </summary>
        /// <param name="parameters">An object containing the portfolio, the security and the order</param>
        /// <returns>The total margin in terms of the currency quoted in the order</returns>
        protected override decimal GetInitialMarginRequiredForOrder(
            InitialMarginRequiredForOrderParameters parameters)
        {
            //Get the order value from the non-abstract order classes (MarketOrder, LimitOrder, StopMarketOrder)
            //Market order is approximated from the current security price and set in the MarketOrder Method in QCAlgorithm.

            var fees = parameters.Security.FeeModel.GetOrderFee(
                new OrderFeeParameters(parameters.Security,
                    parameters.Order)).Value;
            var feesInAccountCurrency = parameters.CurrencyConverter.
                ConvertToAccountCurrency(fees).Amount;

            var value = parameters.Order.GetValue(parameters.Security);
            var orderValue = value * GetInitialMarginRequirement(parameters.Security, value);

            return orderValue + Math.Sign(orderValue) * feesInAccountCurrency;
        }

        /// <summary>
        /// Gets the margin currently alloted to the specified holding
        /// </summary>
        /// <param name="security">The security to compute maintenance margin for</param>
        /// <returns>The maintenance margin required for the </returns>
        protected override decimal GetMaintenanceMargin(Security security)
        {
            return security.Holdings.AbsoluteHoldingsCost * GetMaintenanceMarginRequirement(security, security.Holdings.HoldingsCost);
        }

        /// <summary>
        /// Gets the margin cash available for a trade
        /// </summary>
        /// <param name="portfolio">The algorithm's portfolio</param>
        /// <param name="security">The security to be traded</param>
        /// <param name="direction">The direction of the trade</param>
        /// <returns>The margin available for the trade</returns>
        protected override decimal GetMarginRemaining(SecurityPortfolioManager portfolio, Security security, OrderDirection direction)
        {
        	return 100000000m;
        	
            var result = portfolio.MarginRemaining;

            if (direction != OrderDirection.Hold)
            {
                var holdings = security.Holdings;
                //If the order is in the same direction as holdings, our remaining cash is our cash
                //In the opposite direction, our remaining cash is 2 x current value of assets + our cash
                if (holdings.IsLong)
                {
                    switch (direction)
                    {
                        case OrderDirection.Sell:
                            result +=
                                // portion of margin to close the existing position
                                GetMaintenanceMargin(security) +
                                // portion of margin to open the new position
                                security.Holdings.AbsoluteHoldingsValue * GetInitialMarginRequirement(security, security.Holdings.HoldingsValue);
                            break;
                    }
                }
                else if (holdings.IsShort)
                {
                    switch (direction)
                    {
                        case OrderDirection.Buy:
                            result +=
                                // portion of margin to close the existing position
                                GetMaintenanceMargin(security) +
                                // portion of margin to open the new position
                                security.Holdings.AbsoluteHoldingsValue * GetInitialMarginRequirement(security, security.Holdings.HoldingsValue);
                            break;
                    }
                }
            }

            result -= portfolio.TotalPortfolioValue * RequiredFreeBuyingPowerPercent;
            return result < 0 ? 0 : result;
        }

        /// <summary>
        /// The percentage of an order's absolute cost that must be held in free cash in order to place the order
        /// </summary>
        protected override decimal GetInitialMarginRequirement(Security security)
        {
            return GetInitialMarginRequirement(security, security.Holdings.HoldingsValue);
        }

        /// <summary>
        /// The percentage of the holding's absolute cost that must be held in free cash in order to avoid a margin call
        /// </summary>
        public override decimal GetMaintenanceMarginRequirement(Security security)
        {
            return GetMaintenanceMarginRequirement(security, security.Holdings.HoldingsValue);
        }

        /// <summary>
        /// The percentage of an order's absolute cost that must be held in free cash in order to place the order
        /// </summary>
        private decimal GetInitialMarginRequirement(Security security, decimal holding)
        {
            return GetMarginRequirement(security, holding);
        }

        /// <summary>
        /// The percentage of the holding's absolute cost that must be held in free cash in order to avoid a margin call
        /// </summary>
        private decimal GetMaintenanceMarginRequirement(Security security, decimal holding)
        {
            return GetMarginRequirement(security, holding);
        }

        /// <summary>
        /// Private method takes option security and its holding and returns required margin. Method considers all short positions naked.
        /// </summary>
        /// <param name="security">Option security</param>
        /// <param name="value">Holding value</param>
        /// <returns></returns>
        private decimal GetMarginRequirement(Security security, decimal value)
        {
        	return 0m;
        	
            var option = (Option) security;

            if (value == 0m ||
                option.Close == 0m ||
                option.StrikePrice == 0m ||
                option.Underlying == null ||
                option.Underlying.Close == 0m)
            {
                return 0m;
            }

            if (value > 0m)
            {
                return OptionMarginRequirement;
            }

            var absValue = -value;
            var optionProperties = (OptionSymbolProperties) option.SymbolProperties;
            var underlying = option.Underlying;

            // inferring ratios of the option and its underlying to get underlying security value
            var multiplierRatio = underlying.SymbolProperties.ContractMultiplier / optionProperties.ContractMultiplier;
            var quantityRatio = optionProperties.ContractUnitOfTrade;
            var priceRatio = underlying.Close / (absValue / quantityRatio);
            var underlyingValueRatio = multiplierRatio * quantityRatio * priceRatio;

            // calculating underlying security value less out-of-the-money amount
            var amountOTM = option.Right == OptionRight.Call
                ? Math.Max(0, option.StrikePrice - underlying.Close)
                : Math.Max(0, underlying.Close - option.StrikePrice);
            var priceRatioOTM = amountOTM / (absValue / quantityRatio);
            var underlyingValueRatioOTM = multiplierRatio * quantityRatio * priceRatioOTM;

            return OptionMarginRequirement +
                   option.Holdings.AbsoluteQuantity * Math.Max(NakedPositionMarginRequirement * underlyingValueRatio,
                       NakedPositionMarginRequirementOtm * underlyingValueRatio - underlyingValueRatioOTM);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Securities.Option;

namespace QuantConnect.Algorithm.CSharp
{
    public class OptionExperiment3 : QCAlgorithm
    {
        private Symbol _optionSymbol;
        private String ticker = "SPY";
        private int min_expiry = 40;
        private int max_expiry = 50;
        private decimal short_moneyness = 0.98m;
        private decimal long_moneyness = 0.8m;
        private decimal short_premium_pcnt = 0.02m;
        private decimal long_premium_pcnt = 0.01m;
        private string liquidate = null;
        private OptionContract ShortPut = null;
        private OptionContract LongPut = null;
        private List<OptionContract> PrevLongPuts = new List<OptionContract>();
        private decimal MaxLoss = 0;
        private decimal MaxWin = 0;
        private decimal mult = 1;
        private decimal TakeProfit = 0.95m;

        public override void Initialize()
        {
            // SetStartDate(2015, 7, 7);
            // SetEndDate(2016, 11, 1);
            SetStartDate(2010, 1, 1);
            // SetStartDate(2018, 1, 1);
            SetCash(60000);

            SetSecurityInitializer(x => { 
			    if (x is Option) 
			    {
			        x.MarginModel = new CustomOptionMarginModel();
			    }
            });
            
            var equity = AddEquity(ticker, Resolution.Minute);
            equity.SetLeverage(10000000);
            var option = AddOption(ticker, Resolution.Minute);
            option.MarginModel = new CustomOptionMarginModel();
            _optionSymbol = option.Symbol;

            Securities[ticker].SetDataNormalizationMode(DataNormalizationMode.Raw);

            option.SetFilter(universe => from symbol in universe
            								.IncludeWeeklys()
                                            .Expiration(TimeSpan.FromDays(min_expiry), TimeSpan.FromDays(max_expiry))
                                            .Strikes(-500, +1)
                                         select symbol);

            option.PriceModel = OptionPriceModels.CrankNicolsonFD();
            
            SetWarmup(TimeSpan.FromDays(365));
            SetBenchmark(ticker);
        }
        
        public override void OnData(Slice slice)
        {
        	if (IsWarmingUp) return;
        	
        	ProcessPrevLongPuts();
        	
			ProcessCurrentShortPut();        	

            if (liquidate != null)
            {
				ProcessLiquidate();			
				return;
            }
            
            if (ShortPut == null)
            {
            	Invest(slice);
            }
        }
        
        public void ProcessPrevLongPuts()
        {
        	PrevLongPuts.RemoveAll(put => {
        		if (put == null || Portfolio[put.Symbol] == null || !Portfolio[put.Symbol].Invested)
        		{
        			return true;
        		}

        		if (Portfolio[put.Symbol].UnrealizedProfit >= 0)
        		{
        			this.Liquidate(put.Symbol);
        			return true;
        		}

	            if (put != null && put.Expiry < Time.AddDays(1))
	            {
        			this.Liquidate(put.Symbol);
        			return true;
	            }
        		
        		return false;
        	});
        }
        
        public void ProcessCurrentShortPut()
        {
        	if (ShortPut == null)
        	{
        		return;
        	}
        	
            if (ShortPut.Expiry < Time.AddDays(1))
            {
            	liquidate = "Liq @ 1 DTE";
            	return;
            }
            
            if (MaxWin > 0 && this.Portfolio.TotalUnrealizedProfit > MaxWin * TakeProfit) {
            	liquidate = "Take Profit @ .95";
            	return;
        	}
        }
        
        public void ProcessLiquidate()
        {
    		if (Portfolio[ticker] != null && Portfolio[ticker].Invested) {
    			this.Liquidate(ticker);
    		}
    		
    		if (ShortPut != null)
    		{
        		this.Liquidate(ShortPut.Symbol, liquidate);
        		ShortPut = null;
    		}
    		
    		if (LongPut != null) {
    			// PrevLongPuts.Add(LongPut);
        		this.Liquidate(LongPut.Symbol, liquidate);
        		LongPut = null;
    		}
    		
    		MaxWin = 0;
    		MaxLoss = 0;
    		liquidate = null;
        }
        
        public void Invest(Slice slice)
        {
            OptionChain chain;
            
            if (slice.OptionChains.TryGetValue(_optionSymbol, out chain))
            {
                ShortPut = chain
                    .Where(x => x.Right == OptionRight.Put
                    		 && x.Strike > 0
                    		 && x.BidPrice > 0
                    		 && x.BidPrice > chain.Underlying.Price * short_premium_pcnt)
                    .OrderBy(x => Math.Abs(chain.Underlying.Price * short_moneyness - x.Strike))
                    .ThenByDescending(x => x.Expiry)
                    .FirstOrDefault();

                if (ShortPut == null || ShortPut.Expiry < Time.AddDays(min_expiry))
                {
	                ShortPut = null;
	                LongPut = null;
	                return;
                }

                LongPut = chain
                    .Where(x => x.Right == OptionRight.Put
                    		 && x.Expiry == ShortPut.Expiry
                    		 && chain.Underlying.Price * long_moneyness >= x.Strike
                    		 && x.AskPrice <= ShortPut.BidPrice * long_premium_pcnt)
                    .OrderByDescending(x => x.Strike)
                    .FirstOrDefault();
                    
                if (LongPut == null)
                {
	                ShortPut = null;
	                LongPut = null;
	                return;
                }
                
                this.Debug("Underlying: " + chain.Underlying.Price + ", Short strike: " + ShortPut.Strike.ToString() + ", Long strike: " + LongPut.Strike.ToString());
                
                var width = ShortPut.Strike - LongPut.Strike;
                var cash = Portfolio.CashBook["USD"].Amount;
                var count = Math.Floor(cash / width / 100.00m);
                
                var LongOrder = this.MarketOrder(LongPut.Symbol, 1.00m * mult * count, asynchronous: false);
                var ShortOrder = this.MarketOrder(ShortPut.Symbol, -1.00m * mult * count, asynchronous: false);
                
                MaxLoss = width * 100 * count;
                MaxWin = (ShortOrder.AverageFillPrice - LongOrder.AverageFillPrice) * 100.00m * count;
            }

        }

        public override void OnAssignmentOrderEvent(OrderEvent assignmentEvent)
        {
            liquidate = "Liq assigned shares";
        }
    }
    
    public class QuandlVix : Quandl
    {
	   public QuandlVix() : base(valueColumnName: "vix close") { }
    }
}