//------------------------------------------------------------------------------------------------ public static double calcFwdBondMeasure(string measure, AnalyticsBondStaticData bondstatic, double fwdPrice, DateTime asofDate, DateTime settle, SpreadConventions convs, SortedDictionary<DateTime,DiscountFactors> ForecastCurves, SortedDictionary<DateTime, DiscountFactors> DiscountCurves, List<DateTime> hols, Tuple<DateTime,double> previous = null) //------------------------------------------------------------------------------------------------ { //string identifier = bondstatic.Id; // Setup Params DateTime FwdDate = settle; DateTime effectiveDate = bondstatic.EffectiveDate; DateTime firstCpnDate = bondstatic.FirstCouponDate; DateTime maturity = bondstatic.MaturityDate; double coupon = bondstatic.Coupon; long bondCpnFreq = bondstatic.CpnFreq; long fixedFreq = convs.SwapFixedFrequency; long floatFreq = convs.SwapFloatFrequency; BondAnalytics.Country eCountry = bondstatic.Country; BondAnalytics.DayCountType dct = convs.DayCount; bool bOIS = !convs.bForecastCurve; int blendIdx = convs.BlendIndex; double output = double.NaN; switch (measure) { case "Yield": { output = 10000.0 * BondAnalytics.SolveYield(eCountry, FwdDate, fwdPrice, effectiveDate, firstCpnDate, maturity, coupon, bondCpnFreq)[0]; break; } case "TrueSpread": { if (!ForecastCurves.ContainsKey(asofDate)) { output = double.NaN; break; } DiscountFactors curve = ForecastCurves[asofDate]; DateTime[] dfDates = curve.CurveDates; double[] dfs = curve.Dfs; // Check. Sometimes (holidays) the first date on the curve can be repeated, which will cause an exception in the pricer. if (dfDates[0] == dfDates[1]) { dfDates = dfDates.GetSubArray(1, dfDates.GetLength(0)).ToArray(); dfs = dfs.GetSubArray(1, dfs.GetLength(0)).ToArray(); } output = -10000.0 * BondAnalytics.SolveZSpread(eCountry, FwdDate, fwdPrice, effectiveDate, firstCpnDate, maturity, coupon, bondCpnFreq, dfDates, dfs, hols); break; } case "MMS": { if (!ForecastCurves.ContainsKey(asofDate)) { output = double.NaN; break; } var discountCurve = DiscountCurves[asofDate]; var forecastCurve = ForecastCurves[asofDate]; DateTime[] discDfDates = discountCurve.CurveDates; DateTime[] fcstDfDates = forecastCurve.CurveDates; double[] discDfs = discountCurve.Dfs; double[] fcstDfs = forecastCurve.Dfs; //double mms = BondAnalytics.CalcMMS(FwdDate, maturity, dct, fixedFreq, floatFreq, discDfDates, discDfs, fcstDfDates, fcstDfs, hols, null, null, null, null, null, firstCpnDate, blendIdx); var mms = SwapAnalytics.CalcSwapRate(FwdDate, maturity, 0.0, dct, fixedFreq, dct, floatFreq, discDfDates, discDfs, fcstDfDates, fcstDfs, hols, 0.0, blendIdx, bOIS ? blendIdx : 0); output = 10000.0 * mms; break; } case "Spread": { if (!ForecastCurves.ContainsKey(asofDate)) { output = double.NaN; break; } var discountCurve = DiscountCurves[asofDate]; var forecastCurve = ForecastCurves[asofDate]; DateTime[] discDfDates = discountCurve.CurveDates; DateTime[] fcstDfDates = forecastCurve.CurveDates; double[] discDfs = discountCurve.Dfs; double[] fcstDfs = forecastCurve.Dfs; var mms = SwapAnalytics.CalcSwapRate(FwdDate, maturity, 0.0, dct, fixedFreq, dct, floatFreq, discDfDates, discDfs, fcstDfDates, fcstDfs, hols, 0.0, blendIdx, bOIS ? blendIdx : 0); double yield = 0.0; yield = BondAnalytics.SolveYield(eCountry, FwdDate, fwdPrice, effectiveDate, firstCpnDate, maturity, coupon, bondCpnFreq)[0]; output = 10000.0 * (mms - yield); break; } case "FuturesDV01": { double previous_price = double.NaN; if (previous != null) { previous_price = previous.Item2; } // Get the CTD Bond DV01 here double dv01 = BondAnalytics.SolveYield(eCountry, FwdDate, fwdPrice, effectiveDate, firstCpnDate, maturity, coupon, bondCpnFreq)[1] / 1000000 * 100; // Get next price output = dv01; break; } case "MMSDV01": { // Setup current swap curve if (!ForecastCurves.ContainsKey(asofDate)) { output = double.NaN; break; } var discountCurve = DiscountCurves[asofDate]; var forecastCurve = ForecastCurves[asofDate]; double rate = BondAnalytics.CalcMMS(FwdDate, maturity, dct, fixedFreq, floatFreq, discountCurve.CurveDates, discountCurve.Dfs, forecastCurve.CurveDates, forecastCurve.Dfs, hols, null, null, null, null, null, firstCpnDate, blendIdx); // Get Current DV01 double dv01 = SwapAnalytics.CalcSwapDv01(FwdDate, maturity, 1, rate, dct, dct, fixedFreq, floatFreq, discountCurve.CurveDates, discountCurve.Dfs, forecastCurve.CurveDates, forecastCurve.Dfs, hols); // DV01 adjusted PnL output = dv01 * 100; // 100 Notional Terms here break; } case "FuturesPnlDV01Adj": { double previous_fwdprice = double.NaN; if (previous != null) { previous_fwdprice = previous.Item2; } // Get the CTD Bond DV01 here double dv01 = BondAnalytics.SolveYield(eCountry, FwdDate, fwdPrice, effectiveDate, firstCpnDate, maturity, coupon, bondCpnFreq)[1] / 1000000 * 100; // Get next price output = (fwdPrice - previous_fwdprice) / Math.Abs(dv01); //Normalise into 100 par terms break; } case "FuturesPnl": { double previous_fwdprice = double.NaN; if (previous != null) { previous_fwdprice = previous.Item2; } // PnL in notional terms output = fwdPrice - previous_fwdprice; // Actual pnl is given by price change of the CTD bond break; } case "SwapPnl": { // Previous date, curve to get original MMS rate DateTime previousDate = DateTime.MinValue; if (previous != null) { previousDate = previous.Item1; } if (!ForecastCurves.ContainsKey(previousDate)) { output = double.NaN; break; } var discountCurve = DiscountCurves[previousDate]; var forecastCurve = ForecastCurves[previousDate]; double previouRate = BondAnalytics.CalcMMS(FwdDate, maturity, dct, fixedFreq, floatFreq, discountCurve.CurveDates, discountCurve.Dfs, forecastCurve.CurveDates, forecastCurve.Dfs, hols, null, null, null, null, null, firstCpnDate, blendIdx); previouRate *= 100; // For cents // Setup current swap curve if (!ForecastCurves.ContainsKey(asofDate)) { output = double.NaN; break; } discountCurve = DiscountCurves[asofDate]; forecastCurve = ForecastCurves[asofDate]; // Get PnL of rate //priceSwap(DateTime startDate, DateTime endDate, int payRec, double fixedRate, BondAnalytics.DayCountType fixedDctType, long fixedFreq, double floatSpread, BondAnalytics.DayCountType floatDctType, long floatFreq, DateTime[] discCurveDates, double[] discDfs, DateTime[] fcstCurveDates, double[] fcstDfs, List<DateTime> holidays, double lastFixing = 0.0, int blendIndexD = 0, int blendIndexF = 0, bool incPrin = false) double pnl = SwapAnalytics.PriceSwap(FwdDate, maturity, 1, previouRate, dct, fixedFreq, 0.0, dct, floatFreq, discountCurve.CurveDates, discountCurve.Dfs, forecastCurve.CurveDates, forecastCurve.Dfs, hols); // Raw PnL innotional terms output = pnl * 100; // pnl in cents break; } case "SwapPnlDV01Adj": { // Previous date, curve to get original MMS rate DateTime previousDate = DateTime.MinValue; if (previous != null) { previousDate = previous.Item1; } if (!ForecastCurves.ContainsKey(previousDate)) { output = double.NaN; break; } var discountCurve = DiscountCurves[previousDate]; var forecastCurve = ForecastCurves[previousDate]; double previousRate = BondAnalytics.CalcMMS(FwdDate, maturity, dct, fixedFreq, floatFreq, discountCurve.CurveDates, discountCurve.Dfs, forecastCurve.CurveDates, forecastCurve.Dfs, hols, null, null, null, null, null, firstCpnDate, blendIdx); previousRate *= 100; // To put this into cents // Setup current swap curve if (!ForecastCurves.ContainsKey(asofDate)) { output = double.NaN; break; } discountCurve = DiscountCurves[asofDate]; forecastCurve = ForecastCurves[asofDate]; // Get PnL of rate double pnl = SwapAnalytics.PriceSwap(FwdDate, maturity, 1, previousRate, dct, fixedFreq, 0.0, dct, floatFreq, discountCurve.CurveDates, discountCurve.Dfs, forecastCurve.CurveDates, forecastCurve.Dfs, hols); // Get Current DV01 double dv01 = SwapAnalytics.CalcSwapDv01(FwdDate, maturity, 1, previousRate, dct, dct, fixedFreq, floatFreq, discountCurve.CurveDates, discountCurve.Dfs, forecastCurve.CurveDates, forecastCurve.Dfs, hols); // DV01 adjusted PnL output = pnl / Math.Abs(dv01); // 100 Notional Terms here break; } case "SpreadPnlDV01Adj": { // FUTURES. double previous_fwdprice = double.NaN; if (previous != null) { previous_fwdprice = previous.Item2; } // Get the CTD Bond DV01 here double dv01 = BondAnalytics.SolveYield(eCountry, FwdDate, fwdPrice, effectiveDate, firstCpnDate, maturity, coupon, bondCpnFreq)[1] / 1000000 * 100; // Get next price double futures01AdjPnl = (fwdPrice - previous_fwdprice) / Math.Abs(dv01); // SWAP. // Previous date, curve to get original MMS rate DateTime previousDate = DateTime.MinValue; if (previous != null) { previousDate = previous.Item1; } if (!ForecastCurves.ContainsKey(previousDate)) { output = double.NaN; break; } var discountCurve = DiscountCurves[previousDate]; var forecastCurve = ForecastCurves[previousDate]; double previousRate = BondAnalytics.CalcMMS(FwdDate, maturity, dct, fixedFreq, floatFreq, discountCurve.CurveDates, discountCurve.Dfs, forecastCurve.CurveDates, forecastCurve.Dfs, hols, null, null, null, null, null, firstCpnDate, blendIdx); previousRate *= 100; // To put in cents // Setup current swap curve if (!ForecastCurves.ContainsKey(asofDate)) { output = double.NaN; break; } discountCurve = DiscountCurves[asofDate]; forecastCurve = ForecastCurves[asofDate]; // Get PnL of rate double pnl = SwapAnalytics.PriceSwap(FwdDate, maturity, 1, previousRate, dct, fixedFreq, 0.0, dct, floatFreq, discountCurve.CurveDates, discountCurve.Dfs, forecastCurve.CurveDates, forecastCurve.Dfs, hols); // Get Current DV01 dv01 = SwapAnalytics.CalcSwapDv01(FwdDate, maturity, 1, previousRate, dct, dct, fixedFreq, floatFreq, discountCurve.CurveDates, discountCurve.Dfs, forecastCurve.CurveDates, forecastCurve.Dfs, hols); // DV01 adjusted PnL double swap01AdjPnl = pnl / Math.Abs(dv01); // Notional Terms here output = swap01AdjPnl - futures01AdjPnl; break; } case "SpreadHedgeRatio": { // Get Bond DV01 double previous_price = double.NaN; if (previous != null) { previous_price = previous.Item2; } // Get the CTD Bond DV01 here double futuresDv01 = BondAnalytics.SolveYield(eCountry, FwdDate, fwdPrice, effectiveDate, firstCpnDate, maturity, coupon, bondCpnFreq)[1] / 1000000;// Match futures notional (1mil notional. prices in cents) // Setup current swap curve if (!ForecastCurves.ContainsKey(asofDate)) { output = double.NaN; break; } var discountCurve = DiscountCurves[asofDate]; var forecastCurve = ForecastCurves[asofDate]; double mms = BondAnalytics.CalcMMS(FwdDate, maturity, dct, fixedFreq, floatFreq, discountCurve.CurveDates, discountCurve.Dfs, forecastCurve.CurveDates, forecastCurve.Dfs, hols, null, null, null, null, null, firstCpnDate, blendIdx); mms *= 100; // Get Swap DV01 double swapDv01 = SwapAnalytics.CalcSwapDv01(FwdDate, maturity, 1, mms, dct, dct, fixedFreq, floatFreq, discountCurve.CurveDates, discountCurve.Dfs, forecastCurve.CurveDates, forecastCurve.Dfs, hols); output = Math.Abs(futuresDv01) / Math.Abs(swapDv01); break; } } return output; }
//------------------------------------------------------------------------------------------------ public static double calcFwdBondMeasure(string measure, AnalyticsBondStaticData bondstatic, double fwdPrice, DateTime asofDate, DateTime settle, SpreadConventions convs, SortedDictionary<DateTime, Tuple<DateTime,double>[]> ForecastCurves, SortedDictionary<DateTime, Tuple<DateTime, double>[]> DiscountCurves, List<DateTime> hols, Tuple<DateTime, double> previous = null) //------------------------------------------------------------------------------------------------ { // Cast forecast and discount curves var newForecastCurves = new SortedDictionary<DateTime, DiscountFactors>( ForecastCurves.ToDictionary(kvp => kvp.Key, kvp => new DiscountFactors(kvp.Value))); var newDiscountCurves = new SortedDictionary<DateTime, DiscountFactors>( DiscountCurves.ToDictionary(kvp => kvp.Key, kvp => new DiscountFactors(kvp.Value))); // Run them through original code return calcFwdBondMeasure(measure,bondstatic,fwdPrice,asofDate,settle,convs,newForecastCurves,newDiscountCurves,hols,previous); }
private static string GetBondPriceSnapSource(AnalyticsBondStaticData bondData) { switch (bondData.Country) { case BondAnalytics.Country.US: return "snaps-nyk"; case BondAnalytics.Country.JP: return "snaps-jpy"; case BondAnalytics.Country.AU: return "snaps-aus"; default: return "snaps-ldn"; } }
// Retrieve historical bond prices from Symmetry.TS public static double[] GetHistoricPrices(this ICarbonClient client, AnalyticsBondStaticData bondData, List<DateTime> dateSeries) { double[] result = new double[dateSeries.Count]; for (int i = 0; i < dateSeries.Count; i++) { object o = null; try { var snapSource = GetBondPriceSnapSource(bondData); o = client.GetCbnTimeSeriesDataCell(bondData.Id, snapSource, dateSeries[i], "close"); } catch { // Missing data } if (o is double) { result[i] = (double)o; } } return result; }
private static IReadOnlyDictionary<string, IReadOnlyDictionary<DateTime, double>> BulkGetHistoricPricesFromCarbonOneBond(ICarbonClient client, AnalyticsBondStaticData bondData, List<DateTime> dateSeries) { var dict = new Dictionary<DateTime, double>(); var dates = new List<DateTime>(); if (bondData != null) { foreach (var date in dateSeries) { var d = GetBondPricesFromCache(bondData.Id, date); if (d.HasValue) { dict[date] = d.Value; } else if (date <= DateTime.Today) { dates.Add(date); } } if (dates.Count > 0) { var inputDateKind = dateSeries.FirstOrDefault().Kind; var snapSource = GetBondPriceSnapSource(bondData); var end = dates.Max(); var start = dates.Min(); SLog.log.InfoFormat("Request bond price for {0} days", dates.Count); start = new DateTime(start.Year, start.Month, start.Day, 0, 0, 0, DateTimeKind.Utc); end = new DateTime(end.Year, end.Month, end.Day, 0, 0, 0, DateTimeKind.Utc); var datas = client.GetTimeSeriesData(bondData.Id, snapSource, start, end, "close"); if (datas != null) { foreach (var data in datas) { try { var close = data.Value.AsDouble(); var date = new DateTime(data.Key.Year, data.Key.Month, data.Key.Day, 0, 0, 0, inputDateKind); dict[date] = close; AddBondPricesToCache(bondData.Id, date, close); } catch (Exception ex) { SLog.log.ErrorFormat("Failed to convert close price for {0} : {1}", bondData.Id, ex); } } } } } return new Dictionary<string, IReadOnlyDictionary<DateTime, double>> { { bondData.Id, dict } }; }
private static AnalyticsBondStaticData ConvertCarbonBondStaticToAnalyticBondStatic(ICarbonClient client, ITimeSeries data) { var result = new AnalyticsBondStaticData { Id = client.GetSingleStaticDataInternal<string>(data, "isin"), MaturityDate = client.GetSingleStaticDataInternal<DateTime>(data, "maturityDt"), EffectiveDate = client.GetSingleStaticDataInternal<DateTime>(data, "effectiveDt") }; // Check if there is a more recent effective date due to subsequent auctions. DateTime latestEffectiveDate; object o = data.GetCarbonBondMostRecentEffectiveDate(); if (o is DateTime) latestEffectiveDate = (DateTime)o; else latestEffectiveDate = result.EffectiveDate; result.FirstCouponDate = client.GetSingleStaticDataInternal<DateTime>(data, "firstCpnDt"); result.Coupon = client.GetSingleStaticDataInternal<double>(data, "cpnRate"); return DecorateAnalyticsBondStaticData(result, latestEffectiveDate); }
private static AnalyticsBondStaticData DecorateAnalyticsBondStaticData(AnalyticsBondStaticData result, DateTime latestEffectiveDate) { if (!result.MaturityDate.IsValidDate() || !result.EffectiveDate.IsValidDate() || !result.FirstCouponDate.IsValidDate()) { return null; } if (result.Id.Substring(0, 2) == "US") { // This is relevant for US bonds where there are standard maturities for new issuance - eg. 2,3,5,7,10,30yr tenors. double yrs = (result.MaturityDate - latestEffectiveDate).TotalDays / 365.25; if (yrs < 2.5) result.Series = 2; else if (yrs < 4) result.Series = 3; else if (yrs < 6) result.Series = 5; else if (yrs < 8.5) result.Series = 7; else if (yrs < 20) result.Series = 10; else result.Series = 30; } else result.Series = 0; BondAnalytics.Country eCountry; // Map to country using first two characters of ISIN BondAnalytics.CountryMappings.TryGetValue(result.Id.Substring(0, 2), out eCountry); result.Country = eCountry; result.CpnFreq = 6; switch (eCountry) { case BondAnalytics.Country.US: result.Ccy = "USD"; result.SettleDelay = 1; break; case BondAnalytics.Country.BE: case BondAnalytics.Country.DE: case BondAnalytics.Country.ES: case BondAnalytics.Country.FR: case BondAnalytics.Country.IT: result.Ccy = "EUR"; result.CpnFreq = (eCountry == BondAnalytics.Country.IT ? 6 : 12); result.SettleDelay = 2; break; case BondAnalytics.Country.UK: result.Ccy = "GBP"; result.SettleDelay = 1; break; case BondAnalytics.Country.CA: result.Ccy = "CAD"; result.SettleDelay = 1; break; case BondAnalytics.Country.JP: result.Ccy = "JPY"; result.SettleDelay = 2; break; } return result; }
private static AnalyticsBondStaticData ConvertCarbonBondToAnalyticsBondStaticData(CarbonBond bond) { var result = new AnalyticsBondStaticData { Id = bond.Isin, MaturityDate = bond.Maturity.ToDateTime(), EffectiveDate = bond.EffectiveDate.HasValue ? bond.EffectiveDate.Value.ToDateTime() : default(DateTime), FirstCouponDate = bond.FirstCouponDate.HasValue ? bond.FirstCouponDate.Value.ToDateTime() : default(DateTime), Coupon = bond.Coupon ?? default(double) }; var latestEffectiveDate = result.EffectiveDate; if (bond.Auctions != null && bond.Auctions.Count > 0) { var effectiveDates = bond.Auctions.Where(a => a.EffectiveDate.HasValue) .Select(a => a.EffectiveDate.Value.ToDateTime()) .ToList(); if (effectiveDates.Count > 0) { latestEffectiveDate = effectiveDates.Max(); } } return DecorateAnalyticsBondStaticData(result, latestEffectiveDate); }
//------------------------------------------------------------------------------------------------ public double calcFuturesMeasure(string measure, string identifier, double price, DateTime asofDate, Tuple<DateTime, double> previous = null) //------------------------------------------------------------------------------------------------ { // Get conventions var staticRow = CombinedStatic.Rows.TryGet(identifier).ValueOrDefault; //staticRow.Print(); if (staticRow == null) return double.NaN; // Setup Params DateTime FwdDate = staticRow.GetAs<DateTime>("fut_dlv_dt_last"); // Bond static var bondstatic = new AnalyticsBondStaticData(); bondstatic.EffectiveDate = TimeSeriesUtilities.LazySafeTryGetAs(staticRow, "EffectiveDate", DateTime.MinValue); //staticRow.TryGetAs<DateTime>("EffectiveDate") .OrDefault(DateTime.MinValue); bondstatic.FirstCouponDate = TimeSeriesUtilities.LazySafeTryGetAs(staticRow, "FirstCouponDate", DateTime.MinValue); //staticRow.TryGetAs<DateTime>("FirstCouponDate", ConversionKind.Exact).ValueOrDefault; bondstatic.MaturityDate = TimeSeriesUtilities.LazySafeTryGetAs(staticRow, "Maturity", DateTime.MinValue); //staticRow.TryGetAs<DateTime>("Maturity").OrDefault(DateTime.MinValue); bondstatic.Coupon = staticRow.GetAs<double>("Coupon"); bondstatic.Country = Conv_.Country; bondstatic.Id = identifier; bondstatic.CpnFreq = Conv_.BondCouponFrequency; // Prices - adjusted into forward prices double convFactor = staticRow.TryGetAs<double>("fut_cnvs_factor").OrDefault(double.NaN); double fwdPrice = convFactor * price; var previousFwd = previous == null ? null : new Tuple<DateTime, double>(previous.Item1, convFactor*previous.Item2); // Main loop if (measure == "Price") return price; double metric = MeasureCalculator.calcFwdBondMeasure(measure, bondstatic, fwdPrice, asofDate, FwdDate, Conv_, ForecastCurves, DiscountCurves, Holidays_, previousFwd); return metric; }