Fast Simulations with Compile

You are trying to implement Euler-Maruyama simulation method for a 2-stage short-term interest rate model which is given by the following system of SDEs: $$\begin{eqnarray} \mathrm{d} \theta_t &=& -\lambda_\theta \left( \theta_t - \bar\theta\right) \mathrm{d}t + \sigma_\theta \mathrm{d}W_{\theta,t} \\ \mathrm{d} \pi_t &=& -\lambda_\pi\left( \theta_t - \pi_t \right) \mathrm{d}t + \sigma_\pi \mathrm{d} W_{\pi,t} \end{eqnarray} $$ where $W_\theta$ and $W_\pi$ are independent standard Wiener processes.

Here is the compiled code implementing the above.

cfEM = Compile[{{lambdaTheta, _Real}, {thetaBar, _Real}, {sigmaTheta, \
_Real}, {lambdaPi, _Real}, {sigmaPi, _Real}, {th0, _Real}, {pi0, \
_Real}, {dt, _Real}, {steps, _Integer}}, 
   Module[{zs, bag, thc, pic, zths, zpis},
    zths = RandomReal[NormalDistribution[0, Sqrt[dt]], steps];
    zpis = RandomReal[NormalDistribution[0, Sqrt[dt]], steps];
    thc = th0; pic = pi0;
    bag = Internal`Bag[{thc, pic}];
    Do[
     pic += -lambdaPi dt (pic - thc) + sigmaPi zpis[[k]];
     thc += -lambdaTheta dt (thc - thetaBar) + sigmaTheta zths[[k]];
     Internal`StuffBag[bag, {thc, pic}, 1];
     , {k, 1, steps}];
    Partition[Internal`BagPart[bag, All], 2]
    ]
   ];

Call example:

In[14]:= AbsoluteTiming[
 Length[data = cfEM[0.07, 2., 1.2, 1, 1.25, 2., 2., 1/252., 5 252]]]

Out[14]= {0., 1261}

enter image description here

Note, however, that Euler-Maruyama method is only approximate, while your system of SDEs is Gaussian. An exact simulation method, known as Ozaki method, is available and is not hard to code. See this book for the details of the 1D case. You would need to generalize it to the 2D case, but it is not very hard.


There are two areas for optimization that I see here.

The first, if possible, is to generate all your random data in advance and then access it with an incrementing index, e.g. list[[i++]].

The second is to partially evaluate the definitions of thetaNext and piNext for a given set of parameters.

A note: Random has been deprecated for some time now and may produce inferior results. You should be using RandomReal/RandomInteger in version 7 or RandomVariate in version 8.

Update

Here is a cleaner implementation of my recommendations. Should this be inapplicable my original code is visible in the edit history of this post.

Given your definitions and parameters:

thetaNext[thetaNow_] := 
  thetaNow + (-lambdaTheta*(thetaNow - thetaBar)*deltaT + 
     sigmaTheta*norTheta[0, 1]*Sqrt[deltaT]);

piNext[piNow_, thetaNow_] := 
  piNow + (-lambdaPi*(piNow - thetaNow)*deltaT + 
     sigmaPi*norPi[0, 1]*Sqrt[deltaT]);

lambdaTheta = 0.07; sigmaTheta = 1.2; thetaBar = 2; lambdaPi = 1.0;
sigmaPi = 1.25; deltaT = 1/12;
steps = 15000;
T = 5;
deltaT = 1/steps; // N
Maturity = T*steps;

We can reduce the core of your NestList function (for these specific parameters) as follows:

func =
 Block[{norTheta, norPi, Part},
  Function @@ FullSimplify @ {
     {piNext[#[[1]], #[[2]]], thetaNext[#[[2]]]} /.
      {norPi[0, 1] -> #2[[1]], norTheta[0, 1] -> #2[[2]]}
    }
 ]
{0.916667 #1[[1]] + 0.0833333 #1[[2]] + 0.360844 #2[[1]], 0.0116667 + 0.994167 #1[[2]] + 0.34641 #2[[2]]} &

We will use the second argument of this function to insert the random data, which we create with:

piList = RandomReal[NormalDistribution[0, 1], Maturity];
thetaList = RandomReal[NormalDistribution[0, 1], Maturity];
rands = {piList, thetaList}\[Transpose];

(A shorter form exists for this specific data but I am trying to retain some generality.)

And which we use FoldList to provide:

FoldList[func, {2, 2}, rands]\[Transpose] // Timing // First

0.219

By comparison your code takes 4.368 seconds on my machine with the same parameters.