Main Content

One-Factor Model Calibration

This example demonstrates techniques to calibrate a one-factor model for estimating portfolio credit losses using the creditDefaultCopula or creditMigrationCopula classes.

This example uses equity return data as a proxy for credit fluctuations. With equity data, sensitivity to a single factor is estimated as a correlation between a stock and an index. The data set contains daily return data for a series of equities, but the one-factor model requires calibration on a year-over-year basis. Assuming that there is no autocorrelation, then the daily cross-correlation between a stock and the market index is equal to the annual cross-correlation. For stocks exhibiting autocorrelation, this example shows how to compute implied annual correlations incorporating the effect of autocorrelation.

Fitting a One-Factor Model

Since corporate defaults are rare, it is common to use a proxy for creditworthiness when calibrating default models. The one-factor copula models the credit worthiness of a company using a latent variable, A:


where X is the systemic credit factor, w is the weight that defines the sensitivity of a company to the one factor, and ϵ is the idiosyncratic factor. w and ϵ have mean of 0 and variance of 1 and typically are assumed to be either Gaussian or else t distributions.

Compute the correlation between X and A:


Since X and A have a variance of 1 by construction and ϵ is uncorrelated with X, then:


If you use stock returns as a proxy for A and the market index returns are a proxy for X, then the weight parameter, w, is the correlation between the stock and the index.

Prepare the Data

Use the returns of the Dow Jones Industrial Average (DJIA) as a signal for the overall credit movement of the market. The returns for the 30 component companies are used to calibrate the sensitivity of each company to the systemic credit movement. Weights for other companies in the stock market are estimated in the same way.

% Read one year of DJIA price data.
t = readtable('dowPortfolio.xlsx');

% The table contains dates and the prices for each company at market close
% as well as the DJIA.
       Dates        DJI      AA       AIG      AXP      BA        C  
    ___________    _____    _____    _____    _____    _____    _____

    03-Jan-2006    10847    28.72    68.41    51.53    68.63    45.26
    04-Jan-2006    10880    28.89    68.51    51.03    69.34    44.42
    05-Jan-2006    10882    29.12     68.6    51.57    68.53    44.65
    06-Jan-2006    10959    29.02    68.89    51.75    67.57    44.65
    09-Jan-2006    11012    29.37    68.57    53.04    67.01    44.43
    10-Jan-2006    11012    28.44    69.18    52.88    67.33    44.57
    11-Jan-2006    11043    28.05     69.6    52.59     68.3    44.98
    12-Jan-2006    10962    27.68    69.04     52.6     67.9    45.02
% We separate the dates and the index from the table and compute daily returns using
% tick2ret.
dates = t{2:end,1};
index_adj_close = t{:,2};
stocks_adj_close = t{:,3:end};

index_returns = tick2ret(index_adj_close);
stocks_returns = tick2ret(stocks_adj_close);

Compute Single Factor Weights

Compute the single-factor weights from the correlation coefficients between the index returns and the stock returns for each company.

[C,daily_pval] = corr([index_returns stocks_returns]);
w_daily = C(2:end,1);

These values can be used directly when using a one-factor creditDefaultCopula or creditMigrationCopula.

Linear regression is often used in the context of factor models. For a one-factor model, a linear regression of the stock returns on the market returns is used by exploiting the fact that the correlation coefficient matches the square root of the coefficient of determination (R-squared) of a linear regression.

w_daily_regress = zeros(30,1);
for i = 1:30
    lm = fitlm(index_returns,stocks_returns(:,i));
    w_daily_regress(i) = sqrt(lm.Rsquared.Ordinary);

% The regressed R values are equal to the index cross correlations.
fprintf('Max Abs Diff : %e\n',max(abs(w_daily_regress(:) - w_daily(:))))
Max Abs Diff : 8.326673e-16

This linear regression fits a model of the form A=α+βX+ϵ, which in general does not match the one-factor model specifications. For example, A and X do not have a zero mean and a standard deviation of 1. In general, there is no relationship between the coefficient β and the standard deviation of the error term ϵ. Linear regression is used above only as a tool to get the correlation coefficient between the variables given by the square root of the R-squared value.

For one-factor model calibration, a useful alternative is to fit a linear regression using the standardized stock and market return data A and X. "Standardize" here means to subtract the mean and divide by the standard deviation. The model is A=α+βX+ϵ. However, because both A and X have a zero mean, the intercept α is always zero, and because both A and X have standard deviation of 1, the standard deviation of the error term satisfies std(ϵ)=1-β2. This exactly matches the specifications of the coefficients of a one-factor model. The one-factor parameter w is set to the coefficient β, and is the same as the value found directly through correlation earlier.

w_regress_std = zeros(30,1);
index_returns_std = zscore(index_returns);
stocks_returns_std = zscore(stocks_returns);
for i = 1:30
    lm = fitlm(index_returns_std,stocks_returns_std(:,i));
    w_regress_std(i) = lm.Coefficients{'x1','Estimate'};

% The regressed R values are equal to the index cross correlations.
fprintf('Max Abs Diff : %e\n',max(abs(w_regress_std(:) - w_daily(:))))
Max Abs Diff : 5.551115e-16

This approach makes it natural to explore the distributional assumptions of the variables. The creditDefaultCopula and creditMigrationCopula objects support either normal distributions, or t distributions for the underlying variables. For example, when using normplot the market returns have heavy tails, therefore a t-copula is more consistent with the data.


Estimating Correlations for Longer Periods

The weights are computed based on the daily correlation between the stocks and the index. However, the usual goal is to estimate potential losses from credit defaults at some time further in the future, often one year out.

To that end, it is necessary to calibrate the weights such that they correspond to the one-year correlations. It is not practical to calibrate directly against historical annual return data since any reasonable data set does not have enough data to be statistically significant due to the sparsity of the data points.

You then face the problem of computing annual return correlation from a more frequently sampled data set, for example, daily returns. One approach to solving this problem is to use an overlapping window. This way you can consider the set of all overlapping periods of a given length.

% As an example, consider an overlapping 1-week window.
index_overlapping_returns = index_adj_close(6:end) ./ index_adj_close(1:end-5) - 1;
stocks_overlapping_returns = stocks_adj_close(6:end,:) ./ stocks_adj_close(1:end-5,:) - 1;

C = corr([index_overlapping_returns stocks_overlapping_returns]);
w_weekly_overlapping = C(2:end,1);

% Compare the correlation with the daily correlation.
% Show the daily vs. the overlapping weekly correlations.
barh([w_daily w_weekly_overlapping])
title('Correlation with the Index');
legend('daily','overlapping weekly');

The maximum cross-correlation p-value for daily returns show a strong statistical significance.

maxdailypvalue = max(daily_pval(2:end,1));
    'rownames',{'Maximum p-value'}))

    Maximum p-value    1.5383e-08

Moving to an overlapping rolling-window-style weekly correlation gives slightly different correlations. This is a convenient way to estimate longer period correlations from daily data. However, the returns of adjacent overlapping windows are correlated so the corresponding p-values for the overlapping weekly returns are not valid since the p-value calculation in the corr function does not account for overlapping window data sets. For example, adjacent overlapping window returns are composed of many of the same datapoints. This tradeoff is necessary since moving to nonoverlapping windows could result is an unacceptably sparse sample.

% Compare to non-overlapping weekly returns
fridays = weekday(dates) == 6;
index_weekly_close = index_adj_close(fridays);
stocks_weekly_close = stocks_adj_close(fridays,:);

index_weekly_returns = tick2ret(index_weekly_close);
stocks_weekly_returns = tick2ret(stocks_weekly_close);

[C,weekly_pval] = corr([index_weekly_returns stocks_weekly_returns]);
w_weekly_nonoverlapping = C(2:end,1);
maxweeklypvalue = max(weekly_pval(2:end,1));

% Compare the correlation with the daily and overlapping.
barh([w_daily w_weekly_overlapping w_weekly_nonoverlapping])
title('Correlation with the Index');
legend('daily','overlapping weekly','non-overlapping weekly');

The p-values for the nonoverlapping weekly correlations are much higher, indicating a loss of statistical significance.

% Compute the number of samples in each series.
numDaily = numel(index_returns);
numOverlapping = numel(index_overlapping_returns);
numWeekly = numel(index_weekly_returns);

    'rownames',{'Maximum p-value','Sample Size'}))
                         Daily       Overlapping    Non_Overlapping
                       __________    ___________    _______________

    Maximum p-value    1.5383e-08        NaN            0.66625    
    Sample Size               250        246                 50    

Extrapolating Annual Correlation

A common assumption with financial data is that asset returns are temporally uncorrelated. That is, the asset return at time T is uncorrelated to the previous return at time T-1. Under this assumption, the annual cross-correlation is exactly equal to the daily cross-correlation.

Let Xt be the daily log return of the market index on day t and At be the daily return of a correlated asset. Using CAPM, the relation is modeled as:


The one-factor model is a special case of this relationship.

Under the assumption that asset and index returns are each uncorrelated with their respective past, then:

y, st:




Let the aggregate annual (log) return for each series be



where T could be 252 depending on the underlying daily data.

Let σX2=var(Xt) and σA2=var(At) be the daily variances, which are estimated from the daily return data.

The daily covariance between Xt and At is:


The daily correlation between Xt and At is:


Consider the variances and covariances for the aggregate year of returns. Under the assumption of no autocorrelation:




The annual correlation between the asset and the index is:


Under the assumption of no autocorrelation, notice that the daily cross-correlation is in fact equal to the annual cross-correlation. You can use this assumption directly in the one-factor model by setting the one-factor weights to the daily cross-correlation.

Handling Autocorrelation

If the assumption that assets have no autocorrelation is loosened, then the transformation from daily to annual cross-correlation between assets is not as straightforward. The var(X) now has additional terms.

First consider the simplest case of computing the variance of X when T is equal to 2.


Since σ1=σ2=σX, then:


Consider T = 3. Indicate the correlation between daily returns that are k days apart as ρΔk.


In the general case, for the variance of an aggregate T-day return with autocorrelation from trailing k days, there is:


This is also the same formula for the asset variance:


The covariance between X and A as shown earlier is equal to βvar(X).

Therefore, the cross-correlation between the index and the asset with autocorrelation from a trailing 1 through k days is:




Note that βσXσA is the weight under the assumption of no autocorrelation. The square root term provides the adjustment to account for autocorrelation in the series. The adjustment depends more on the difference between the index autocorrelation and the stock autocorrelation, rather than the magnitudes of these autocorrelations. So the annual one-factor weight adjusted for autocorrelation is:


Compute Weights with Autocorrelation

Look for autocorrelation in each of the stocks with the previous day's return, and adjust the weights to incorporate the effect of a one-day autocorrelation.

corr1 = zeros(30,1);
pv1 = zeros(30,1);
for stockidx = 1:30
    [corr1(stockidx),pv1(stockidx)] = corr(stocks_returns(2:end,stockidx),stocks_returns(1:end-1,stockidx));
autocorrIdx = find(pv1 < 0.05)
autocorrIdx = 4×1


There are four stocks with low p-values that may indicate the presence of autocorrelation. Estimate the annual cross-correlation with the index under this model, considering the one-day autocorrelation.

% The weights based off of yearly cross correlation are equal to the daily cross
% correlation multiplied by an additional factor.
T = 252;

w_yearly = w_daily;
[rho_index, pval_index] = corr(index_returns(1:end-1),index_returns(2:end));

% Check to see if the index has any significant autocorrelation.
fprintf('One day autocorrelation in the index p-value: %f\n',pval_index);
One day autocorrelation in the index p-value: 0.670196
if pval_index < 0.05
    % If the p-value indicates there is no significant autocorrelation in the index,
    % set its rho to 0.
    rho_index = 0;

w_yearly(autocorrIdx) = w_yearly(autocorrIdx) .*...
    sqrt((T/2 + (T-1) .* rho_index) ./ (T/2 + (T-1) .* corr1(autocorrIdx)));

% Compare the adjusted annual cross correlation values to the daily values.
barh([w_daily(autocorrIdx) w_yearly(autocorrIdx)])
allNames = t.Properties.VariableNames(3:end);
title('Annual One Factor Weights');
legend('No autocorrelation','With autocorrelation','location','southeast');

See Also

| | | | |

Related Examples

More About