private ISpread GetUnderlyingBondZeroSpread(Bond bond, IMarketCondition market) { if (market.CreditSpread.HasValue) { return(market.CreditSpread.Value); } else { var bMarket = market.UpdateCondition(new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, market.UnderlyingDiscountCurve.Value)); return(new ZeroSpread(BondPricingFunctions.ZeroSpread(bond, bMarket))); } }
private double CalcPv(BondFutures bondFuture, IMarketCondition market) { var discountCurve = market.DiscountCurve.Value; var dayCount = bondFuture.DayCount; var tree = _interestRateModel.Tree(GetGridTimes(bondFuture, market), 0.0, discountCurve); var deliveryTime = dayCount.CalcDayCountFraction(market.ValuationDate, bondFuture.UnderlyingMaturityDate); var indexAtDelivery = tree.IndexAtT(deliveryTime); var bonds = bondFuture.Deliverables; var ais = bonds.Select(x => x.GetAccruedInterest(bondFuture.UnderlyingMaturityDate, market, true)).ToArray(); var conversionFactors = bonds.Select(x => bondFuture.GetConversionFactor(x, market)).ToArray(); var zeroSpreads = bonds.Select((bond, i) => BondPricingFunctions.ZeroSpread(bond, market) ).ToArray(); var nNodesAtDelivery = tree.TrinomialTree.Branchings[indexAtDelivery].Size; var futurePricesAtDelivery = new double[nNodesAtDelivery]; for (var j = 0; j < nNodesAtDelivery; ++j) { var simulationRate = tree.ShortRate(indexAtDelivery, j); var bondPrices = bonds .Select((x, i) => { var cfs = x.GetCashflows(market).Where(cf => cf.PaymentDate > bondFuture.UnderlyingMaturityDate).ToArray(); var bPrice = 0.0; foreach (var cf in cfs) { var T = dayCount.CalcDayCountFraction(market.ValuationDate, cf.PaymentDate); var P = tree.DiscountBond(deliveryTime, T, simulationRate); var rate = discountCurve.Compound.CalcRateFromDf(P, T - deliveryTime); var df = 1.0 / discountCurve.Compound.CalcCompoundRate(T - deliveryTime, rate + zeroSpreads[i]); bPrice += cf.PaymentAmount * df; } return(bPrice); }).ToArray(); var futurePricesAtNodes = bondPrices .Select((price, k) => price - ais[k]) .Select((cleanPrice, k) => cleanPrice / conversionFactors[k]).ToList(); futurePricesAtDelivery[j] = futurePricesAtNodes.Min(); } return(tree.ReverseInduction(null, null, indexAtDelivery, futurePricesAtDelivery, false)[0][0]); }
//only used if we switch from CTD model to basket model private double CalcDv01(BondFutures bondFuture, IMarketCondition market) { var discountCurve = market.DiscountCurve.Value; var dayCount = bondFuture.DayCount; var tree = _interestRateModel.Tree(GetGridTimes(bondFuture, market), 0.0, discountCurve); var deliveryTime = dayCount.CalcDayCountFraction(market.ValuationDate, bondFuture.UnderlyingMaturityDate); var bonds = bondFuture.Deliverables; var ais = bonds.Select(x => x.GetAccruedInterest(bondFuture.UnderlyingMaturityDate, market, true)).ToArray(); var conversionFactors = bonds.Select(x => bondFuture.GetConversionFactor(x, market)).ToArray(); var zeroSpreads = bonds.Select((bond, i) => BondPricingFunctions.ZeroSpread(bond, market) ).ToArray(); var simulationRate = tree.ShortRate(0, 0); var bondPrices = bonds .Select((x, i) => { var cfs = x.GetCashflows(market).Where(cf => cf.PaymentDate > bondFuture.UnderlyingMaturityDate).ToArray(); var bPrice = 0.0; foreach (var cf in cfs) { var T = dayCount.CalcDayCountFraction(market.ValuationDate, cf.PaymentDate); var P = tree.DiscountBond(deliveryTime, T, simulationRate); var rate = discountCurve.Compound.CalcRateFromDf(P, T - deliveryTime); var df = 1.0 / discountCurve.Compound.CalcCompoundRate(T - deliveryTime, rate + zeroSpreads[i]); bPrice += cf.PaymentAmount * df; } return(bPrice); }).ToArray(); var futurePrices = bondPrices .Select((price, k) => price - ais[k]) .Select((cleanPrice, k) => cleanPrice / conversionFactors[k]).ToList(); var futurePrice = futurePrices.Min(); var index = futurePrices.FirstIndexOf(x => (x - futurePrice).IsAlmostZero()); var ctdBond = bonds[index]; var ctdBondPrice = market.MktQuote.Value[ctdBond.Id]; return (new BondEngine().Calculate(ctdBond, market, PricingRequest.Dv01).Dv01); }
public override IPricingResult GetRisks(Forward <Bond> trade, IMarketCondition market, PricingRequest pricingRequest) { var result = new PricingResult(market.ValuationDate, pricingRequest); var bondEngine = new BondEngine(); var bMarket = market.UpdateCondition(new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, market.UnderlyingDiscountCurve.Value)); if (result.IsRequested(PricingRequest.Dv01)) { var bondZeroSpread = BondPricingFunctions.ZeroSpread(trade.Underlying, bMarket); IMarketCondition bondMktUp; IMarketCondition bondMktDown; if (market.FixingCurve.HasValue) { bondMktUp = bMarket.UpdateCondition( new UpdateMktConditionPack <IYieldCurve>(x => x.FixingCurve, bMarket.FixingCurve.Value.Shift(1)), new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(1).GetSpreadedCurve(new ZeroSpread(bondZeroSpread))) ); bondMktDown = bMarket.UpdateCondition( new UpdateMktConditionPack <IYieldCurve>(x => x.FixingCurve, bMarket.FixingCurve.Value.Shift(-1)), new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(-1).GetSpreadedCurve(new ZeroSpread(bondZeroSpread))) ); } else { bondMktUp = bMarket.UpdateCondition( new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(1).GetSpreadedCurve(new ZeroSpread(bondZeroSpread))) ); bondMktDown = bMarket.UpdateCondition( new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(-1).GetSpreadedCurve(new ZeroSpread(bondZeroSpread))) ); } var fwdMarket = market.UpdateCondition(new UpdateMktConditionPack <ISpread>(x => x.CreditSpread, new ZeroSpread(bondZeroSpread))); var upPv = bondEngine.Calculate(trade.Underlying, bondMktUp, PricingRequest.Pv).Pv; var downPv = bondEngine.Calculate(trade.Underlying, bondMktDown, PricingRequest.Pv).Pv; if (fwdMarket.FixingCurve.HasValue) { var fwdMktUp = fwdMarket.UpdateCondition( new UpdateMktConditionPack <IYieldCurve>(x => x.FixingCurve, bMarket.FixingCurve.Value.Shift(1)), new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(1)), new UpdateMktConditionPack <IYieldCurve>(x => x.UnderlyingDiscountCurve, bMarket.UnderlyingDiscountCurve.Value.Shift(1)), new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, bMarket.MktQuote.Value.UpdateKey(trade.Underlying.Id, Tuple.Create(PriceQuoteType.Dirty, upPv))) ); var fwdMktDown = fwdMarket.UpdateCondition( new UpdateMktConditionPack <IYieldCurve>(x => x.FixingCurve, bMarket.FixingCurve.Value.Shift(-1)), new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(-1)), new UpdateMktConditionPack <IYieldCurve>(x => x.UnderlyingDiscountCurve, bMarket.UnderlyingDiscountCurve.Value.Shift(-1)), new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, bMarket.MktQuote.Value.UpdateKey(trade.Underlying.Id, Tuple.Create(PriceQuoteType.Dirty, downPv))) ); result.Dv01 = (CalcPv(trade, fwdMktDown) - CalcPv(trade, fwdMktUp)) / 2.0; } else { var fwdMktUp = fwdMarket.UpdateCondition( new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(1)), new UpdateMktConditionPack <IYieldCurve>(x => x.UnderlyingDiscountCurve, bMarket.UnderlyingDiscountCurve.Value.Shift(1)), new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, bMarket.MktQuote.Value.UpdateKey(trade.Underlying.Id, Tuple.Create(PriceQuoteType.Dirty, upPv))) ); var fwdMktDown = fwdMarket.UpdateCondition( new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(-1)), new UpdateMktConditionPack <IYieldCurve>(x => x.UnderlyingDiscountCurve, bMarket.UnderlyingDiscountCurve.Value.Shift(-1)), new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, bMarket.MktQuote.Value.UpdateKey(trade.Underlying.Id, Tuple.Create(PriceQuoteType.Dirty, downPv))) ); result.Dv01 = (CalcPv(trade, fwdMktDown) - CalcPv(trade, fwdMktUp)) / 2.0; } } if (result.IsRequested(PricingRequest.Dv01Underlying)) { var factor = trade.Notional / trade.Underlying.Notional; result.Dv01Underlying = bondEngine.Calculate(trade.Underlying, bMarket, PricingRequest.Dv01).Dv01 *factor; } return(result); }
/// <summary> /// 计算一个债券的估值和基本风险指标 /// </summary> /// <param name="bond">债券</param> /// <param name="market">市场数据对象</param> /// <param name="request">计算请求类型</param> /// <returns>计算结果</returns> public override IPricingResult Calculate(Bond bond, IMarketCondition market, PricingRequest request) { var result = new PricingResult(market.ValuationDate, request); var cfs = bond.GetCashflows(market, true); var cfAis = bond.GetAiCashflows(market, false); //Ai and AiEod are mutually exclusive requests var isEod = result.IsRequested(PricingRequest.AiEod); if (result.IsRequested(PricingRequest.Ai) || result.IsRequested(PricingRequest.AiEod)) { result.Ai = bond.GetAccruedInterest(market.ValuationDate, cfAis, isEod); result.AiDays = bond.GetAccruedInterestDays(market.ValuationDate, cfAis, isEod); } if (result.IsRequested(PricingRequest.Ytm) || result.IsRequested(PricingRequest.DirtyPrice) || result.IsRequested(PricingRequest.CleanPrice) || result.IsRequested(PricingRequest.ModifiedDuration) || result.IsRequested(PricingRequest.Convexity) || (result.IsRequested(PricingRequest.DollarDuration)) || result.IsRequested(PricingRequest.DollarConvexity) || result.IsRequested(PricingRequest.MacDuration) || result.IsRequested(PricingRequest.Pv01) || result.IsRequested(PricingRequest.ZeroSpread) || result.IsRequested(PricingRequest.Dv01) || result.IsRequested(PricingRequest.KeyRateDv01)) { double unitDirtyPrice = 0; double unitCleanPrice = 0; if (double.IsNaN(result.Ai)) { result.Ai = bond.GetAccruedInterest(market.ValuationDate, cfAis, isEod); result.AiDays = bond.GetAccruedInterestDays(market.ValuationDate, cfAis, isEod); } var bondQuote = market.MktQuote.Value[bond.Id]; if (bondQuote.Item1 == PriceQuoteType.Dirty) { result.DirtyPrice = bondQuote.Item2 * bond.Notional / 100.0; result.Ytm = _bondYieldPricer.YieldFromFullPrice(cfs, bond.PaymentDayCount, bond.PaymentFreq, bond.StartDate, market.ValuationDate, result.DirtyPrice, bond.BondTradeingMarket, bond.IrregularPayment); result.CleanPrice = result.DirtyPrice - result.Ai; unitDirtyPrice = bondQuote.Item2; unitCleanPrice = result.CleanPrice / bond.Notional * 100.0; } else if (bondQuote.Item1 == PriceQuoteType.Clean) { result.CleanPrice = bondQuote.Item2 * bond.Notional / 100.0; result.DirtyPrice = result.CleanPrice + result.Ai; unitCleanPrice = bondQuote.Item2; unitDirtyPrice = result.DirtyPrice / bond.Notional * 100; result.Ytm = _bondYieldPricer.YieldFromFullPrice(cfs, bond.PaymentDayCount, bond.PaymentFreq, bond.StartDate, market.ValuationDate, result.DirtyPrice, bond.BondTradeingMarket, bond.IrregularPayment); } else { result.Ytm = bondQuote.Item2; result.DirtyPrice = _bondYieldPricer.FullPriceFromYield(cfs, bond.PaymentDayCount, bond.PaymentFreq, bond.StartDate, market.ValuationDate, result.Ytm, bond.BondTradeingMarket, bond.IrregularPayment); result.CleanPrice = result.DirtyPrice - result.Ai; unitCleanPrice = result.CleanPrice / 100.0; unitDirtyPrice = result.DirtyPrice / 100.0; } if (result.IsRequested(PricingRequest.Convexity) || result.IsRequested(PricingRequest.ModifiedDuration) || result.IsRequested(PricingRequest.DollarConvexity) || result.IsRequested(PricingRequest.DollarDuration) || result.IsRequested(PricingRequest.MacDuration) || result.IsRequested(PricingRequest.Pv01)) { result.MacDuration = _bondYieldPricer.GetMacDuration(cfs, bond.PaymentDayCount, bond.PaymentFreq, bond.StartDate, market.ValuationDate, result.Ytm, bond.BondTradeingMarket); //modified duration here = 1% move, price change, per 100 notional dollar bond result.ModifiedDuration = BondPricingFunctions.GetModifiedDuration(cfs, bond.PaymentDayCount, bond.PaymentFreq, bond.StartDate, market.ValuationDate, result.Ytm, bond.BondTradeingMarket, bond.IrregularPayment, _bondYieldPricer); //1% convexity here, modified duration move, further multiplied by 10000 result.Convexity = BondPricingFunctions.GetConvexity(cfs, bond.PaymentDayCount, bond.PaymentFreq, bond.StartDate, market.ValuationDate, result.Ytm, bond.BondTradeingMarket, bond.IrregularPayment, _bondYieldPricer); //1% impact, dollar duration is modified duration in dollar term, but scales with notional, for calculating bond book avg portfolio duration, and display purpose result.DollarModifiedDuration = result.ModifiedDuration * unitDirtyPrice * 0.0001 * bond.Notional; //1% impact, dollar convexity is for calculating both pnl and avg book convexity result.DollarConvexity = result.Convexity / 100 * unitDirtyPrice * bond.Notional; //1bp dollar impact, scales with Notional result.Pv01 = (-result.DollarModifiedDuration + 0.5 * result.DollarConvexity * 0.0001 * 0.0001) / 100.0; } } if (result.IsRequested(PricingRequest.Pv)) { result.Pv = cfs.Where(x => x.PaymentDate > market.ValuationDate) .Sum(x => x.PaymentAmount * market.DiscountCurve.Value.GetDf(market.ValuationDate, x.PaymentDate)); } if (result.IsRequested(PricingRequest.ZeroSpread) || result.IsRequested(PricingRequest.Dv01) || result.IsRequested(PricingRequest.KeyRateDv01) || result.IsRequested(PricingRequest.ZeroSpreadDelta)) { result.ZeroSpread = BondPricingFunctions.ZeroSpread(cfs, market.DiscountCurve.Value, market.ValuationDate, result.DirtyPrice); result.ZeroSpreadDelta = BondPricingFunctions.ZeroSpreadRisk(cfs, market.DiscountCurve.Value, market.ValuationDate, result.ZeroSpread); if (market.CreditSpread.HasValue) { market = market.UpdateCondition(new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, market.DiscountCurve.Value.GetSpreadedCurve(market.CreditSpread.Value))); } else { market = market.UpdateCondition(new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, market.DiscountCurve.Value.GetSpreadedCurve(new ZeroSpread(result.ZeroSpread)))); } } if (result.IsRequested(PricingRequest.Dv01)) { result.Dv01 = base.Calculate(bond, market, PricingRequest.Dv01).Dv01; } if (result.IsRequested(PricingRequest.KeyRateDv01)) { result.KeyRateDv01 = base.Calculate(bond, market, PricingRequest.KeyRateDv01).KeyRateDv01; } if (result.IsRequested(PricingRequest.Cashflow)) { result.Cashflows = bond.GetCashflows(market, false); result.CashflowDict = result.Cashflows.ToDictionary(x => x.ToCfKey(), x => x.PaymentAmount); } //if (bond.Coupon is FloatingCoupon) //{ // var primeRateDate = market.ValuationDate; // //var primeRateCashflow = cfAis.FirstOrDefault(x => x.AccrualEndDate > market.ValuationDate && x.AccrualStartDate <= market.ValuationDate); // //if (primeRateCashflow != null) // //{ // // primeRateDate = primeRateCashflow.AccrualStartDate; // //} // var fixingCurve = market.FixingCurve ?? market.DiscountCurve ?? market.RiskfreeCurve; // var fixingTuple = bond.Coupon.GetPrimeCoupon(market.HistoricalIndexRates, fixingCurve.Value, primeRateDate); // if (fixingTuple != null && fixingTuple.Item1 != null) // result.ProductSpecific = new Dictionary<string, Dictionary<string, RateRecord>> // { // { // "currentPrimeRate", // new Dictionary<string, RateRecord> // { // { // bond.Id, // new RateRecord // { // Rate = fixingTuple.Item2, // Date = fixingTuple.Item1.ToString() // } // } // } // } // }; //} return(result); }