Overall Statistics |
Total Trades 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio 0 Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio 0 Tracking Error 0 Treynor Ratio 0 Total Fees $0.00 |
using System; using System.Linq; using QuantConnect.Data; using QuantConnect.Data.Market; using QuantConnect.Orders; using QuantConnect.Data.UniverseSelection; using QuantConnect.Securities; using QuantConnect.Securities.Option; namespace QuantConnect.Algorithm.CSharp { /// <summary> /// Selected volatility skew metric for a given (single) underlying. /// </summary> public class OptionsHistoryAlgorithm : QCAlgorithm { private const string UnderlyingTicker = "GLD"; public readonly Symbol Underlying = QuantConnect.Symbol.Create(UnderlyingTicker, SecurityType.Equity, Market.USA); public readonly Symbol OptionSymbol = QuantConnect.Symbol.Create(UnderlyingTicker, SecurityType.Option, Market.USA); Resolution resolution = Resolution.Minute; //default private Slice lastSlice = null; private bool debugLog = true; // more detailed logging // selected expiration // CAUTION: When changing, modify option chain pre-filtering accordingly private int optionExpDays = 30; // simple moneyness and option types for fixed strikes to calculate skew private decimal mny1 = 0.90m; private decimal mny2 = 1.10m; private decimal mnyPrefilt = 0.15m; private OptionRight opttype1 = OptionRight.Put; private OptionRight opttype2 = OptionRight.Call; public override void Initialize() { SetStartDate(2011, 8, 1); SetEndDate(2011, 9, 1); SetCash(1000000); var equity = AddEquity(UnderlyingTicker, resolution); var option = AddOption(UnderlyingTicker, resolution); equity.SetDataNormalizationMode(DataNormalizationMode.Raw); // specity pricing model to get IV calculated option.PriceModel = OptionPriceModels.CrankNicolsonFD(); //option.EnableGreekApproximation = true; // option chain pre-filtering option.SetFilter(universe => from symbol in universe .Expiration(TimeSpan.FromDays(optionExpDays-29), TimeSpan.FromDays(optionExpDays+29)) where Math.Abs(symbol.ID.StrikePrice - universe.Underlying.Price) / universe.Underlying.Price <= mnyPrefilt select symbol); // daily log at a specific time if (debugLog) { Log("Time, Underlying1T1, Underlying2T2, Skew, iv1, iv2, exp1, exp2"); } else { Log("Time, Underlying, Skew"); } Schedule.On(DateRules.EveryDay(UnderlyingTicker), TimeRules.At(10, 0), () => { //Log("EveryDay 30 min after open: " + Time); OptionSkewLog(lastSlice); }); } /// <summary> /// Event - v3.0 DATA EVENT HANDLER: (Pattern) Basic template for user to override for receiving all subscription data in a single event /// </summary> /// <param name="slice">The current slice of data keyed by symbol string</param> public override void OnData(Slice slice) { lastSlice = slice; } // fixed-strike skew private void OptionSkewLog(Slice slice) { if (slice == null) { return; } OptionChain chain; if (slice.OptionChains.TryGetValue(OptionSymbol, out chain)) { // identify nearest expirations var ofront = chain .Where(x => (x.Expiry - Time).Days < optionExpDays) // front .OrderBy(x => Math.Abs((x.Expiry - Time).Days - optionExpDays)) .FirstOrDefault(); if (ofront == null) { Log("No matching front expiration"); return; } var expFront = ofront.Expiry; //Log(expFront.ToString()); var oback = chain .Where(x => (x.Expiry - Time).Days > optionExpDays) // back .OrderBy(x => Math.Abs((x.Expiry - Time).Days - optionExpDays)) .FirstOrDefault(); if (oback == null) { Log("No matching back expiration"); return; } var expBack = oback.Expiry; //Log(expBack.ToString()); // identify closest front contracts to fixed strikes var o1front = chain .Where(x => x.Expiry == expFront) .Where(x => x.Right == opttype1) .Where(x => x.BidPrice > 0.01m && x.AskPrice > 0.01m) // non-zero IV .OrderBy(x => Math.Abs(mny1*chain.Underlying.Price - x.Strike)) .FirstOrDefault(); if (o1front == null) { Log("No matching front contract 1"); return; } //Log(o1front.Symbol.Value); var o2front = chain .Where(x => x.Expiry == expFront) .Where(x => x.Right == opttype2) .Where(x => x.BidPrice > 0.01m && x.AskPrice > 0.01m) // non-zero IV .OrderBy(x => Math.Abs(mny2*chain.Underlying.Price - x.Strike)) .FirstOrDefault(); if (o2front == null) { Log("No matching front contract 2"); return; } //Log(o2front.Symbol.Value); // enforce identical strikes (to front ones) on closest back contracts var o1back = chain .Where(x => x.Expiry == expBack) .Where(x => x.Right == opttype1) .Where(x => x.BidPrice > 0.01m && x.AskPrice > 0.01m) // non-zero IV .Where(x => Math.Abs(x.Strike - o1front.Strike) < 0.01m) // match the strikes .FirstOrDefault(); if (o1back == null) { Log("No matching back contract 1"); return; } //Log(o1back.Symbol.Value); var o2back = chain .Where(x => x.Expiry == expBack) .Where(x => x.Right == opttype2) .Where(x => x.BidPrice > 0.01m && x.AskPrice > 0.01m) // non-zero IV .Where(x => Math.Abs(x.Strike - o2front.Strike) < 0.01m) // match the strikes .FirstOrDefault(); if (o2back == null) { Log("No matching back contract 2"); return; } //Log(o2back.Symbol.Value); // interpolate IV between nearest front and back contracts for each strike var iv1 = InterpTwoExpLinear( (expBack - Time).Days - optionExpDays, optionExpDays - (expFront - Time).Days, o1front.ImpliedVolatility, o1back.ImpliedVolatility); //Log(iv1.ToString("##.####")); //Log(o1front.ImpliedVolatility.ToString("##.####")); //Log(o1back.ImpliedVolatility.ToString("##.####")); var iv2 = InterpTwoExpLinear( (expBack - Time).Days - optionExpDays, optionExpDays - (expFront - Time).Days, o2front.ImpliedVolatility, o2back.ImpliedVolatility); // fixed strike skew as abs. difference of implied volatilities var skew = iv1 - iv2; if (debugLog) { // csv: Underlying1T1, Underlying2T2, Skew, iv1, iv2, exp1, exp2 Log(string.Format(", {0}, {1}, {2}, {3}, {4}, {5}, {6}", o1front.UnderlyingLastPrice, o2back.UnderlyingLastPrice, skew.ToString("##.####"), iv1, iv2, expFront, expBack )); } else { //csv: Underlying, Skew Log(string.Format(", {0}, {1}", o1front.UnderlyingLastPrice, skew.ToString("##.####"))); } } } // linear time-interpolation (days-differences used) between two values private decimal InterpTwoExpLinear(int t2mtx, int txmt1, decimal v1, decimal v2) { if (t2mtx + txmt1 < 1) { // t2 - t1 < 1 Day: should not normally be the case return 0.50m * (v1 + v2); } return (v1*t2mtx + v2*txmt1) / (t2mtx + txmt1); } } }