Skip to content

Commit

Permalink
More mc VaR work
Browse files Browse the repository at this point in the history
  • Loading branch information
gavbrennan committed Sep 19, 2022
1 parent 40ed357 commit 38417d3
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 76 deletions.
41 changes: 13 additions & 28 deletions src/Qwack.Models/MCModels/FactorReturnPayoff.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public FactorReturnPayoff(string[] assetIds, DateTime[] simDates)
_simDates = simDates;
}

public Dictionary<string, int> AssetIndices { get; private set; } = new Dictionary<string, int>();
public Dictionary<DateTime, int> DateIndices { get; private set; } = new Dictionary<DateTime, int>();

public bool IsComplete => _isComplete;

public IAssetInstrument AssetInstrument { get; private set; }
Expand All @@ -60,11 +63,17 @@ public void Finish(IFeatureCollection collection)
throw new Exception($"Assets {missingAssets} not found in MC engine");
}

for(var a = 0; a < AssetIds.Length; a++)
{
AssetIndices[AssetIds[a]] = _assetIndices[a];
}

var dates = collection.GetFeature<ITimeStepsFeature>();
_dateIndexes = new int[_simDates.Length];
for (var i = 0; i < _simDates.Length; i++)
{
_dateIndexes[i] = dates.GetDateIndex(_simDates[i]);
DateIndices[_simDates[i]] = _dateIndexes[i];
}

var engine = collection.GetFeature<IEngineFeature>();
Expand Down Expand Up @@ -114,34 +123,10 @@ public void SetupFeatures(IFeatureCollection pathProcessFeaturesCollection)
dates.AddDates(_simDates);
}

public double[][][] ResultsByPath
{
get
{
var vecLen = Vector<double>.Count;
var results = new double[_assetIndices.Length][][]; // [_dateIndexes.Length][_rawNumberOfPaths];
for (var a = 0; a < _assetIndices.Length; a++)
{
results[a] = new double[_dateIndexes.Length][];
for (var tix = 0; tix < _dateIndexes.Length; tix++)
{
results[a][tix] = new double[_rawNumberOfPaths];
for (var i = 0; i < _results.Length; i++)
{
for (var j = 0; j < vecLen; j++)
{
var c = i * vecLen + j;
if (c >= results.Length)
break;
// _results[a][resultIx][tIx] = steps[_dateIndexes[tIx]];
//results[a][tix][c] = _results[a][i][tix][j];
}
}
}
}
return results;
}
}
/// <summary>
/// [AssetIx][PathIx][TimeIx]
/// </summary>
public double[][][] ResultsByPath => _results;

public CashFlowSchedule ExpectedFlows(IAssetFxModel model) => null;

Expand Down
91 changes: 43 additions & 48 deletions src/Qwack.Models/Risk/McVaRCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class McVaRCalculator
private readonly ICurrencyProvider _currencyProvider;
private readonly ICalendarProvider _calendarProvider;
private readonly IFutureSettingsProvider _futureSettingsProvider;
private readonly Dictionary<DateTime, IAssetFxModel> _bumpedModels = new();
private readonly List<IAssetFxModel> _bumpedModels = new();
private readonly Dictionary<string, double> _spotFactors = new();


Expand All @@ -54,12 +54,14 @@ public void AddSpotFactor(string assetId, double vol)

public void CalculateModels()
{
var allAssetIds = _portfolio.AssetIds().Where(x => !(x.Length == 7 && x[3] == '/')).ToArray();
var allAssetIds = _portfolio.AssetIds().Concat(_portfolio.Instruments.Select(x => x.Currency.Ccy).Where(x => x != "USD").Select(x =>$"USD/{x}")).ToArray();
var simulatedIds = allAssetIds.Intersect(_spotFactors.Keys).ToArray();

foreach(var simulatedId in simulatedIds)
{
_model.AddVolSurface(simulatedId, new ConstantVolSurface(_model.BuildDate, _spotFactors[simulatedId]) { AssetId = simulatedId });
if(simulatedId.Length==6 && simulatedId[3]=='/')
_model.FundingModel.VolSurfaces.Add(simulatedId, new ConstantVolSurface(_model.BuildDate, _spotFactors[simulatedId]) { AssetId = simulatedId });
}

_logger.LogInformation("Simulating {nFac} spot factors", simulatedIds.Length);
Expand All @@ -73,13 +75,35 @@ public void CalculateModels()
ReportingCurrency=_currencyProvider.GetCurrencySafe("USD")
};

var fp = new FactorReturnPayoff(simulatedIds, new DateTime[] { _model.BuildDate.AddDays(1) });
var vd = _model.BuildDate.AddDays(1);
var fp = new FactorReturnPayoff(simulatedIds, new DateTime[] { _model.BuildDate, vd });

var mcModel = new AssetFxMCModel(_model.BuildDate, fp, _model, mcSettings, _currencyProvider, _futureSettingsProvider, _calendarProvider);
mcModel.Engine.RunProcess();

var dix = fp.DateIndices[vd];
for (var p = 0; p < mcSettings.NumberOfPaths; p++)
{
var pModel = _model.Clone();
for (var a = 0; a < simulatedIds.Length; a++)
{
var price0 = fp.ResultsByPath[a][p][0];
var price1 = fp.ResultsByPath[a][p][dix];
var bump = price1 / price0 - 1.0;

if (IsFx(simulatedIds[a]))
pModel = RelativeShiftMutator.FxSpotShift(_currencyProvider.GetCurrencySafe(simulatedIds[a].Split('/').Last()), bump, pModel);
else
pModel = RelativeShiftMutator.AssetCurveShift(simulatedIds[a], bump, pModel);
}

_bumpedModels.Add(pModel);
}
}

public (double VaR, DateTime ScenarioDate) CalculateVaR(double ci, Currency ccy, string[] excludeTradeIds)
private static bool IsFx(string assetId) => assetId.Length == 7 && assetId[3] == '/';

public double CalculateVaR(double ci, Currency ccy, string[] excludeTradeIds)
{
if (!_resultsCache.Any())
{
Expand All @@ -95,11 +119,11 @@ public void CalculateModels()
var ixCi = (int)System.Math.Floor(sortedResults.Count() * (1.0 - ci));
var ciResult = sortedResults[System.Math.Min(System.Math.Max(ixCi, 0), sortedResults.Count - 1)];
var basePvForSet = _basePvCube.Filter(filterDict, true).SumOfAllRows;
return (ciResult.Value - basePvForSet, ciResult.Key);
return ciResult.Value - basePvForSet;
}
}

public (double VaR, DateTime ScenarioDate) CalculateVaRInc(double ci, Currency ccy, string[] includeTradeIds)
public double CalculateVaRInc(double ci, Currency ccy, string[] includeTradeIds)
{
if (!_resultsCache.Any())
{
Expand All @@ -115,7 +139,7 @@ public void CalculateModels()
var ixCi = (int)System.Math.Floor(sortedResults.Count() * (1.0 - ci));
var ciResult = sortedResults[System.Math.Min(System.Math.Max(ixCi, 0), sortedResults.Count - 1)];
var basePvForSet = _basePvCube.Filter(filterDict, false).SumOfAllRows;
return (ciResult.Value - basePvForSet, ciResult.Key);
return ciResult.Value - basePvForSet;
}
}

Expand All @@ -129,56 +153,27 @@ public double[] CalculateVaRRange(double[] cis)
return ciResults.Select(x => x.Value - basePvForSet).ToArray();
}

public BetaAnalysisResult ComputeBeta(double referenceNav, Dictionary<DateTime, double> benchmarkPrices, DateTime? earliestDate = null)
{
if (!earliestDate.HasValue)
earliestDate = DateTime.MinValue;

var basePv = _basePvCube.SumOfAllRows;
var results = _resultsCache.ToDictionary(x => x.Key, x => x.Value.SumOfAllRows - basePv + referenceNav);
var intersectingDates = results.Keys.Intersect(benchmarkPrices.Keys).Where(x => x > earliestDate).OrderBy(x => x).ToArray();
var pfReturns = new List<double>();
var benchmarkReturns = new List<double>();
for (var t = 1; t < intersectingDates.Length; t++)
{
var y = intersectingDates[t - 1];
var d = intersectingDates[t];
pfReturns.Add(System.Math.Log(results[d] / results[y]));
benchmarkReturns.Add(System.Math.Log(benchmarkPrices[d] / benchmarkPrices[y]));
}

var lrResult = LinearRegression.LinearRegressionVector(benchmarkReturns.ToArray(), pfReturns.ToArray());

return new BetaAnalysisResult
{
LrResult = lrResult,
BenchmarkReturns = benchmarkReturns.ToArray(),
PortfolioReturns = pfReturns.ToArray(),
BenchmarkPrices = benchmarkPrices
};
}

public Dictionary<string, double> GetBaseValuations() => _basePvCube.Pivot("TradeId", AggregationAction.Sum).ToDictionary("TradeId").ToDictionary(x => x.Key as string, x => x.Value.Sum(r => r.Value));

public Dictionary<string, double> GetContributions(DateTime scenarioDate)
public Dictionary<string, double> GetContributions(int ix)
{
var cube = _resultsCache[scenarioDate];
var cube = _resultsCache[ix];
var diff = cube.Difference(_basePvCube);

return diff.Pivot("TradeId", AggregationAction.Sum).ToDictionary("TradeId").ToDictionary(x => x.Key as string, x => x.Value.Sum(r => r.Value));
}

public (double VaR, DateTime ScenarioDate) CalculateVaR(double ci, Currency ccy) => CalculateVaR(ci, ccy, _portfolio);
public double CalculateVaR(double ci, Currency ccy) => CalculateVaR(ci, ccy, _portfolio);

private readonly ConcurrentDictionary<DateTime, ICube> _resultsCache = new();
private readonly ConcurrentDictionary<int, ICube> _resultsCache = new();
private ICube _basePvCube;
public (double VaR, DateTime ScenarioDate) CalculateVaR(double ci, Currency ccy, Portfolio pf, bool parallelize = true)
public double CalculateVaR(double ci, Currency ccy, Portfolio pf, bool parallelize = true)
{
_basePvCube = pf.PV(_model, ccy);
var basePv = _basePvCube.SumOfAllRows;
_resultsCache.Clear();
var results = new ConcurrentDictionary<DateTime, double>();
var varFunc = new Action<DateTime, IAssetFxModel>((d, m) =>
var results = new ConcurrentDictionary<int, double>();
var varFunc = new Action<int, IAssetFxModel>((d, m) =>
{
var cube = pf.PV(m, ccy, false);
var scenarioPv = cube.SumOfAllRows;
Expand All @@ -187,23 +182,23 @@ public Dictionary<string, double> GetContributions(DateTime scenarioDate)
});
if (parallelize)
{
Parallel.ForEach(_bumpedModels, kv =>
Parallel.For(0, _bumpedModels.Count, ix =>
{
varFunc(kv.Key, kv.Value);
varFunc(ix, _bumpedModels[ix]);
});
}
else
{
foreach (var kv in _bumpedModels)
for (var ix=0; ix<_bumpedModels.Count; ix++)
{
varFunc(kv.Key, kv.Value);
varFunc(ix, _bumpedModels[ix]);
}
}

var sortedResults = results.OrderBy(kv => kv.Value).ToList();
var ixCi = (int)System.Math.Floor(sortedResults.Count() * (1.0 - ci));
var ciResult = sortedResults[System.Math.Min(System.Math.Max(ixCi, 0), sortedResults.Count - 1)];
return (ciResult.Value, ciResult.Key);
return ciResult.Value;
}

internal class VaRSpotScenarios
Expand Down

0 comments on commit 38417d3

Please sign in to comment.