Skip to content

Estimating Greeks of Option Pricing Model

In this example, we use GradInf to develop a new gradient estimator for a discrete stochastic model of option pricing. The model simulates the price of an asset in accordance with the trinomial option pricing model, and returns the expected payoff of a European-style option.

Writing the Model

First, we define the model below.

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE RebindableSyntax #-}

import Prelude
import Control.Monad
import Control.Monad.Bayes.Sampler.Lazy
import Data.Functor.Identity
import Control.Applicative
import System.Random (setStdGen, mkStdGen)

import Numeric.GradInf
import Numeric.GradInf.Inference.Base
import Numeric.GradInf.Primitives.DeterministicPrimitives
import Numeric.GradInf.Primitives.CategoricalCRN
import Numeric.GradInf.Primitives.IterateP

-- $setup (use a fixed seed)
-- >>> setStdGen (mkStdGen 42)

optionPricingKernel :: forall m d i b mat.
    (DeterministicPrimitives d i b mat, CategoricalCRN m d d)
    => [d] -> d -> m d
optionPricingKernel theta x = do
    let r = theta !! 0
    let sigma = theta !! 1
    let deltaT = theta !! 2
    let denom = (exp (sigma * (sqrt (deltaT / 2)))
                 - exp (-sigma * (sqrt (deltaT / 2))))
    let sqrtPU = (exp (r * deltaT / 2)
                  - exp (-sigma * (sqrt (deltaT / 2))))
                 / denom
    let sqrtPD = (exp (sigma * (sqrt (deltaT / 2)))
                  - exp (r * deltaT / 2))
                 / denom
    let pU = sqrtPU * sqrtPU
    let pD = sqrtPD * sqrtPD
    let pM = 1 - pU - pD
    let u = exp (sigma * (sqrt (2 * deltaT)))
    let d = exp (-sigma * (sqrt (2 * deltaT)))
    x' <- categoricalCRN ([pU, pD, pM], [x * u, x * d, x])
    return x'

optionPricingModel :: forall m d i b mat.
    ( DeterministicPrimitives d i b mat
    , CategoricalCRN m d d, IterateP m d )
    => Int -> [d] -> m d
optionPricingModel n theta = do
    x <- iterateP (optionPricingKernel theta) 40 !! n
    let r = theta !! 0
    let deltaT = theta !! 2
    let t = fromIntegral n * deltaT
    return $ if (isGreater x 41) :: b then
        exp(-r * t) * (x - 41)
    else
        0

Differentiating the Model

We now apply GradInf to compute gradient estimates. For our inference algorithm, we will use twisted Sequential Monte Carlo, supplying a simple twist function that uses the perturbation in the current option pricing value as an estimate of the perturbation in the final option pricing value.

twistFunc :: (Num d) => Int -> Coupled d -> d
twistFunc _ (Coupled (xL, xR)) = xR - xL

getGradientEstimates :: Sampler [Double]
getGradientEstimates = do
    let n = 50
    let deltaT = 0.5 / n
    replicateM 5 (fmap runIdentity (gradInfAD
      (optionPricingModel 50
        . (\r -> [r, 0.05, fromDouble deltaT])
        . runIdentity)
      (twistedSMCInferenceAlg twistFunc 1
        :: forall d i b. (Num d)
        => TwistedSMCInferenceAlg Coupling d i b)
      (Identity 0.15)))

-- |
-- >>> sampler getGradientEstimates
-- [16.859912080819587,13.889656771799789,23.11158408562197,6.798670922887533,10.00694667989766]

We can now calculate the empirical mean and variance of the gradient estimator.

meanAndVariance :: [Double] -> (Double, Double)
meanAndVariance xs = do
    let n = length xs
    let mu = (sum xs) / (fromIntegral n)
    let var = (sum (map (\x -> (x - mu) * (x - mu)) xs))
              / (fromIntegral (n - 1))
    (mu, var)

printMeanAndVariance :: IO ()
printMeanAndVariance = do
    samples <- sampler getGradientEstimates
    print (meanAndVariance samples)

-- |
-- >>> printMeanAndVariance
-- (14.133354108205307,39.73173399763917)