Strange Basic Option Valuation Problem - Part II

classic Classic list List threaded Threaded
2 messages Options
UNG
Reply | Threaded
Open this post in threaded view
|

Strange Basic Option Valuation Problem - Part II

UNG

Consider the following European option:

        Option::Type type(Option::Call);
        Real underlying = 30.07;
        Real strike = 35.0;
        Spread dividendYield = 0.0;
        Rate riskFreeRate = 0.0433;

        Date todaysDate(16, January, 2006);
        Date settlementDate(16, January, 2006);
        Date exerciseDate(18, February, 2006);

        DayCounter dayCounter = Actual360();
        double optPx = 0.55;

Compare results from:
 
** Option calculator
http://www.888options.com/resources/options_calc.jsp
(Bloomberg results are the same within 4th decimal)
I.V.  0.549 Delta 0.2105 Gamma 0.0576 Vega  0.0261
Rho   0.0052

** Quantlib:
impliedVol:     0.549073
delta:  0.0567826
gamma:  0.0417672
theta:  -0.00485107
vega:   0.0103857
rho:    0.150655

Attached is QuantLib default example with hardcoded
parameters.

---8<-----------------------------------------------------------------------------------------------------

/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil;
c-basic-offset: 4 -*- */

/*!
 Copyright (C) 2003 Ferdinando Ametrano
 Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl

 This file is part of QuantLib, a
free-software/open-source library  for financial
quantitative analysts and developers -
http://quantlib.org/

 QuantLib is free software: you can redistribute it
and/or modify it  under the terms of the QuantLib
license.  You should have received a  copy of the
license along with this program; if not, please email
<[hidden email]>. The license is also
available online at
<http://quantlib.org/reference/license.html>.

 This program is distributed in the hope that it will
be useful, but WITHOUT  ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS  FOR A
PARTICULAR PURPOSE.  See the license for more details.
*/

#include <ql/quantlib.hpp>
#include <iostream>

using namespace QuantLib;

#if defined(QL_ENABLE_SESSIONS)
namespace QuantLib {

    Integer sessionId() { return 0; }

}
#endif


// This will be included in the library after a bit of
redesign class WeightedPayoff {
    public:
        WeightedPayoff(Option::Type type,
               Time maturity,
               Real strike,
               Real s0,
               Volatility sigma,
               Rate r,
               Rate q)
        : type_(type), maturity_(maturity),
        strike_(strike),
        s0_(s0),
        sigma_(sigma),r_(r), q_(q){}

        Real operator()(Real x) const {
           Real nuT =
(r_-q_-0.5*sigma_*sigma_)*maturity_;
           return std::exp(-r_*maturity_)
               *PlainVanillaPayoff(type_,
strike_)(s0_*std::exp(x))
               *std::exp(-(x - nuT)*(x
-nuT)/(2*sigma_*sigma_*maturity_))
             
/std::sqrt(2.0*M_PI*sigma_*sigma_*maturity_);
        }
private:
    Option::Type type_;
    Time maturity_;
    Real strike_;
    Real s0_;
    Volatility sigma_;
    Rate r_,q_;
};


int main(int, char* [])
{
    try {
        QL_IO_INIT

        std::cout << "Using " << QL_VERSION <<
std::endl << std::endl;

        // our option
        Option::Type type(Option::Call);
        Real underlying = 30.07; //50.78; //51.69;
        Real strike = 35.0; // 52.5;
        Spread dividendYield = 0.0; // 0.0070964;
//0.0;
        Rate riskFreeRate = 0.0433; //0.01;

        Date todaysDate(16, January, 2006);
        Date settlementDate(16, January, 2006);
        Settings::instance().evaluationDate() =
todaysDate;

        Date exerciseDate(18, February, 2006);
        DayCounter dayCounter = Actual360();
        Time maturity =
dayCounter.yearFraction(settlementDate,
                                               
exerciseDate);

        Volatility volatility = 0.3;
        std::cout << "option type = "  << type <<
std::endl;
        std::cout << "Time to maturity = "        <<
maturity
                  << std::endl;
        std::cout << "Underlying price = "        <<
underlying
                  << std::endl;
        std::cout << "Strike = "                  <<
strike
                  << std::endl;
        std::cout << "Risk-free interest rate = " <<
io::rate(riskFreeRate)
                  << std::endl;
        std::cout << "Dividend yield = " <<
io::rate(dividendYield)
                  << std::endl;
        std::cout << "Volatility = " <<
io::volatility(volatility)
                  << std::endl;
        std::cout << std::endl;

//        Date midlifeDate(19, November, 1998);
//        std::vector<Date> exDates(2);
//        exDates[0]=midlifeDate;
//        exDates[1]=exerciseDate;
//
        boost::shared_ptr<Exercise> exercise(
                                          new
EuropeanExercise(exerciseDate));
//        boost::shared_ptr<Exercise> amExercise(
//                                          new
AmericanExercise(settlementDate,
//                                                    
          exerciseDate));
//        boost::shared_ptr<Exercise> berExercise(new
BermudanExercise(exDates));
//
//
        Handle<Quote> underlyingH(
            boost::shared_ptr<Quote>(new
SimpleQuote(underlying)));

        // bootstrap the yield/dividend/vol curves
        Handle<YieldTermStructure> flatTermStructure(
            boost::shared_ptr<YieldTermStructure>(
                new FlatForward(settlementDate,
riskFreeRate, dayCounter)));
        Handle<YieldTermStructure> flatDividendTS(
            boost::shared_ptr<YieldTermStructure>(
                new FlatForward(settlementDate,
dividendYield, dayCounter)));
        Handle<BlackVolTermStructure> flatVolTS(
            boost::shared_ptr<BlackVolTermStructure>(
                new BlackConstantVol(settlementDate,
volatility, dayCounter))); //
//        std::vector<Date> dates(4);
//        dates[0] = settlementDate + 1*Months;
//        dates[1] = exerciseDate;
//        dates[2] = exerciseDate + 6*Months;
//        dates[3] = exerciseDate + 12*Months;
//        std::vector<Real> strikes(4);
//        strikes[0] = underlying*0.9;
//        strikes[1] = underlying;
//        strikes[2] = underlying*1.1;
//        strikes[3] = underlying*1.2;
//
//        Matrix vols(4,4);
//        vols[0][0] = volatility*1.1;
//                     vols[0][1] = volatility;
//                                  vols[0][2] =
volatility*0.9;
//                                              
vols[0][3] = volatility*0.8;
//        vols[1][0] = volatility*1.1;
//                     vols[1][1] = volatility;
//                                  vols[1][2] =
volatility*0.9;
//                                              
vols[1][3] = volatility*0.8;
//        vols[2][0] = volatility*1.1;
//                     vols[2][1] = volatility;
//                                  vols[2][2] =
volatility*0.9;
//                                              
vols[2][3] = volatility*0.8;
//        vols[3][0] = volatility*1.1;
//                     vols[3][1] = volatility;
//                                  vols[3][2] =
volatility*0.9;
//                                              
vols[3][3] = volatility*0.8;
//
//        Handle<BlackVolTermStructure> blackSurface(
//          
boost::shared_ptr<BlackVolTermStructure>(
//                new
BlackVarianceSurface(settlementDate, dates,
//                                         strikes,
vols, dayCounter)));


        boost::shared_ptr<StrikedTypePayoff>
payoff(new
            PlainVanillaPayoff(type, strike));

        boost::shared_ptr<BlackScholesProcess>
stochasticProcess(new
            BlackScholesProcess(underlyingH,
flatDividendTS,
                                flatTermStructure,
                                //  blackSurface
                                flatVolTS));

        EuropeanOption option(stochasticProcess,
payoff, exercise);


        std::string method;
        Real value, discrepancy, rightValue,
relativeDiscrepancy;

        std::cout << std::endl << std::endl;

        // write column headings
        std::cout <<
"Method\t\tValue\t\tEstimatedError\tDiscrepancy"
                     "\tRel. Discr." << std::endl;

        // method: Black-Scholes Engine
        method = "Black-Scholes";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new AnalyticEuropeanEngine()));
       
        double optPx = 0.55; //1.6; //1.75;
       
        double impliedVol = option.impliedVolatility(
optPx );        
        double delta = option.delta();
        double gamma = option.gamma();
        double theta = option.theta()/365.0;
        double vega = option.vega()/100.0;
        double rho = option.rho();
        double divRho = option.dividendRho();

        std::cout << "impliedVol: " << "\t" <<
impliedVol << std::endl
            << "delta: " << "\t" << delta << std::endl
            << "gamma: " << "\t" << gamma << std::endl
            << "theta: " << "\t" << theta << std::endl
            << "vega: " << "\t" << vega << std::endl
            << "rho: " << "\t" << rho << std::endl
            << "divRho: " << "\t" << divRho <<
std::endl;


        rightValue = value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t\t" <<
relativeDiscrepancy << std::endl;


        // method: Integral
        method = "Integral";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new IntegralEngine()));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

/*
        // method: Integral
        method = "Binary Cash";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new IntegralCashOrNothingEngine(1.0)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        // method: Integral
        method = "Binary Asset";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new IntegralAssetOrNothingEngine()));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

*/
        Size timeSteps = 801;

        // Binomial Method (JR)
        method = "Binomial (JR)";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new
BinomialVanillaEngine<JarrowRudd>(timeSteps)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;


        // Binomial Method (CRR)
        method = "Binomial (CRR)";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new
BinomialVanillaEngine<CoxRossRubinstein>(timeSteps)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        // Equal Probability Additive Binomial Tree
(EQP)
        method = "Additive (EQP)";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new
BinomialVanillaEngine<AdditiveEQPBinomialTree>(timeSteps)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        // Equal Jumps Additive Binomial Tree
(Trigeorgis)
        method = "Bin. Trigeorgis";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new
BinomialVanillaEngine<Trigeorgis>(timeSteps)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        // Tian Binomial Tree (third moment matching)
        method = "Binomial Tian";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new
BinomialVanillaEngine<Tian>(timeSteps)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        // Leisen-Reimer Binomial Tree
        method = "Binomial LR";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new
BinomialVanillaEngine<LeisenReimer>(timeSteps)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        // Finite Differences

        method = "Finite Diff.";
        timeSteps = 100;
        Size gridPoints = 100;
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new FDEuropeanEngine(timeSteps,
gridPoints)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        // Monte Carlo Method
        timeSteps = 1;

        method = "MC (crude)";
        Size mcSeed = 42;

        boost::shared_ptr<PricingEngine> mcengine1;
        mcengine1 =
           
MakeMCEuropeanEngine<PseudoRandom>().withSteps(timeSteps)
                                               
.withTolerance(0.02)
                                               
.withSeed(mcSeed);
        option.setPricingEngine(mcengine1);

        value = option.NPV();
        Real errorEstimate = option.errorEstimate();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << errorEstimate <<
"\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        method = "MC (Sobol)";
        timeSteps = 1;
        Size nSamples = 32768;  // 2^15

        boost::shared_ptr<PricingEngine> mcengine2;
        mcengine2 =
           
MakeMCEuropeanEngine<LowDiscrepancy>().withSteps(timeSteps)
                                                 
.withSamples(nSamples);
        option.setPricingEngine(mcengine2);

        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        return 0;
    } catch (std::exception& e) {
        std::cout << e.what() << std::endl;
        return 1;
    } catch (...) {
        std::cout << "unknown error" << std::endl;
        return 1;
    }
}

---8<-----------------------------------------------------------------------------------------------------




Reply | Threaded
Open this post in threaded view
|

Re: Strange Basic Option Valuation Problem - Part II

Luigi Ballabio
Hi,

On 1/16/06, UNG <[hidden email]> wrote:
> ** Option calculator
> http://www.888options.com/resources/options_calc.jsp
> (Bloomberg results are the same within 4th decimal)
> I.V.  0.549 Delta 0.2105 Gamma 0.0576 Vega  0.0261
> Rho   0.0052

These are the results when the volatility is set to 0.549, aren't they?
In QuantLib, calling the impliedVolatility() method returns the
implied volatility, but it does not set the volatility of the option
to the result. Therefore...

> ** Quantlib:
> impliedVol:     0.549073
> delta:  0.0567826
> gamma:  0.0417672
> theta:  -0.00485107
> vega:   0.0103857
> rho:    0.150655

...the above are still the results for the volatility you began with,
namely, 0.30. You can compare them with the corresponding results from
the calculator.

If you want to set the volatility to the implied one, you have to do
it yourself. A way to do it in your code is to replace

Volatility volatility = 0.3;

with

boost::shared_ptr<SimpleQuote> volQuote(new SimpleQuote(0.30));
Handle<Quote> volatility(volQuote);

so that you can modify the volatility. Later in the code, you can write

double impliedVol = option.impliedVolatility(optPx );
// now set the new value...
volQuote->setValue(impliedVol);
// so that the results below are now those you want
double delta = option.delta();
etc. etc

Later,
    Luigi