Using the Parallel Computing Toolbox and the parfor loop in Matlab for Finance

By on February 16th, 2015

If you are familiar with Matlab you might have come across a loop-structure called a parfor-loop. If you have not, I hope this post can help you speed up some of your code.

Strictly speaking, the parfor loop allows a multi-core computer to run several computations simultaneously by creating simultaneous instances of Matlab, called “workers”, that are capable of executing commands in parallel.

So how do you use the parfor-loop? Exactly the same way as a normal loop, more or less. I need to emphasize “more or less” here, because for the most part there are no structural differences between the two procedures, other than the parfor only support incrementing using non-decreasing integers.

In terms of variable handling there are some more noticeable differences. If we are only dealing with indexed variables the two will for the most part work similarly. Nonindexed variables on the other hand are not dealt with equally. In fact all nonindexed variables are considered temporary under the parfor, which means they are have the value 0 when the loop is terminated.

Furthermore, there cannot be dependencies throughout the loop, i.e. we cannot increment a variable inside a loop for instance, since the loop will not be executed in order and therefore such a procedure would not make much sense when running parallel computations.

Let us look some examples:

Example 1:

Say we have a set of n tickers for which we want to obtain historical stock prices using the fetch-command from the Datafeed toolbox. Some sample code is provided below:

parpool('local')
tickers = {'AAPL';'BAC';'YHOO';'AMZN';'MSFT';'C';'F';'GILD';'T';'XOM';'INTC';'CSCO';'NFLX';'MU';'PBR';'JCP';'BBRY'};

t = datenum(['01/01/14']) %Start time
T = datenum(['12/01/15']) %End time

%% For-loop
for i = 1:length(tickers)
 stock_data = fetch(yahoo,tickers(i),'Adj Close',t,T,'m'); %Fetching data
 prices_par(:,i) = stock_data(:,2);
end

%% Parfor-loop
parfor i = 1:length(tickers)
 stock_data = fetch(yahoo,tickers(i),'Adj Close',t,T,'m'); %Fetching data
 prices_parfor(:,i) = stock_data(:,2);
end

Here the pool is initiated using the parpool(‘local’)-command, and it should be noted that only one pool can be running at a time, which means that in the case a pool is already running this will throw an error. It is therefore also possible to use the gcp()-command, which stands for “get current pool”, to check whether a pool is running and if not, start one.

In terms of the speed of these loops the parfor will perform relatively better, depending on the number of cores your computer have. For instance, with a quad-core computer you can expect the parfor loop to complete in at least 1/4th of the time it takes the normal for loop to do the same. However, in this case a lot also depend on the connection to the database you are using.

Example 2:

Another great way of using the parfor loop is in Monte Carlo simulations. Below is some sample code simulating stock price paths for the Heston model (simulating standard Geometric Brownian Motions can be done very quickly by using cumsum(), so a for-loop isn’t needed at all in this case).

gcp()

randn('state',2)
n = 100; % Timesteps
m = 20000; % Paths

S0 = 100; % Initial Stock price
V0 = 0.25; % Initial variance
dt = 1/252; % Size of time step
r = 0.05; % Drift
k = 5; % Reversion speed
theta = 0.2; % Long-term variance
eta = 0.1; % Volatility of variance
rho = -0.8; % Correlation

parfor i = 1:m
 S = S0;
 V = V0;
 vrand = randn(1,n);
 srand = randn(1,n);
 for j = 1:n
 dS = r*S*dt + sqrt(V)*S*sqrt(dt)*srand(j);
 dV = k*(theta - V)*dt + eta*sqrt(V)*sqrt(dt)*(rho*srand(j) + sqrt(1-rho^2)*vrand(j));

 S = S + dS;
 V = V + dV;

 variance(i,j) = V;
 price(i,j) = S;
 end
end
plot(price')

On my machine, running this code takes about 3 seconds, whereas the normal for-loop takes about 70 seconds.

Note that the parfor cannot be added to the inner loop. The reason of course is that there is dependency throughout the inner loop since the change dS depends on the previous value of S and V, which means the computations cannot be split up into parts.

Example 3:

Another application the parfor is particularly convenient for is when applying some sort of function along a vector or matrix. Say you used the fetch()-command from example 1 and you wanted to translate the column of date-numbers it returns to their respective dates written as a date-string:

gcp();

t = datenum(['01/01/04'])
T = datenum(['12/01/15'])

stock_data = fetch(yahoo,'BAC','Adj Close',t,T,'w'); %Fetching data

parfor i = 1:length(stock_data)
 date{i} = datestr(stock_data(i,1));
end

Since there are no dependencies throughout the loop the parfor splits the loop into parts and executes it simultaneously. For this simple file the gain isn’t really significant, but for large matrices or vectors the parfor can provide massive time saving.

Then as a summary, the areas I have personally found the most use for the parfor is when:

  1. Collecting and reading in data from files or other external sources
  2. Simulating time-series using Monte Carlo
  3. Applying some function to an array of variables.

As demonstrated, using the parallel-computing toolbox can significantly speed up computations and reduce lead-time in trading.

Lastly, if you want more info on the parfor-loop I suggest checking out this link.

Leave a Reply

Your email address will not be published. Required fields are marked *