Skip to content

Commit 3a10afe

Browse files
authored
Option Delta Indicator (#7704)
* Add base indicator for greeks * Add option delta indicator * Add helper method * Add unit tests * Address peer review * Fix minor bugs * Change OptionDelta into Delta * rename helper method
1 parent 00e1cae commit 3a10afe

11 files changed

+971
-278
lines changed

Algorithm/QCAlgorithm.Indicators.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,48 @@ public DonchianChannel DCH(Symbol symbol, int period, Resolution? resolution = n
528528
return DCH(symbol, period, period, resolution, selector);
529529
}
530530

531+
/// <summary>
532+
/// Creates a new Delta indicator for the symbol The indicator will be automatically
533+
/// updated on the symbol's subscription resolution
534+
/// </summary>
535+
/// <param name="symbol">The option symbol whose values we want as an indicator</param>
536+
/// <param name="riskFreeRate">The risk free rate</param>
537+
/// <param name="optionModel">The option pricing model used to estimate Delta</param>
538+
/// <param name="ivModel">The option pricing model used to estimate IV</param>
539+
/// <param name="resolution">The desired resolution of the data</param>
540+
/// <returns>A new Delta indicator for the specified symbol</returns>
541+
[DocumentationAttribute(Indicators)]
542+
public Delta D(Symbol symbol, decimal? riskFreeRate = null, OptionPricingModelType optionModel = OptionPricingModelType.BlackScholes,
543+
OptionPricingModelType? ivModel = null, Resolution? resolution = null)
544+
{
545+
var name = CreateIndicatorName(symbol, $"Delta({riskFreeRate},{optionModel},{ivModel})", resolution);
546+
IRiskFreeInterestRateModel riskFreeRateModel = riskFreeRate.HasValue
547+
? new ConstantRiskFreeRateInterestRateModel(riskFreeRate.Value)
548+
// Make it a function so it's lazily evaluated: SetRiskFreeInterestRateModel can be called after this method
549+
: new FuncRiskFreeRateInterestRateModel((datetime) => RiskFreeInterestRateModel.GetInterestRate(datetime));
550+
var delta = new Delta(name, symbol, riskFreeRateModel, optionModel, ivModel);
551+
RegisterIndicator(symbol, delta, ResolveConsolidator(symbol, resolution));
552+
RegisterIndicator(symbol.Underlying, delta, ResolveConsolidator(symbol, resolution));
553+
return delta;
554+
}
555+
556+
/// <summary>
557+
/// Creates a new Delta indicator for the symbol The indicator will be automatically
558+
/// updated on the symbol's subscription resolution
559+
/// </summary>
560+
/// <param name="symbol">The option symbol whose values we want as an indicator</param>
561+
/// <param name="riskFreeRate">The risk free rate</param>
562+
/// <param name="optionModel">The option pricing model used to estimate Delta</param>
563+
/// <param name="ivModel">The option pricing model used to estimate IV</param>
564+
/// <param name="resolution">The desired resolution of the data</param>
565+
/// <returns>A new Delta indicator for the specified symbol</returns>
566+
[DocumentationAttribute(Indicators)]
567+
public Delta Δ(Symbol symbol, decimal? riskFreeRate = null, OptionPricingModelType optionModel = OptionPricingModelType.BlackScholes,
568+
OptionPricingModelType? ivModel = null, Resolution? resolution = null)
569+
{
570+
return D(symbol, riskFreeRate, optionModel, ivModel, resolution);
571+
}
572+
531573
/// <summary>
532574
/// Creates a new DoubleExponentialMovingAverage indicator.
533575
/// </summary>

Indicators/Delta.cs

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3+
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using MathNet.Numerics.Distributions;
18+
using Python.Runtime;
19+
using QuantConnect.Data;
20+
21+
namespace QuantConnect.Indicators
22+
{
23+
/// <summary>
24+
/// Option Delta indicator that calculate the delta of an option
25+
/// </summary>
26+
/// <remarks>sensitivity of option price relative to $1 of underlying change</remarks>
27+
public class Delta : OptionGreeksIndicatorBase
28+
{
29+
/// <summary>
30+
/// Initializes a new instance of the Delta class
31+
/// </summary>
32+
/// <param name="name">The name of this indicator</param>
33+
/// <param name="option">The option to be tracked</param>
34+
/// <param name="riskFreeRateModel">Risk-free rate model</param>
35+
/// <param name="optionModel">The option pricing model used to estimate Delta</param>
36+
/// <param name="ivModel">The option pricing model used to estimate IV</param>
37+
public Delta(string name, Symbol option, IRiskFreeInterestRateModel riskFreeRateModel,
38+
OptionPricingModelType optionModel = OptionPricingModelType.BlackScholes, OptionPricingModelType? ivModel = null)
39+
: base(name, option, riskFreeRateModel, optionModel: optionModel, ivModel: ivModel)
40+
{
41+
}
42+
43+
/// <summary>
44+
/// Initializes a new instance of the Delta class
45+
/// </summary>
46+
/// <param name="option">The option to be tracked</param>
47+
/// <param name="riskFreeRateModel">Risk-free rate model</param>
48+
/// <param name="optionModel">The option pricing model used to estimate Delta</param>
49+
/// <param name="ivModel">The option pricing model used to estimate IV</param>
50+
public Delta(Symbol option, IRiskFreeInterestRateModel riskFreeRateModel,
51+
OptionPricingModelType optionModel = OptionPricingModelType.BlackScholes, OptionPricingModelType? ivModel = null)
52+
: this($"Delta({optionModel})", option, riskFreeRateModel, optionModel, ivModel)
53+
{
54+
}
55+
56+
/// <summary>
57+
/// Initializes a new instance of the Delta class
58+
/// </summary>
59+
/// <param name="name">The name of this indicator</param>
60+
/// <param name="option">The option to be tracked</param>
61+
/// <param name="riskFreeRateModel">Risk-free rate model</param>
62+
/// <param name="optionModel">The option pricing model used to estimate Delta</param>
63+
/// <param name="ivModel">The option pricing model used to estimate IV</param>
64+
public Delta(string name, Symbol option, PyObject riskFreeRateModel,
65+
OptionPricingModelType optionModel = OptionPricingModelType.BlackScholes, OptionPricingModelType? ivModel = null)
66+
: base(name, option, riskFreeRateModel, optionModel: optionModel, ivModel: ivModel)
67+
{
68+
}
69+
70+
/// <summary>
71+
/// Initializes a new instance of the Delta class
72+
/// </summary>
73+
/// <param name="option">The option to be tracked</param>
74+
/// <param name="riskFreeRateModel">Risk-free rate model</param>
75+
/// <param name="optionModel">The option pricing model used to estimate Delta</param>
76+
/// <param name="ivModel">The option pricing model used to estimate IV</param>
77+
public Delta(Symbol option, PyObject riskFreeRateModel, OptionPricingModelType optionModel = OptionPricingModelType.BlackScholes,
78+
OptionPricingModelType? ivModel = null)
79+
: this($"Delta({optionModel})", option, riskFreeRateModel, optionModel, ivModel)
80+
{
81+
}
82+
83+
/// <summary>
84+
/// Initializes a new instance of the Delta class
85+
/// </summary>
86+
/// <param name="option">The option to be tracked</param>am>
87+
/// <param name="riskFreeRate">Risk-free rate, as a constant</param>
88+
/// <param name="optionModel">The option pricing model used to estimate Delta</param>
89+
/// <param name="ivModel">The option pricing model used to estimate IV</param>
90+
public Delta(string name, Symbol option, decimal riskFreeRate = 0.05m,
91+
OptionPricingModelType optionModel = OptionPricingModelType.BlackScholes, OptionPricingModelType? ivModel = null)
92+
: base(name, option, riskFreeRate, optionModel: optionModel, ivModel: ivModel)
93+
{
94+
}
95+
96+
/// <summary>
97+
/// Initializes a new instance of the Delta class
98+
/// </summary>
99+
/// <param name="option">The option to be tracked</param>
100+
/// <param name="riskFreeRate">Risk-free rate, as a constant</param>
101+
/// <param name="optionModel">The option pricing model used to estimate Delta</param>
102+
/// <param name="ivModel">The option pricing model used to estimate IV</param>
103+
public Delta(Symbol option, decimal riskFreeRate = 0.05m, OptionPricingModelType optionModel = OptionPricingModelType.BlackScholes,
104+
OptionPricingModelType? ivModel = null)
105+
: this($"Delta({optionModel})", option, riskFreeRate, optionModel, ivModel)
106+
{
107+
}
108+
109+
// Calculate the theoretical option price
110+
private decimal TheoreticalDelta(decimal spotPrice, decimal timeToExpiration, decimal volatility,
111+
OptionPricingModelType optionModel = OptionPricingModelType.BlackScholes)
112+
{
113+
var math = OptionGreekIndicatorsHelper.DecimalMath;
114+
115+
switch (optionModel)
116+
{
117+
case OptionPricingModelType.BinomialCoxRossRubinstein:
118+
var upFactor = math(Math.Exp, volatility * math(Math.Sqrt, timeToExpiration / OptionGreekIndicatorsHelper.Steps));
119+
if (upFactor == 1)
120+
{
121+
// provide a small step to estimate delta
122+
upFactor = 1.00001m;
123+
}
124+
125+
var sU = spotPrice * upFactor;
126+
var sD = spotPrice * 1m / upFactor;
127+
128+
var fU = OptionGreekIndicatorsHelper.CRRTheoreticalPrice(volatility, sU, Strike, timeToExpiration, RiskFreeRate, Right);
129+
var fD = OptionGreekIndicatorsHelper.CRRTheoreticalPrice(volatility, sD, Strike, timeToExpiration, RiskFreeRate, Right);
130+
131+
return (fU - fD) / (sU - sD);
132+
133+
case OptionPricingModelType.BlackScholes:
134+
default:
135+
var norm = new Normal();
136+
var d1 = OptionGreekIndicatorsHelper.CalculateD1(spotPrice, Strike, timeToExpiration, RiskFreeRate, volatility);
137+
138+
if (Right == OptionRight.Call)
139+
{
140+
return math(norm.CumulativeDistribution, d1);
141+
}
142+
143+
return -math(norm.CumulativeDistribution, -d1);
144+
}
145+
}
146+
147+
// Calculate the Delta of the option
148+
protected override decimal CalculateGreek(DateTime time)
149+
{
150+
var spotPrice = UnderlyingPrice.Current.Value;
151+
var timeToExpiration = Convert.ToDecimal((Expiry - time).TotalDays) / 365m;
152+
var volatility = ImpliedVolatility.Current.Value;
153+
154+
return TheoreticalDelta(spotPrice, timeToExpiration, volatility, _optionModel);
155+
}
156+
}
157+
}

Indicators/ImpliedVolatility.cs

Lines changed: 5 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -26,60 +26,17 @@ namespace QuantConnect.Indicators
2626
/// <summary>
2727
/// Implied Volatility indicator that calculate the IV of an option using Black-Scholes Model
2828
/// </summary>
29-
public class ImpliedVolatility : IndicatorBase<IndicatorDataPoint>, IIndicatorWarmUpPeriodProvider
29+
public class ImpliedVolatility : OptionIndicatorBase
3030
{
31-
private readonly Symbol _optionSymbol;
32-
private readonly Symbol _underlyingSymbol;
3331
private BaseDataConsolidator _consolidator;
3432
private RateOfChange _roc;
3533
private decimal _impliedVolatility;
36-
private OptionPricingModelType _optionModel;
37-
38-
/// <summary>
39-
/// Risk-free rate model
40-
/// </summary>
41-
private readonly IRiskFreeInterestRateModel _riskFreeInterestRateModel;
42-
43-
/// <summary>
44-
/// Gets the expiration time of the option
45-
/// </summary>
46-
public DateTime Expiry => _optionSymbol.ID.Date;
47-
48-
/// <summary>
49-
/// Gets the option right (call/put) of the option
50-
/// </summary>
51-
public OptionRight Right => _optionSymbol.ID.OptionRight;
52-
53-
/// <summary>
54-
/// Gets the strike price of the option
55-
/// </summary>
56-
public decimal Strike => _optionSymbol.ID.StrikePrice;
57-
58-
/// <summary>
59-
/// Gets the option style (European/American) of the option
60-
/// </summary>
61-
public OptionStyle Style => _optionSymbol.ID.OptionStyle;
62-
63-
/// <summary>
64-
/// Risk Free Rate
65-
/// </summary>
66-
public Identity RiskFreeRate { get; set; }
6734

6835
/// <summary>
6936
/// Gets the historical volatility of the underlying
7037
/// </summary>
7138
public IndicatorBase<IndicatorDataPoint> HistoricalVolatility { get; }
7239

73-
/// <summary>
74-
/// Gets the option price level
75-
/// </summary>
76-
public IndicatorBase<IndicatorDataPoint> Price { get; }
77-
78-
/// <summary>
79-
/// Gets the underlying's price level
80-
/// </summary>
81-
public IndicatorBase<IndicatorDataPoint> UnderlyingPrice { get; }
82-
8340
/// <summary>
8441
/// Initializes a new instance of the ImpliedVolatility class
8542
/// </summary>
@@ -90,37 +47,21 @@ public class ImpliedVolatility : IndicatorBase<IndicatorDataPoint>, IIndicatorWa
9047
/// <param name="optionModel">The option pricing model used to estimate IV</param>
9148
public ImpliedVolatility(string name, Symbol option, IRiskFreeInterestRateModel riskFreeRateModel, int period = 252,
9249
OptionPricingModelType optionModel = OptionPricingModelType.BlackScholes)
93-
: base(name)
50+
: base(name, option, riskFreeRateModel, period, optionModel)
9451
{
95-
var sid = option.ID;
96-
if (!sid.SecurityType.IsOption())
97-
{
98-
throw new ArgumentException("ImpliedVolatility only support SecurityType.Option.");
99-
}
100-
101-
_optionSymbol = option;
102-
_underlyingSymbol = option.Underlying;
10352
_roc = new(1);
104-
_riskFreeInterestRateModel = riskFreeRateModel;
105-
_optionModel = optionModel;
106-
107-
RiskFreeRate = new Identity(name + "_RiskFreeRate");
10853
HistoricalVolatility = IndicatorExtensions.Times(
10954
IndicatorExtensions.Of(
11055
new StandardDeviation(period),
11156
_roc
11257
),
11358
Convert.ToDecimal(Math.Sqrt(252))
11459
);
115-
Price = new Identity(name + "_Close");
116-
UnderlyingPrice = new Identity(name + "_UnderlyingClose");
117-
60+
11861
_consolidator = new(TimeSpan.FromDays(1));
11962
_consolidator.DataConsolidated += (_, bar) => {
12063
_roc.Update(bar.EndTime, bar.Price);
12164
};
122-
123-
WarmUpPeriod = period;
12465
}
12566

12667
/// <summary>
@@ -159,8 +100,7 @@ public ImpliedVolatility(string name, Symbol option, PyObject riskFreeRateModel,
159100
/// <param name="optionModel">The option pricing model used to estimate IV</param>
160101
public ImpliedVolatility(Symbol option, PyObject riskFreeRateModel, int period = 252,
161102
OptionPricingModelType optionModel = OptionPricingModelType.BlackScholes)
162-
: this($"IV({option.Value},{period},{optionModel})", option,
163-
RiskFreeInterestRateModelPythonWrapper.FromPyObject(riskFreeRateModel), period, optionModel)
103+
: this($"IV({option.Value},{period},{optionModel})", option, riskFreeRateModel, period, optionModel)
164104
{
165105
}
166106

@@ -187,8 +127,7 @@ public ImpliedVolatility(string name, Symbol option, decimal riskFreeRate = 0.05
187127
/// <param name="optionModel">The option pricing model used to estimate IV</param>
188128
public ImpliedVolatility(Symbol option, decimal riskFreeRate = 0.05m, int period = 252,
189129
OptionPricingModelType optionModel = OptionPricingModelType.BlackScholes)
190-
: this($"IV({option.Value},{period},{riskFreeRate},{optionModel})", option,
191-
new ConstantRiskFreeRateInterestRateModel(riskFreeRate), period, optionModel)
130+
: this($"IV({option.Value},{period},{riskFreeRate},{optionModel})", option, riskFreeRate, period, optionModel)
192131
{
193132
}
194133

@@ -197,11 +136,6 @@ public ImpliedVolatility(Symbol option, decimal riskFreeRate = 0.05m, int period
197136
/// </summary>
198137
public override bool IsReady => HistoricalVolatility.Samples >= 2 && Price.Current.Time == UnderlyingPrice.Current.Time;
199138

200-
/// <summary>
201-
/// Required period, in data points, for the indicator to be ready and fully initialized.
202-
/// </summary>
203-
public int WarmUpPeriod { get; }
204-
205139
/// <summary>
206140
/// Computes the next value
207141
/// </summary>
@@ -281,10 +215,7 @@ public override void Reset()
281215
};
282216

283217
_roc.Reset();
284-
RiskFreeRate.Reset();
285218
HistoricalVolatility.Reset();
286-
Price.Reset();
287-
UnderlyingPrice.Reset();
288219
base.Reset();
289220
}
290221
}

0 commit comments

Comments
 (0)