17 using MathNet.Numerics.RootFinding;
31 private decimal _impliedVolatility;
32 private Func<decimal, decimal, decimal> SmoothingFunction;
45 : base(name, option, riskFreeRateModel, dividendYieldModel, mirrorOption, optionModel)
47 if (mirrorOption !=
null)
53 SmoothingFunction = (impliedVol, mirrorImpliedVol) =>
57 return mirrorImpliedVol;
61 return mirrorImpliedVol;
78 : this($
"IV({option},{mirrorOption},{riskFreeRateModel},{dividendYieldModel},{optionModel})", option, riskFreeRateModel,
79 dividendYieldModel, mirrorOption, optionModel)
109 : this($
"IV({option},{mirrorOption},{riskFreeRateModel},{dividendYieldModel},{optionModel})", option,
110 riskFreeRateModel, dividendYieldModel, mirrorOption, optionModel)
139 : this($
"IV({option},{mirrorOption},{riskFreeRateModel},{dividendYield},{optionModel})", option, riskFreeRateModel, dividendYield,
140 mirrorOption, optionModel)
170 : this($
"IV({option},{mirrorOption},{riskFreeRateModel},{dividendYield},{optionModel})", option, riskFreeRateModel,
171 dividendYield, mirrorOption, optionModel)
200 : this($
"IV({option},{mirrorOption},{riskFreeRate},{dividendYield},{optionModel})", option, riskFreeRate,
201 dividendYield, mirrorOption, optionModel)
211 SmoothingFunction =
function;
220 SmoothingFunction =
PythonUtil.ToFunc<decimal, decimal, decimal>(
function);
229 var time =
Price.Current.Time;
237 return _impliedVolatility;
241 private static double TheoreticalPrice(
double volatility,
double spotPrice,
double strikePrice,
double timeTillExpiry,
double riskFreeRate,
244 if (timeTillExpiry <= 0)
249 return optionModel
switch
252 OptionPricingModelType.BinomialCoxRossRubinstein => OptionGreekIndicatorsHelper.CRRTheoreticalPrice(volatility, spotPrice, strikePrice, timeTillExpiry, riskFreeRate, dividendYield, optionType),
253 OptionPricingModelType.ForwardTree => OptionGreekIndicatorsHelper.ForwardTreeTheoreticalPrice(volatility, spotPrice, strikePrice, timeTillExpiry, riskFreeRate, dividendYield, optionType),
254 _ => OptionGreekIndicatorsHelper.BlackTheoreticalPrice(volatility, spotPrice, strikePrice, timeTillExpiry, riskFreeRate, dividendYield, optionType),
266 var strike = (
double)
Strike;
267 var timeTillExpiryDouble = (double)timeTillExpiry;
270 var optionPrice = (double)
Price.Current.Value;
280 mirrorOptionPrice, underlyingPrice, riskFreeRate, dividendYield,
_optionModel);
282 if (mirrorImpliedVol.HasValue)
284 if (impliedVol.HasValue)
287 return SmoothingFunction(impliedVol.Value, mirrorImpliedVol.Value);
289 return mirrorImpliedVol.Value;
293 return impliedVol ?? 0;
296 private decimal?
CalculateIV(
Symbol optionSymbol,
double strike,
double timeTillExpiry,
OptionRight right,
double optionPrice,
double underlyingPrice,
299 GetRootFindingMethodParameters(optionSymbol, strike, timeTillExpiry, optionPrice, underlyingPrice, riskFreeRate, dividendYield,
300 optionModel, out var accuracy, out var lowerBound, out var upperBound);
302 decimal? impliedVol =
null;
305 Func<double, double> f = (vol) => TheoreticalPrice(vol, underlyingPrice, strike, timeTillExpiry, riskFreeRate, dividendYield, right, optionModel) - optionPrice;
306 impliedVol = Convert.ToDecimal(Brent.FindRoot(f, lowerBound, upperBound, accuracy, 100));
310 Log.
Error(
"ImpliedVolatility.CalculateIV(): Fail to converge, returning 0.");
316 private void GetRootFindingMethodParameters(Symbol optionSymbol,
double strike,
double timeTillExpiry,
double optionPrice,
318 out
double accuracy, out
double lowerBound, out
double upperBound)
321 accuracy = Math.Max(1e-4, 1e-4 * optionPrice);
328 var initialGuess = (double)(
CalculateIV(optionSymbol, strike, timeTillExpiry, optionSymbol.ID.OptionRight, optionPrice,
330 if (initialGuess != 0)
332 lowerBound = Math.Max(lowerBound, initialGuess * 0.5);
333 upperBound = Math.Min(upperBound, initialGuess * 1.5);