| 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);
}
}
}