Overall Statistics
Total Trades
3
Average Win
19.28%
Average Loss
0%
Compounding Annual Return
52.807%
Drawdown
12.400%
Expectancy
0
Net Profit
7.120%
Sharpe Ratio
0.962
Loss Rate
0%
Win Rate
100%
Profit-Loss Ratio
0
Alpha
1.449
Beta
-2.043
Annual Standard Deviation
0.66
Annual Variance
0.435
Information Ratio
0.333
Tracking Error
0.708
Treynor Ratio
-0.311
Total Fees
$0.00
using System.Net;

namespace QuantConnect {

    public static class OptionSettings 
    {
    	public static Dictionary<string, OptionSpecs> specs = new Dictionary<string, OptionSpecs>();
    	public static SortedDictionary<DateTime, decimal> RFR = new SortedDictionary<DateTime, decimal>();
    	public static QCAlgorithm Algorithm;
        
        /// <summary>
        /// Initialises the option framework
		/// </summary>
        public static void Initialize(QCAlgorithm algorithm, decimal r = 0M) {
        	Algorithm = algorithm;
        	if (r != 0) // if user wants to use a fixed interest rate
        	{
        		RFR.Add(new DateTime(1500,1,1), r);
        		RFR.Add(new DateTime(3000,1,1), r);
        		return;
        	}
        	// download 3-Month Treasury Bill: Secondary Market Rate from QUANDL
        	string rfr = "";
			using (WebClient wc = new WebClient())
			{
			    rfr = wc.DownloadString("https://www.quandl.com/api/v3/datasets/FRED/DTB3.csv");
			}
			if (string.IsNullOrEmpty(rfr)) {
				throw new System.NullReferenceException("Cannot download data: QUANDL cannot be reached");
			} else {
                string[] lines = rfr.Split(new string[] { "\n" }, StringSplitOptions.None);
                foreach (string l in lines)
                {
                    if (l.Contains("DATE") || string.IsNullOrEmpty(l)) { continue; }
                    string[] values = l.Split(',');
                    DateTime d = DateTime.ParseExact(values[0], "yyyy-MM-dd", CultureInfo.InvariantCulture);
                    decimal v = Decimal.Parse(values[1]);
                    RFR.Add(d, v);
                }
			}
			// use LINQ to reverse the order of the dictionary
			RFR.Keys.Reverse();
        }
        
        /// <summary>
        /// Registers option by creating a new OptionSpecs for this option
		/// </summary>
        public static void RegisterOption(string name, int i, int m, string maturityType = "MONTHLY", decimal dividendYield = 0M) {
        	string type = name.Substring(name.IndexOf('.')+1).ToUpper();
        	if (name.ToUpper().Contains("CALL")) {
        		specs.Add(name, new OptionSpecs(type, Algorithm.StartDate, i, m, maturityType, dividendYield));
        	} else if (name.ToUpper().Contains("PUT")) {
        		specs.Add(name, new OptionSpecs(type, Algorithm.StartDate, i, m, maturityType, dividendYield));
        	} else {
        		 throw new System.ArgumentException("This is not a valid option type", type);
        	}
        }
        
        public static void UpdateAllMaturities(DateTime Now) {
			foreach(var pair in specs) {
			    pair.Value.UpdateMaturity(Now);
			}
        }
        
        /// <summary>
        /// Prints the greeks (makes no sense if you don't have the ACTUAL price of the option)
		/// </summary>
        public static void PrintAllGreeksOf(Option o) {
        		Console.WriteLine("Option:\t"+o.Symbol);
        		Console.WriteLine("---------------------");
                Console.WriteLine("Value:\t\t"+o.Value);
                // save all option greeks
                Console.WriteLine("Delta:\t\t"+o.Delta);
                Console.WriteLine("Gamma:\t\t"+o.Gamma);
                Console.WriteLine("Vega:\t\t"+o.Vega);
                Console.WriteLine("Theta:\t\t"+o.Theta);
                Console.WriteLine("Rho:\t\t"+o.Rho);
                Console.WriteLine("Volga:\t\t"+o.Volga);
                Console.WriteLine("Vanna:\t\t"+o.Vanna);
                Console.WriteLine("Charm:\t\t"+o.Charm);
                Console.WriteLine("Color:\t\t"+o.Color);
                Console.WriteLine("Dual Delta:\t"+o.DualDelta);
                Console.WriteLine("Dual Gamma:\t"+o.DualGamma);
                Console.WriteLine("---------------------");
        }
    }
    
	public class OptionSpecs
    {
    	public SortedDictionary<DateTime, decimal> LastTwoWeeks	{ get; set; }
    	public bool NoStrike 			{ get; set; }
    	public decimal Price 			{ get; set; }
		public string OptionType 		{ get; set; }
		public string MaturityType 		{ get; set; }
        public decimal Maturity 		{ get; set; }
        public int DaysUntilMaturity 	{ get; set; } // so the user can always ask how many days until maturity?
        public int MaturitiesAway		{ get; set; } // the k'th nearest maturity on which this option expires
        public DateTime MaturityDate 	{ get; set; }
        public decimal Strike 			{ get; set; }
        public int StrikesAway			{ get; set; }
        public decimal DividendYield 	{ get; set; }
        
        /// <summary>
        /// Initialises the option specs
		/// </summary>
        public OptionSpecs(string type, DateTime Now, int strikesAway, int maturitiesAway, string maturityType, decimal dividendYield) {
        	this.Price 			  = -1;
        	this.OptionType 	  = type;
        	this.LastTwoWeeks 	  = new SortedDictionary<DateTime, decimal>();
        	this.MaturitiesAway   = maturitiesAway;
        	this.MaturityType 	  = maturityType.ToUpper();
			this.DividendYield    = dividendYield;
			this.StrikesAway 	  = strikesAway;
			// find corresponding strike
        	FindAndSetStrike(strikesAway, Price);
        	// find corresponding maturity
        	FindAndSetMaturity(Now);
        }
        /// <summary>
        /// Calculates a new option based on the properties of the class (which strike away? which maturity away?)
		/// </summary>
        public void ChooseOption(DateTime Now, decimal Price) {
        	// find corresponding strike
        	FindAndSetStrike(StrikesAway, Price);
        	// find corresponding maturity
        	FindAndSetMaturity(Now);
        }
        
        public void UpdatePrice(decimal price) {
        	this.Price = price;
        }
        /// <summary>
        /// Update maturity given the current time
		/// </summary>
        public void UpdateMaturity(DateTime Now) {
        	this.Maturity = (decimal)CountWeekDays(Now, this.MaturityDate)/365M;
        	this.DaysUntilMaturity = CountWeekDays(Now, this.MaturityDate);	
        }
        /// <summary>
        /// Finds a given maturity, given the number of maturities away.
        /// This builds a grid of the 'third fridays of the months' and chooses the MaturityDaysAway'th one.
		/// </summary>
        public void FindAndSetMaturity(DateTime Now) {
        	 DateTime NearestMaturity = new DateTime(1500, 1, 1);
		    if (MaturityType.Equals("MONTHLY")) {
	        	// make sure that the maturity is at least 'a fixed number of months' away
				DateTime MinimalMaturity = AddBusinessDays(Now, (MaturitiesAway)*20);
			    DateTime TempDate = new DateTime(MinimalMaturity.Year, MinimalMaturity.Month, 1);
		    	// if monthly option, search for next third friday of the month
			    TempDate = TempDate.AddMonths(-1); // prepare for loop
	        	while (MinimalMaturity.Date >= NearestMaturity.Date) {
					// add month
				    TempDate = TempDate.AddMonths(1);
				    // find first friday of this month
				    while (TempDate.DayOfWeek != DayOfWeek.Friday) {
				    	TempDate = TempDate.AddDays(1);	
				    }
					// add two weeks
				    TempDate = TempDate.AddDays(14);
				    NearestMaturity = TempDate;
				    TempDate = new DateTime(TempDate.Year, TempDate.Month, 1);
	        	}
		    } else if (MaturityType.Equals("WEEKLY")) {
	        	// make sure that the maturity is at least 'a fixed number of weeks' away
				DateTime MinimalMaturity = AddBusinessDays(Now, (MaturitiesAway)*5);
			    DateTime TempDate = new DateTime(MinimalMaturity.Year, MinimalMaturity.Month, 1);
		    	// if weekly option: search for next friday
				int daysUntilFriday = ((int) DayOfWeek.Friday - (int) MinimalMaturity.DayOfWeek + 7) % 7;
				NearestMaturity = MinimalMaturity.AddDays(daysUntilFriday);
			} else {
				throw new System.ArgumentException("This is not a valid option maturity type", MaturityType);
			}
        	this.Maturity = ((decimal)CountWeekDays(Now, NearestMaturity)) / 365M;
        	this.MaturityDate = NearestMaturity;
        	this.DaysUntilMaturity = CountWeekDays(Now, NearestMaturity);
        }

        /// <summary>
        /// find nearest strike price (grid of 1 dollars below a stock price of 250. Otherwise, grid is per 5 dollars).
		/// </summary>
		public void FindAndSetStrike(int strikesAway, decimal price) {
			decimal strike = 0.0M;
			if (price <= 250) {
				if (strikesAway >= 0) {
					if (OptionType.Equals("CALL"))
						strike = Math.Ceiling((price+((decimal)strikesAway*1.0M))/1.0M)*1.0M;
					else
						strike = Math.Floor((price-((decimal)strikesAway*1.0M))/1.0M)*1.0M;
				} else {
					if (OptionType.Equals("CALL"))
						strike = Math.Floor((price+((decimal)strikesAway*1.0M))/1.0M)*1.0M;
					else
						strike = Math.Ceiling((price-((decimal)strikesAway*1.0M))/1.0M)*1.0M;
				}
			} else {
				if (strikesAway >= 0) {
					strike = Math.Ceiling((price+((decimal)strikesAway*5.0M))/5.0M)*5.0M;
				} else {
					strike = Math.Floor((price+((decimal)strikesAway*5.0M))/5.0M)*5.0M;
				}
			}
        	this.Strike = strike;
        }
        
		/// <summary>
        /// Counts the number of week days (business days). Not so robust. Better to use real calendar.
		/// </summary>
        public int CountWeekDays(DateTime d0, DateTime d1)
        {
            int ndays = 1 + Convert.ToInt32((d1 - d0).TotalDays);
            int nsaturdays = (ndays + Convert.ToInt32(d0.DayOfWeek)) / 7;
            return ndays - 2 * nsaturdays
                   - (d0.DayOfWeek == DayOfWeek.Sunday ? 1 : 0)
                   + (d1.DayOfWeek == DayOfWeek.Saturday ? 1 : 0);
        }
        
		/// <summary>
        /// Adds business days to any given input date.
		/// </summary>
	    public DateTime AddBusinessDays(DateTime date, int addDays)
	    {
	        while (addDays != 0)
	        {
	            date = date.AddDays(Math.Sign(addDays));
	            if ((date.DayOfWeek != DayOfWeek.Saturday)&&(date.DayOfWeek != DayOfWeek.Sunday))
	            {
	                addDays = addDays - Math.Sign(addDays);
	            }
	        }
	        return date;
	    }
	    
		/// <summary>
        /// Adds a month to any given input date, but makes sure its the exact same day-of-the-month
		/// </summary>
		public static DateTime AddMonths(DateTime date, int months)
		{
			DateTime newDate = date;
			for (int i = 0; i < months; i++) {
				if (date.Day != DateTime.DaysInMonth(date.Year, date.Month))
					newDate = date.AddMonths(1);
				else 
					newDate = date.AddDays(1).AddMonths(1).AddDays(-1);	
			}
			return newDate;
		}
    }
}                        
namespace QuantConnect 
{   
    /// <summary>
    /// Framework for backtesting algorithms on theoretical options
    //  by: Jean-Paul van Brakel (jeanpaulvbrakel@gmail.com)
    /// </summary>
    public class OptionFrameWork : QCAlgorithm
    {
    	Resolution _res 		= Resolution.Daily;
    	string option 			= "SPY";
    	string[] options 		= {"CALL", "PUT"};
		string _maturityType 	= "MONTHLY";	// type of option maturities (also: "WEEKLY")
    	int _nof_strikes 		= 3; 			// number of option strikes that you want to include
    	int _nof_maturities 	= 3;			// number of option maturities that you want to include
    	bool first = true;
    	
        public override void Initialize()
        {
            SetStartDate(2013, 01, 01);
            SetEndDate(2013, 02, 28);
            
            // initialise option settings
            //OptionSettings.Initialize(this);
            //  ^this will automatically download the interest rate from QUANDL
            //  if you want to speed up your backtest, use a fixed interest rate instead:
            OptionSettings.Initialize(this, 0.05M);
            // we can also pass a dividend-yield parameter to change the theoretical prices:
            // OptionSettings.Initialize(this, 0.05M, 0.01M);
            
			// create 3 in-the-money and 3-out-of-the-money options for 10 different maturities
			for (int i = -_nof_strikes; i <= _nof_strikes; i++) {
				for (int m = 0; m <= _nof_maturities; m++) {
					foreach (string o in options) {
						AddData<Option>(option+"."+o+"."+i+"."+m, _res);	
						OptionSettings.RegisterOption(option+"."+o+"."+i+"."+m,i,m,_maturityType);
						Securities[option+"."+o+"."+i+"."+m].Exchange = new EquityExchange();	
					}
				}
			}
        }
        public override void OnData(Slice slice)
        {
			if (!Portfolio.HoldStock) {
				// we can order our options like any other asset
				// option contracts go per 100 (so multiply with 100!)
				Order("SPY.CALL.1.0", 80*100);
				// ^this order did the following:
				//  > order 80 call contracts with the first in-the-money strike
				// 	  and the first upcoming maturity
				
				// can also find the date of maturity itself:
				Console.WriteLine("Date of maturity:\t"+OptionSettings.specs["SPY.CALL.1.1"].MaturityDate);
				// or the days until maturity: OptionSettings.specs["SPY.CALL.1.1"].DaysUntilMaturity
			}

			if (slice.Time.Date == new DateTime(2013, 02, 11)) {
				if (slice.ContainsKey("SPY.CALL.-3.3")) {
					// can also access all greeks. For example:
					OptionSettings.PrintAllGreeksOf(slice.Get<Option>("SPY.CALL.-2.1"));
					
					// or, other example:
					Console.WriteLine("RHO:\t"+slice.Get<Option>("SPY.CALL.-3.3").Rho);
				}
			}

        }
    }
}                        
using System.Linq;

namespace QuantConnect {
    /// <summary>
    /// Unfinished code to backtest using theoretical options
    /// this ensures semi-realistic backtesting because theoretical options
    /// feature the time-value-of-money. However, volatility is estimated from
    /// the underlying which is not always a good approximation.
    /// 
    /// Mind you: this code is unfinished (so I'm sorry if it's messy)
    /// composed by:	Jean-Paul van Brakel
    /// 				jeanpaulvbrakel@gmail.com
    ///
    /// </summary>
    public class Option : BaseData
    {
        // define derived properties
        public string  optionType 			{ get; set; }
		public decimal Delta;
		public decimal Gamma;
		public decimal Vega;
		public decimal Theta;
		public decimal Rho;
		public decimal Volga;
		public decimal Vanna;
		public decimal Charm;
		public decimal Color;
		public decimal DualDelta;
		public decimal DualGamma;

        // override so we can properly set it from the tradebar
        public override DateTime EndTime { get; set; }

        /// <summary>
        /// Reader converts each line of the data source into BaseData objects. Each data type creates its own factory method, and returns a new instance of the object 
        /// each time it is called. 
        /// </summary>
        /// <param name="config">Subscription data config setup object</param>
        /// <param name="line">Line of the source document</param>
        /// <param name="date">Date of the requested data</param>
        /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
        /// <returns>Instance of the T:BaseData object generated by this line of the CSV</returns>
        public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
        {
        	OptionSpecs specs = OptionSettings.specs[config.Symbol];
        	
            try
            {
                config = HackSubscriptionDataConfig(config);

                // use the tradebar reader implementation to read QC csv files
                var tradebar = (TradeBar)new TradeBar().Reader(config, line, date, isLiveMode);
                
                // get name of option
                string OptionName = config.Symbol+"."+specs.OptionType;
                
                // update price of option (before calculation of Black-Scholes price)
                OptionSettings.specs[OptionName].UpdatePrice(tradebar.Close);
                
                // get nearest value for the risk-free rate
				var keys  = new List<DateTime>(OptionSettings.RFR.Keys);
	        	var index = keys.BinarySearch(tradebar.EndTime);
	        	KeyValuePair<DateTime, decimal> rfr = OptionSettings.RFR.ElementAt((int)Math.Abs(index)-1);

                // update last known price to this price
                if (specs.LastTwoWeeks.Count == 0) {
                	specs.LastTwoWeeks.Add(tradebar.EndTime.Date, tradebar.Close);
                } else if (tradebar.Time.Date > specs.LastTwoWeeks.Keys.Last().Date) {
                	specs.LastTwoWeeks.Add(tradebar.EndTime.Date, tradebar.Close);
                	while (specs.LastTwoWeeks.Count > 20)
                		specs.LastTwoWeeks.Remove(specs.LastTwoWeeks.Keys.First());
                } else if (tradebar.Time.Date == specs.LastTwoWeeks.Keys.Last()) {
                	specs.LastTwoWeeks[specs.LastTwoWeeks.Keys.Last()] = tradebar.Close;
                }
                OptionSettings.specs[OptionName].LastTwoWeeks = specs.LastTwoWeeks;
                double[] ltw = Array.ConvertAll(specs.LastTwoWeeks.Values.ToArray(), x => (double)x);
		               
		        // check if current time is larger than maturity
		        if (tradebar.Time.Date > specs.MaturityDate.Date) {
		        	// liquidate current option
		        	OptionSettings.Algorithm.Liquidate(OptionName);
		        	// find new option for this maturity and strike (according to new price)
	        		OptionSettings.specs[OptionName].ChooseOption(tradebar.EndTime, tradebar.Close);
		        }
				// update option maturities (re-calculate)
	            OptionSettings.UpdateAllMaturities(tradebar.Time.Date);
                
                // reload specs
                specs = OptionSettings.specs[OptionName];

                // now that we have all needed variables, can can calculate the theoretical option price
                // we do this using the Black-Scholes formula by Fischer Black and Myron Scholes (1973)
                // The Pricing of Options and Corporate Liabilities
                // The Journal of Political Economy, Vol. 81, No. 3, pp. 637-654
				decimal BlackScholesPrice = 0M;
				double Volatility = BlackScholes.HistoricalVolatility(ltw);
                if (specs.OptionType.Contains("CALL")) {
                	// calculate Black-Scholes price for CALL option:
                	BlackScholesPrice = (decimal) BlackScholes.blsCall((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
                	Delta = (decimal) BlackScholes.blsdeltaCall((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Gamma = (decimal) BlackScholes.blsgamma((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Vega = (decimal) BlackScholes.blsvega((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Theta = (decimal) BlackScholes.blsthetaCall((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Rho = (decimal) BlackScholes.blsrhoCall((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Volga = (decimal) BlackScholes.blsvolga((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Vanna = (decimal) BlackScholes.blsvanna((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Charm = (decimal) BlackScholes.blscharmCall((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Color = (decimal) BlackScholes.blscolor((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					DualDelta = (decimal) BlackScholes.blsdualdeltaCall((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					DualGamma = (decimal) BlackScholes.blsdualgamma((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
                } else if (specs.OptionType.Contains("PUT")) {
                	// calculate Black-Scholes price for PUT option:
                	BlackScholesPrice = (decimal) BlackScholes.blsPut((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
                	Delta = (decimal) BlackScholes.blsdeltaPut((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Gamma = (decimal) BlackScholes.blsgamma((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Vega = (decimal) BlackScholes.blsvega((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Theta = (decimal) BlackScholes.blsthetaPut((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Rho = (decimal) BlackScholes.blsrhoPut((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Volga = (decimal) BlackScholes.blsvolga((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Vanna = (decimal) BlackScholes.blsvanna((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Charm = (decimal) BlackScholes.blscharmPut((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					Color = (decimal) BlackScholes.blscolor((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					DualDelta = (decimal) BlackScholes.blsdualdeltaPut((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
					DualGamma = (decimal) BlackScholes.blsdualgamma((double)tradebar.Close,(double)specs.Strike,(double)rfr.Value,(double)specs.Maturity,Volatility,(double)specs.DividendYield);
                }
                BlackScholesPrice = Math.Max(Math.Round(BlackScholesPrice,2), (decimal) 0.01);
                Symbol = OptionName;
                Time = tradebar.Time;
                EndTime = tradebar.EndTime;
                // save BlackScholesPrice as the value of the option
                Value = BlackScholesPrice;
                
                // we can now return the option
                return this;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Return the URL string source of the file. This will be converted to a stream 
        /// </summary>
        /// <param name="config">Configuration object</param>
        /// <param name="date">Date of this source file</param>
        /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
        /// <returns>String URL of source file.</returns>
        public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
        {
            // the source of our data is the same as normal trade bar data
            config = HackSubscriptionDataConfig(config);
            return new TradeBar().GetSource(config, date, isLiveMode);
        }

        /// <summary>
        /// Reroutes the configuration to an equity trade bar
        /// </summary>
        private static SubscriptionDataConfig HackSubscriptionDataConfig(SubscriptionDataConfig config)
        {
        	// removing the option to find the symbol of the underlying
        	string symbol = config.Symbol.Value;
        	string ticker = symbol.Substring(0, symbol.IndexOf("."));
        	
            // we need to override the configuration produced by the AddData method so it will get the
            // data from the same place we get equity tradebar equity data
            config = new SubscriptionDataConfig(typeof(TradeBar), SecurityType.Equity,
                ticker,
                // reuse all the other options so we get the right data
                config.Resolution, config.Market, config.TimeZone, config.FillDataForward,
                config.ExtendedMarketHours, config.IsInternalFeed, config.IsCustomData
                );
            return config;
        }
    }
    
	/// <summary>
	/// I'm sorry Jared, I didn't write any documentation/ comments for this code
	/// I hope you can figure it out. If not, you can always contact me.
	/// </summary>
    public static class BlackScholes
    {
    	private static double[] nsia = { 2.50662823884, -18.61500062529, 41.39119773534, -25.44106049637 };
        private static double[] nsib = { -8.4735109309, 23.08336743743, -21.06224101826, 3.13082909833 };
        private static double[] nsic = { 0.3374754822726147, 0.9761690190917186, 0.1607979714918209, 0.0276438810333863, 0.0038405729373609, 0.0003951896511919, 0.0000321767881768, 0.0000002888167364, 0.0000003960315187 };

        //cumulative normal distribution function
        private static double CND(double X)
        {
            double L = 0.0;
            double K = 0.0;
            double dCND = 0.0;
            const double a1 = 0.31938153;
            const double a2 = -0.356563782;
            const double a3 = 1.781477937;
            const double a4 = -1.821255978;
            const double a5 = 1.330274429;
            L = Math.Abs(X);
            K = 1.0 / (1.0 + 0.2316419 * L);
            dCND = 1.0 - 1.0 / Math.Sqrt(2 * Convert.ToDouble(Math.PI.ToString())) *
                Math.Exp(-L * L / 2.0) * (a1 * K + a2 * K * K + a3 * Math.Pow(K, 3.0) +
                a4 * Math.Pow(K, 4.0) + a5 * Math.Pow(K, 5.0));

            if (X < 0)
            {
                return 1.0 - dCND;
            }
            else
            {
                return dCND;
            }
        }

        //function phi
        private static double phi(double x)
        {
            double phi = 0.0;
            phi = Math.Exp(-x * x / 2) / Math.Sqrt(2 * Math.PI);
            return phi;
        }

        public static double NORMSINV(double probability)
        {
            double r = 0;
            double x = 0;
            x = probability - 0.5;
            if (Math.Abs(x) < 0.42)
            {
                r = x * x;
                r = x * (((nsia[3] * r + nsia[2]) * r + nsia[1]) * r + nsia[0]) / ((((nsib[3] * r + nsib[2]) * r + nsib[1]) * r + nsib[0]) * r + 1);
                return r;
            }
            r = probability;
            if (x > 0)
                r = 1 - probability;

            r = Math.Log(-Math.Log(r));
            r = nsic[0] + r * (nsic[1] + r * (nsic[2] + r * (nsic[3] + r * (nsic[4] + r * (nsic[5] + r * (nsic[6] + r * (nsic[7] + r * nsic[7])))))));
            return Math.Abs(r);
        }
        
        public static double NORMINV(double probability, double mean, double standard_deviation)
        {
            return (NORMSINV(probability) * standard_deviation + mean);
        }

        public static double NORMINV(double probability, double[] values)
        {
            return NORMINV(probability, Mean(values), StandardDeviation(values));
        }

        public static double Mean(double[] values)
        {
            double tot = 0;
            foreach (double val in values)
                tot += val;

            return (tot / values.Length);
        }
        
        public static double StandardDeviation(double[] values)
        {
            return Math.Sqrt(Variance(values));
        }
        
        public static double Variance(double[] values)
        {
            double m = Mean(values);
            double result = 0;
            foreach (double d in values)
                result += Math.Pow((d - m), 2);

            return (result / values.Length);
        }

		// calculate centered historical volatility over the last two weeks
		// derived from the annualized log difference of current price and last price
		public static double HistoricalVolatility(double[] historicalPrices) {
			double[] logDifferences = new double[historicalPrices.Length-1];
			for (int i = 1; i < historicalPrices.Length; i++) {
				logDifferences[i-1] = Math.Log(historicalPrices[i]/historicalPrices[i-1]);
			}
			double meanLogDifferences = Mean(logDifferences);
			double sumSquaredVariances = 0;
			for (int i = 0; i < logDifferences.Length; i++) {
				logDifferences[i] = Math.Pow(logDifferences[i] - meanLogDifferences,2);
				sumSquaredVariances += Math.Pow(logDifferences[i] - meanLogDifferences,2);
			}
			double sqrtAverageVariance = Math.Sqrt(sumSquaredVariances/logDifferences.Length);
			double annualizedVariance = sqrtAverageVariance*Math.Sqrt(252);
			
			return annualizedVariance;
		}


        //Call pricer
        public static double blsCall(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            double Call = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            d2 = d1 - Volatility * Math.Sqrt(Time);
            Call = Price * Math.Exp(-Yield * Time) * CND(d1) - Strike * Math.Exp(-Rate * Time) * CND(d2);
            return Call;
        }

        //Put pricer
        public static double blsPut(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            double Put = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            d2 = d1 - Volatility * Math.Sqrt(Time);
            Put = Strike * Math.Exp(-Rate * Time) * CND(-d2) - Price * Math.Exp(-Yield * Time) * CND(-d1);
            return Put;
        }

        //delta for Call
        public static double blsdeltaCall(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            return Math.Exp(-Yield * Time) * CND(d1);
        }

        //delta for Put
        public static double blsdeltaPut(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            return Math.Exp(-Yield * Time) * CND(d1) - 1;
        }

        //gamma is the same for Put and Call
        public static double blsgamma(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            return Math.Exp(-Yield * Time) * phi(d1) / (Price * Volatility * Math.Sqrt(Time));
        }

        //vega is the same for Put and Call
        public static double blsvega(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            return Price * Math.Exp(-Yield * Time) * phi(d1) * Math.Sqrt(Time);
        }

        //theta for Call
        public static double blsthetaCall(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            d2 = d1 - Volatility * Math.Sqrt(Time);
            return -Math.Exp(-Yield * Time) *( Price * phi(d1) * Volatility / (2 * Math.Sqrt(Time))) - Rate * Strike * Math.Exp(-Rate * Time) * CND(d2) + Yield * Price * Math.Exp(-Yield * Time) * CND(d1);
        }

        //theta for Put
        public static double blsthetaPut(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            d2 = d1 - Volatility * Math.Sqrt(Time);
            return -Math.Exp(-Yield * Time) *( Price * phi(d1) * Volatility / (2 * Math.Sqrt(Time))) + Rate * Strike * Math.Exp(-Rate * Time) * CND(-d2) - Yield * Price * Math.Exp(-Yield * Time) * CND(-d1);
        }

        //rho for Call
        public static double blsrhoCall(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            d2 = d1 - Volatility * Math.Sqrt(Time);
            return Strike * Time * Math.Exp(-Rate * Time) * CND(d2);
        }

        //rho for Put
        public static double blsrhoPut(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            d2 = d1 - Volatility * Math.Sqrt(Time);
            return -Strike * Time * Math.Exp(-Rate * Time) * CND(-d2);
        }

        //volga is the same for Call and Put
        public static double blsvolga(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            d2 = d1 - Volatility * Math.Sqrt(Time);
            return Price * Math.Exp(-Yield * Time) * phi(d1) * Math.Sqrt(Time) * d1 * d2 / Volatility;

        }

        //vanna is the same for Call and Put
        public static double blsvanna(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            double vanna = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            d2 = d1 - Volatility * Math.Sqrt(Time);
            vanna = -Math.Exp(-Yield * Time) * phi(d1) * d2 / Volatility;
            return vanna;
        }

        //charm for Call
        public static double blscharmCall(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            double charmC = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            d2 = d1 - Volatility * Math.Sqrt(Time);
            charmC = -Yield * Math.Exp(-Yield * Time) * CND(d1) + Math.Exp(-Yield * Time) * phi(d1) * (2 * (Rate - Yield) * Time - d2 * Volatility * Math.Sqrt(Time)) / (2 * Time * Volatility * Math.Sqrt(Time));
            return charmC;
        }

        //charm for Put
        public static double blscharmPut(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            double charmP = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            d2 = d1 - Volatility * Math.Sqrt(Time);
            charmP = Yield * Math.Exp(-Yield * Time) * CND(-d1) - Math.Exp(-Yield * Time) * phi(d1) * (2 * (Rate - Yield) * Time - d2 * Volatility * Math.Sqrt(Time)) / (2 * Time * Volatility * Math.Sqrt(Time));
            return charmP;
        }

        //color is the same for Call and Put
        public static double blscolor(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            double color = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            d2 = d1 - Volatility * Math.Sqrt(Time);
            color = -Math.Exp(-Yield * Time) * (phi(d1) / (2 * Price * Time * Volatility * Math.Sqrt(Time))) * (2 * Yield * Time + 1 + (2 * (Rate - Yield) * Time - d2 * Volatility * Math.Sqrt(Time)) * d1 / (2 * Time * Volatility * Math.Sqrt(Time)));
            return color;
        }

        //dual delta for Call
        public static double blsdualdeltaCall(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            double ddelta = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            d2 = d1 - Volatility * Math.Sqrt(Time);
            ddelta = -Math.Exp(-Rate * Time) * CND(d2);
            return ddelta;
        }

        //dual delta for Put
        public static double blsdualdeltaPut(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            double ddelta = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            d2 = d1 - Volatility * Math.Sqrt(Time);
            ddelta = Math.Exp(-Rate * Time) * CND(-d2);
            return ddelta;
        }

        //dual gamma is the same for Call and Put
        public static double blsdualgamma(double Price, double Strike, double Rate, double Time, double Volatility, double Yield)
        {
            double d1 = 0.0;
            double d2 = 0.0;
            double dgamma = 0.0;
            d1 = (Math.Log(Price / Strike) + (Rate - Yield + Volatility * Volatility / 2.0) * Time) / (Volatility * Math.Sqrt(Time));
            d2 = d1 - Volatility * Math.Sqrt(Time);
            dgamma = Math.Exp(-Rate * Time) * phi(d2) / (Strike * Volatility * Math.Sqrt(Time));
            return dgamma;
        }
    }
}