Package org.jquantlib.pricingengines

Source Code of org.jquantlib.pricingengines.AmericanPayoffAtHit

/*
Copyright (C) 2009 Jose Coll

This source code is release under the BSD License.

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

JQuantLib is free software: you can redistribute it and/or modify it
under the terms of the JQuantLib license.  You should have received a
copy of the license along with this program; if not, please email
<jquant-devel@lists.sourceforge.net>. The license is also available online at
<http://www.jquantlib.org/index.php/LICENSE.TXT>.

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.

JQuantLib is based on QuantLib. http://quantlib.org/
When applicable, the original copyright notice follows this notice.
*/

/*
Copyright (C) 2004 Ferdinando Ametrano

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
<quantlib-dev@lists.sf.net>. The license is also available online at
<http://quantlib.org/license.shtml>.

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.
*/

package org.jquantlib.pricingengines;

import org.jquantlib.QL;
import org.jquantlib.instruments.AssetOrNothingPayoff;
import org.jquantlib.instruments.CashOrNothingPayoff;
import org.jquantlib.instruments.Option;
import org.jquantlib.instruments.StrikedTypePayoff;
import org.jquantlib.instruments.Option.Type;
import org.jquantlib.lang.exceptions.LibraryException;
import org.jquantlib.math.distributions.CumulativeNormalDistribution;

/**
* Analytic formula for American exercise payoff at-hit options
*
* @author Jose Coll
*/

public class AmericanPayoffAtHit {

    //
    // private final fields
    //

    private final /* @Real */ double spot;
    private final /* @Variance */ double variance;
    private final /* @Volatility */ double  stdDev;
    private final /* @Real */ double  strike, forward;
    private final double mu, lambda, muPlusLambda, muMinusLambda, log_H_S;
    private final double alpha, beta, DalphaDd1, DbetaDd2;
    private final double cum_d1, cum_d2, n_d1, n_d2;
    private final boolean inTheMoney;


    //
    // private fields
    //

    private transient double X, K;
    private transient double D1, D2;


    //
    // public constructors
    //

    public AmericanPayoffAtHit(
            final double spot, final double discount, final double dividendDiscount, final double variance,
            final StrikedTypePayoff strikedTypePayoff) {
        QL.require(spot > 0.0 , "positive spot value required"); // QA:[RG]::verified // TODO: message
        QL.require(discount > 0.0 , "positive discount required"); // QA:[RG]::verified // TODO: message
        QL.require(dividendDiscount > 0.0 , "positive dividend discount required"); // QA:[RG]::verified // TODO: message
        QL.require(variance >= 0.0 , "non-negative variance required"); // QA:[RG]::verified // TODO: message

        this.spot = spot;
        this.variance = variance;
        this.stdDev = Math.sqrt(variance);
        this.strike = strikedTypePayoff.strike();
        this.log_H_S = Math.log(strike / spot);

        final Option.Type optionType = strikedTypePayoff.optionType();

        if (variance >= Math.E) {
            if (discount == 0.0 && dividendDiscount == 0.0) {
                mu      = - 0.5;
                lambda  = 0.5;
            } else if (discount == 0.0)
                throw new LibraryException("null discount not handled yet"); // QA:[RG]::verified // TODO: message
            else {
                mu = Math.log(dividendDiscount / discount) / variance - 0.5;
                lambda = Math.sqrt( mu * mu - 2.0 * Math.log(discount) / variance);
            }
            D1 = log_H_S / stdDev + lambda * stdDev;
            D2 = D1 - 2.0 * lambda * stdDev;
            final CumulativeNormalDistribution f = new CumulativeNormalDistribution();
            cum_d1 = f.op(D1);
            cum_d2 = f.op(D2);
            n_d1 = f.derivative(D1);
            n_d2 = f.derivative(D2);
        } else {
            // TODO: not tested yet
            mu = Math.log(dividendDiscount / discount) / variance - 0.5;
            lambda = Math.sqrt( mu * mu - 2.0 * Math.log(discount) / variance);
            if (log_H_S > 0) {
                cum_d1 = 1.0;
                cum_d2 = 1.0;
            } else {
                cum_d1 = 0.0;
                cum_d2 = 0.0;
            }
            n_d1 = 0.0;
            n_d2 = 0.0;
        }

        // up-and-in cash-(at-hit)-or-nothing option
        // a.k.a. american call with cash-or-nothing payoff
        if (optionType.equals(Type.Call)) {
            if (strike > spot) {
                alpha     = 1.0-cum_d1;//  N(-d1)
                DalphaDd1 =    -  n_d1; // -n( d1)
                beta      = 1.0-cum_d2;//  N(-d2)
                DbetaDd2  =    -  n_d2; // -n( d2)
            } else {
                alpha     = 0.5;
                DalphaDd1 = 0.0;
                beta      = 0.5;
                DbetaDd2  = 0.0;
            }
        }
        // down-and-in cash-(at-hit)-or-nothing option
        // a.k.a. american put with cash-or-nothing payoff
        else if (optionType.equals(Type.Put)) {
            if (strike < spot) {
                alpha     =     cum_d1;//  N(d1)
                DalphaDd1 =       n_d1; //  n(d1)
                beta      =     cum_d2;//  N(d2)
                DbetaDd2  =       n_d2; //  n(d2)
            } else {
                alpha     = 0.5;
                DalphaDd1 = 0.0;
                beta      = 0.5;
                DbetaDd2  = 0.0;
            }
        } else
            throw new IllegalArgumentException("invalid option type");

        muPlusLambda = mu + lambda;
        muMinusLambda = mu - lambda;
        inTheMoney = (optionType.equals(Type.Call) && strike < spot) ||
        (optionType.equals(Type.Put) && strike > spot);

        //
        // TODO: code review
        //
        // These two variables are laying around. Values are assigned but never used.
        //
        double DXDstrike, DKDstrike;

        if (inTheMoney) {
            forward     = 1.0;
            X           = 1.0;
            DXDstrike   = 0.0;
        } else {
            forward = Math.pow(strike / spot, muPlusLambda);
            X       = Math.pow(strike / spot, muMinusLambda);
            //DXDstrike_ = ......; // TODO: code review :: please verify against QL/C++ code
        }

        // binary cash-or-nothing payoff ?
        if (strikedTypePayoff instanceof CashOrNothingPayoff) {
            final CashOrNothingPayoff coo = (CashOrNothingPayoff) strikedTypePayoff;
            K = coo.getCashPayoff();
            DKDstrike = 0.0;
        }

        // binary asset-or-nothing payoff ?
        if (strikedTypePayoff instanceof AssetOrNothingPayoff) {
            final AssetOrNothingPayoff aoo = (AssetOrNothingPayoff) strikedTypePayoff;
            if (inTheMoney) {
                K = spot;
                DKDstrike = 0.0;
            } else {
                K = aoo.strike();
                DKDstrike = 1.0;
            }
        }
    }


    //
    // public methods
    //

    public /* @Real */ double value() /* @ReadOnly */ {
        /* @Real */ final double result = K * (forward * alpha + X * beta);
        return result;
    }

    public double delta() /* @ReadOnly */{
        final double tempDelta = - spot * stdDev;
        final double DalphaDs = DalphaDd1/tempDelta;
        final double DbetaDs  = DbetaDd2/tempDelta;

        double DforwardDs, DXDs;
        if (inTheMoney) {
            DforwardDs = 0.0;
            DXDs       = 0.0;
        } else {
            DforwardDs = -muPlusLambda  * forward / spot;
            DXDs       = -muMinusLambda * X       / spot;
        }

        final double delta = K * (
                DalphaDs * forward + alpha * DforwardDs
                + DbetaDs  * X       + beta  * DXDs
        );
        return delta;
    }

    public double gamma() /* @ReadOnly */{

        final double tempDelta = - spot * stdDev;
        final double DalphaDs = DalphaDd1/tempDelta;
        final double DbetaDs  = DbetaDd2/tempDelta;
        final double D2alphaDs2 = -DalphaDs/spot*(1-D1/stdDev);
        final double D2betaDs2  = -DbetaDs /spot*(1-D2/stdDev);

        double DforwardDs, DXDs, D2forwardDs2, D2XDs2;
        if (inTheMoney) {
            DforwardDs = 0.0;
            DXDs       = 0.0;
            D2forwardDs2 = 0.0;
            D2XDs2       = 0.0;
        } else {
            DforwardDs = -muPlusLambda  * forward / spot;
            DXDs       = -muMinusLambda * X       / spot;
            D2forwardDs2 = muPlusLambda  * forward / (spot*spot)*(1+muPlusLambda);
            D2XDs2       = muMinusLambda * X       / (spot*spot)*(1+muMinusLambda);
        }

        final double gamma = K * (
                D2alphaDs2 * forward   + DalphaDs * DforwardDs
                + DalphaDs   * DforwardDs + alpha   * D2forwardDs2
                + D2betaDs2  * X         + DbetaDs  * DXDs
                + DbetaDs    * DXDs       + beta    * D2XDs2
        );

        return gamma;
    }

    public double rho(final /* @Time */ double maturity) /* @ReadOnly */ {
        QL.require(maturity > 0.0 , "negative maturity not allowed"); // QA:[RG]::verified // TODO: message
        final double DalphaDr = -DalphaDd1/(lambda*stdDev) * (1.0 + mu);
        final double DbetaDr  =  DbetaDd2 /(lambda*stdDev) * (1.0 + mu);
        double DforwardDr, DXDr;
        if (inTheMoney) {
            DforwardDr = 0.0;
            DXDr       = 0.0;
        } else {
            DforwardDr = forward * (1.0+(1.0+mu)/lambda) * log_H_S / variance;
            DXDr       = X       * (1.0-(1.0+mu)/lambda) * log_H_S / variance;
        }

        return maturity * K * (
                DalphaDr * forward
                + alpha   * DforwardDr
                + DbetaDr  * X
                + beta    * DXDr);
    }

}
TOP

Related Classes of org.jquantlib.pricingengines.AmericanPayoffAtHit

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.