(* :Title: EulerSimulate *) (* :Author: Mark Fisher *) (* :Context: EulerSimulate` *) (* :Mathematica Version: 4.0 *) (* :History: Version 1.0 February 1999. Version 1.1 September 1999. Use RandomArray instead of StandardNormal. Version 1.2 June 2000. Improved compiling. Modified argument list. Added option InitialTime. Added scalar verion. *) (* :Keywords: Ito's lemma, stochastic calculus, random walk, brownian motion, option pricing, Black-Scholes *) (* :Summary: simulate SDEs via Euler approximation *) (* :Discussion: This package impliments first-order Euler simulation for any number of Ito processes with any number of arbitrarily-correlated Brownian motions. The sole function is EulerSimulate. *) (* :Requirements: This package imports the package Statistics`NormalDistribution.m *) (* :Examples: Example 1 Geometric brownian motion: sim = EulerSimulate[.2 s, .1 s, {s, 100}, {1, 10^3}]; ListPlot[sim, PlotJoined -> True] Example 2 To let time play an active role, model time explicitly as an Ito process: sim = EulerSimulate[{1, x - t}, {{0}, {1}}, {t, 0}, {x, 0}, {1, 10^3}, IncludeTime -> False]; ListPlot[sim, PlotJoined -> True] Example 3 Three "affine" state variables: mu = {5(m - r), .1( .05 - m), 1( .01 - v)}; sig = Re @ {{Sqrt[v], 0, 0}, {0, .1 Sqrt[m], 0}, {0, 0, .1 Sqrt[v]}}; sim = EulerSimulate[mu, sig, {r, .05}, {m, .05}, {v, .01}, {1, 10^3}]; Show @ GraphicsArray @ Table[ListPlot[sim[[All, {1, i}]], PlotJoined -> True, DisplayFunction -> Identity, Frame -> True, Axes -> False], {i, 2, 4}] *) BeginPackage["EulerSimulate`", {"Statistics`NormalDistribution`", "Statistics`Common`DistributionsCommon`"}] EulerSimulate::usage = "EulerSimulate[drift, diffusion, {x, x0}, {duration, nsteps}] returns a list of simulated values for the Ito process determined by the arguments. The arguments specify the drift and diffusion, the symbolic name and initial value {x, x0}, and the length of time and the number of time steps,{duration, nsteps}. The default settings for the options are Compiled -> True, IncludeTime -> True, InitialTime -> 0, and IntermediateValues -> True. A system of Ito processes can be simulated by specifying the drift as a vector and the the diffusion as a matrix, and by providing a {x, x0} pair for each process in a sequence. By default, EulerSimulate generates a matrix of orthogonal standard normal deviates; alternatively, the vector form of EulerSimulate allows the user to supply a matrix of shocks as a final optional argument." IncludeTime::usage = "IncludeTime is an option for EulerSimulate. The default setting is IncludeTime -> True." InitialTime::usage = "InitialTime is an option for Eulersimulate. The default setting is InitialTime -> 0." IntermediateValues::usage = "IntermediateValues is an option for EulerSimulate. The default setting is IntermediateValues -> True." Begin["`Private`"] Options[EulerSimulate] = {Compiled -> True, IncludeTime -> True, InitialTime -> 0, IntermediateValues -> True} (* main engine: vector version, takes shocks as argument *) EulerSimulate[drift_?VectorQ, diffusion_?MatrixQ, x:{_Symbol, _?NumericQ}.., {duration_?NumericQ, nsteps_Integer}, devs_?(MatrixQ[#, NumberQ]&), opts___?OptionQ] /; (Length[drift] == Length[diffusion] == Length[{x}] && Dimensions[devs][[2]] == Dimensions[diffusion][[2]]) := Module[{compile, time, t0, list, dt, sqrtdt, args0, args, errargs, fun, sim}, {compile, time, t0, list} = {Compiled, IncludeTime, InitialTime, IntermediateValues} /. {opts} /. Options[EulerSimulate]; sqrtdt = Sqrt[ dt = N[duration/nsteps] ]; {args, args0} = Transpose[{x}]; errargs = Unique[]& /@ Range[Last @ Dimensions[diffusion]]; fun = If[TrueQ[compile], Compile, Function] @@ {Join[args, errargs], args + drift dt + (diffusion.errargs) sqrtdt}; sim = If[TrueQ[list], FoldList, Fold] [fun @@ Join[#1, #2]&, N[args0], devs]; If[TrueQ[time] && TrueQ[list], Flatten /@ Transpose[ {Range[N[t0], t0 + duration, duration/nsteps], sim}], sim ] ] (* vector version, constructs shocks and calls main engine *) EulerSimulate[drift_?VectorQ, diffusion_?MatrixQ, x:{_Symbol, _?NumericQ}.., {duration_?NumericQ, nsteps_Integer}, opts___?OptionQ] /; (Length[drift] == Length[diffusion] == Length[{x}]) := EulerSimulate[drift, diffusion, x, {duration, nsteps}, (* make the random number matrix *) RandomArray[NormalDistribution[0,1], {nsteps, Last @ Dimensions[diffusion]}], opts] (* scalar version, calls vector version *) EulerSimulate[drift_, diffusion_, {x_Symbol, x0_?NumericQ}, {duration_?NumericQ, nsteps_Integer}, opts___?OptionQ] := If[TrueQ[IncludeTime /. {opts} /. Options[EulerSimulate]], (* then do nothing *) Identity, (* else flatten the result *) Flatten] @ EulerSimulate[{drift}, If[VectorQ[diffusion], {diffusion}, {{diffusion}}], {x, x0}, {duration, nsteps}, opts] End[] EndPackage[]