#!/usr/bin/python

"""
 Copyright (C) 2000, 2001, 2002 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 ferdinando@ametrano.net
 The license is also available online at http://quantlib.org/html/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.
"""

__version__ = "$Revision: 1.12 $"
# $Source: /cvsroot/quantlib/QuantLib-Python/QuantLib/test/binary_option.py,v $

import QuantLib
import unittest
from math import exp


def relErr(x1, x2, reference):
    if reference != 0.0:
        return abs(x1-x2)/reference
    else:
        return 10e10

class BinaryOptionTest(unittest.TestCase):
    def runTest(self):
        "Testing binary option pricer"
        pricer = QuantLib.BinaryOption

        rangeUnder = [100]
        rangeQrate = [0.04, 0.05, 0.06]
        rangeResTime = [1.0]
        rangeStrike = [50, 99.5, 100, 100.5, 150]
        rangeVol = [ 0.11, 0.5, 1.2]
        rangeRrate = [ 0.01, 0.05, 0.15]

        err_delta = 5e-5
        err_gamma = 5e-5
        err_theta = 5e-5
        err_rho  =  5e-5
        err_divRho= 5e-5
        err_vega =  5e-5

        for typ in ['Call','Put','Straddle']:
          for under in rangeUnder:
            for Qrate in rangeQrate:
              for resTime in rangeResTime:
                for Rrate in rangeRrate:
                  for strike in rangeStrike:
                    for vol in rangeVol:
                      #Check Greeks
                      dS = under/10000.0
                      dT = resTime/10000.0
                      dVol = vol/10000.0
                      dR = Rrate/10000.0
                      dQ = Qrate/10000.0
                      opt = pricer(typ,under,strike,Qrate,Rrate,resTime,vol)
                      opt_val = opt.value()
                      if opt_val > 1e-6:
                        optPs = pricer(typ, under+dS, strike, Qrate,
                          Rrate,    resTime ,   vol)
                        optMs = pricer(typ, under-dS, strike, Qrate,
                          Rrate,    resTime ,   vol)
                        optPt = pricer(typ, under   , strike, Qrate,
                          Rrate,    resTime+dT, vol)
                        optMt = pricer(typ, under   , strike, Qrate,
                          Rrate,    resTime-dT, vol)
                        optPr = pricer(typ, under   , strike, Qrate,
                          Rrate+dR, resTime   , vol)
                        optMr = pricer(typ, under   , strike, Qrate,
                          Rrate-dR, resTime   , vol)
                        optPq = pricer(typ, under   , strike, Qrate+dQ,
                          Rrate, resTime   , vol)
                        optMq = pricer(typ, under   , strike, Qrate-dQ,
                          Rrate, resTime   , vol)
                        optPv = pricer(typ, under   , strike, Qrate,
                          Rrate   , resTime   , vol+dVol)
                        optMv = pricer(typ, under   , strike, Qrate,
                          Rrate   , resTime   , vol-dVol)

                        deltaNum = (optPs.value()-optMs.value())/(2*dS)
                        gammaNum = (optPs.delta()-optMs.delta())/(2*dS)
                        thetaNum =-(optPt.value()-optMt.value())/(2*dT)
                        rhoNum   = (optPr.value()-optMr.value())/(2*dR)
                        divRhoNum= (optPq.value()-optMq.value())/(2*dQ)
                        vegaNum  = (optPv.value()-optMv.value())/(2*dVol)

                        delta  = opt.delta()
                        gamma  = opt.gamma()
                        theta  = opt.theta()
                        rho    = opt.rho()
                        divRho = opt.dividendRho()
                        vega   = opt.vega()

                        if not (relErr(delta,deltaNum,under)<=err_delta
                                and relErr(gamma,gammaNum,under)<=err_gamma
                                and relErr(theta,thetaNum,under)<=err_theta
                                and relErr(rho,  rhoNum,  under)<=err_rho
                                and relErr(divRho,divRhoNum,under)<=err_divRho
                                and relErr(vega, vegaNum, under)<=err_vega):
                            self.fail("""
Option details: %(typ)s %(under)f %(strike)f %(Qrate)f %(Rrate)f %(resTime)f %(vol)f
    value  = %(opt_val)+9.5f
    delta  = %(delta)+9.5f, deltaNum  = %(deltaNum)+9.5f
    gamma  = %(gamma)+9.5f, gammaNum  = %(gammaNum)+9.5f
    theta  = %(theta)+9.5f, thetaNum  = %(thetaNum)+9.5f
    rho    = %(rho)+9.5f, rhoNum  = %(rhoNum)+9.5f
    divRho = %(divRho)+9.5f, divRhoNum = %(divRhoNum)+9.5f
    vega   = %(vega)+9.5f, vegaNum   = %(vegaNum)+9.5f
	                        """ % locals())


if __name__ == '__main__':
    print 'testing QuantLib', QuantLib.__version__, QuantLib.QuantLibc.__file__, QuantLib.__file__
    import sys
    suite = unittest.TestSuite()
    suite.addTest(BinaryOptionTest())
    if sys.hexversion >= 0x020100f0:
        unittest.TextTestRunner(verbosity=2).run(suite)
    else:
        unittest.TextTestRunner().run(suite)
    raw_input('press any key to continue')

