Example #1
0
 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)));
     }
 }
Example #2
0
        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]);
        }
Example #3
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);
        }
Example #4
0
        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);
        }
Example #5
0
        /// <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);
        }