private void DoTestPureBond(Bond bond, Double expected, String date = "2017-10-20", Double precision = 1e-4) { var bondengine = new BondEngine(); var market = TestMarket_Jin(date, spot: 6.07, CBprice: 97.5); var res = bondengine.Calculate(bond, market, PricingRequest.Pv | PricingRequest.ZeroSpread | PricingRequest.ZeroSpreadDelta | PricingRequest.Ytm); var pv = res.Pv; Assert.AreEqual(expected, pv, precision); }
public void PureBondPnLTest() { var bond = GeliBondPart(); var t = "2017-10-25"; var t1 = "2017-10-26"; var bondengine = new BondEngine(); var tClose = 97.5; var t1Close = 96.5; var tMarket = TestMarket_Jin(t, spot: 6.07, CBprice: tClose, yield: 0.05); //actually bond price var tMarketRollToT1 = TestMarket_Jin(t1, spot: 6.07, CBprice: tClose, yield: 0.05); var t1Market = TestMarket_Jin(t1, spot: 6.07, CBprice: t1Close, yield: 0.055); var actualPnl = t1Close - tClose; var pricingRequests = PricingRequest.Pv | PricingRequest.ZeroSpread | PricingRequest.ZeroSpreadDelta | PricingRequest.Ytm | PricingRequest.AiEod | PricingRequest.KeyRateDv01 | PricingRequest.Convexity; var tResult = bondengine.Calculate(bond, tMarket, pricingRequests); var t1Result = bondengine.Calculate(bond, t1Market, pricingRequests); var tResultRecalibrated = bondengine.Calculate(bond, tMarketRollToT1, PricingRequest.Pv); //1. var timePnL = tResultRecalibrated.Pv - tResult.Pv; var curveMove = (0.055 - 0.05) * 1e4; var pv01 = tResult.KeyRateDv01.First(); var pv01PnL = pv01.Value.Select(x => new CurveRisk(x.Tenor, 1.0 * x.Risk * curveMove)); var curvePnL = pv01PnL.Sum(x => x.Risk); var zspreadPnL = (t1Result.ZeroSpread - tResult.ZeroSpread) * tResult.ZeroSpreadDelta * 1e4; var carryPnL = t1Result.Ai - tResult.Ai; var rolllDown = timePnL - carryPnL; var pnlConverixy = 0.5 * Math.Pow(t1Result.Ytm - tResult.Ytm, 2.0) * tResult.Convexity * tClose; //originally without var estimatedPL = timePnL + curvePnL + zspreadPnL + pnlConverixy; Assert.AreEqual(true, Math.Abs((estimatedPL - actualPnl) / actualPnl) < 0.01); }
public void TestQbTfDeliverableBondYield() { var files = new[] { "QbTF1606CfTest.txt", "QbTF1609CfTest.txt", "QbTF1612CfTest.txt" }; for (var k = 0; k < files.Length; ++k) { var bondPrices = new Dictionary <string, Tuple <PriceQuoteType, double> >(); var targetYtm = new List <double>(); var targetAi = new List <double>(); var bonds = File.ReadAllLines(@"./Data/BondFuture/" + files[k]) .Select(x => { var splits = x.Split(','); var startDate = new Date(DateTime.Parse(splits[3])); var maturityDate = new Date(DateTime.Parse(splits[4])); var rate = Convert.ToDouble(splits[1]); var frequency = splits[2] == "Annual" ? Frequency.Annual : Frequency.SemiAnnual; targetYtm.Add(Convert.ToDouble(splits[8])); targetAi.Add(Convert.ToDouble(splits[6]) - Convert.ToDouble(splits[7])); bondPrices[splits[0]] = Tuple.Create(PriceQuoteType.Dirty, Convert.ToDouble(splits[6])); return(new Bond( splits[0], startDate, maturityDate, 100.0, CurrencyCode.CNY, new FixedCoupon(rate), CalendarImpl.Get("chn_ib"), frequency, Stub.LongEnd, new ActActIsma(), new ActActIsma(), BusinessDayConvention.None, BusinessDayConvention.None, null, TradingMarket.ChinaInterBank)); }).ToArray(); var market = new BondFutureTests().TestMarket("2016-04-06", bondPrices); var engine = new BondEngine(); for (var i = 0; i < bonds.Length; ++i) { var result = engine.Calculate(bonds[i], market, PricingRequest.Ytm | PricingRequest.Ai); Assert.AreEqual(Math.Round(result.Ai, 4), targetAi[i], 1e-5); Assert.AreEqual(Math.Round(result.Ytm, 6), targetYtm[i], 1e-7); } } }
public void BondForwardTest() { var bond = new Bond( "010000", new Date(2013, 2, 25), new Date(2015, 2, 25), 100, CurrencyCode.CNY, new FixedCoupon(0.0375), CalendarImpl.Get("chn_ib"), Frequency.SemiAnnual, Stub.LongEnd, new ActActIsma(), new Act365(), BusinessDayConvention.None, BusinessDayConvention.None, null, TradingMarket.ChinaInterBank ); var bondForward = new Forward <Bond>( new Date(2013, 5, 28), new Date(2013, 8, 28), 100.0, 98.57947065, bond, CurrencyCode.CNY ); var referenceDate = new Date(2013, 8, 23); var yieldCurve = new YieldCurve( "中债国债收收益率曲线", referenceDate, new[] { Tuple.Create((ITerm) new Term("1D"), 0.035), Tuple.Create((ITerm) new Term("1Y"), 0.035) }, BusinessDayConvention.ModifiedFollowing, new Act365(), CalendarImpl.Get("chn_ib"), CurrencyCode.CNY, Compound.Simple, Interpolation.CubicHermiteMonotic, YieldCurveTrait.SpotCurve ); var market = new MarketCondition ( x => x.ValuationDate.Value = referenceDate, x => x.MktQuote.Value = new Dictionary <string, Tuple <PriceQuoteType, double> > { { "010000", Tuple.Create(PriceQuoteType.Dirty, 99.71798177) } }, x => x.DiscountCurve.Value = yieldCurve, x => x.FixingCurve.Value = yieldCurve, x => x.RiskfreeCurve.Value = yieldCurve, x => x.UnderlyingDiscountCurve.Value = yieldCurve, x => x.HistoricalIndexRates.Value = new Dictionary <IndexType, SortedDictionary <Date, double> >() ); var bondengine = new BondEngine(); var zeroSpread = bondengine.Calculate(bond, market, PricingRequest.ZeroSpread).ZeroSpread; var engine = new ForwardEngine <Bond>(); var result = engine.Calculate(bondForward, market, PricingRequest.Pv); Assert.AreEqual(-0.68888788, result.Pv, 1e-8); }
private static ConvertibleBondAnalytics computeAnalytics(IMarketCondition market, ConvertibleBond cb, double putRedemptionPrice) { //analytics part var cbSpot = market.MktQuote.Value[cb.Bond.Id].Item2; var stockPrice = market.SpotPrices.Value.Values.First(); var conversionValue = stockPrice * cb.ConversionRatio; var premiumToShareInPct = (cbSpot / conversionValue - 1) * 100; // in Pct var arbitragePL = conversionValue - cbSpot; //buy cb, sell stock var timeToMaturity = (cb.UnderlyingMaturityDate - market.ValuationDate) / 365.0; //bond analytics var bondengine = new BondEngine(); var yieldToMaturity = bondengine.Calculate(cb.Bond, market, PricingRequest.Ytm).Ytm * 100.0; var bondFloor = bondengine.Calculate(cb.Bond, market, PricingRequest.Pv).Pv; double premiumToBondInPct = (cbSpot / bondFloor - 1) * 100; //in Pct //parity vs bond floor premium var parityFloorPremiumInPct = (conversionValue / bondFloor - 1) * 100.0; //option analytics var impliedOptionValue = cbSpot - bondFloor; var impliedUnitOptionPremium = impliedOptionValue / cb.ConversionRatio; var option = createOption(strike: cb.ConversionOption.Strike, expiry: cb.ConversionOption.UnderlyingMaturityDate, firstConversionDate: cb.ConversionOption.StartDate, American: false); var engine = new AnalyticalVanillaEuropeanOptionEngine(); var impliedVol = engine.ImpliedVol(option, market, impliedUnitOptionPremium); //Note: assume simple calc here: putPrice 103, current price 109.69, then if we can put the bond now, returnOnPut = (103 - 109.69)/109.69 var returnOnPut = (putRedemptionPrice / cbSpot - 1.0) * 100; //cb style based on parity/ bond floor relationship //if parity is worth 20% more than bond floor, then stock like //if parity is worth -20% less than bond floor, then bond like //otherwise, cb is in balance mode var cbStatus = ConvertibleBondStatus.Balance; if (parityFloorPremiumInPct > 20) { cbStatus = ConvertibleBondStatus.StockLike; } else if (parityFloorPremiumInPct < -20) { cbStatus = ConvertibleBondStatus.BondLike; } return(new ConvertibleBondAnalytics( conversionValue: conversionValue, premiumToShareInPct: premiumToShareInPct, arbitragePL: arbitragePL, timeToMaturity: timeToMaturity, yieldToMaturity: yieldToMaturity, bondFloor: bondFloor, premiumToBondInPct: premiumToBondInPct, parityFloorPremiumInPct: parityFloorPremiumInPct, optionValue: impliedOptionValue, impliedVol: impliedVol, returnOnPut: returnOnPut, cbStatus: cbStatus)); }