/*! \warning currently, this method returns the Black-Scholes implied volatility using analytic formulas for European options and a finite-difference method for American and Bermudan options. It will give unconsistent results if the pricing was performed with any other methods (such as jump-diffusion models.) \warning options with a gamma that changes sign (e.g., binary options) have values that are <b>not</b> monotonic in the volatility. In these cases, the calculation can fail and the result (if any) is almost meaningless. Another possible source of failure is to have a target value that is not attainable with any volatility, e.g., a target value lower than the intrinsic value in the case of American options. */ //public double impliedVolatility(double price, GeneralizedBlackScholesProcess process, // double accuracy = 1.0e-4, int maxEvaluations = 100, double minVol = 1.0e-7, double maxVol = 4.0) { public double impliedVolatility(double targetValue, GeneralizedBlackScholesProcess process, double accuracy, int maxEvaluations, double minVol, double maxVol) { if(isExpired()) throw new ApplicationException("option expired"); SimpleQuote volQuote = new SimpleQuote(); GeneralizedBlackScholesProcess newProcess = ImpliedVolatilityHelper.clone(process, volQuote); // engines are built-in for the time being IPricingEngine engine; switch (exercise_.type()) { case Exercise.Type.European: engine = new AnalyticEuropeanEngine(newProcess); break; case Exercise.Type.American: engine = new FDAmericanEngine(newProcess); break; case Exercise.Type.Bermudan: engine = new FDBermudanEngine(newProcess); break; default: throw new ArgumentException("unknown exercise type"); } return ImpliedVolatilityHelper.calculate(this, engine, volQuote, targetValue, accuracy, maxEvaluations, minVol, maxVol); }
public override void calculate() { Utils.QL_REQUIRE(process_.x0() > 0.0, () => "negative or null underlying given"); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; Utils.QL_REQUIRE(payoff != null, () => "non-striked payoff given"); Exercise exercise = arguments_.exercise; double t = process_.riskFreeRate().link.dayCounter().yearFraction(process_.riskFreeRate().link.referenceDate(), exercise.lastDate()); double a = model_.link.parameters()[0]; double sigma = model_.link.parameters()[1]; double eta = process_.blackVolatility().link.blackVol(exercise.lastDate(), payoff.strike()); double varianceOffset; if (a * t > Math.Pow(Const.QL_EPSILON, 0.25)) { double v = sigma * sigma / (a * a) * (t + 2 / a * Math.Exp(-a * t) - 1 / (2 * a) * Math.Exp(-2 * a * t) - 3 / (2 * a)); double mu = 2 * rho_ * sigma * eta / a * (t - 1 / a * (1 - Math.Exp(-a * t))); varianceOffset = v + mu; } else { // low-a algebraic limit double v = sigma * sigma * t * t * t * (1 / 3.0 - 0.25 * a * t + 7 / 60.0 * a * a * t * t); double mu = rho_ * sigma * eta * t * t * (1 - a * t / 3.0 + a * a * t * t / 12.0); varianceOffset = v + mu; } Handle <BlackVolTermStructure> volTS = new Handle <BlackVolTermStructure>( new ShiftedBlackVolTermStructure(varianceOffset, process_.blackVolatility())); GeneralizedBlackScholesProcess adjProcess = new GeneralizedBlackScholesProcess(process_.stateVariable(), process_.dividendYield(), process_.riskFreeRate(), volTS); AnalyticEuropeanEngine bsmEngine = new AnalyticEuropeanEngine(adjProcess); VanillaOption option = new VanillaOption(payoff, exercise); option.setupArguments(bsmEngine.getArguments()); bsmEngine.calculate(); results_ = bsmEngine.getResults() as OneAssetOption.Results; }
/*! \warning currently, this method returns the Black-Scholes * implied volatility using analytic formulas for * European options and a finite-difference method * for American and Bermudan options. It will give * unconsistent results if the pricing was performed * with any other methods (such as jump-diffusion * models.) * * \warning options with a gamma that changes sign (e.g., * binary options) have values that are <b>not</b> * monotonic in the volatility. In these cases, the * calculation can fail and the result (if any) is * almost meaningless. Another possible source of * failure is to have a target value that is not * attainable with any volatility, e.g., a target * value lower than the intrinsic value in the case * of American options. */ //public double impliedVolatility(double price, GeneralizedBlackScholesProcess process, // double accuracy = 1.0e-4, int maxEvaluations = 100, double minVol = 1.0e-7, double maxVol = 4.0) { public double impliedVolatility(double targetValue, GeneralizedBlackScholesProcess process, double accuracy, int maxEvaluations, double minVol, double maxVol) { if (isExpired()) { throw new ApplicationException("option expired"); } SimpleQuote volQuote = new SimpleQuote(); GeneralizedBlackScholesProcess newProcess = ImpliedVolatilityHelper.clone(process, volQuote); // engines are built-in for the time being IPricingEngine engine; switch (exercise_.type()) { case Exercise.Type.European: engine = new AnalyticEuropeanEngine(newProcess); break; case Exercise.Type.American: throw new NotImplementedException(); // engine = new FDAmericanEngine(newProcess); break; case Exercise.Type.Bermudan: throw new NotImplementedException(); // engine = new FDBermudanEngine(newProcess); break; default: throw new ArgumentException("unknown exercise type"); } return(ImpliedVolatilityHelper.calculate(this, engine, volQuote, targetValue, accuracy, maxEvaluations, minVol, maxVol)); }
public override void calculate() { AmericanExercise ex = arguments_.exercise as AmericanExercise; Utils.QL_REQUIRE(ex != null, () => "non-American exercise given"); Utils.QL_REQUIRE(ex.payoffAtExpiry(), () => "payoff must be at expiry"); Utils.QL_REQUIRE(ex.dates()[0] <= process_.blackVolatility().link.referenceDate(), () => "American option with window exercise not handled yet"); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; Utils.QL_REQUIRE(payoff != null, () => "non-striked payoff given"); double spot = process_.stateVariable().link.value(); Utils.QL_REQUIRE(spot > 0.0, () => "negative or null underlying given"); double variance = process_.blackVolatility().link.blackVariance(ex.lastDate(), payoff.strike()); double?barrier = arguments_.barrier; Utils.QL_REQUIRE(barrier > 0.0, () => "positive barrier value required"); Barrier.Type barrierType = arguments_.barrierType; // KO degenerate cases if ((barrierType == Barrier.Type.DownOut && spot <= barrier) || (barrierType == Barrier.Type.UpOut && spot >= barrier)) { // knocked out, no value results_.value = 0; results_.delta = 0; results_.gamma = 0; results_.vega = 0; results_.theta = 0; results_.rho = 0; results_.dividendRho = 0; return; } // KI degenerate cases if ((barrierType == Barrier.Type.DownIn && spot <= barrier) || (barrierType == Barrier.Type.UpIn && spot >= barrier)) { // knocked in - is a digital european Exercise exercise = new EuropeanExercise(arguments_.exercise.lastDate()); IPricingEngine engine = new AnalyticEuropeanEngine(process_); VanillaOption opt = new VanillaOption(payoff, exercise); opt.setPricingEngine(engine); results_.value = opt.NPV(); results_.delta = opt.delta(); results_.gamma = opt.gamma(); results_.vega = opt.vega(); results_.theta = opt.theta(); results_.rho = opt.rho(); results_.dividendRho = opt.dividendRho(); return; } double riskFreeDiscount = process_.riskFreeRate().link.discount(ex.lastDate()); AnalyticBinaryBarrierEngine_helper helper = new AnalyticBinaryBarrierEngine_helper( process_, payoff, ex, arguments_); results_.value = helper.payoffAtExpiry(spot, variance, riskFreeDiscount); }
VanillaOption makeOption(StrikedTypePayoff payoff, Exercise exercise, Quote u, YieldTermStructure q, YieldTermStructure r, BlackVolTermStructure vol, EngineType engineType, int binomialSteps, int samples) { GeneralizedBlackScholesProcess stochProcess = makeProcess(u, q, r, vol); IPricingEngine engine; switch (engineType) { case EngineType.Analytic: engine = new AnalyticEuropeanEngine(stochProcess); break; case EngineType.JR: engine = new BinomialVanillaEngine<JarrowRudd>(stochProcess, binomialSteps); break; case EngineType.CRR: engine = new BinomialVanillaEngine<CoxRossRubinstein>(stochProcess, binomialSteps); break; case EngineType.EQP: engine = new BinomialVanillaEngine<AdditiveEQPBinomialTree>(stochProcess, binomialSteps); break; case EngineType.TGEO: engine = new BinomialVanillaEngine<Trigeorgis>(stochProcess, binomialSteps); break; case EngineType.TIAN: engine = new BinomialVanillaEngine<Tian>(stochProcess, binomialSteps); break; case EngineType.LR: engine = new BinomialVanillaEngine<LeisenReimer>(stochProcess, binomialSteps); break; case EngineType.JOSHI: engine = new BinomialVanillaEngine<Joshi4>(stochProcess, binomialSteps); break; case EngineType.FiniteDifferences: engine = new FDEuropeanEngine(stochProcess, binomialSteps, samples); break; case EngineType.Integral: engine = new IntegralEngine(stochProcess); break; //case EngineType.PseudoMonteCarlo: // engine = MakeMCEuropeanEngine<PseudoRandom>(stochProcess) // .withSteps(1) // .withSamples(samples) // .withSeed(42); // break; //case EngineType.QuasiMonteCarlo: // engine = MakeMCEuropeanEngine<LowDiscrepancy>(stochProcess) // .withSteps(1) // .withSamples(samples); // break; default: throw new ArgumentException("unknown engine type"); } VanillaOption option = new EuropeanOption(payoff, exercise); option.setPricingEngine(engine); return option; }
public override void calculate() { PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); Utils.QL_REQUIRE(payoff.strike() > 0.0, () => "strike must be positive"); double K = payoff.strike(); double S = process_.x0(); Utils.QL_REQUIRE(S >= 0.0, () => "negative or null underlying given"); Utils.QL_REQUIRE(!triggered(S), () => "barrier touched"); DoubleBarrier.Type barrierType = arguments_.barrierType; Utils.QL_REQUIRE(barrierType == DoubleBarrier.Type.KnockOut || barrierType == DoubleBarrier.Type.KnockIn, () => "only KnockIn and KnockOut options supported"); double L = arguments_.barrier_lo.GetValueOrDefault(); double H = arguments_.barrier_hi.GetValueOrDefault(); double K_up = Math.Min(H, K); double K_down = Math.Max(L, K); double T = residualTime(); double rd = riskFreeRate(); double dd = riskFreeDiscount(); double rf = dividendYield(); double df = dividendDiscount(); double vol = volatility(); double mu = rd - rf - vol * vol / 2.0; double sgn = mu > 0 ? 1.0 :(mu < 0 ? -1.0: 0.0); //rebate double R_L = arguments_.rebate.GetValueOrDefault(); double R_H = arguments_.rebate.GetValueOrDefault(); //european option EuropeanOption europeanOption = new EuropeanOption(payoff, arguments_.exercise); IPricingEngine analyticEuropeanEngine = new AnalyticEuropeanEngine(process_); europeanOption.setPricingEngine(analyticEuropeanEngine); double european = europeanOption.NPV(); double barrierOut = 0; double rebateIn = 0; for (int n = -series_; n < series_; n++) { double d1 = D(S / H * Math.Pow(L / H, 2.0 * n), vol * vol + mu, vol, T); double d2 = d1 - vol * Math.Sqrt(T); double g1 = D(H / S * Math.Pow(L / H, 2.0 * n - 1.0), vol * vol + mu, vol, T); double g2 = g1 - vol * Math.Sqrt(T); double h1 = D(S / H * Math.Pow(L / H, 2.0 * n - 1.0), vol * vol + mu, vol, T); double h2 = h1 - vol * Math.Sqrt(T); double k1 = D(L / S * Math.Pow(L / H, 2.0 * n - 1.0), vol * vol + mu, vol, T); double k2 = k1 - vol * Math.Sqrt(T); double d1_down = D(S / K_down * Math.Pow(L / H, 2.0 * n), vol * vol + mu, vol, T); double d2_down = d1_down - vol * Math.Sqrt(T); double d1_up = D(S / K_up * Math.Pow(L / H, 2.0 * n), vol * vol + mu, vol, T); double d2_up = d1_up - vol * Math.Sqrt(T); double k1_down = D((H * H) / (K_down * S) * Math.Pow(L / H, 2.0 * n), vol * vol + mu, vol, T); double k2_down = k1_down - vol * Math.Sqrt(T); double k1_up = D((H * H) / (K_up * S) * Math.Pow(L / H, 2.0 * n), vol * vol + mu, vol, T); double k2_up = k1_up - vol * Math.Sqrt(T); if (payoff.optionType() == Option.Type.Call) { barrierOut += Math.Pow(L / H, 2.0 * n * mu / (vol * vol)) * (df * S * Math.Pow(L / H, 2.0 * n) * (f_.value(d1_down) - f_.value(d1)) - dd * K * (f_.value(d2_down) - f_.value(d2)) - df * Math.Pow(L / H, 2.0 * n) * H * H / S * Math.Pow(H / S, 2.0 * mu / (vol * vol)) * (f_.value(k1_down) - f_.value(k1)) + dd * K * Math.Pow(H / S, 2.0 * mu / (vol * vol)) * (f_.value(k2_down) - f_.value(k2))); } else if (payoff.optionType() == Option.Type.Put) { barrierOut += Math.Pow(L / H, 2.0 * n * mu / (vol * vol)) * (dd * K * (f_.value(h2) - f_.value(d2_up)) - df * S * Math.Pow(L / H, 2.0 * n) * (f_.value(h1) - f_.value(d1_up)) - dd * K * Math.Pow(H / S, 2.0 * mu / (vol * vol)) * (f_.value(g2) - f_.value(k2_up)) + df * Math.Pow(L / H, 2.0 * n) * H * H / S * Math.Pow(H / S, 2.0 * mu / (vol * vol)) * (f_.value(g1) - f_.value(k1_up))); } else { Utils.QL_FAIL("option type not recognized"); } double v1 = D(H / S * Math.Pow(H / L, 2.0 * n), -mu, vol, T); double v2 = D(H / S * Math.Pow(H / L, 2.0 * n), mu, vol, T); double v3 = D(S / L * Math.Pow(H / L, 2.0 * n), -mu, vol, T); double v4 = D(S / L * Math.Pow(H / L, 2.0 * n), mu, vol, T); rebateIn += dd * R_H * sgn * (Math.Pow(L / H, 2.0 * n * mu / (vol * vol)) * f_.value(sgn * v1) - Math.Pow(H / S, 2.0 * mu / (vol * vol)) * f_.value(-sgn * v2)) + dd * R_L * sgn * (Math.Pow(L / S, 2.0 * mu / (vol * vol)) * f_.value(-sgn * v3) - Math.Pow(H / L, 2.0 * n * mu / (vol * vol)) * f_.value(sgn * v4)); } //rebate paid at maturity if (barrierType == DoubleBarrier.Type.KnockOut) { results_.value = barrierOut; } else { results_.value = european - barrierOut; } results_.additionalResults["vanilla"] = european; results_.additionalResults["barrierOut"] = barrierOut; results_.additionalResults["barrierIn"] = european - barrierOut; }
public override void calculate(DateTime calcDate, FP_Parameter fp_parameter) { // master data load this.indexOptionDAO_.SelectOwn(); // market data load // index data clsHDAT_MARKETDATA_TB clstb = new clsHDAT_MARKETDATA_TB(); string calcDateStr = calcDate.ToString("yyyyMMdd"); QLNet.Settings.setEvaluationDate(calcDate); clstb.REF_DT = calcDateStr; clstb.INDEX_CD = this.indexOptionDAO_.UNDERLYING_INDEX_CD; int checkNum = clstb.SelectOwn(); if (checkNum == 0) { throw new Exception("market data does not exist : " + calcDateStr + " " + clstb.INDEX_CD); } double indexData = clstb.LAST; // curveData -------------------------------------------------- string curve_cd = "IRSKRW"; YieldCurve curveManager = new YieldCurve(); curveManager.loadCurveData(calcDate,curve_cd,clsHDAT_CURVEDATA_TB.RATE_TYP_Type.YTM); QLNet.YieldTermStructure yield_ts = curveManager.yieldCurve(); // calculate string maturityDateStr = this.indexOptionDAO_.MATURITY_DT; System.Globalization.CultureInfo us = new System.Globalization.CultureInfo("en-US"); DateTime maturityDate = DateTime.ParseExact(maturityDateStr, "yyyyMMdd", us); DayCounter dc = new Actual365Fixed(); Calendar cal = new NullCalendar(); double vol = 0.3; double strike = this.indexOptionDAO_.STRIKE; PlainVanillaPayoff strikePayoff = new PlainVanillaPayoff(Option.Type. Call, strike); Exercise exercise = new EuropeanExercise(maturityDate); VanillaOption q_option = new VanillaOption(strikePayoff,exercise); Handle<Quote> x0 = new Handle<Quote>(new SimpleQuote(indexData)); FlatForward flatForward = new FlatForward(calcDate,0.01,dc); Handle<YieldTermStructure> dividendTS = new Handle<YieldTermStructure>(flatForward); Handle<YieldTermStructure> riskFreeTS = new Handle<YieldTermStructure>(yield_ts); BlackConstantVol blackConstVol = new BlackConstantVol(calcDate,cal,vol,dc); Handle<BlackVolTermStructure> blackVolTS = new Handle<BlackVolTermStructure>(blackConstVol); GeneralizedBlackScholesProcess process =new GeneralizedBlackScholesProcess(x0 ,dividendTS,riskFreeTS,blackVolTS); AnalyticEuropeanEngine europeanEngine = new AnalyticEuropeanEngine(process); q_option.setPricingEngine(europeanEngine); double value = q_option.NPV(); double indexMultiplier = this.indexOptionDAO_.INDEX_MULTIPLIER; int quantity = this.indexOptionDAO_.QUANTITY; clsHITM_FP_GREEKRESULT_TB result_tb = new clsHITM_FP_GREEKRESULT_TB(); result_tb.FP_GREEKRESULT_ID = IDGenerator.getNewGreekResultID(this.indexOptionDAO_.INSTRUMENT_ID,calcDateStr); result_tb.CALC_DT = calcDateStr; result_tb.INSTRUMENT_ID = this.indexOptionDAO_.INSTRUMENT_ID; result_tb.INSTRUMENT_TYP = this.indexOptionDAO_.INSTRUMENT_TYP; result_tb.UNDERLYING_ID = "KOSPI200"; result_tb.UNDERLYING_VALUE = indexData; //result_tb.SEQ = 1; result_tb.DELTA = (q_option.delta() * indexData / 100) * indexMultiplier * quantity; // 1% Delta result_tb.GAMMA = 0.5 * (q_option.gamma() * indexData / 100) * indexMultiplier * quantity; // 1% Gamma result_tb.VEGA = q_option.vega() / 100 * indexMultiplier * quantity; // 1% point Vega result_tb.CALC_PRICE = value * indexMultiplier * quantity; result_tb.CALCULATED_FLAG = (int)clsHITM_FP_GREEKRESULT_TB.CALCULATED_FLAG_Type.CALCULATED; result_tb.CALCULATED_TIME = DateTime.Now.ToString("HHmmss"); ; result_tb.CALCULATE_TYP = (int)clsHITM_FP_GREEKRESULT_TB.CALCULATE_TYP_Type.ANALYTICS; // price if (result_tb.UpdateDateResult() == 0) { throw new Exception("update result fail. no exist , calcDate : " + calcDate.ToString("yyyyMMdd") + " , inst_id : " + result_tb.INSTRUMENT_ID); } // delta // gamma and others : no exist ? }