Overall Statistics |
Total Trades 1 Average Win 0% Average Loss 0% Compounding Annual Return 19.268% Drawdown 0.700% Expectancy 0 Net Profit 0% Sharpe Ratio 4.099 Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0.176 Beta -0.117 Annual Standard Deviation 0.031 Annual Variance 0.001 Information Ratio -2.063 Tracking Error 0.134 Treynor Ratio -1.098 Total Fees $1.00 |
namespace QuantConnect { /* * Example of how to use Put pricing with Black-Scholes in an algorithm * by: Jean-Paul van Brakel */ public class BasicTemplateAlgorithm : QCAlgorithm { // ticker to be used private readonly string _ticker = "AAPL"; // number of periods to be used in volatility calculation private static int _vol_periods = 14; private readonly RollingWindow<TradeBar> PriceHistory = new RollingWindow<TradeBar>(_vol_periods); // define option maturity date private readonly DateTime _maturityDate = new DateTime(2015, 1, 16); // third friday of the month //Initialize the data and resolution you require for your strategy: public override void Initialize() { //Start and End Date range for the backtest: SetStartDate(2015, 1, 1); SetWarmup(TimeSpan.FromDays(_vol_periods)); SetEndDate(2015, 1, 2); //Cash allocation SetCash(25000); //Add as many securities as you like. All the data will be passed into the event handler: AddSecurity(SecurityType.Equity, _ticker, Resolution.Minute); //Initialise plot Chart plotter = new Chart("DerivativePlot", ChartType.Stacked); plotter.AddSeries(new Series("Price", SeriesType.Line)); plotter.AddSeries(new Series("Put price", SeriesType.Line)); AddChart(plotter); } //Data Event Handler: New data arrives here. public void OnData(TradeBars data) { PriceHistory.Add(data[_ticker]); if (!PriceHistory.IsReady) return; // specify option settings here: double price = (double)PriceHistory[0].Close; double strike = 110; // strike price of option double rate = 0.05; // risk-free rate of return to use in calculation // recalculate annualised time to maturity double maturity = (BlackScholes.CountWeekDays(data.Time,_maturityDate)/250); double[] _p_history = new double[PriceHistory.Count]; for (int i = 0; i < PriceHistory.Count; i++) // copy close (you can change this to your liking) _p_history[i] = (double)PriceHistory[i].Close; // approximate volatility with historical volatility of the underlying double volatility = BlackScholes.HistoricalVolatility(_p_history); double yield = 0.01; // approximation to the annualised dividend yields(%) for AAPL // calculate Black-Scholes option value for a European PUT (also approximation to American PUT) double _optionPrice = BlackScholes.blsPut(price, strike, rate, maturity, volatility, yield); if (data.Time >= StartDate) { Plot("DerivativePlot", "Price", price); Plot("DerivativePlot", "Put price", _optionPrice); } // put your actual trading logic here: if (!Portfolio.HoldStock) { Order(_ticker, -100); } } } }
namespace QuantConnect { /// <summary> /// Black Scholes class to calculate the theoretical prices of European options (and Americans as an approximation) /// </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; } /// <summary> /// Counts the number of week days (business days). Not so robust. Better to use real calendar. /// </summary> public static double CountWeekDays(DateTime d0, DateTime d1) { int ndays = 1 + Convert.ToInt32((d1 - d0).TotalDays); int nsaturdays = (ndays + Convert.ToInt32(d0.DayOfWeek)) / 7; return (double) (ndays - 2 * nsaturdays - (d0.DayOfWeek == DayOfWeek.Sunday ? 1 : 0) + (d1.DayOfWeek == DayOfWeek.Saturday ? 1 : 0)); } } }