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);
251 throw new ArgumentException(
"The given symbol was not target or reference symbol");
254 var time =
Price.Current.Time;
261 return _impliedVolatility;
272 return _impliedVolatility;
276 private double TheoreticalPrice(
double volatility,
double spotPrice,
double strikePrice,
double timeTillExpiry,
double riskFreeRate,
279 if (timeTillExpiry <= 0)
284 return optionModel
switch
287 OptionPricingModelType.BinomialCoxRossRubinstein => OptionGreekIndicatorsHelper.CRRTheoreticalPrice(volatility, spotPrice, strikePrice, timeTillExpiry, riskFreeRate, dividendYield, optionType),
288 OptionPricingModelType.ForwardTree => OptionGreekIndicatorsHelper.ForwardTreeTheoreticalPrice(volatility, spotPrice, strikePrice, timeTillExpiry, riskFreeRate, dividendYield, optionType),
289 _ => OptionGreekIndicatorsHelper.BlackTheoreticalPrice(volatility, spotPrice, strikePrice, timeTillExpiry, riskFreeRate, dividendYield, optionType),
301 var strike = (
double)
Strike;
302 var timeTillExpiryDouble = (double)timeTillExpiry;
305 var optionPrice = (double)
Price.Current.Value;
315 mirrorOptionPrice, underlyingPrice, riskFreeRate, dividendYield,
_optionModel);
317 if (mirrorImpliedVol.HasValue)
319 if (impliedVol.HasValue)
322 return SmoothingFunction(impliedVol.Value, mirrorImpliedVol.Value);
324 return mirrorImpliedVol.Value;
328 return impliedVol ?? 0;
331 private decimal?
CalculateIV(
Symbol optionSymbol,
double strike,
double timeTillExpiry,
OptionRight right,
double optionPrice,
double underlyingPrice,
334 GetRootFindingMethodParameters(optionSymbol, strike, timeTillExpiry, optionPrice, underlyingPrice, riskFreeRate, dividendYield,
335 optionModel, out var accuracy, out var lowerBound, out var upperBound);
337 decimal? impliedVol =
null;
340 Func<double, double> f = (vol) => TheoreticalPrice(vol, underlyingPrice, strike, timeTillExpiry, riskFreeRate, dividendYield, right, optionModel) - optionPrice;
341 impliedVol = Convert.ToDecimal(Brent.FindRoot(f, lowerBound, upperBound, accuracy, 100));
345 Log.
Error(
"ImpliedVolatility.CalculateIV(): Fail to converge, returning 0.");
351 private void GetRootFindingMethodParameters(Symbol optionSymbol,
double strike,
double timeTillExpiry,
double optionPrice,
353 out
double accuracy, out
double lowerBound, out
double upperBound)
356 accuracy = Math.Max(1e-4, 1e-4 * optionPrice);
363 var initialGuess = (double)(
CalculateIV(optionSymbol, strike, timeTillExpiry, optionSymbol.ID.OptionRight, optionPrice,
365 if (initialGuess != 0)
367 lowerBound = Math.Max(lowerBound, initialGuess * 0.5);
368 upperBound = Math.Min(upperBound, initialGuess * 1.5);