-
Notifications
You must be signed in to change notification settings - Fork 1
/
ExplicitEuler.lhs
161 lines (131 loc) · 5.47 KB
/
ExplicitEuler.lhs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
\documentclass[12pt]{article}
%include polycode.fmt
\usepackage[pdftex,pagebackref,letterpaper=true,colorlinks=true,pdfpagemode=none,urlcolor=blue,linkcolor=blue,citecolor=blue,pdfstartview=FitH]{hyperref}
\usepackage{amsmath,amsfonts}
\usepackage{graphicx}
\usepackage{color}
\setlength{\oddsidemargin}{0pt}
\setlength{\evensidemargin}{0pt}
\setlength{\textwidth}{6.0in}
\setlength{\topmargin}{0in}
\setlength{\textheight}{8.5in}
\setlength{\parindent}{0in}
\setlength{\parskip}{5px}
\newcommand{\half}{\frac{1}{2}}
\newcommand{\cobind}{\mathbin{=\mkern-6.7mu>\!\!\!>}}
%format =>> = "\cobind "
\begin{document}
Can we get better performance for pricing financial options?
First let us translate our pricer using the
\href{http://idontgetoutmuch.wordpress.com/2012/04/01/solving-a-partial-differential-equation-comonadically/}{Explicit
Euler Method} to use \href{http://repa.ouroborus.net}{repa}.
\begin{code}
{-# LANGUAGE FlexibleContexts, TypeOperators #-}
{-# OPTIONS_GHC -Wall -fno-warn-name-shadowing -fno-warn-type-defaults #-}
import Data.Array.Repa as Repa
import Data.Array.Repa.Eval
import Control.Monad
r, sigma, k, t, xMax, deltaX, deltaT :: Double
m, n, p :: Int
r = 0.05
sigma = 0.2
k = 50.0
t = 3.0
m = 80
p = 2
xMax = 150
deltaX = xMax / (fromIntegral m)
n = 800
deltaT = t / (fromIntegral n)
data PointedArrayU a = PointedArrayU Int (Array U DIM1 a)
deriving Show
singleUpdaterP :: PointedArrayU Double -> Double
singleUpdaterP (PointedArrayU j _x) | j == 0 = 0.0
singleUpdaterP (PointedArrayU j _x) | j == m = xMax - k
singleUpdaterP (PointedArrayU j x) = a * x! (Z :. j-1) +
b * x! (Z :. j) +
c * x! (Z :. j+1)
where
a = deltaT * (sigma^2 * (fromIntegral j)^2 - r * (fromIntegral j)) / 2
b = 1 - deltaT * (r + sigma^2 * (fromIntegral j)^2)
c = deltaT * (sigma^2 * (fromIntegral j)^2 + r * (fromIntegral j)) / 2
priceAtTP :: PointedArrayU Double
priceAtTP = PointedArrayU 0 (fromListUnboxed (Z :. m+1)
[ max 0 (deltaX * (fromIntegral j) - k) | j <- [0..m] ])
coBindU :: (Source U a, Source U b, Target U b, Monad m) =>
PointedArrayU a -> (PointedArrayU a -> b) -> m (PointedArrayU b)
coBindU (PointedArrayU i a) f = computeP newArr >>= return . PointedArrayU i
where
newArr = traverse a id g
where
g _get (Z :. j) = f $ PointedArrayU j a
testN :: Int -> IO (PointedArrayU Double)
testN n = h priceAtTP
where
h = foldr (>=>) return
(take n $ Prelude.zipWith flip (repeat coBindU) (repeat singleUpdaterP))
\end{code}
So far so good but this has not bought us very much over using {\em
Data.Array} or {\em Data.Vector}. Morevoer we have not been able to
use the costate comonad because of the restrictions on types imposed
by {\em repa}.
Let us re-write the above without even attempting to mimic the cobind
operator. Thus we no longer need our pointed array.
\begin{code}
singleUpdater :: Array D DIM1 Double -> Array D DIM1 Double
singleUpdater a = traverse a id f
where
Z :. m = extent a
f _get (Z :. ix) | ix == 0 = 0.0
f _get (Z :. ix) | ix == m-1 = xMax - k
f get (Z :. ix) = a * get (Z :. ix-1) +
b * get (Z :. ix) +
c * get (Z :. ix+1)
where
a = deltaT * (sigma^2 * (fromIntegral ix)^2 - r * (fromIntegral ix)) / 2
b = 1 - deltaT * (r + sigma^2 * (fromIntegral ix)^2)
c = deltaT * (sigma^2 * (fromIntegral ix)^2 + r * (fromIntegral ix)) / 2
priceAtT :: Array U DIM1 Double
priceAtT = fromListUnboxed (Z :. m+1) [max 0 (deltaX * (fromIntegral j) - k) | j <- [0..m]]
\end{code}
Now suppose we want to price a spot ladder then we would like to apply the same update function to slightly modified payoffs (the terminal boundary condition). First let's utilise our single update function to work over two dimensional grid.
\begin{code}
multiUpdater :: Source r Double => Array r DIM2 Double -> Array D DIM2 Double
multiUpdater a = fromFunction (extent a) f
where
f :: DIM2 -> Double
f (Z :. ix :. jx) = (singleUpdater x)!(Z :. ix)
where
x :: Array D DIM1 Double
x = slice a (Any :. jx)
\end{code}
We define our spot ladder on a call to be many calls each with a
strike differing by 10 basis points (a basis point is one hundredth of
one percent).
\begin{code}
priceAtTMulti :: Array U DIM2 Double
priceAtTMulti = fromListUnboxed (Z :. m+1 :. p+1)
[ max 0 (deltaX * (fromIntegral j) - (k + (fromIntegral l)/1000.0))
| j <- [0..m]
, l <- [0..p]
]
\end{code}
Now we can test our spot ladder pricer. Note that we force the
evaluation of our two dimensional array at each time step. If we do
not do that then we end up with a leak as presumably {\em repa} tries to
fuse many updates. Moreover fusing updates across time steps does not
really buy us anything.
\begin{code}
testMulti :: IO (Array U DIM2 Double)
testMulti = updaterM priceAtTMulti
where
updaterM :: Monad m => Array U DIM2 Double -> m (Array U DIM2 Double)
updaterM = foldr (>=>) return (replicate n (computeP . multiUpdater))
pickAtStrike :: Monad m => Int -> Array U DIM2 Double -> m (Array U DIM1 Double)
pickAtStrike n t = computeP $ slice t (Any :. n :. All)
main :: IO ()
main = do t <- testMulti
vStrikes <- pickAtStrike 27 t
putStrLn $ show vStrikes
\end{code}
\end{document}