public override IPricingResult Calculate(BondFutures bondFuture, IMarketCondition market, PricingRequest request) { var beginValuation = DateTime.Now; var pricingRequest = CheckParameterCondition(bondFuture, market, request); //if (result.IsRequested(PricingRequest.Dv01)) //{ // result.Dv01 = CalcDv01(bondFuture, market); //} var result = new PricingResult(market.ValuationDate, pricingRequest); //if (result.IsRequested(PricingRequest.Pv)) //{ // result.Pv = CalcPv(bondFuture, market); //} if (result.IsRequested(PricingRequest.DirtyPrice)) { result.DirtyPrice = market.MktQuote.Value.Where(x => x.Key == bondFuture.Id).Select(x => x.Value.Item2).First() * bondFuture.Notional / 100; } if (result.IsRequested(PricingRequest.ConvertFactors)) { result.ConvertFactors = CalcConvertFactors(bondFuture, market); } if (result.IsRequested(PricingRequest.Irr) || result.IsRequested(PricingRequest.Pv01) || result.IsRequested(PricingRequest.KeyRateDv01) || result.IsRequested(PricingRequest.ZeroSpread) || result.IsRequested(PricingRequest.ZeroSpreadDelta) || result.IsRequested(PricingRequest.UnderlyingPv) || result.IsRequested(PricingRequest.Basis) || result.IsRequested(PricingRequest.Convexity) || result.IsRequested(PricingRequest.Ytm) || result.IsRequested(PricingRequest.CheapestToDeliver) || result.IsRequested(PricingRequest.ModifiedDuration) || result.IsRequested(PricingRequest.MacDuration) ) { //TODO: wierd update logic, why bother? var mktQuote = market.MktQuote.Value.Keys.Where(quoteKey => !quoteKey.Contains(bondFuture.Id + "_")).ToDictionary(quoteKey => quoteKey, quoteKey => market.MktQuote.Value[quoteKey]); var updateMarket = market.UpdateCondition(new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, mktQuote)); var yieldPricer = new BondFuturesYieldPricer(bondFuture, updateMarket); //calculate convert factors if (!result.ConvertFactors.Any()) { result.ConvertFactors = CalcConvertFactors(bondFuture, market); } var convertFactors = result.ConvertFactors; //calculate IRR result.ProductSpecific = yieldPricer.CalcEquation("FromFuturesPriceAndBondPrice"); //calculate pv01 var maxIrr = result.ProductSpecific["Irr"].Values.Select(x => x.Rate).Max(); var ctdBondId = result.ProductSpecific["Irr"].First(x => x.Value.Rate == maxIrr).Key; var ctdBond = bondFuture.Deliverables.First(x => x.Id == ctdBondId); var cf = convertFactors[ctdBondId]; var scaling = bondFuture.Notional / (100.0 * cf); var engine = new BondEngineCn(); result.CheapestToDeliver = ctdBondId; //two risks here are CTD risk, not bond futures risk var resultCTD = engine.Calculate(ctdBond, market, PricingRequest.All); result.ZeroSpread = resultCTD.ZeroSpread; result.UnderlyingPv = resultCTD.Pv * scaling; result.Basis = yieldPricer.CalcFutureCtdBasis(ctdBond, cf); result.Ytm = resultCTD.Ytm; result.MacDuration = resultCTD.MacDuration; result.ModifiedDuration = resultCTD.ModifiedDuration; result.Convexity = resultCTD.Convexity; result.DollarConvexity = resultCTD.DollarConvexity * scaling; // 1% price impact result.DollarModifiedDuration = resultCTD.DollarModifiedDuration * scaling; // same order of magnitutude of CTD dollar modifiedDuration, good for pnl attribution //convert to bond futures risk result.Pv01 = resultCTD.Pv01 * scaling; // underlying pv01 is foreach (var kvp in resultCTD.KeyRateDv01) { foreach (var risk in kvp.Value) { risk.Risk *= scaling; } } result.KeyRateDv01 = resultCTD.KeyRateDv01; result.ZeroSpreadDelta = resultCTD.ZeroSpreadDelta * scaling; } if (result.IsRequested(PricingRequest.FairQuote)) { var mktQuote = market.MktQuote.Value.Keys.Where(quoteKey => !quoteKey.Equals(bondFuture.Id)).ToDictionary(quoteKey => quoteKey, quoteKey => market.MktQuote.Value[quoteKey]); var updateMarket = market.UpdateCondition(new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, mktQuote)); var yieldPricer = new BondFuturesYieldPricer(bondFuture, updateMarket); result.ProductSpecific = yieldPricer.CalcEquation("FromBondPriceAndIrr"); } if (result.IsRequested(PricingRequest.MktQuote)) { result.ProductSpecific = CalcMktFuturePrice(bondFuture, market); } if (result.IsRequested(PricingRequest.UnderlyingFairQuote)) { var mktQuote = market.MktQuote.Value.Keys.Where(quoteKey => quoteKey.Contains(bondFuture.Id)).ToDictionary(quoteKey => quoteKey, quoteKey => market.MktQuote.Value[quoteKey]); var updateMarket = market.UpdateCondition(new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, mktQuote)); var yieldPricer = new BondFuturesYieldPricer(bondFuture, updateMarket); result.ProductSpecific = yieldPricer.CalcEquation("FromFuturesPriceAndIrr"); } var endValuation = DateTime.Now; result.CalcTimeInMilliSecond = (endValuation - beginValuation).TotalMilliseconds; return(result); }
private Dictionary <string, Dictionary <string, RateRecord> > CalcMktFuturePrice(BondFutures bondFuture, IMarketCondition market) { var psDict = new Dictionary <string, Dictionary <string, RateRecord> >(); var fundingCurve = market.RiskfreeCurve.HasValue ? market.RiskfreeCurve.Value : YieldCurve.GetConstRateCurve(market.DiscountCurve.Value, 0.0); var reinvestmentCurve = market.DiscountCurve.HasValue ? market.DiscountCurve.Value : YieldCurve.GetConstRateCurve(market.RiskfreeCurve.Value, 0.0); var bonds = bondFuture.Deliverables; var length = bonds.Length; var aiAtStart = bonds.Select(x => x.GetAccruedInterest(market.ValuationDate, market, false)).ToArray(); var aiAtEnd = bonds.Select(x => x.GetAccruedInterest(bondFuture.UnderlyingMaturityDate, market, false)).ToArray(); var cf = bonds.Select(x => bondFuture.GetConversionFactor(x, market)).ToArray(); var coupons = new double[length]; var timeWeightedCoupon = new double[length]; var couponsAccruedByReinvestment = new double[length]; for (var i = 0; i < length; ++i) { var bond = bonds[i]; var cashflows = bond.GetCashflows(market, false).Where(x => x.PaymentDate > market.ValuationDate && x.PaymentDate <= bondFuture.UnderlyingMaturityDate).ToArray(); if (cashflows.Any()) { coupons[i] = cashflows.Sum(x => x.PaymentAmount); timeWeightedCoupon[i] = cashflows.Sum(x => x.PaymentAmount * bondFuture.DayCount.CalcDayCountFraction(x.PaymentDate, bondFuture.UnderlyingMaturityDate)); couponsAccruedByReinvestment[i] = cashflows.Sum(x => x.PaymentAmount * (reinvestmentCurve.GetCompoundedRate2(x.PaymentDate, bondFuture.UnderlyingMaturityDate) - 1.0)); } else { coupons[i] = 0.0; timeWeightedCoupon[i] = 0.0; couponsAccruedByReinvestment[i] = 0.0; } } var interestIncome = bonds.Select((x, i) => aiAtEnd[i] - aiAtStart[i] + coupons[i] + couponsAccruedByReinvestment[i]).ToArray(); //var interestIncome = bonds.Select((x, i) => aiAtEnd[i] - aiAtStart[i] + coupons[i]).ToArray(); var dirtyPrice = new double[length]; var cleanPrice = new double[length]; var ytm = new double[length]; var modifiedDuration = new double[length]; var bondEngine = new BondEngine(); for (var i = 0; i < length; ++i) { var bResult = bondEngine.Calculate(bonds[i], market, PricingRequest.Ytm | PricingRequest.ModifiedDuration); dirtyPrice[i] = bResult.DirtyPrice; cleanPrice[i] = bResult.CleanPrice; ytm[i] = bResult.Ytm; } var interestCostRate = fundingCurve.GetCompoundedRate2(market.ValuationDate, bondFuture.UnderlyingMaturityDate) - 1.0; var interestCost = dirtyPrice.Select(x => x * interestCostRate).ToArray(); var futurePrice = new double[length]; var irr = new double[length]; var basis = new double[length]; var pnl = new double[length]; var netBasis = new double[length]; var invoicePrice = new double[length]; var margin = new double[length]; var spread = new double[length]; var fundingRate = fundingCurve.GetSpotRate(0.0); var compoundedRate = fundingCurve.GetCompoundedRate2(market.ValuationDate, bondFuture.UnderlyingMaturityDate); var bondPricesCompounedByFunding = bonds.Select((x, i) => dirtyPrice[i] * compoundedRate).ToArray(); for (var i = 0; i < bonds.Length; ++i) { futurePrice[i] = (bondPricesCompounedByFunding[i] - aiAtEnd[i] - coupons[i]) / cf[i]; var newMarket = market.UpdateCondition( new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, market.MktQuote.Value.UpdateKey(bondFuture.Id, Tuple.Create(PriceQuoteType.Dirty, futurePrice[i])))); var yieldPricer = new BondFuturesYieldPricer(bondFuture, newMarket); var tmpResult = yieldPricer.CalcEquation("FromFuturesPriceAndBondPrice"); invoicePrice[i] = tmpResult["InvoicePrice"][bonds[i].Id].Rate; margin[i] = tmpResult["Margin"][bonds[i].Id].Rate; basis[i] = tmpResult["Basis"][bonds[i].Id].Rate; pnl[i] = tmpResult["PnL"][bonds[i].Id].Rate; netBasis[i] = tmpResult["NetBasis"][bonds[i].Id].Rate; irr[i] = tmpResult["Irr"][bonds[i].Id].Rate; spread[i] = irr[i] - fundingRate; } psDict["FuturesPrice"] = futurePrice.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["BondDirtyPrice"] = dirtyPrice.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["BondCleanPrice"] = cleanPrice.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["BondYieldToMaturity"] = ytm.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["ConversionFactor"] = cf.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["AiStart"] = aiAtStart.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["AiEnd"] = aiAtEnd.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["Coupon"] = coupons.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["InterestIncome"] = interestIncome.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["InterestCost"] = interestCost.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["PnL"] = pnl.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["InvoicePrice"] = invoicePrice.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["Margin"] = margin.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["Spread"] = spread.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["Irr"] = irr.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["Basis"] = basis.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["NetBasis"] = netBasis.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["ModifiedDuration"] = modifiedDuration.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); psDict["TimeWeightedCoupon"] = timeWeightedCoupon.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord { Rate = x })).ToDictionary(x => x.Item1, x => x.Item2); return(psDict); }
public override IPricingResult Calculate(Bond bond, IMarketCondition market, PricingRequest request) { var beginValuation = DateTime.Now; var result = new PricingResult(market.ValuationDate, request); var isCleanPriceRound = bond.RoundCleanPrice; var bondQuote = market.MktQuote.Value.ContainsKey(bond.Id) ? market.MktQuote.Value[bond.Id] : null; var bMktQuote = new Dictionary <string, Tuple <PriceQuoteType, double> >(); IPricingResult resultOptionBond = new PricingResult(market.ValuationDate, request); IPricingResult resultSimpleBond; if (bondQuote != null && (bondQuote.Item1 == PriceQuoteType.YtmExecution || bondQuote.Item1 == PriceQuoteType.YtmCallExecution || bondQuote.Item1 == PriceQuoteType.YtmPutExecution)) { bMktQuote[bond.Id] = Tuple.Create(PriceQuoteType.Ytm, bondQuote.Item2); var ytmMarket = market.UpdateCondition(new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, bMktQuote)); // Get Call or Put cleanprice var pricingRequest = PricingRequest.CleanPrice; if (result.IsRequested(PricingRequest.AiEod)) { pricingRequest = pricingRequest | PricingRequest.AiEod; } resultOptionBond = CalcOptionBond(bond, ytmMarket, pricingRequest, bondQuote.Item1); // Parse market var cleanMarket = UpdateCleanPriceMarket(bond.Id, resultOptionBond.CleanPrice, isCleanPriceRound, market); resultSimpleBond = _bondEngine.Calculate(bond, cleanMarket, request); } else { if (isCleanPriceRound && bondQuote != null) { if (bondQuote.Item1 == PriceQuoteType.Clean) { var cleanPriceMarket = UpdateCleanPriceMarket(bond.Id, bondQuote.Item2, isCleanPriceRound, market); resultSimpleBond = _bondEngine.Calculate(bond, cleanPriceMarket, request); } else { resultSimpleBond = _bondEngine.Calculate(bond, market, request); var cleanPriceMarket = UpdateCleanPriceMarket(bond.Id, resultSimpleBond.CleanPrice, isCleanPriceRound, market); resultSimpleBond = _bondEngine.Calculate(bond, cleanPriceMarket, request); } } else { resultSimpleBond = _bondEngine.Calculate(bond, market, request); } // Parse market bMktQuote[bond.Id] = Tuple.Create(PriceQuoteType.Clean, double.IsNaN(resultSimpleBond.CleanPrice) ? 0.0 : resultSimpleBond.CleanPrice); var executionYieldPricingRequest = PricingRequest.Ytm; if (result.IsRequested(PricingRequest.AiEod)) { executionYieldPricingRequest = PricingRequest.Ytm | PricingRequest.AiEod; } var newMarket = market.UpdateCondition(new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, bMktQuote)); if (result.IsRequested(PricingRequest.YtmExecution)) { resultOptionBond = CalcOptionBond(bond, newMarket, executionYieldPricingRequest, PriceQuoteType.YtmExecution); } } result = (PricingResult)resultSimpleBond; result.YieldToCall = resultOptionBond.YieldToCall; result.YieldToPut = resultOptionBond.YieldToPut; result.CallDate = resultOptionBond.CallDate; result.PutDate = resultOptionBond.PutDate; var endValuation = DateTime.Now; result.CalcTimeInMilliSecond = (endValuation - beginValuation).TotalMilliseconds; return(result); }
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="option">期权</param> /// <param name="market">市场</param> /// <param name="request">计算请求</param> /// <returns>计算结果</returns> public override IPricingResult Calculate(IOption option, IMarketCondition market, PricingRequest request) { var result = new PricingResult(market.ValuationDate, request); bool isExpired = option.ExerciseDates.Last() < market.ValuationDate; bool isExpiredforTheta = option.ExerciseDates.Last() <= market.ValuationDate; bool onMaturityDate = (option.ExerciseDates.Last() == market.ValuationDate); if (isExpired) { result.Pv = result.Delta = result.DeltaCash = result.Gamma = result.GammaCash = result.Theta = 0.0; result.ThetaPnL = result.CalenderThetaPnL = result.Vega = result.Rho = result.StoppingTime = 0.0; result.DVegaDvol = result.DVegaDt = result.DDeltaDvol = result.DDeltaDt = 0.0; result.PricingVol = 0.0; return(result); } else { var now = DateTime.Now; var expiryDayCutoffTime = new DateTime(now.Year, now.Month, now.Day, 15, 0, 0); var expiryDayStartTime = new DateTime(now.Year, now.Month, now.Day, 9, 30, 0); var T = (expiryDayCutoffTime - now).TotalSeconds / (expiryDayCutoffTime - expiryDayStartTime).TotalSeconds / 365.0; SetPricingVol(result, option, market); //Calc delta/pv differently on expiry day if (onMaturityDate && T > 0.0) { if (result.IsRequested(PricingRequest.Pv) && double.IsNaN(result.Pv)) { result.Pv = DoCalcExpiryPv(option, market, T); } if (result.IsRequested(PricingRequest.Delta) && double.IsNaN(result.Delta)) { result.Delta = DoCalcExpiryDelta(option, market, T); result.DeltaCash = result.Delta * market.SpotPrices.Value.Values.First(); } } else if (onMaturityDate && T <= 0.0) { if (result.IsRequested(PricingRequest.Pv) && double.IsNaN(result.Pv)) { var pv = CalcPvs(option, market.ToArrayT()); result.Pv = pv[0]; result.PricingVol = 0.0; } if (result.IsRequested(PricingRequest.Delta) && double.IsNaN(result.Delta)) { var pv = CalcPvs(option, new[] { market })[0]; if (option.OptionType == OptionType.Call) { result.Delta = (pv > 0) ? 1 * option.Notional : 0; result.DeltaCash = result.Delta * market.SpotPrices.Value.Values.First(); } else { result.Delta = (pv > 0) ? -1 * option.Notional: 0; result.DeltaCash = result.Delta * market.SpotPrices.Value.Values.First(); } } } //on other days if (result.IsRequested(PricingRequest.Pv) && double.IsNaN(result.Pv)) { var pv = CalcPvs(option, market.ToArrayT()); result.Pv = pv[0]; } if (result.IsRequested(PricingRequest.Delta) && double.IsNaN(result.Delta)) { result.Delta = CalcDelta(option, market); result.DeltaCash = result.Delta * market.SpotPrices.Value.Values.First(); } if (result.IsRequested(PricingRequest.Gamma) && double.IsNaN(result.Gamma)) { result.Gamma = CalcGamma(option, market); result.GammaCash = result.Gamma * market.SpotPrices.Value.Values.First() * market.SpotPrices.Value.Values.First() / 100; } if (result.IsRequested(PricingRequest.Vega) && double.IsNaN(result.Vega)) { result.Vega = CalcVega(option, market); result.FwdDiffVega = CalcFwdDiffVega(option, market); } if (result.IsRequested(PricingRequest.Rho) && double.IsNaN(result.Rho)) { var markets = new[] { market, market.UpdateCondition(new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, market.DiscountCurve.Value.Shift(10))), }; var pvs = CalcPvs(option, markets); result.Rho = (pvs[1] - pvs[0]) / 10.0; } if (result.IsRequested(PricingRequest.DVegaDvol) || result.IsRequested(PricingRequest.DVegaDt) || result.IsRequested(PricingRequest.DDeltaDvol) || result.IsRequested(PricingRequest.DDeltaDt)) { CalcHighOrder(option, market, result, isExpiredforTheta); } if (result.IsRequested(PricingRequest.StoppingTime) && double.IsNaN(result.StoppingTime)) { var stoppingTime = StoppingTime(option, market.ToArrayT()); result.StoppingTime = stoppingTime; } if (result.IsRequested(PricingRequest.Theta)) { if (isExpiredforTheta) { result.ThetaPnL = result.CalenderThetaPnL = result.Theta = 0.0; return(result); } else { var thetaResults = CalcTheta(option, market); result.Theta = thetaResults[0]; // this theta is for time pnl result.ThetaPnL = thetaResults[1]; result.CalenderThetaPnL = thetaResults[2]; //in trading day mode, we cannot calc calendar theta } } } if (option.IsMoneynessOption && result.IsRequested(PricingRequest.Pv) && (option.InitialSpotPrice != 0.0)) { result.PctPv = result.Pv / option.InitialSpotPrice; } 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); }
/// <summary> /// 计算一个金融衍生品交易的定价和风险指标 /// </summary> /// <param name="trade">交易</param> /// <param name="market">市场数据对象</param> /// <param name="request">计算请求类型</param> /// <returns>计算结果</returns> public override IPricingResult Calculate(TTrade trade, IMarketCondition market, PricingRequest request) { var result = new PricingResult(market.ValuationDate, request); if (result.IsRequested(PricingRequest.Pv)) { result.Pv = CalcPv(trade, market); } if (result.IsRequested(PricingRequest.Carry)) { result.Carry = CalcCarry(trade, market); } if (result.IsRequested(PricingRequest.Dv01)) { if (double.IsNaN(result.Pv)) { result.Pv = CalcPv(trade, market); } var mktDown = market.FixingCurve.HasValue ? market.UpdateCondition( new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, market.DiscountCurve.Value.Shift(1)), new UpdateMktConditionPack <IYieldCurve>(x => x.FixingCurve, market.FixingCurve.Value.Shift(1))) : market.UpdateCondition( new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, market.DiscountCurve.Value.Shift(1))); result.Dv01 = CalcPv(trade, mktDown) - result.Pv; } if (result.IsRequested(PricingRequest.Cashflow)) { result.Cashflows = trade.GetCashflows(market, false); } //Ai and AiEod are mutually exclusive requests var isEod = result.IsRequested(PricingRequest.AiEod); if (result.IsRequested(PricingRequest.Ai) || result.IsRequested(PricingRequest.AiEod)) { if (result.Cashflows == null || result.Cashflows.Length == 0) { result.Cashflows = trade.GetCashflows(market, false); } result.Ai = trade.GetAccruedInterest(market.ValuationDate, market, isEod); } if (result.IsRequested(PricingRequest.KeyRateDv01)) { if (double.IsNaN(result.Pv)) { result.Pv = CalcPv(trade, market); } var dc = new Dictionary <string, CurveRisk[]>(); var fc = new Dictionary <string, CurveRisk[]>(); Parallel.Invoke( () => CalcDiscountDv01(trade, market, result.Pv, ref dc), () => CalcResetDv01(trade, market, result.Pv, ref fc) ); result.KeyRateDv01 = PricingResultExtension.Aggregate(dc, fc); } if (result.IsRequested(PricingRequest.FairQuote)) { result.FairQuote = GetFairQuote(trade, market); } if (result.IsRequested(PricingRequest.MacDuration)) { if (result.Cashflows == null || result.Cashflows.Length == 0) { result.Cashflows = trade.GetCashflows(market, false); } var weightedCf = 0.0; var totalCf = 0.0; foreach (var cashflow in result.Cashflows) { if (cashflow.PaymentDate > market.ValuationDate) { var t = market.DiscountCurve.Value.DayCount.CalcDayCountFraction(market.ValuationDate, cashflow.PaymentDate); var df = market.DiscountCurve.Value.GetDf(market.ValuationDate, cashflow.PaymentDate); weightedCf += cashflow.PaymentAmount * df * t; totalCf += cashflow.PaymentAmount * df; } } result.MacDuration = weightedCf / totalCf; } if (result.IsRequested(PricingRequest.Pv01)) { result.Pv01 = CalcPv01(trade, market, result.Pv); } Date valueDate = result.ValuationDate; if (result.IsRequested(PricingRequest.ProductSpecific)) { var yieldCurve = market.DiscountCurve.Value; #region var psDict = new Dictionary <string, Dictionary <string, RateRecord> >(); var dayGap = new DayGap("+0BD"); var T = (trade is InterestRateSwap) ? (trade as InterestRateSwap).FloatingLeg : trade as SwapLeg; //forward rate points var tenors = new[] { "1D", "7D", "3M", "1Y" }; var fwdStartInTenors = new List <string> { "1D", "1W", "2W", "1M", "2M", "3M", "4M", "5M", "6M", "7M", "8M", "9M", "10M", "11M", "1Y" }; var totalMonths = Convert.ToInt16((yieldCurve.KeyPoints.Last().Item1 - yieldCurve.KeyPoints.First().Item1) / 30.0) + 1; for (var i = 15; i <= totalMonths; i += 3) { fwdStartInTenors.Add(i + "M"); } foreach (var tenor in tenors) { var fwdRates = new Dictionary <string, RateRecord>(); var fwdTerm = new Term(tenor); foreach (var fwdStartInTenor in fwdStartInTenors) { var fwdStartDate = dayGap.Get(T.Calendar, new Term(fwdStartInTenor).Next(valueDate)); var fwdEndDate = dayGap.Get(T.Calendar, fwdTerm.Next(fwdStartDate)); if (fwdEndDate < yieldCurve.KeyPoints.Last().Item1) { fwdRates[fwdStartInTenor] = new RateRecord() { Date = fwdStartDate.ToString(), Rate = yieldCurve.GetForwardRate(fwdStartDate, fwdTerm) }; } } psDict["forwardrates" + tenor] = fwdRates; } //spot rate var spotRates = new Dictionary <string, RateRecord>(); var spotInTenors = fwdStartInTenors; foreach (var spotInTenor in spotInTenors) { var spotDate = dayGap.Get(T.Calendar, new Term(spotInTenor).Next(valueDate)); if (spotDate <= yieldCurve.KeyPoints.Last().Item1) { spotRates[spotInTenor] = new RateRecord { Date = spotDate.ToString(), Rate = yieldCurve.ZeroRate(valueDate, spotDate, Compound.Simple) }; } } psDict["spotRates"] = spotRates; //key rates var rates = new Dictionary <string, RateRecord>(); var ccTenors = yieldCurve.GetKeyTenors().ToArray(); var mktInstruments = yieldCurve.MarketInstruments; if (mktInstruments != null) { if (mktInstruments.Length != ccTenors.Length) { throw new PricingBaseException("Number of calibration instruments mismatches number of calibrated points!"); } } for (var i = 0; i < ccTenors.Count(); ++i) { //var spotDate = mktInstruments != null ? mktInstruments[i].Instrument.GetClibrationDate() : dayGap.Get(T.Calendar, new Term(ccTenors[i]).Next(valueDate)); var spotDate = dayGap.Get(T.Calendar, new Term(ccTenors[i]).Next(valueDate)); rates[ccTenors[i]] = new RateRecord() { ContinuousRate = yieldCurve.ZeroRate(valueDate, spotDate), Date = spotDate.ToString(), DiscountFactor = yieldCurve.GetDf(valueDate, spotDate), Rate = mktInstruments == null?yieldCurve.GetSpotRate(spotDate) : mktInstruments[i].TargetValue, ProductType = mktInstruments == null ? "None" : (mktInstruments[i].Instrument is Deposit) ? "Index" : "Swap", ZeroRate = yieldCurve.ZeroRate(valueDate, spotDate, Compound.Simple), Term = ccTenors[i] }; } psDict["rates"] = rates; //discount at cash flow dates var dfs = new Dictionary <string, RateRecord>(); var dates = result.Cashflows.Select(x => x.PaymentDate); foreach (var date in dates) { dfs[date.ToString()] = new RateRecord { DiscountFactor = yieldCurve.GetDf(date) }; } psDict["discountfactor"] = dfs; //qb rate return result.ProductSpecific = psDict; #endregion } return(result); }
public override IPricingResult Calculate(AsianOption trade, IMarketCondition market, PricingRequest request) { var result = new PricingResult(market.ValuationDate, request); var exerciseDate = trade.ExerciseDates.Last(); var remainingObsDates = trade.ObservationDates.Where(x => x > market.ValuationDate).ToArray(); var n = trade.ObservationDates.Count(); var n2 = remainingObsDates.Count(); var m = n - n2; if (trade.Fixings.Count != m) { throw new PricingLibraryException("AsianOption: number of fixings does not match!"); } var fixingAverage = trade.Fixings.Any() ? trade.Fixings.Average(x => x.Value) : 0.0; if (!remainingObsDates.Any()) { //already fix on all observation days var cfs = trade.GetPayoff(new[] { fixingAverage }); result.Pv = cfs.Sum(x => x.PaymentAmount * market.DiscountCurve.Value.GetDf(market.ValuationDate, x.PaymentDate)); result.Delta = 0.0; result.DeltaCash = 0.0; result.Gamma = 0.0; result.GammaCash = 0.0; result.Vega = 0.0; result.Theta = 0.0; result.Rho = 0.0; return(result); } //TODO: do it properly var newSpot = market.SpotPrices.Value.Values.First(); var sigma = market.VolSurfaces.Value.Values.First().GetValue(exerciseDate, trade.Strike, newSpot); var newSigma = market.VolSurfaces.Value.Values.First().GetValue(exerciseDate, trade.Strike, newSpot); double newStrike; var factor = 1.0; if (remainingObsDates.Count() == 1) { newStrike = trade.ObservationDates.Count() * trade.Strike - (n - 1) * trade.Fixings.Average(x => x.Value); factor = 1.0 / n; } else { var T = AnalyticalOptionPricerUtil.timeToMaturityFraction(market.ValuationDate, exerciseDate, trade); var T2 = AnalyticalOptionPricerUtil.timeToMaturityFraction(remainingObsDates[0], remainingObsDates.Last(), trade); var t1 = AnalyticalOptionPricerUtil.timeToMaturityFraction(market.ValuationDate, remainingObsDates[0], trade); var h = T2 / (n2 - 1); var S = market.SpotPrices.Value.Values.First(); var riskFreeRate = market.DiscountCurve.Value.ZeroRate(market.ValuationDate, exerciseDate); var dividendRate = market.DividendCurves.Value.Values.First().ZeroRate(market.ValuationDate, exerciseDate); var b = riskFreeRate - dividendRate; var sigma2 = sigma * sigma; double eat, eat2; if (riskFreeRate.AlmostEqual(dividendRate)) { eat = market.SpotPrices.Value.Values.First(); eat2 = S * S * Math.Exp(sigma2 * t1) / n / n * ( (1 - Math.Exp(sigma2 * h * n)) / (1 - Math.Exp(sigma2 * h)) + 2.0 / (1 - Math.Exp(sigma2 * h)) * (n - (1 - Math.Exp(sigma2 * h * n)) / (1 - Math.Exp(sigma2 * h))) ); } else { var o = 2 * b + sigma2; eat = S / n2 * Math.Exp(b * t1) * (1 - Math.Exp(b * h * n2)) / (1 - Math.Exp(b * h)); eat2 = S * S * Math.Exp(o * t1) / n / n * ( (1 - Math.Exp(o * h * n)) / (1 - Math.Exp(o * h)) + 2.0 / (1 - Math.Exp((b + sigma2) * h)) * ((1 - Math.Exp(b * h * n)) / (1 - Math.Exp(b * h)) - (1 - Math.Exp(o * h * n)) / (1 - Math.Exp(o * h))) ); } newSpot = eat; newSigma = Math.Sqrt(Math.Log(eat2 / eat / eat) / T); newStrike = (n * trade.Strike - m * fixingAverage) / (n - m) - m / (n - m); factor = 1.0 * (n - m) / n; } var newTrade = new VanillaOption(trade.StartDate, trade.UnderlyingMaturityDate, trade.Exercise, trade.OptionType, newStrike, trade.UnderlyingProductType, trade.Calendar, trade.DayCount, trade.PayoffCcy, trade.SettlementCcy, trade.ExerciseDates, trade.ObservationDates, trade.Notional, trade.SettlmentGap, trade.OptionPremiumPaymentDate, trade.OptionPremium); var newVol = market.VolSurfaces.Value.Values.First().BumpVolSurf(newSigma - sigma); var newMarket = market.UpdateCondition( new UpdateMktConditionPack <Dictionary <string, double> >(x => x.SpotPrices, new Dictionary <string, double> { { "", newSpot } }), new UpdateMktConditionPack <Dictionary <string, IVolSurface> >(x => x.VolSurfaces, new Dictionary <string, IVolSurface> { { "", newVol } }) ); var bsEngine = new AnalyticalVanillaEuropeanOptionEngine(); result = (PricingResult)bsEngine.Calculate(newTrade, newMarket, request); result.Pv *= factor; result.Delta *= factor; result.Gamma *= factor; result.Vega *= factor; result.Theta *= factor; result.Rho *= factor; return(result); }
public static Tuple <Date, double>[] Calibrate( string name, Date referenceDate, IMarketInstrument[] marketInstruments, BusinessDayConvention bda, IDayCount daycount, ICalendar calendar, Compound compound, Interpolation interpolation, YieldCurveTrait trait, CurrencyCode currency, IMarketCondition baseMarket = null, Expression <Func <IMarketCondition, object> >[] expression = null, double initialValue = double.NaN, double initialGuess = 0.05, //double xmin = -0.9999, double xmin = -0.5, double xmax = 1.0 ) { var accuracy = 1.0e-14; if (marketInstruments.Any(x => x.Instrument is CreditDefaultSwap) || trait == YieldCurveTrait.DiscountCurve) { initialValue = 1.0; initialGuess = 1.0; xmin = 1.0e-64; } var len = marketInstruments.Length; var points = marketInstruments.Select(x => Tuple.Create(x.Instrument.GetCalibrationDate(), initialGuess)).ToList(); var calibrationStartIndex = 0; if (!double.IsNaN(initialValue)) { points.Insert(0, Tuple.Create(referenceDate, initialValue)); calibrationStartIndex = 1; } if (interpolation == Interpolation.ForwardFlat) { //forward flat interpolation, the final instrument is not needed in calibration points = points.Take(len - 1).ToList(); points.Insert(0, Tuple.Create(referenceDate, 0.05)); calibrationStartIndex = 0; } else if (trait == YieldCurveTrait.ForwardCurve) { points.Insert(0, Tuple.Create(referenceDate, 0.05)); calibrationStartIndex = 1; } baseMarket = baseMarket ?? new MarketCondition(x => x.ValuationDate.Value = referenceDate); expression = expression ?? new Expression <Func <IMarketCondition, object> >[] { x => x.DiscountCurve, x => x.FixingCurve }; IYieldCurve calibratedCurve = new YieldCurve(name, referenceDate, points.ToArray(), bda, daycount, calendar, currency, compound, interpolation, trait, baseMarket); var finalInstrument = marketInstruments.Last(); var smooth = (finalInstrument.Instrument is InterestRateSwap); double preCalibrationValue = 0.0; if (smooth) { preCalibrationValue = finalInstrument.Instrument.ModelValue( baseMarket.UpdateCondition(expression.Select(ex => (IUpdateMktConditionPack) new UpdateMktConditionPack <IYieldCurve>(ex, calibratedCurve)).ToArray()), finalInstrument.CalibMethod); } while (true) { for (var i = 0; i < len; ++i) { var calibrateFunc = new CalibrateMarketInstrument(marketInstruments[i], calibratedCurve, baseMarket, i + calibrationStartIndex, expression); //Note: alternative brent solver gives same result, but slower convergence, //var calibrateFunc = new CalibrateMarketInstrument2(marketInstruments[i], calibratedCurve, baseMarket, i + calibrationStartIndex, expression); double finalR; try { finalR = BrentZero.Solve(calibrateFunc, xmin, xmax, accuracy); //finalR = BrentZero2<IUnivariateFunction>.DoSolve(calibrateFunc, xmin, xmax, 0.05, 0); } catch (Exception ex) { throw new PricingLibraryException(string.Format("Error when bootstrapping {0}th point", i)); } calibratedCurve = UpdateKeyRate(calibratedCurve, i + calibrationStartIndex, finalR); } if (!smooth) { break; } else { var postCalibrationValue = finalInstrument.Instrument.ModelValue( baseMarket.UpdateCondition(expression.Select(ex => (IUpdateMktConditionPack) new UpdateMktConditionPack <IYieldCurve>(ex, calibratedCurve)).ToArray()), finalInstrument.CalibMethod); if (Math.Abs(postCalibrationValue - preCalibrationValue) < 1e-12) { break; } preCalibrationValue = postCalibrationValue; } } //if initial value is provided for 0D, 不插0D if (!double.IsNaN(initialValue)) { return(calibratedCurve.KeyPoints.ToArray()); } //NEVER EVER insert points at the end, inset at MOST ONE point at the beginning return(new[] { Tuple.Create(referenceDate, calibratedCurve.KeyPoints[0].Item2) } // Insert 0D point to avoid interpolation jump at the beginning .Union(calibratedCurve.KeyPoints) .ToArray()); }