## Running Back-tests in parallel

Once you start experimenting with many different asset allocation algorithms, the computation time of running the back-tests can be substantial. One simple way to solve the computation time problem is to run the back-tests in parallel. I.e. if the asset allocation algorithm does not use the prior period holdings to make decision about current allocation, we can run many periods in parallel.

In the Update for Backtesting Asset Allocation Portfolios post, I show cased the portfolio.allocation.helper() function in strategy.r at github. The portfolio.allocation.helper() function is a user-friendly interface to evaluate multiple asset allocation algorithms over given asset universe in a sequential fashion.

Following is a sample code from the Update for Backtesting Asset Allocation Portfolios post:

#***************************************************************** # Code Strategies #****************************************************************** obj = portfolio.allocation.helper(data$prices, periodicity = 'months', lookback.len = 60, min.risk.fns = list( EW=equal.weight.portfolio, RP=risk.parity.portfolio, MD=max.div.portfolio, MV=min.var.portfolio, MVE=min.var.excel.portfolio, MV2=min.var2.portfolio, MC=min.corr.portfolio, MCE=min.corr.excel.portfolio, MC2=min.corr2.portfolio, MS=max.sharpe.portfolio(), ERC = equal.risk.contribution.portfolio, # target retunr / risk TRET.12 = target.return.portfolio(12/100), TRISK.10 = target.risk.portfolio(10/100), # rso RSO.RP.5 = rso.portfolio(risk.parity.portfolio, 5, 500), # others MMaxLoss = min.maxloss.portfolio, MMad = min.mad.portfolio, MCVaR = min.cvar.portfolio, MCDaR = min.cdar.portfolio, MMadDown = min.mad.downside.portfolio, MRiskDown = min.risk.downside.portfolio, MCorCov = min.cor.insteadof.cov.portfolio ) )

To run the same strategies in parallel, I created the portfolio.allocation.helper.parallel() function in strategy.r at github. There is one extra input that you need to specify: cores – number of CPU processors used for computations.

For example, the code below will use 2 CPU processors to run back-test computations. It will run faster than the portfolio.allocation.helper() function.

#***************************************************************** # Code Strategies #****************************************************************** obj = portfolio.allocation.helper.parallel(cores = 2, data$prices, periodicity = 'months', lookback.len = 60, min.risk.fns = list( EW=equal.weight.portfolio, RP=risk.parity.portfolio, MD=max.div.portfolio, MV=min.var.portfolio, MVE=min.var.excel.portfolio, MV2=min.var2.portfolio, MC=min.corr.portfolio, MCE=min.corr.excel.portfolio, MC2=min.corr2.portfolio, MS=max.sharpe.portfolio(), ERC = equal.risk.contribution.portfolio, # target retunr / risk TRET.12 = target.return.portfolio(12/100), TRISK.10 = target.risk.portfolio(10/100), # rso RSO.RP.5 = rso.portfolio(risk.parity.portfolio, 5, 500), # others MMaxLoss = min.maxloss.portfolio, MMad = min.mad.portfolio, MCVaR = min.cvar.portfolio, MCDaR = min.cdar.portfolio, MMadDown = min.mad.downside.portfolio, MRiskDown = min.risk.downside.portfolio, MCorCov = min.cor.insteadof.cov.portfolio ) )

Hopefully, I did not ruin your prolong lunch plansðŸ™‚

Hey Michael,

Nice post. One question:

Would it make sense to “parallelized” the standard backtest loops that you used to use (loop through each rebalancing month)? I am asking because I am not sure if lookback dependencies (given a current rebalancing month t, get t-120 history) violate cluster computing principles whereby each task needs to be similar such that when the master (in a master slave architecture) aggregates the results, they make sense. I personally don’t think it violates this because each rebalancing month can be individual tasks that can be passed on to N different workers. All that is needed is the time stamp such that the master can sort afterwards. I may be wrong. Thoughts?

Cheers,

M

Hi Michael,

Thanks for this improvement.

On my system, the execution of this code produces an error Error in { :

task 1 failed – “could not find function “portfolio.allocation.helper””

In my experience of “parallelizing” R tasks, I had to reload the libraries (in this case it would be sit.gz) in each and every parallel process created. I did not see this reloading instruction in your code. Am I missing something ?

Thanks,

Pierre