from Monte Carlo example of the Multi-Factor coupled Commodity Forward curves Simulator by Ahmos Sansom
Implementation of the Multi-Factor multi commodity forward curve simulator

MultiFactorForwardCurveSimulator(nSims, Seed, historicalForwardCurves, nDays, nFactors)
function [FCSims, AllVolFNs, volFNsCorrs, initFCs]  = MultiFactorForwardCurveSimulator(nSims, Seed, historicalForwardCurves, nDays, nFactors)

%**************************************************************************
%
% This module simulates the Clewlow and Strickland multi factor 
% forward curves for multi commodities.
%
% The calibration is automated by using the full historical dataset.
%
% Inputs:
%        nSims - number of simulations
%        Seed - seed for random number generator
%        FC - Contains historical forward data
%        nDays - number of days to simulate
%        nFactors - number of factors to use
%
% Outputs:
%        FCSims - daily FC sims
%        AllVolFNs - all the volatility functions
%        volFNsCorrs - volatility functions
%        initFCs - last FCs to start the simulations
%
% Ahmos Sansom - March 2013
%**************************************************************************

% Determine number of markets, historical prices and maturities
NMarkets = size(historicalForwardCurves,3);
NMaturities =  size(historicalForwardCurves,2);
NPrices =  size(historicalForwardCurves,1);


%% Step 1 - Calibration

% Allocate
returns = zeros(NPrices -1, NMaturities, NMarkets);
CoVEachMarket = zeros(NMaturities, NMaturities, NMarkets);
AllVolFNs = zeros(NMaturities, NMaturities, NMarkets);

for i = 1:NMarkets
    % 1.1 Calculate relative log returns
    returns(:,:, i) = price2ret(historicalForwardCurves(:,:,i),1:NPrices,'Continuous');
    
    % sometimes returns are normalised to zero mean - see discussion in
    % Carol Alexander and http://web.mit.edu/ceepr/www/publications/workingpapers/2009-002.pdf
    % Ignored here as well are seasonally affects and returns are relative,
    % i.e. aligned to front month - improvemnts can be to ensure that when
    % the contract changes, the returns are aligned accordingly, could add
    % exponential weighting...etc

    % 1.2 - PCA of each commodity
    % get covariance matrix of returns
    CoVEachMarket(:,:,i) = cov(returns(:,:,i));

    % PCA of covariance matrix of each commodity
    [coeff, latent] = pcacov(CoVEachMarket(:,:,i));
    
    %Determine volatility functions
    volFn = zeros(NMaturities, NMaturities);
    for j=1:NMaturities
        for k=1:NMaturities
            volFn(k,j) = coeff(k,j) * sqrt(latent(j,1));
        end
    end
    AllVolFNs(:,:,i) = volFn;
end

% 1.3 Determine correlation matrix across volatility functions

% The idea is to mininmise [V]_{A}[\rho][V]_{B}^{T}-[\Sigma]_{AB} = 0 to solve
% for the correlation matrix [\rho].
%
% where: [V]_{A} is the set of volatility function for market A and
% [V]_{B} is the set of volatility function for market B.
%
% The initial correlation matrix is based on the initial correlation across the factors.
% 
% See Multi-Factor Mulit-Commodity Models & Parameter Estimation Process by Lacima March 2008 that can improve the initial starting point and 
% http://web.mit.edu/ceepr/www/publications/workingpapers/2009-002.pdf for some technical explanation.

%Allocate
volFNsCorrs = zeros(NMarkets*nFactors);
tmpCorrelationMatrix = zeros(nFactors);

%Optimisation parameters - upper and lower bounds
options = optimset('TolFun',1e-10,'TolX',1e-10);
lb = -ones(nFactors);
ub = ones(nFactors);

for i=1:NMarkets
    for j=i:NMarkets
        for k=1:nFactors
            for l=1:nFactors
                if(i==j)
                    % volatility functions in the same market, so 0 or 1 (diagonal). 
                    if (k==l)
                       volFNsCorrs(nFactors*(i-1) + k, nFactors*(j-1) + l) = 1;
                    else
                       volFNsCorrs(nFactors*(i-1) + k, nFactors*(j-1) + l) = 0;
                    end
                else
                    tmpCorrelationMatrix(k, l) = corr(AllVolFNs(:,k,i),AllVolFNs(:,l,j));
                    % Determine correlation and take account of symmetry
                    volFNsCorrs(nFactors * (i-1) + k, nFactors * (j-1) + l) = tmpCorrelationMatrix(k, l);
                    volFNsCorrs(nFactors * (j-1) + l, nFactors * (i-1) + k) = volFNsCorrs(nFactors * (i-1) + k, nFactors * (j-1) + l);
                end
            end
        end
        
        % Optimise correlation matrix                
        if (i~=j )
            covAB = zeros(NMaturities);
            for k=1:NMaturities
                for l=1:NMaturities
                    covAB(k,l) = GetCov(returns(:,k, i),returns(:,l, j));
                    
                end
            end
            %covAB = returns(:,:, i)' * returns(:,:, j); % if mean is zero
            optimisedCorrs = lsqnonlin(@(x)f(x,AllVolFNs(:,1:nFactors,i),AllVolFNs(:,1:nFactors,j)',covAB),tmpCorrelationMatrix,lb,ub,options);
            
            % add the optimised correlation matrix
            for k=1:nFactors
                for l=1:nFactors
                    volFNsCorrs(nFactors * (i-1) + k, nFactors * (j-1) + l) = optimisedCorrs(k, l);
                    volFNsCorrs(nFactors * (j-1) + l, nFactors * (i-1) + k) = volFNsCorrs(nFactors * (i-1) + k, nFactors * (j-1) + l);
                end
            end
        end
                        
        
    end
end

% Probably should add some code to ensure correlation matrix is positive
% semi definite.

%% Step 2 - Simulate

% Check number of days to simulate is valid - assume 21 working days per
% matuirity
if(nDays > NMaturities*21)
  error(message('Too may days for forward curve'));
end

% initialise random number generator
s = RandStream('twister','Seed',Seed);
RandStream.setGlobalStream(s)

% Get last forward curves - starting point for simulations
initFCs = zeros(NMarkets,NMaturities);
for i=1:NMarkets
    initFCs(i,:) = historicalForwardCurves(end,:,i);
end

%Allocate simulated forward sims variable
FCSims = zeros(nSims,NMaturities,nDays,NMarkets);

% Set up matrix with number of working days per maturity - assume that each
% month has 21 working days per maturity
workingDaysperMaturity = zeros(NMaturities,1);
count = 0;
for i=1:NMaturities
    workingDaysperMaturity(i) = 21;
    count = count + workingDaysperMaturity(i);
    if(count > nDays)
        workingDaysperMaturity(i) = nDays -(i-1)*21;
        break;
    end
end


% Loop plan
        % 1. First two loops control the number of days to time march the FC; NMaturities is the number of maturitis in the FC
        %    2. Each maturity has n days to maturity which is also a loop
        %       3. Loop over the number of simulations (nSims)
        %          4. Loop over every maturity (iMaturity always starting from first month to ensure that each day moving forward, the volatility structured is rolled forward.
        %             5.  Loop over every forward curve market(NMarkets)
        %                6. Loop over evey factor (nFactors) to determine volatility and drfit
        %                

% Loop round the maturity steps, i.e how many days to run for each maturity
monthStartDay = 1;
for iMonth = 1:NMaturities
            
    %Number of days to simulate, i.e. number of days until contract matures for each month                
    ndaysPerMat = workingDaysperMaturity(iMonth);
               
    % j is the number days to simulate over, starts from 1
    for j = monthStartDay:(ndaysPerMat + monthStartDay - 1)
        j % output day
        
        %Get correlated random numbers using cholesky decomposition
        mydeltaz = randn(nSims,nFactors*NMarkets)*chol(volFNsCorrs);
        
        for i = 1:nSims % simulate curves
                                        
            % Loop round maturities - reduces as you move into the next
            % maturity bucket
            for iMaturity = 1:NMaturities - (iMonth - 1)
                                                    
                 % Set maturity step to store results in correct location -
                 % assumes move into next maturity using iMonth, could
                 % assume iMatStep = iMaturity, but forward curve would
                 % always start from same point as moving forward.
                 iMatStep = iMaturity + (iMonth - 1);

                 for iCurve = 1:NMarkets % Loop around markets
                    
                     %Initialise volatility 
                     sigma1 = 0.0;
                     sigma2 = 0.0;

                     for k = 1:nFactors
                         % Assumes volatility function always start from
                         % first vol (iMaturity) when moving forward in
                         % time to next maturity bucket and time step is
                         % set to unity
                         sigma1 = sigma1 + AllVolFNs(iMaturity, k, iCurve) * AllVolFNs(iMaturity, k, iCurve);
                         sigma2 = sigma2 + AllVolFNs(iMaturity, k, iCurve) * mydeltaz(i, nFactors * (iCurve - 1) + k);
                     end                                                        % Pick up respective correlated random number
                                
                     % update forward curve
                     if (j == 1)
                         FCSims(i, iMatStep, j, iCurve) = initFCs(iCurve, iMatStep) * exp(-0.5 * sigma1 + sigma2);
                     else
                         FCSims(i, iMatStep, j, iCurve) = FCSims(i, iMatStep, j - 1, iCurve) * exp(-0.5 * sigma1 + sigma2);
                     end
                     
                 end
            end
        end % end of sims
    end
    
    %updated counter
    monthStartDay = monthStartDay + ndaysPerMat;
    
end

Contact us