Skip to content

Commit

Permalink
Merge 0c53b92 into ae5b57b
Browse files Browse the repository at this point in the history
  • Loading branch information
gavbrennan committed Jul 2, 2019
2 parents ae5b57b + 0c53b92 commit d417e14
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 27 deletions.
6 changes: 4 additions & 2 deletions clients/Qwack.Excel/Cubes/CubeFunctions.cs
Expand Up @@ -134,16 +134,18 @@ public class CubeFunctions
public static object FilterCube(
[ExcelArgument(Description = "Output cube name")] string OutputObjectName,
[ExcelArgument(Description = "Input cube name")] string InputObjectName,
[ExcelArgument(Description = "Filter fields and values")] object[,] FilterDetails)
[ExcelArgument(Description = "Filter fields and values")] object[,] FilterDetails,
[ExcelArgument(Description = "Filter out? default false (i.e. filter in)")] object FilterOut)
{
return ExcelHelper.Execute(_logger, () =>
{
var filterOut = FilterOut.OptionalExcel(false);
var cubeCache = ContainerStores.GetObjectCache<ICube>();
var inCube = cubeCache.GetObjectOrThrow(InputObjectName, $"Could not find cube {InputObjectName}");
var filterDeets = FilterDetails.RangeToKvList<string, object>();
var outCube = inCube.Value.Filter(filterDeets);
var outCube = inCube.Value.Filter(filterDeets, filterOut);
cubeCache.PutObject(OutputObjectName, new SessionItem<ICube> { Name = OutputObjectName, Value = outCube });
return OutputObjectName + '¬' + cubeCache.GetObject(OutputObjectName).Version;
Expand Down
17 changes: 17 additions & 0 deletions clients/Qwack.Excel/Instruments/InstrumentFunctions.cs
Expand Up @@ -1086,6 +1086,23 @@ public class InstrumentFunctions
});
}

[ExcelFunction(Description = "Returns a subset of trades from a portfolio object", Category = CategoryNames.Instruments, Name = CategoryNames.Instruments + "_" + nameof(FilterPortfolioByDate), IsThreadSafe = true)]
public static object FilterPortfolioByDate(
[ExcelArgument(Description = "Output object name")] string ObjectName,
[ExcelArgument(Description = "Input portfolio object name")] string PortfolioName,
[ExcelArgument(Description = "Date to filter out on-or-before")] DateTime FilterDate)
{
return ExcelHelper.Execute(_logger, () =>
{
var pFolioCache = ContainerStores.GetObjectCache<Portfolio>();
var pfIn = pFolioCache.GetObjectOrThrow(PortfolioName, $"Portfolio {PortfolioName} not found");
var pf = pfIn.Value.FilterOnSettleDate(FilterDate);
pFolioCache.PutObject(ObjectName, new SessionItem<Portfolio> { Name = ObjectName, Value = pf });
return ObjectName + '¬' + pFolioCache.GetObject(ObjectName).Version;
});
}

[ExcelFunction(Description = "Displays a portfolio of instruments", Category = CategoryNames.Instruments, Name = CategoryNames.Instruments + "_" + nameof(DisplayPortfolio), IsThreadSafe = true)]
public static object DisplayPortfolio(
[ExcelArgument(Description = "Object name")] string ObjectName)
Expand Down
73 changes: 62 additions & 11 deletions clients/Qwack.Excel/Risk/RiskFunctions.cs
Expand Up @@ -11,6 +11,8 @@
using Qwack.Excel.Instruments;
using Qwack.Models.Risk;
using Qwack.Core.Instruments.Funding;
using System.Linq;
using System.Collections.Generic;

namespace Qwack.Excel.Curves
{
Expand Down Expand Up @@ -119,15 +121,45 @@ public class RiskFunctions
});
}

[ExcelFunction(Description = "Returns fx delta of a portfolio given an AssetFx model", Category = CategoryNames.Risk, Name = CategoryNames.Risk + "_" + nameof(PortfolioFxDeltaSpecific), IsThreadSafe = true)]
public static object PortfolioFxDeltaSpecific(
[ExcelArgument(Description = "Result object name")] string ResultObjectName,
[ExcelArgument(Description = "Portolio object name")] string PortfolioName,
[ExcelArgument(Description = "Asset-FX or MC model name")] string ModelName,
[ExcelArgument(Description = "Pairs to bump")] object[] PairsToBump,
[ExcelArgument(Description = "Home currency, e.g. ZAR")] string HomeCcy,
[ExcelArgument(Description = "Compute gamma, default false")] object ComputeGamma)
{
return ExcelHelper.Execute(_logger, () =>
{
var gamma = ComputeGamma.OptionalExcel(false);
var model = InstrumentFunctions.GetModelFromCache(ModelName, PortfolioName);
var ccy = ContainerStores.CurrencyProvider[HomeCcy];
var pairsToBumpStr = PairsToBump.ObjectRangeToVector<string>();
var pairsToBump = pairsToBumpStr.Select(p => p.FxPairFromString(ContainerStores.CurrencyProvider, ContainerStores.CalendarProvider)).ToList();
var result = model.FxDeltaSpecific(ccy, pairsToBump, ContainerStores.CurrencyProvider, gamma);
return PushCubeToCache(result, ResultObjectName);
});
}

[ExcelFunction(Description = "Returns theta and charm of a portfolio given an AssetFx model", Category = CategoryNames.Risk, Name = CategoryNames.Risk + "_" + nameof(PortfolioThetaCharm), IsThreadSafe = true)]
public static object PortfolioThetaCharm(
[ExcelArgument(Description = "Result object name")] string ResultObjectName,
[ExcelArgument(Description = "Portolio object name")] string PortfolioName,
[ExcelArgument(Description = "Asset-FX model name")] string ModelName,
[ExcelArgument(Description = "Fwd value date, usually T+1")] DateTime FwdValDate,
[ExcelArgument(Description = "Reporting currency")] string ReportingCcy)
[ExcelArgument(Description = "Reporting currency")] string ReportingCcy,
[ExcelArgument(Description = "List of FxPairs for fx metrics")] object[] PairsToRisk)
{
return ExcelHelper.Execute(_logger, () => ThetaCharm(ResultObjectName, PortfolioName, ModelName, FwdValDate, ReportingCcy, true));

return ExcelHelper.Execute(_logger, () =>
{
var pairs = (PairsToRisk == null || !PairsToRisk.Any() || PairsToRisk.First() is ExcelMissing) ? null :
PairsToRisk.ObjectRangeToVector<string>()
.Select(x => x.FxPairFromString(ContainerStores.CurrencyProvider, ContainerStores.CalendarProvider))
.ToList();
return ThetaCharm(ResultObjectName, PortfolioName, ModelName, FwdValDate, ReportingCcy, true, pairs);
});
}

[ExcelFunction(Description = "Returns theta of a portfolio given an AssetFx model", Category = CategoryNames.Risk, Name = CategoryNames.Risk + "_" + nameof(PortfolioTheta), IsThreadSafe = true)]
Expand All @@ -138,14 +170,14 @@ public class RiskFunctions
[ExcelArgument(Description = "Fwd value date, usually T+1")] DateTime FwdValDate,
[ExcelArgument(Description = "Reporting currency")] string ReportingCcy)
{
return ExcelHelper.Execute(_logger, () => ThetaCharm(ResultObjectName, PortfolioName, ModelName, FwdValDate, ReportingCcy, false));
return ExcelHelper.Execute(_logger, () => ThetaCharm(ResultObjectName, PortfolioName, ModelName, FwdValDate, ReportingCcy, false, null));
}

private static string ThetaCharm(string ResultObjectName, string PortfolioName, string ModelName,DateTime FwdValDate, string ReportingCcy, bool computeCharm)
private static string ThetaCharm(string ResultObjectName, string PortfolioName, string ModelName,DateTime FwdValDate, string ReportingCcy, bool computeCharm, List<FxPair> fxPairs=null)
{
var model = InstrumentFunctions.GetModelFromCache(ModelName, PortfolioName);
var ccy = ContainerStores.CurrencyProvider[ReportingCcy];
var result = model.AssetThetaCharm(FwdValDate, ccy, ContainerStores.CurrencyProvider, computeCharm);
var result = model.AssetThetaCharm(FwdValDate, ccy, ContainerStores.CurrencyProvider, computeCharm, fxPairs);
return PushCubeToCache(result, ResultObjectName);
}

Expand Down Expand Up @@ -308,7 +340,8 @@ private static string ThetaCharm(string ResultObjectName, string PortfolioName,
[ExcelArgument(Description = "Bump step size asset")] double BumpStepAsset,
[ExcelArgument(Description = "Bump step size fx")] double BumpStepFx,
[ExcelArgument(Description = "Risk metric to produce for each scenario")] object RiskMetric,
[ExcelArgument(Description = "Return differential to base case, default True")] object ReturnDiff)
[ExcelArgument(Description = "Return differential to base case, default True")] object ReturnDiff,
[ExcelArgument(Description = "List of FxPairs for fx metrics")] object[] PairsToRisk)
{
return ExcelHelper.Execute(_logger, () =>
{
Expand All @@ -320,20 +353,38 @@ private static string ThetaCharm(string ResultObjectName, string PortfolioName,
throw new Exception($"Unknown risk metric {RiskMetric}");
var retDiff = ReturnDiff.OptionalExcel(true);
var isFxFx = AssetId.Length == 7 && AssetId[3] == '/';
var isFxFx = AssetId.Length == 7 && AssetId[3] == '/' &&
Currency.Length == 7 && Currency[3] == '/';
ICube result = null;
if (isFxFx)
{
//var ccy = ContainerStores.CurrencyProvider.GetCurrency(Currency);
//var ccy2 = ContainerStores.CurrencyProvider.GetCurrency(AssetId.Substring(4));
//var riskMatrix = new RiskMatrix(ccy2, ccy, bType, metric, BumpStepAsset, BumpStepFx, NScenarios, ContainerStores.CurrencyProvider, retDiff);
//result = riskMatrix.Generate(model, model.Portfolio);
var pair1 = Currency.FxPairFromString(ContainerStores.CurrencyProvider, ContainerStores.CalendarProvider);
var pair2 = AssetId.FxPairFromString(ContainerStores.CurrencyProvider, ContainerStores.CalendarProvider);
var riskMatrix = new RiskMatrix(pair2, pair1, bType, metric, BumpStepAsset, BumpStepFx, NScenarios, ContainerStores.CurrencyProvider, retDiff);
if(PairsToRisk!=null && PairsToRisk.Any())
{
riskMatrix.FxPairsForDelta = PairsToRisk.ObjectRangeToVector<string>()
.Select(x => x.FxPairFromString(ContainerStores.CurrencyProvider, ContainerStores.CalendarProvider))
.ToList();
}
result = riskMatrix.Generate(model, model.Portfolio);
}
else
{
var ccy = ContainerStores.CurrencyProvider.GetCurrency(Currency);
var riskMatrix = new RiskMatrix(AssetId, ccy, bType, metric, BumpStepAsset, BumpStepFx, NScenarios, ContainerStores.CurrencyProvider, retDiff);
if (PairsToRisk != null && PairsToRisk.Any())
{
riskMatrix.FxPairsForDelta = PairsToRisk.ObjectRangeToVector<string>()
.Select(x => x.FxPairFromString(ContainerStores.CurrencyProvider, ContainerStores.CalendarProvider))
.ToList();
}
result = riskMatrix.Generate(model, model.Portfolio);
}
Expand Down
3 changes: 3 additions & 0 deletions src/Qwack.Core/Basic/FxMatrix.cs
Expand Up @@ -49,6 +49,9 @@ public FxPair GetFxPair(string pair)

public double GetSpotRate(Currency ccy) => SpotRates.TryGetValue(ccy, out var spotRate) ? spotRate : throw new Exception($"Spot rate for currency {ccy.Ccy} not found");

public bool TryGetSpotRate(Currency ccy, out double spotRate) => SpotRates.TryGetValue(ccy, out spotRate);


public FxPair GetFxPair(Currency domesticCcy, Currency foreignCcy)
{
if(domesticCcy==foreignCcy)
Expand Down
10 changes: 8 additions & 2 deletions src/Qwack.Core/Cubes/CubeEx.cs
Expand Up @@ -155,7 +155,7 @@ public static ICube Pivot(this ICube cube, string[] fieldsToAggregateBy, Aggrega
return outCube;
}

public static ICube Filter(this ICube cube, Dictionary<string, object> fieldsToFilterOn)
public static ICube Filter(this ICube cube, Dictionary<string, object> fieldsToFilterOn, bool filterOut=false)
{
foreach (var fieldToFilterOn in fieldsToFilterOn.Keys)
if (!cube.DataTypes.ContainsKey(fieldToFilterOn))
Expand All @@ -178,6 +178,9 @@ public static ICube Filter(this ICube cube, Dictionary<string, object> fieldsToF
break;
}
}
if (filterOut)
rowIsRelevant = !rowIsRelevant;

if (rowIsRelevant)
outCube.AddRow(row.MetaData, row.Value);
}
Expand Down Expand Up @@ -236,7 +239,7 @@ public static ICube BucketTimeAxis(this ICube cube, string timeFieldName, string
return output;
}

public static ICube Filter(this ICube cube, List<KeyValuePair<string, object>> fieldsToFilterOn)
public static ICube Filter(this ICube cube, List<KeyValuePair<string, object>> fieldsToFilterOn, bool filterOut=false)
{
foreach (var fieldToFilterOn in fieldsToFilterOn.Select(x=>x.Key))
if (!cube.DataTypes.ContainsKey(fieldToFilterOn))
Expand Down Expand Up @@ -270,6 +273,9 @@ public static ICube Filter(this ICube cube, List<KeyValuePair<string, object>> f
break;
}
}
if (filterOut)
rowIsRelevant = !rowIsRelevant;

if (rowIsRelevant)
outCube.AddRow(row.MetaData, row.Value);
}
Expand Down
3 changes: 2 additions & 1 deletion src/Qwack.Core/Instruments/Funding/CashBalance.cs
Expand Up @@ -58,7 +58,8 @@ public CashBalance(Currency currency, double notional, DateTime? payDate = null)
Notional = Notional,
PillarDate = PillarDate,
SolveCurve = SolveCurve,
TradeId = TradeId
TradeId = TradeId,
PortfolioName = PortfolioName
};

public IFundingInstrument SetParRate(double parRate) => Clone();
Expand Down
9 changes: 8 additions & 1 deletion src/Qwack.Core/Instruments/Portfolio.cs
Expand Up @@ -420,7 +420,11 @@ public static Portfolio RollWithLifecycle(this Portfolio pf, DateTime rollfromDa
wrapper.CashBalances.Add(new CashBalance(wfxf.ForeignCCY, -wfxf.DomesticQuantity * wfxf.Strike) { TradeId = i.TradeId + "f" });
} break;
case FixedRateLoanDeposit wl:
if (wl.EndDate < rollToDate && wl.EndDate >= rollfromDate)
if (wl.StartDate < rollToDate && wl.StartDate >= rollfromDate)
{
wrapper.CashBalances.Add(new CashBalance(wl.Currency, wl.Notional) { TradeId = i.TradeId + "nStart" });
}
else if (wl.EndDate < rollToDate && wl.EndDate >= rollfromDate)
{
wrapper.CashBalances.Add(new CashBalance(wl.Currency, -wl.Notional) { TradeId = i.TradeId + "n" });
}
Expand Down Expand Up @@ -463,5 +467,8 @@ public static Portfolio CashAccrual(this Portfolio pf, DateTime rollToDate, IFun

return o;
}

public static Portfolio FilterOnSettleDate(this Portfolio pf, DateTime filterOutOnOrBefore)
=> new Portfolio { Instruments = pf.Instruments.Where(x => x.LastSensitivityDate > filterOutOnOrBefore || (x is CashBalance)).ToList() };
}
}
2 changes: 2 additions & 0 deletions src/Qwack.Models/Models/AssetProductEx.cs
Expand Up @@ -851,6 +851,8 @@ private static (double pv, string ccy, string tradeId, string tradeType) Compute
break;
case CashWrapper wrapper:
(pv, ccy, tradeId, tradeType) = ComputePV(wrapper.UnderlyingInstrument, model, pvCcy);
if (reportingCurrency != null)
ccy = reportingCurrency.Ccy;
foreach(var cb in wrapper.CashBalances)
{
var p = ComputePV(cb, model, pvCcy);
Expand Down

0 comments on commit d417e14

Please sign in to comment.