Real AnalyticDoubleBarrierEngine::putKO() const { Real mu1 = 2 * costOfCarry() / volatilitySquared() + 1; Real bsigma = (costOfCarry() + volatilitySquared() / 2.0) * residualTime() / stdDeviation(); Real acc1 = 0; Real acc2 = 0; for (int n = -series_ ; n <= series_ ; ++n) { Real L2n = std::pow(barrierLo(), 2 * n); Real U2n = std::pow(barrierHi(), 2 * n); Real y1 = std::log( underlying()* U2n / (std::pow(barrierLo(), 2 * n + 1)) ) / stdDeviation() + bsigma; Real y2 = std::log( underlying()* U2n / (strike() * L2n) ) / stdDeviation() + bsigma; Real y3 = std::log( std::pow(barrierLo(), 2 * n + 2) / (barrierLo() * underlying() * U2n) ) / stdDeviation() + bsigma; Real y4 = std::log( std::pow(barrierLo(), 2 * n + 2) / (strike() * underlying() * U2n) ) / stdDeviation() + bsigma; acc1 += std::pow( std::pow(barrierHi(), n) / std::pow(barrierLo(), n), mu1-2) * (f_(y1 - stdDeviation()) - f_(y2 - stdDeviation())) - std::pow( std::pow(barrierLo(), n+1) / (std::pow(barrierHi(), n) * underlying()), mu1-2 ) * (f_(y3-stdDeviation()) - f_(y4-stdDeviation())); acc2 += std::pow( std::pow(barrierHi(), n) / std::pow(barrierLo(), n), mu1 ) * (f_(y1) - f_(y2)) - std::pow( std::pow(barrierLo(), n+1) / (std::pow(barrierHi(), n) * underlying()), mu1 ) * (f_(y3) - f_(y4)); } Real rend = std::exp(-dividendYield() * residualTime()); Real kov = strike() * riskFreeDiscount() * acc1 - underlying() * rend * acc2; return std::max(0.0, kov); }
void ForwardVanillaEngine<Engine>::setup() const { ext::shared_ptr<StrikedTypePayoff> argumentsPayoff = ext::dynamic_pointer_cast<StrikedTypePayoff>( this->arguments_.payoff); QL_REQUIRE(argumentsPayoff, "wrong payoff given"); ext::shared_ptr<StrikedTypePayoff> payoff( new PlainVanillaPayoff(argumentsPayoff->optionType(), this->arguments_.moneyness * process_->x0())); // maybe the forward value is "better", in some fashion // the right level is needed in order to interpolate // the vol Handle<Quote> spot = process_->stateVariable(); QL_REQUIRE(spot->value() >= 0.0, "negative or null underlting given"); Handle<YieldTermStructure> dividendYield( ext::shared_ptr<YieldTermStructure>( new ImpliedTermStructure(process_->dividendYield(), this->arguments_.resetDate))); Handle<YieldTermStructure> riskFreeRate( ext::shared_ptr<YieldTermStructure>( new ImpliedTermStructure(process_->riskFreeRate(), this->arguments_.resetDate))); // The following approach is ok if the vol is at most // time dependant. It is plain wrong if it is asset dependant. // In the latter case the right solution would be stochastic // volatility or at least local volatility (which unfortunately // implies an unrealistic time-decreasing smile) Handle<BlackVolTermStructure> blackVolatility( ext::shared_ptr<BlackVolTermStructure>( new ImpliedVolTermStructure(process_->blackVolatility(), this->arguments_.resetDate))); ext::shared_ptr<GeneralizedBlackScholesProcess> fwdProcess( new GeneralizedBlackScholesProcess(spot, dividendYield, riskFreeRate, blackVolatility)); originalEngine_ = ext::shared_ptr<Engine>(new Engine(fwdProcess)); originalEngine_->reset(); originalArguments_ = dynamic_cast<VanillaOption::arguments*>( originalEngine_->getArguments()); QL_REQUIRE(originalArguments_, "wrong engine type"); originalResults_ = dynamic_cast<const VanillaOption::results*>( originalEngine_->getResults()); QL_REQUIRE(originalResults_, "wrong engine type"); originalArguments_->payoff = payoff; originalArguments_->exercise = this->arguments_.exercise; originalArguments_->validate(); }
Real AnalyticComplexChooserEngine::ComplexChooser() const{ Real S = process_->x0(); Real b; Real v; Real r = riskFreeRate(choosingTime()); Real Xc = arguments_.strikeCall; Real Xp = arguments_.strikePut; Time T = choosingTime(); Time Tc = callMaturity()-choosingTime(); Time Tp = putMaturity()-choosingTime(); Real i = CriticalValueChooser(); b = riskFreeRate(choosingTime()) - dividendYield(choosingTime()); v = volatility(T); Real d1 = (log(S / i) + (b + pow(v, 2) / 2)*T) / (v*sqrt(T)); Real d2 = d1 - v*sqrt(T); b = riskFreeRate(callMaturity()) - dividendYield(callMaturity()); v = volatility(Tc); Real y1 = (log(S / Xc) + (b + pow(v, 2) / 2)*Tc) / (v*sqrt(Tc)); b = riskFreeRate(putMaturity()) - dividendYield(putMaturity()); v = volatility(Tp); Real y2 = (log(S / Xp) + (b + pow(v, 2) / 2)*Tp) / (v*sqrt(Tp)); Real rho1 = sqrt(T / Tc); Real rho2 = sqrt(T / Tp); b = riskFreeRate(callMaturity()) - dividendYield(callMaturity()); r = riskFreeRate(callMaturity()); Real ComplexChooser = S * exp((b - r)*Tc) * BivariateCumulativeNormalDistributionDr78(rho1)(d1, y1) - Xc * exp(-r*Tc)*BivariateCumulativeNormalDistributionDr78(rho1)(d2, y1 - v * sqrt(Tc)) ; b = riskFreeRate(putMaturity()) - dividendYield(putMaturity()); r = riskFreeRate(putMaturity()); ComplexChooser-= S * exp((b - r)*Tp) * BivariateCumulativeNormalDistributionDr78(rho2)(-d1, -y2); ComplexChooser+= Xp * exp(-r*Tp) * BivariateCumulativeNormalDistributionDr78(rho2)(-d2, -y2 + v * sqrt(Tp)); return ComplexChooser; }
Disposable<Array> HestonSLVProcess::drift(Time t, const Array& x) const { Array tmp(2); const Real s = std::exp(x[0]); const Volatility vol = std::sqrt(x[1])*leverageFct_->localVol(t, s, true); tmp[0] = riskFreeRate()->forwardRate(t, t, Continuous) - dividendYield()->forwardRate(t, t, Continuous) - 0.5*vol*vol; tmp[1] = kappa_*(theta_ - x[1]); return tmp; }
Real AnalyticContinuousFloatingLookbackEngine::A(Real eta) const { Volatility vol = volatility(); Real lambda = 2.0*(riskFreeRate() - dividendYield())/(vol*vol); Real s = underlying()/minmax(); Real d1 = std::log(s)/stdDeviation() + 0.5*(lambda+1.0)*stdDeviation(); Real n1 = f_(eta*d1); Real n2 = f_(eta*(d1-stdDeviation())); Real n3 = f_(eta*(-d1+lambda*stdDeviation())); Real n4 = f_(eta*-d1); Real pow_s = std::pow(s, -lambda); return eta*((underlying() * dividendDiscount() * n1 - minmax() * riskFreeDiscount() * n2) + (underlying() * riskFreeDiscount() * (pow_s * n3 - dividendDiscount()* n4/riskFreeDiscount())/ lambda)); }
Disposable<Array> HestonSLVProcess::evolve( Time t0, const Array& x0, Time dt, const Array& dw) const { Array retVal(2); const Real ex = std::exp(-kappa_*dt); const Real m = theta_+(x0[1]-theta_)*ex; const Real s2 = x0[1]*sigma_*sigma_*ex/kappa_*(1-ex) + theta_*sigma_*sigma_/(2*kappa_)*(1-ex)*(1-ex); const Real psi = s2/(m*m); if (psi < 1.5) { const Real b2 = 2/psi-1+std::sqrt(2/psi*(2/psi-1)); const Real b = std::sqrt(b2); const Real a = m/(1+b2); retVal[1] = a*(b+dw[1])*(b+dw[1]); } else { const Real p = (psi-1)/(psi+1); const Real beta = (1-p)/m; const Real u = CumulativeNormalDistribution()(dw[1]); retVal[1] = ((u <= p) ? 0.0 : std::log((1-p)/(1-u))/beta); } const Real mu = riskFreeRate()->forwardRate(t0, t0+dt, Continuous) - dividendYield()->forwardRate(t0, t0+dt, Continuous); const Real rho1 = std::sqrt(1-rho_*rho_); const Volatility l_0 = leverageFct_->localVol(t0, x0[0], true); const Real v_0 = 0.5*(x0[1]+retVal[1])*l_0*l_0; retVal[0] = x0[0]*std::exp(mu*dt - 0.5*v_0*dt + rho_/sigma_*l_0 * ( retVal[1] - kappa_*theta_*dt + 0.5*(x0[1]+retVal[1])*kappa_*dt - x0[1]) + rho1*std::sqrt(v_0*dt)*dw[0]); return retVal; }
Rate AnalyticBarrierEngine::mu() const { Volatility vol = volatility(); return (riskFreeRate() - dividendYield())/(vol * vol) - 0.5; }
void FFTEngine::precalculate(const std::vector<boost::shared_ptr<Instrument> >& optionList) { // Group payoffs by expiry date // as with FFT we can compute a bunch of these at once resultMap_.clear(); typedef std::vector<boost::shared_ptr<StrikedTypePayoff> > PayoffList; typedef std::map<Date, PayoffList> PayoffMap; PayoffMap payoffMap; for (std::vector<boost::shared_ptr<Instrument> >::const_iterator optIt = optionList.begin(); optIt != optionList.end(); optIt++) { boost::shared_ptr<VanillaOption> option = boost::dynamic_pointer_cast<VanillaOption>(*optIt); QL_REQUIRE(option, "instrument must be option"); QL_REQUIRE(option->exercise()->type() == Exercise::European, "not an European Option"); boost::shared_ptr<StrikedTypePayoff> payoff = boost::dynamic_pointer_cast<StrikedTypePayoff>(option->payoff()); QL_REQUIRE(payoff, "non-striked payoff given"); payoffMap[option->exercise()->lastDate()].push_back(payoff); } std::complex<Real> i1(0, 1); Real alpha = 1.25; for (PayoffMap::const_iterator payIt = payoffMap.begin(); payIt != payoffMap.end(); payIt++) { Date expiryDate = payIt->first; // Calculate n large enough for maximum strike, and round up to a power of 2 Real maxStrike = 0.0; for (PayoffList::const_iterator it = payIt->second.begin(); it != payIt->second.end(); it++) { boost::shared_ptr<StrikedTypePayoff> payoff = *it; if (payoff->strike() > maxStrike) maxStrike = payoff->strike(); } Real nR = 2.0 * (std::log(maxStrike) + lambda_) / lambda_; Size log2_n = (static_cast<Size>((std::log(nR) / std::log(2.0))) + 1); Size n = 1 << log2_n; // Strike range (equation 19,20) Real b = n * lambda_ / 2.0; // Grid spacing (equation 23) Real eta = 2.0 * M_PI / (lambda_ * n); // Discount factor Real df = discountFactor(expiryDate); Real div = dividendYield(expiryDate); // Input to fourier transform std::vector<std::complex<Real> > fti; fti.resize(n); // Precalculate any discount factors etc. precalculateExpiry(expiryDate); for (Size i=0; i<n; i++) { Real k_u = -b + lambda_ * i; Real v_j = eta * i; Real sw = eta * (3.0 + ((i % 2) == 0 ? -1.0 : 1.0) - ((i == 0) ? 1.0 : 0.0)) / 3.0; std::complex<Real> psi = df * complexFourierTransform(v_j - (alpha + 1)* i1); psi = psi / (alpha*alpha + alpha - v_j*v_j + i1 * (2 * alpha + 1.0) * v_j); fti[i] = std::exp(i1 * b * v_j) * sw * psi; } // Perform fft std::vector<std::complex<Real> > results(n); FastFourierTransform fft(log2_n); fft.transform(fti.begin(), fti.end(), results.begin()); // Call prices std::vector<Real> prices, strikes; prices.resize(n); strikes.resize(n); for (Size i=0; i<n; i++) { Real k_u = -b + lambda_ * i; prices[i] = (std::exp(-alpha * k_u) / M_PI) * results[i].real(); strikes[i] = std::exp(k_u); } for (PayoffList::const_iterator it = payIt->second.begin(); it != payIt->second.end(); it++) { boost::shared_ptr<StrikedTypePayoff> payoff = *it; Real callPrice = LinearInterpolation(strikes.begin(), strikes.end(), prices.begin())(payoff->strike()); switch (payoff->optionType()) { case Option::Call: resultMap_[expiryDate][payoff] = callPrice; break; case Option::Put: resultMap_[expiryDate][payoff] = callPrice - process_->x0() * div + payoff->strike() * df; break; default: QL_FAIL("Invalid option type"); } } } }
Rate AnalyticDoubleBarrierEngine::costOfCarry() const { return riskFreeRate() - dividendYield(); }