# Historical Value-at-Risk Estimation with US Treasury Bonds

This example shows how to estimate the value at risk (VaR) for a portfolio of US Treasury bonds by using both the historical and filtered historical VaR methods. While this example uses treasury bonds as a typical asset type, you can apply this workflow to any fixed-rate bond with similar par yield data.

VaR is a statistical method that quantifies the risk level associated with a portfolio. The VaR is an estimate of the maximum loss a portfolio can realize over a specified time horizon and at a given confidence level.

To apply the workflow for this example, you:

1. Transform yield curve data into zero-curve data.

2. Create and price a portfolio of US Treasury bonds by using `FixedBond`

(Financial Instruments Toolbox) in the Financial Instruments Toolbox™.

3. Calculate the VaR for the portfolio of US Treasury bonds over a series of trading days by using historical zero-curve changes.

4. Backtest the VaR results to validate the methodology by using the `varbacktest`

object.

5. Improve the VaR performance by implementing a filtered version of the historical VaR method.

### Load and Prepare the Data

Load par yield data for US Treasury bonds and then remove dates with `NaN`

values. This example uses a preexisting data set, although you can load the same data using the FRED (Federal Reserve Economic Data) connection in Datafeed Toolbox™.

load Data_USYieldCurve.mat % Take out last ~5 years of data. You need both the numerical data from % "Data", as well as the dates from "dates". Data = Data(4030:end,:); Dates = dates(4030:end,:); % Remove the NaN values. [Data,badDataIndex] = rmmissing(Data,1); Dates(badDataIndex,:) = []; % When interest rates equal 0, the returns become infinite, so you must % perturb the rates from zero. You can avoid this issue by using differences % instead of returns, but the filtered historical VaR assumes return % data, so use returns for the basic historical VaR. ZeroInd = Data == 0; Data(ZeroInd) = .00001;

### Transform Par Yield Rates into Zero Curve Rates

The loaded data is for the par yield curve. The par yield curve is the yield at which a bond trades at a face value. However, a zero curve is a more useful curve type for bond pricing. A zero curve is the yield curve a bond type has if its structure is a single payment at maturity, Therefore, you can convert the par rates into zero rates using `pyld2zero`

.

To use `pyld2zero`

, get the settle dates from the imported data and create dates for each tenor on the rate curve for each day in the data set. Then, you can use these dates along with the par yield data as inputs for `pyld2zero`

to construct an equivalent zero curve for each day.

% Initialize zero curves, one for each day in our data set. n = size(Data,1); ZeroCurvesData = zeros(n,size(Data,2)); % Convert the numerical dates into datetime variables, % where each date is a settle date for an associated zero curve. SettleDates = datetime(Dates,'ConvertFrom','datenum'); % Construct the tenor dates for the zero curves associated with the days in % the data set. To do this, add a vector of durations (with entries % corresponding to each tenor) to each settle date. ZeroTimes = [calmonths([1, 3, 6]) calyears([1 2 3 5 7 10 20 30])]; ZeroDates = SettleDates + ZeroTimes; % Cycle through each day in the data set and use pyld2zero to construct a % zero curve for the given day. for i=1:n ZeroCurvesData(i,:) = pyld2zero(Data(i,:),ZeroDates(i,:)',SettleDates(i))'; end

Once you have a set of zero curves for each day in the data set, you can calculate the returns. This calculation gives you a set of daily * changes* to the zero curve. You can use these changes, when you want to simulate changes to a given zero curve during the VaR calculation. However, if you do not perturb the interest rates away from zero, some of the returns are infinite. Another way to handle this problem is to use differences instead of returns, but returns work better for the filtered historical method. This example uses returns for the basic historical method.

ZeroReturns = tick2ret(ZeroCurvesData);

### Create Portfolio of Bonds

Before you can use the zero curves and zero curve returns to estimate VaR values, you need a portfolio of bonds. In MATLAB®, you can use Financial Instruments Toolbox™ to represent bonds as `FixedBond`

(Financial Instruments Toolbox) objects using the `fininstrument`

(Financial Instruments Toolbox) function. For more information, see Workflow to Price an Interest-Rate Instrument (Financial Instruments Toolbox).

To use `fininstrument`

, select `'FixedBond'`

as the input type and use the `'Maturity'`

input argument to correspond to the 5-, 7-, and 10-year tenors. When pricing the bonds using `finpricer`

(Financial Instruments Toolbox), use the zero curves from the par yield US Treasury bond data. You can consider these fixed-rate bonds as similar to the US Treasury bonds. Therefore, when using `fininstrument`

, set the `'Period'`

value to `2`

because US Treasury bonds make two payments per year.

Create a portfolio with a 10-year, 7-year, and 5-year bond.

% Create a 10-year bond. CouponRate = 0.06; Maturity = datetime(2006,1,1) + calyears(10); Period = 2; Treasury10YearBond = fininstrument("FixedBond",Maturity=Maturity,CouponRate=CouponRate, ... Period=Period,Principal=100,Name="10YearTreasury"); % Create a 7-year bond. CouponRate = 0.05; Maturity = datetime(2006,1,1) + calyears(7); Period = 2; Treasury7YearBond = fininstrument("FixedBond",Maturity=Maturity,CouponRate=CouponRate, ... Period=Period,Principal=100,Name="7YearTreasury"); % Create a 5-year bond. CouponRate = 0.045; Maturity = datetime(2006,1,1) + calyears(5); Period = 2; Treasury5YearBond = fininstrument("FixedBond",Maturity=Maturity,CouponRate=CouponRate, ... Period=Period,Principal=100,Name="5YearTreasury"); MyPortfolio = [Treasury5YearBond, Treasury7YearBond, Treasury10YearBond];

### Calculate Actual P&L Values

When you calculate the VaR values, you can evaluate the performance of the VaR methodology by comparing the VaR values to actual realized profit-and-loss values (P&L). You can use the following code to calculate the realized P&L values. Using the code, loop through the `ZeroCurves`

vector, and for each day, create a `MyPricer`

object using `finpricer`

(Financial Instruments Toolbox) with the relevant zero curve. You can use the `MyPricer`

object with the `price`

(Financial Instruments Toolbox) function to price the bonds. You can then sum the prices of the bonds in the portfolio to derive the total value of the portfolio for a specific day.

n = size(Data,1); PortfolioValues = zeros(n,1); CurveBasis = 3; for i = 1:n ZeroCurve = ratecurve('Zero',SettleDates(i),ZeroDates(i,:),ZeroCurvesData(i,:),Basis=CurveBasis); MyPricer = finpricer("Discount",DiscountCurve=ZeroCurve); prices = price(MyPricer,MyPortfolio); PortfolioValues(i) = sum(prices); end

Once you calculate the value of the portfolio for each trading day, you can derive the P&L values by differencing the daily portfolio values.

ActualPandL = diff(PortfolioValues); % Plot the P&L values for visualization. figure; plot(SettleDates(1:end-1),ActualPandL); title('P&L Over Time');

### Initialize VaR Variables

Initialize the variables before beginning the VaR calculation.

% Set the VaR threshold (95% and 99%). pVaR = [0.95 0.99]; % Set the number of historical zero-curve perturbations that you will consider % when calculating each VaR value. LookBackWindowSize = 250; % Find number of trading days in the data set. SampleSize = size(ZeroCurvesData,1); % Select the day in the data set on to begin calculating VaR values. DayBeforeStartIndex = 949; TestWindowStart = DayBeforeStartIndex + 1; TestWindow = TestWindowStart:SampleSize; % Initialize the VaR variables. Historical95 = zeros(length(TestWindow),1); Historical99 = zeros(length(TestWindow),1);

### Calculate Historical VaR

The code that follows loops through the zero-curve data and, on each day, the code uses the zero curve to calculate the daily VaR value. It makes this calculation by looking back at historical zero-curve returns, and using those returns to adjust the current zero curve, it changes the price of your portfolio. After running this code for each day in the lookback window, you obtain a set of simulated P&L values. Using these simulated P&L values, you can extract a percentile loss. For example, you can get the P&L loss at the 99th percentile for the 99% VaR.

for t = TestWindow % Get current zero curve and the differences from the Lookback Window. previousDayTenors = ZeroCurvesData(t-1,:); LookBackWindow = t-LookBackWindowSize-1:t-2; returns = ZeroReturns(LookBackWindow,:); % Calculate what the zero curve will be if the tenors undergo % historical changes. GeneratedZeroCurves = previousDayTenors.*(1+returns); % Get the settle date for the trading day. SettleDate = SettleDates(t,:); % Get the cashflows for each bond in the portfolio and use these % to price the bond with discount factors derived from a zero curve. CF = cashflows(MyPortfolio,SettleDate); % Initialize the discount factors that are derived from the historically % simulated zero curve using a for loop. DF = zeros(LookBackWindowSize,height(CF)); for i = 1:LookBackWindowSize ZeroCurve = ratecurve('Zero',SettleDate,ZeroDates(t,:),GeneratedZeroCurves(i,:)); DF(i,:) = discountfactors(ZeroCurve,CF.Time); end % Derive the portfolio value for every scenario by multiplying the % cashflows by the discount factors and then summing the results. PortValue = sum(CF{:,:}'*DF')'; % Calculate the simulated P&L values for each historical return. To do % this subtract yesterday's portfolio value from the simulated % portfolio values. SimulatedPandL = PortValue - PortfolioValues(t-1); % Find the relevant percentile loss for the 95% and 99% VaR values n = t-TestWindowStart + 1; Historical95(n) = hHistoricalVaR(SimulatedPandL,pVaR(1)); Historical99(n) = hHistoricalVaR(SimulatedPandL,pVaR(2)); end

### Backtest VaR Values

After calculating the VaR values for the portfolio, validate the VaR methodology by backtesting the results. Use `varbacktest`

and the associated test functions to validate the performance of the VaR methodology.

Create a `varbacktest`

object that contains the P&L values for the portfolio, along with the calculated 95% and 99% VaR values.

vbt = varbacktest(ActualPandL(TestWindow-1),[Historical95,Historical99],VaRLevel=[.95, .99],Time=SettleDates(TestWindow-1,:),VaRID={'Historical95','Historical99'});

Use `plot`

to visualize the `varbacktest`

results.

figure; plot(vbt);

The VaR exceptions cluster around the final months of the test window. This result is negative and suggests that the VaR exceptions are not independent. This can happen with the basic historical method when market behavior changes to a new regime because the historical VaR methodology is slow to update in such circumstances [1], [2]. Also, the VaR might have too many exceptions. To check if the number of VaR violations is a problem, investigate further by using the `varbacktest`

`summary`

and `runtests`

functions.

SummaryTable = summary(vbt)

`SummaryTable=`*2×10 table*
PortfolioID VaRID VaRLevel ObservedLevel Observations Failures Expected Ratio FirstFailure Missing
___________ ______________ ________ _____________ ____________ ________ ________ ______ ____________ _______
"Portfolio" "Historical95" 0.95 0.91971 274 22 13.7 1.6058 79 0
"Portfolio" "Historical99" 0.99 0.9708 274 8 2.74 2.9197 189 0

The `ObservedLevel`

column of the `summary`

output suggests that the 95% confidence level is only 92%, while the 99% VaR was 97%. These results indicate the VaR calculation methodology is not sufficiently conservative. To check the VaR model performance statistically, use `runtests`

.

TestTable = runtests(vbt)

`TestTable=`*2×11 table*
PortfolioID VaRID VaRLevel TL Bin POF TUFF CC CCI TBF TBFI
___________ ______________ ________ ______ ______ ______ ______ ______ ______ ______ ______
"Portfolio" "Historical95" 0.95 yellow reject reject accept accept accept reject reject
"Portfolio" "Historical99" 0.99 yellow reject reject accept reject accept reject reject

The Traffic Light rating for both VaR models is at yellow and, while some of the other `varbacktest`

statistical tests accept the model, most reject it. This result again indicates that the VaR values are an underestimate. One of the tests that fails for both VaRs is the TBFI statistic test that checks if the time between failures is independent.

To run the TBFI test, use `tbfi`

.

tbfi(vbt)

`ans=`*2×14 table*
PortfolioID VaRID VaRLevel TBFI LRatioTBFI PValueTBFI Observations Failures TBFMin TBFQ1 TBFQ2 TBFQ3 TBFMax TestLevel
___________ ______________ ________ ______ __________ __________ ____________ ________ ______ _____ _____ _____ ______ _________
"Portfolio" "Historical95" 0.95 reject 47.986 0.0010898 274 22 1 2 4.5 10 79 0.95
"Portfolio" "Historical99" 0.99 reject 33.218 5.6251e-05 274 8 1 3 5.5 25 189 0.95

The `PValues`

for the 95% and 99% VaRs are about `0.001`

and `0.0001`

. This test indicates high confidence that the failures are not independent.

### Investigate Market Trends

Why might the historical VaR method fail to match actual portfolio behavior? One possibility is that the `LookbackWindow`

setting is too small. At `250`

trading days, the `LookbackWindow`

value represents approximately one year of data. One weakness of the historical approach to VaR estimation is that you need a large set of historical data to accurately estimate tail probabilities [1]. For example, with `250`

observations, the 1% tail from a 99% VaR contains only two data points. You can address this problem by extending the `LookbackWindow`

.

A more difficult problem to solve is the fact that market circumstances change over time. Interest rates can go through periods defined by an upward trend, downward trend, or high/low volatility. When one regime ends and another begins, the previous regime may dominate the historical simulation, leading to erroneous VaR estimates.

To see if the VaR exceptions are explained by changes in market circumstances, use the following code that uses a for-loop. This code finds the value of the portfolio on each day in the data set, where the date is held constant to match the first day in the test window. The date is held constant so that the time-to-maturity remains constant and therefore, the *changes* in the portfolio value are driven solely by the interest-rate curve.

n = size(Data,1); PortfolioValuesForPlot = zeros(n,1); CurveBasis = 3; for i = 1:n ZeroCurve = ratecurve('Zero',SettleDates(TestWindow(1)),ZeroDates(1,:),ZeroCurvesData(i,:),Basis=CurveBasis); MyPricer = finpricer("Discount",DiscountCurve=ZeroCurve); prices = price(MyPricer,MyPortfolio); PortfolioValuesForPlot(i) = sum(prices); end

The test window for VaR calculation uses only the final `250`

days of the data and `LookbackWindow`

is also `250`

, so only the final `500`

days are relevant to this analysis.

figure; plot(SettleDates,PortfolioValuesForPlot); hold on xline(SettleDates(TestWindow(1)),'r'); xline(SettleDates(TestWindow(1)-LookBackWindowSize),'k'); legend({'Portfolio value','Start of test window','Start of lookback window'},"Location","northwest"); hold off

When you plot the portfolio values and look at the relevant 500 zero curves, you can see that the value of the portfolio is highly volatile and trends down for the first half of the initial `LookbackWindow`

value. After this point, the portfolio trends flat, then upwards, until the end of the test window, when the volatility increases and the value goes down. This result explains why the exceptions cluster around the final portion of the test window, as this region experiences the greatest losses. This result also explains why the VaR estimate trends smaller over time. The first half of the initial lookback window contains the worst losses, and, as time moves forward, that period is removed from the calculations. The result is that the test and lookback windows overlay multiple market regimes and the basic historical VaR methodology is too slow to update, which causes the VaR estimate to be too high at first, and too low at the end.

### Filtered Historical VaR

Apply the following two modifications to improve the historical VaR estimates:

1. Increase the size of the `LookbackWindow`

.

2. Employ volatility weighting to adjust to changing market regimes. This methodology is sometimes referred to as a *filtered historical VaR* or *filtered historical simulation VaR *[2].

You can implement the first item by setting `LookBackWindowSize`

to a larger number (`450`

), and then proceed.

% Set the number of historical zero-curve perturbations that you will consider % when calculating each VaR value. LookBackWindowSize = 450;

The second item is the more difficult to implement because it requires calculating volatilities for the return series on a rolling basis and storing the results.

The idea behind filtered historical VaR is to scale each return value ${\mathit{R}}_{\mathit{i}}$ by $\frac{{\sigma}_{\mathit{T}}}{{\sigma}_{\mathit{i}}}$. Here, ${\sigma}_{\mathit{T}}$ is the volatility for the return series calculated on the latest day T, while ${\sigma}_{\mathit{i}}$ was the volatility of the same return series calculated on day i. By scaling the returns in such a manner, you can use the returns from previous days to simulate possible outcomes, but in a manner that updates more rapidly when the market trends change.

For example, if the latest volatility, ${\sigma}_{\mathit{T}}$, is greater than the volatility on day i, ${\sigma}_{\mathit{i}}$, then $\frac{{\sigma}_{\mathit{T}}}{{\sigma}_{\mathit{i}}}>1$. The scaling causes ${\mathit{R}}_{\mathit{i}}$ to increase in magnitude, and, therefore, make it more relevant to recent high-volatility market behavior.

To implement this VaR calculation method, you must first calculate the volatilities for `LookbackWindow`

. Use the Exponentially Weighted Moving Average (EWMA) method for volatility calculation. (The lambda weight in this calculation is taken from an example in [3].)

TenorSigmas = zeros(LookBackWindowSize,length(ZeroCurvesData(1,:))); EWMAlambda = 0.94; n = 0; for t = DayBeforeStartIndex-LookBackWindowSize+1:DayBeforeStartIndex n = n+1; LookBackWindow = t-LookBackWindowSize-1:t-2; returns = ZeroReturns(LookBackWindow,:); % Get the exponentially weighted covariance matrix. [~,ewmaCovMatrix] = ewstats(returns,EWMAlambda); % Remove the diagonal. TenorVariances = diag(ewmaCovMatrix,0); % Store the volatilities. TenorSigmas(n,:) = sqrt(TenorVariances); end

Once you have the volatilities for the initial `LookbackWindow`

, you can begin calculating the filtered historical VaR values.

% Initialize the VaR variables. FilteredHistoricalVaR95 = zeros(length(TestWindow),1); FilteredHistoricalVaR99 = zeros(length(TestWindow),1); % Calculate the VaR. for t = TestWindow % Get current zero curve and the differences from the lookback window. previousDayTenors = ZeroCurvesData(t-1,:); LookBackWindow = t-LookBackWindowSize-1:t-2; % Move each volatility back one day. TenorSigmas = circshift(TenorSigmas,-1,1); % Calculate the latest volatility. returns = ZeroReturns(LookBackWindow,:); [~,ewmaCovMatrix] = ewstats(returns,EWMAlambda); % Update the volatility vector. TenorVariances = diag(ewmaCovMatrix,0); TenorSigmas(end,:) = sqrt(TenorVariances)'; % Calculate the scaling factor for each historical return. FilteredHistoricalSigmas = zeros(LookBackWindowSize,size(returns,2)); for i = 1:LookBackWindowSize FilteredHistoricalSigmas(i,:) = TenorSigmas(end,:)./TenorSigmas(i,:); end % Calculate what the zero curve would be if the tenors undergo % historical changes with a scaling factor. GeneratedScaledZeroCurves = previousDayTenors.*(1+returns.*FilteredHistoricalSigmas); SettleDate = SettleDates(t,:); % Get the cashflows for each bond in the portfolio, and use this result to % to price the bond with discount factors derived from a zero curve. CF = cashflows(MyPortfolio,SettleDate); % Initialize the discount factors, which are derived from the historically % simulated zero curves in the for-loop. FilteredHistoricalDF = zeros(LookBackWindowSize,height(CF)); for i = 1:LookBackWindowSize FilteredHistoricalZeroCurve = ratecurve('Zero',SettleDate,ZeroDates(t,:),GeneratedScaledZeroCurves(i,:)); FilteredHistoricalDF(i,:) = discountfactors(FilteredHistoricalZeroCurve,CF.Time); end % Derive the portfolio value for every scenario by multiplying the % cashflows by the discount factors and then summing the results. FilteredHistoricalPortValue = sum(CF{:,:}'*FilteredHistoricalDF')'; % Find the simulated P&L values. n = t-TestWindowStart + 1; FilteredHistoricalSimulatedPandL = FilteredHistoricalPortValue - PortfolioValues(t-1); % Save the VaR values. FilteredHistoricalVaR95(n) = hHistoricalVaR(FilteredHistoricalSimulatedPandL,pVaR(1)); FilteredHistoricalVaR99(n) = hHistoricalVaR(FilteredHistoricalSimulatedPandL,pVaR(2)); end

### Compare Filtered Historical VaR Results

Once you have the new VaR values for the test window, you can backtest the results and compare these results to the original VaR methodology:

vbtFiltered = varbacktest(ActualPandL(TestWindow-1),[FilteredHistoricalVaR95,FilteredHistoricalVaR99],"VaRLevel",[.95, .99],'Time',SettleDates(TestWindow-1,:),'VaRID',{'FilteredVaR95','FilteredVaR99'}); figure; plot(vbtFiltered);

vbtAllVaRs = varbacktest(ActualPandL(TestWindow-1),[Historical95,Historical99,FilteredHistoricalVaR95,FilteredHistoricalVaR99],"VaRLevel",[.95, .99, .95, .99],'Time',SettleDates(TestWindow-1,:),'VaRID',{'Historical95','Historical99','FilteredVaR95','FilteredVaR99'}); FilteredSummaryTable = summary(vbtAllVaRs)

`FilteredSummaryTable=`*4×10 table*
PortfolioID VaRID VaRLevel ObservedLevel Observations Failures Expected Ratio FirstFailure Missing
___________ _______________ ________ _____________ ____________ ________ ________ ______ ____________ _______
"Portfolio" "Historical95" 0.95 0.91971 274 22 13.7 1.6058 79 0
"Portfolio" "Historical99" 0.99 0.9708 274 8 2.74 2.9197 189 0
"Portfolio" "FilteredVaR95" 0.95 0.93066 274 19 13.7 1.3869 5 0
"Portfolio" "FilteredVaR99" 0.99 0.97445 274 7 2.74 2.5547 5 0

The results have clearly improved, especially for the 95% VaR. The VaR exceptions are not as heavily clustered in a single region and the VaR estimates do not consistently get lower as the lookback window moves. Lastly, the performance of the 95% VaR has gone from 92% to 93%, a significant if not complete improvement.

The test results are also improved for the 95% VaR. The traffic light test (`tl`

) has gone from `'yellow'`

to `'green'`

and all of the `varbacktest`

tests now list `'accept'`

rather than `'reject'`

. The time until first failure test (`tuff`

) has switched from `'accept`

' to `'reject'`

for the 99% VaR, but given that the failure rate has improved and the independence test has improved, the overall results are better for the 99% VaR.

FilteredTestTable = runtests(vbtAllVaRs)

`FilteredTestTable=`*4×11 table*
PortfolioID VaRID VaRLevel TL Bin POF TUFF CC CCI TBF TBFI
___________ _______________ ________ ______ ______ ______ ______ ______ ______ ______ ______
"Portfolio" "Historical95" 0.95 yellow reject reject accept accept accept reject reject
"Portfolio" "Historical99" 0.99 yellow reject reject accept reject accept reject reject
"Portfolio" "FilteredVaR95" 0.95 green accept accept accept accept accept accept accept
"Portfolio" "FilteredVaR99" 0.99 yellow reject reject reject accept accept reject reject

The independence test (`tbfi`

) is now `accept`

for the 95% VaR, although it remains `reject`

for the 99% VaR.

tbfi(vbtAllVaRs)

`ans=`*4×14 table*
PortfolioID VaRID VaRLevel TBFI LRatioTBFI PValueTBFI Observations Failures TBFMin TBFQ1 TBFQ2 TBFQ3 TBFMax TestLevel
___________ _______________ ________ ______ __________ __________ ____________ ________ ______ _____ _____ _____ ______ _________
"Portfolio" "Historical95" 0.95 reject 47.986 0.0010898 274 22 1 2 4.5 10 79 0.95
"Portfolio" "Historical99" 0.99 reject 33.218 5.6251e-05 274 8 1 3 5.5 25 189 0.95
"Portfolio" "FilteredVaR95" 0.95 accept 27.838 0.0866 274 19 1 4 7 16 63 0.95
"Portfolio" "FilteredVaR99" 0.99 reject 17.204 0.016126 274 7 4 4.25 10 68 110 0.95

The p-value for the 95% VaR is now `0.087`

, which is significantly better than `.001`

from the basic historical method. The p-value for the 99% VaR has also improved, going from `0.0001`

to `0.016`

.

There are options to further improve the VaR performance. In addition to scaling the returns based on historical volatilities, you can exponentially weight the simulated zero-curve returns based on age so that older returns contribute less than newer returns to the percentile calculations. This methodology is a form of *probability weighting*, as a return that is less likely should contribute less to the percentile calculation [1]. You can also adjust the lambda values for the volatility calculations or use a different model for the volatility calculations, such as a GARCH model. For a discussion on the strengths and weaknesses of the filtered historical VaR method and ways to improve upon it, see [2].

### References

1. Alexander, C. *Value-at-Risk Models. *John Wiley & Sons, Ltd. 2008.

2. Gurrola-Perez, P. and D. Murphy. *Filtered Historical Simulation Value-at-Risk Models and Their Competitors. *Bank of England, 2015.

3. J.P. Morgan, Reuters. "*RiskMetrics-Technical Document*." Morgan Guaranty Trust Company of New York, 1996.

4. Rockafellar, R. T., and S. Uryasev. "Conditional Value-at-Risk for General Loss Distributions." *Journal of Banking and Finance. *Vol. 26, 2002, pp. 1443–1471.

### Local Functions

function VaR = hHistoricalVaR(Sample,VaRLevel) % Compute historical VaR and ES % See [4] for technical details. % Convert to losses. Sample = -Sample; N = length(Sample); k = ceil(N*VaRLevel); z = sort(Sample); VaR = z(k); end