module Quant.ContingentClaim (
ContingentClaim (..)
, ContingentClaim1
, ContingentClaim2
, ContingentClaim3
, ContingentClaim4
, CCProcessor (..)
, OptionType (..)
, CashFlow (..)
, CCBuilder
, specify
, monitor
, monitor1
, monitor2
, monitor3
, monitor4
, monitor5
, vanillaOption
, binaryOption
, straddle
, arithmeticAsianOption
, geometricAsianOption
, callSpread
, putSpread
, forwardContract
, zcb
, fixedBond
, multiplier
, short
, combine
, terminalOnly
) where
import Control.Monad.Reader
import Control.Monad.Writer.Strict
import Quant.Types
import Quant.Time
import qualified Data.Map as M
newtype ContingentClaim a = ContingentClaim { unCC :: [CCProcessor a] }
type ContingentClaim1 = forall a . Obs1 a => ContingentClaim a
type ContingentClaim2 = forall a . Obs2 a => ContingentClaim a
type ContingentClaim3 = forall a . Obs3 a => ContingentClaim a
type ContingentClaim4 = forall a . Obs4 a => ContingentClaim a
instance Monoid (ContingentClaim a) where
mempty = ContingentClaim []
mappend = combine
data CCProcessor a = CCProcessor {
monitorTime :: Time
, payoutFunc :: [M.Map Time a -> CashFlow]
}
type CCBuilder w r a = WriterT w (Reader r) a
monitor :: Obs1 a => Time -> CCBuilder (ContingentClaim a) (M.Map Time a) Double
monitor = monitor1
monitor1 :: Obs1 a => Time -> CCBuilder (ContingentClaim a) (M.Map Time a) Double
monitor1 = monitorGeneric get1
monitor2 :: Obs2 a => Time -> CCBuilder (ContingentClaim a) (M.Map Time a) Double
monitor2 = monitorGeneric get2
monitor3 :: Obs3 a => Time -> CCBuilder (ContingentClaim a) (M.Map Time a) Double
monitor3 = monitorGeneric get3
monitor4 :: Obs4 a => Time -> CCBuilder (ContingentClaim a) (M.Map Time a) Double
monitor4 = monitorGeneric get4
monitor5 :: Obs5 a => Time -> CCBuilder (ContingentClaim a) (M.Map Time a) Double
monitor5 = monitorGeneric get5
monitorGeneric :: (a -> Double) -> Time -> CCBuilder (ContingentClaim a) (M.Map Time a) Double
monitorGeneric f t = do
tell $ ContingentClaim [CCProcessor t []]
m <- ask
return $ f (m M.! t)
specify :: CCBuilder (ContingentClaim a) (M.Map Time a) CashFlow -> ContingentClaim a
specify x = w `mappend` ContingentClaim [CCProcessor lastW [f]]
where
w = runReader (execWriterT x) M.empty
f = runReader . liftM fst $ runWriterT x
lastW = last0 . map monitorTime $ unCC w
last0 [] = Time 0
last0 [y] = y
last0 (_:ys) = last0 ys
vanillaPayout :: OptionType
-> Double
-> Double
-> Double
vanillaPayout pc strike x = case pc of
Put -> max (strike x) 0
Call -> max (x strike) 0
binaryPayout :: OptionType
-> Double
-> Double
-> Double
-> Double
binaryPayout pc strike amount x = case pc of
Put -> if strike > x then amount else 0
Call -> if x > strike then amount else 0
terminalOnly :: Obs1 a => Time -> (Double -> Double) -> ContingentClaim a
terminalOnly t g = specify $ do
x <- monitor t
return $ CashFlow t $ g x
vanillaOption :: Obs1 a => OptionType -> Double -> Time -> ContingentClaim a
vanillaOption pc strike t = terminalOnly t $ vanillaPayout pc strike
binaryOption :: Obs1 a => OptionType -> Double -> Double -> Time -> ContingentClaim a
binaryOption pc strike amount t = terminalOnly t $ binaryPayout pc strike amount
arithmeticAsianOption :: Obs1 a => OptionType -> Double -> [Time] -> Time -> ContingentClaim a
arithmeticAsianOption pc strike obsTimes t = specify $ do
x <- mapM monitor obsTimes
let avg = sum x / fromIntegral (length obsTimes)
return $ CashFlow t $ vanillaPayout pc strike avg
geometricAsianOption :: Obs1 a => OptionType -> Double -> [Time] -> Time -> ContingentClaim a
geometricAsianOption pc strike obsTimes t = specify $ do
x <- mapM monitor obsTimes
let avg = product x ** (1 / fromIntegral (length obsTimes))
return $ CashFlow t $ vanillaPayout pc strike avg
multiplier :: Double -> ContingentClaim a -> ContingentClaim a
multiplier notional cs = ContingentClaim $ map f (unCC cs)
where f (CCProcessor t g) = CCProcessor t $ (fmap (scale.)) g
scale (CashFlow dt amt) = CashFlow dt (amt*notional)
short :: ContingentClaim a -> ContingentClaim a
short = multiplier (1)
zcb :: Time -> Double -> ContingentClaim a
zcb t amt = specify $ return $ CashFlow t amt
fixedBond :: Double -> Double -> Double -> Int -> ContingentClaim a
fixedBond faceVal intRate freq pmts = zcb (Time $ fromIntegral pmts * freq) faceVal
<> mconcat (map f [1..pmts])
where
f x = zcb (Time $ fromIntegral x * freq) (faceVal * intRate * freq)
forwardContract :: Obs1 a => Time -> ContingentClaim a
forwardContract t = specify $ do
x <- monitor t
return $ CashFlow t x
callSpread :: Obs1 a => Double -> Double -> Time -> ContingentClaim a
callSpread lowStrike highStrike t = mappend (vanillaOption Call lowStrike t)
(short $ vanillaOption Call highStrike t)
putSpread :: Obs1 a => Double -> Double -> Time -> ContingentClaim a
putSpread lowStrike highStrike t = mappend (vanillaOption Put highStrike t)
(short $ vanillaOption Put lowStrike t)
straddle :: Obs1 a => Double -> Time -> ContingentClaim a
straddle strike t = vanillaOption Put strike t <> vanillaOption Call strike t
combine :: ContingentClaim a -> ContingentClaim a -> ContingentClaim a
combine (ContingentClaim x) (ContingentClaim y) = ContingentClaim $ combine' x y
where
combine' (cc1:ccs1) (cc2:ccs2)
| monitorTime cc1 == monitorTime cc2 = let
CCProcessor t mf = cc1
CCProcessor _ mf' = cc2 in
CCProcessor t (mf++mf') : combine' ccs1 ccs2
| monitorTime cc1 > monitorTime cc2 = cc2 : combine' (cc1:ccs1) ccs2
| otherwise = cc1 : combine' ccs1 (cc2:ccs2)
combine' cs1 cs2 = cs1 ++ cs2