Files
ARM64/MarketData/MarketDataLib/ValueAtRisk/HVaR.cs
Sean 26a8c4794b
All checks were successful
Build .NET Project / build (push) Successful in 4m53s
Use AI to fix some issues with the HVaR
2026-02-19 20:12:40 -05:00

110 lines
4.9 KiB
C#
Executable File

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MarketData.DataAccess;
using MarketData.Utils;
namespace MarketData.ValueAtRisk
{
public class HistoricalVaR
{
private int returnDays = 1;
private HistoricalVaR()
{
}
public static VaRResult GetVaR(PortfolioHoldings portfolioHoldings, double percentile,int returnDays=1)
{
VaRResult varResult=new VaRResult();
if (portfolioHoldings == null || portfolioHoldings.Count == 0)
{
varResult.Success = false;
varResult.Message = "Portfolio is null or empty.";
return varResult;
}
// Determine the minimum common price history across holdings
int minPriceCount = int.MaxValue;
for (int i = 0; i < portfolioHoldings.Count; i++)
{
int count = portfolioHoldings[i].Prices.Count;
if (count < minPriceCount)
minPriceCount = count;
}
// Enforce minimum observation requirement (optional but recommended)
if (minPriceCount < 30) // or whatever threshold you prefer
{
varResult.Success = false;
varResult.Message = $"Insufficient common price history ({minPriceCount} observations).";
return varResult;
}
// Truncate all holdings to the common window
for (int i = 0; i < portfolioHoldings.Count; i++)
{
if (portfolioHoldings[i].Prices.Count > minPriceCount)
{
portfolioHoldings[i].Prices = new MarketDataModel.Prices(portfolioHoldings[i].Prices.Skip(portfolioHoldings[i].Prices.Count - minPriceCount).ToList());
}
}
// Calculate total market value and then calculate the weightings of each holding
double marketValue = portfolioHoldings.GetMarketValue();
if (marketValue == 0)
{
varResult.Success = false;
varResult.Message = "Portfolio market value is zero.";
return varResult;
}
for (int index = 0; index < portfolioHoldings.Count; index++)
{
PortfolioHolding portfolioHolding = portfolioHoldings[index];
portfolioHolding.Weight = portfolioHolding.MarketValue / marketValue;
}
// Calculate the weighted returns for the observation period
portfolioHoldings.SetReturnDays(returnDays);
// int numReturns=portfolioHoldings[0].Returns.Length;
int numReturns = portfolioHoldings.Min(p => p.Returns.Length);
WeightedReturnsWithContribution weightedReturnsWithContrbution=new WeightedReturnsWithContribution();
for (int index = 0; index < numReturns; index++)
{
for (int portfolioIndex = 0; portfolioIndex < portfolioHoldings.Count; portfolioIndex++)
{
PortfolioHolding portfolioHolding = portfolioHoldings[portfolioIndex];
WeightedReturn weightedReturn=new WeightedReturn(portfolioHolding.Symbol,portfolioHolding.Prices[index].Date,portfolioHolding.Returns[index] * portfolioHolding.Weight);
weightedReturnsWithContrbution.Add(index,weightedReturn);
}
}
double[] weightedReturns=weightedReturnsWithContrbution.GetWeightedReturns();
List<Contributions> contributionsList=weightedReturnsWithContrbution.GetContributions();
// Organize the weighted returns into bins so we can access the nth percentile rank
// The VaR nth percentile VaR is simply the weighted return at the given rank (i.e.) 90%, 95%, 99% within the bin
// The VaR result will be a negative percent and negative amount to show that this is a loss. We display this as a positive number so we need to take the absolute value of the result
// ** To Do ** bring out the contributors to VaR at the given rank.
BinManager<Contributions> binManager = new BinManager<Contributions>();
BinResult<Contributions> binResult=binManager.GetVaRReturn(weightedReturns,contributionsList,percentile);
Contributions contributions=binResult.Item;
if(null==contributions){varResult.Success=false;return varResult;}
Dictionary<String,PortfolioHolding> portfolioHoldingsBySymbol=new Dictionary<String,PortfolioHolding>();
foreach(PortfolioHolding portfolioHolding in portfolioHoldings){if(!portfolioHoldingsBySymbol.ContainsKey(portfolioHolding.Symbol))portfolioHoldingsBySymbol.Add(portfolioHolding.Symbol,portfolioHolding);}
foreach(Contribution contribution in contributions)
{
if(!portfolioHoldingsBySymbol.ContainsKey(contribution.Symbol))continue;
portfolioHoldingsBySymbol[contribution.Symbol].Contribution=contribution.ContributionValue;
portfolioHoldingsBySymbol[contribution.Symbol].ContributionDate=contribution.AnalysisDate;
}
return new VaRResult(binResult.Value, marketValue * binResult.Value);
}
public int ReturnDays
{
get { return returnDays; }
set { returnDays = value; }
}
}
}