public override IPricingResult Calculate(SwapLeg premiumLeg, IMarketCondition market, PricingRequest request) { var valuationDate = market.ValuationDate; var result = new PricingResult(valuationDate, request); if (result.IsRequested(PricingRequest.Pv)) { result.Pv = CalcRPv(premiumLeg, market); } var accDates = premiumLeg.Accruals.ToArray(); if (result.IsRequested(PricingRequest.Ai)) { if (!accDates.Any() || valuationDate >= accDates.Last() || valuationDate <= premiumLeg.StartDate) { result.Ai = 0.0; } else { var idx = Array.FindIndex(accDates, x => x > valuationDate) - 1; var dcf = premiumLeg.DayCount.CalcDayCountFraction(accDates[idx], valuationDate); CfCalculationDetail[] temp; var coupon = premiumLeg.Coupon.GetCoupon(accDates[idx], accDates[idx + 1], market.FixingCurve.Value, market.HistoricalIndexRates, out temp); result.Ai = premiumLeg.Notional * coupon * dcf; } } return(result); }
public static bool isHighOrderPricing(PricingResult result) { if (result.IsRequested(PricingRequest.DDeltaDt) || result.IsRequested(PricingRequest.DVegaDt) || result.IsRequested(PricingRequest.DDeltaDvol) || result.IsRequested(PricingRequest.DVegaDvol)) { return(true); } else { return(false); } }
public static bool isBasicPricing(PricingResult result) { if (result.IsRequested(PricingRequest.Delta) || result.IsRequested(PricingRequest.Gamma) || result.IsRequested(PricingRequest.Vega) || result.IsRequested(PricingRequest.Theta) || result.IsRequested(PricingRequest.Rho)) { return(true); } else { return(false); } }
public override IPricingResult Calculate(CreditDefaultSwap creditDefaultSwap, IMarketCondition market, PricingRequest request) { var valuationDate = market.ValuationDate; var result = new PricingResult(valuationDate, request); if (result.IsRequested(PricingRequest.Pv)) { result.Pv = _premiumLegEngine.Calculate(creditDefaultSwap.PremiumLeg, market, PricingRequest.Pv).Pv + _protectionLegEngine.Calculate(creditDefaultSwap.ProtectionLeg, market, PricingRequest.Pv).Pv; } if (result.IsRequested(PricingRequest.Ai)) { result.Ai = _premiumLegEngine.Calculate(creditDefaultSwap.PremiumLeg, market, PricingRequest.Ai).Ai; } return(result); }
public override IPricingResult Calculate(CallableBond callableBond, IMarketCondition market, PricingRequest request) { var result = new PricingResult(market.ValuationDate, request); if (result.IsRequested(PricingRequest.Pv)) { result.Pv = CalcPv(callableBond, market); } return(result); }
public override IPricingResult Calculate(Forward <TUnderlying> 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.Dv01)) { result.Dv01 = GetRisks(trade, market, PricingRequest.Dv01).Dv01; } if (result.IsRequested(PricingRequest.Dv01Underlying)) { result.Dv01 = GetRisks(trade, market, PricingRequest.Dv01Underlying).Dv01Underlying; } return(result); }
public override IPricingResult Calculate(Loan trade, IMarketCondition market, PricingRequest request) { var result = new PricingResult(market.ValuationDate, request); if (result.IsRequested(PricingRequest.Cashflow)) { result.Cashflows = trade.GetCashflows(market); result.CashflowDict = result.Cashflows.ToDictionary(x => x.ToCfKey(), x => x.PaymentAmount); } return(result); }
public override IPricingResult Calculate(EquityLinkedNote <TOption> trade, IMarketCondition market, PricingRequest request) { var result = new PricingResult(market.ValuationDate, request); var optionResult = OptionEngine.Calculate(trade.Option, market, request); if (result.IsRequested(PricingRequest.Pv)) { result.Pv = optionResult.Pv + trade.Notional * market.DiscountCurve.Value.GetDf(market.ValuationDate, trade.UnderlyingMaturityDate); } if (result.IsRequested(PricingRequest.Delta)) { result.Delta = optionResult.Delta; } if (result.IsRequested(PricingRequest.Gamma)) { result.Gamma = optionResult.Gamma; } if (result.IsRequested(PricingRequest.Rho)) { result.Rho = optionResult.Rho; } if (result.IsRequested(PricingRequest.Vega)) { result.Vega = optionResult.Vega; } if (result.IsRequested(PricingRequest.Theta)) { result.Theta = optionResult.Theta; } return(result); }
public override IPricingResult Calculate(AbsWithRepurchase trade, IMarketCondition market, PricingRequest request) { var result = new PricingResult(market.ValuationDate, request); if (result.IsRequested(PricingRequest.Cashflow)) { var underlyingLoanCfs = trade.GetCashflows(market, false).ToArray(); var surplus = 0.0; var repurchasedLoanCfs = new List <Cashflow[]> { underlyingLoanCfs }; var underlyingLoanPayDates = repurchasedLoanCfs.SelectMany(x => x.Select(cf => cf.PaymentDate)).Distinct().ToArray(); var payOutCashflows = trade.Tranches.SelectMany(x => x.GetCashflows(market, false)).ToArray(); //assumption: repurchased loans have same features as the original loan excepth the startDate and mautirytDate for (var i = 0; i < underlyingLoanPayDates.Length - 1; ++i) { //repurchase prior to the final cash flow var tCfs = repurchasedLoanCfs.SelectMany(x => x).Where(x => x.PaymentDate == underlyingLoanPayDates[i] && (x.CashflowType == CashflowType.Principal || x.CashflowType == CashflowType.Coupon || x.CashflowType == CashflowType.Tax)).ToArray(); surplus += tCfs.Sum(x => x.PaymentAmount); var payOutAmount = payOutCashflows.Where(x => x.PaymentDate >= underlyingLoanPayDates[i] && x.PaymentDate < underlyingLoanPayDates[i + 1]).Sum(x => x.PaymentAmount); surplus -= payOutAmount; var endDate = trade.PayOutDates.Where(x => x > underlyingLoanPayDates[i]); if (endDate.Any()) { repurchasedLoanCfs.Add(RepurchasedLoanCashflow(underlyingLoanPayDates[i + 1], surplus * trade.RepurchaseRatio, trade.Loan, market, underlyingLoanPayDates[i], endDate.First())); } surplus *= (1.0 - trade.RepurchaseRatio); } var payOutCf = payOutCashflows.Select( x => new Cashflow(x.AccrualEndDate, x.AccrualEndDate, x.PaymentDate, -x.PaymentAmount, x.PaymentCurrency, x.CashflowType, x.IsFixed, market.GetDf(x.PaymentDate), x.CalculationDetails, x.RefStartDate, x.RefEndDate, x.StartPrincipal, x.CouponRate)); result.Cashflows = repurchasedLoanCfs.SelectMany(x => x).Union(payOutCf).ToArray(); } return(result); }
public override IPricingResult Calculate(InterestRateSwap trade, IMarketCondition market, PricingRequest request) { var cfEngine = new CashflowProductEngine <SwapLeg>(); var leg1Result = cfEngine.Calculate(trade.FixedLeg, market, PricingRequest.All); var mkt4Leg2 = market.UpdateCondition(new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, market.FgnDiscountCurve.Value), new UpdateMktConditionPack <IYieldCurve>(x => x.FixingCurve, market.FgnFixingCurve.Value)); var leg2Result = cfEngine.Calculate(trade.FloatingLeg, mkt4Leg2, PricingRequest.All); var result = new PricingResult(market.ValuationDate, request); if (result.IsRequested(PricingRequest.Pv)) { result.Pv = leg1Result.Pv + leg2Result.Pv; } if (result.IsRequested(PricingRequest.Dv01)) { result.Dv01 = leg1Result.Dv01 + leg2Result.Dv01; } if (result.IsRequested(PricingRequest.Pv01)) { var bumpedIrs = trade.Bump(1); var bumpedPv = new CashflowProductEngine <InterestRateSwap>().Calculate(bumpedIrs, market, PricingRequest.Pv).Pv; result.Pv01 = bumpedPv - result.Pv; } if (result.IsRequested(PricingRequest.Ai)) { result.Ai = leg1Result.Ai + leg2Result.Ai; } if (result.IsRequested(PricingRequest.Cashflow)) { result.Cashflows = leg1Result.Cashflows.Union(leg2Result.Cashflows).ToArray(); } if (result.IsRequested(PricingRequest.KeyRateDv01)) { result.KeyRateDv01 = PricingResultExtension.Aggregate(leg1Result.KeyRateDv01, leg2Result.KeyRateDv01); } return(result); }
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); }
public override IPricingResult Calculate(HoldingPeriod holdingPeriod, IMarketCondition market, PricingRequest request) { var beginValuation = DateTime.Now; var result = new PricingResult(market.ValuationDate, request); var psDict = new Dictionary <string, Dictionary <string, RateRecord> >(); var bondId = holdingPeriod.Id; var startDate = holdingPeriod.StartDate; var endDate = holdingPeriod.UnderlyingMaturityDate; var bond = holdingPeriod.UnderlyingBond; _roundCleanPrice = holdingPeriod.UnderlyingBond.RoundCleanPrice; var startBondId = bondId + "_Start"; var endBondId = bondId + "_End"; // Create start and end bond market var startMarket = CreateMarketCondition(bondId, startBondId, startDate, market, holdingPeriod.StartFixingRate); var endMarket = CreateMarketCondition(bondId, endBondId, endDate, market, holdingPeriod.EndFixingRate); // Calc Ai var startAiCashflow = CalcBond(bond, startMarket); var endAiCashflow = CalcBond(bond, endMarket); var principalBetweenTemp = startAiCashflow.Cashflows.Where(x => x.CashflowType == CashflowType.Principal && x.AccrualEndDate > startDate && x.AccrualEndDate <= endDate).Sum(cashflow => cashflow.PaymentAmount); var interestBetweenTemp = startAiCashflow.Cashflows.Where(x => x.CashflowType == CashflowType.Coupon && x.AccrualEndDate > startDate && x.AccrualEndDate <= endDate).Sum(cashflow => cashflow.PaymentAmount); var yieldPricer = new HoldingPeriodYieldPricer(holdingPeriod, startAiCashflow.Ai, endAiCashflow.Ai, principalBetweenTemp, interestBetweenTemp); var holdingPeriodResult = new Dictionary <string, double>(); holdingPeriodResult["holdingCost"] = double.IsNaN(holdingPeriod.HoldingCost) ? 0.0 : holdingPeriod.HoldingCost; holdingPeriodResult["startFrontCommission"] = double.IsNaN(holdingPeriod.StartFrontCommission) ? 0.0 : holdingPeriod.StartFrontCommission; holdingPeriodResult["startBackCommission"] = double.IsNaN(holdingPeriod.StartBackCommission) ? 0.0 : holdingPeriod.StartBackCommission; holdingPeriodResult["endFrontCommission"] = double.IsNaN(holdingPeriod.EndFrontCommission) ? 0.0 : holdingPeriod.EndFrontCommission; holdingPeriodResult["endBackCommission"] = double.IsNaN(holdingPeriod.EndBackCommission) ? 0.0 : holdingPeriod.EndBackCommission; // option date holdingPeriodResult["hasStartOption"] = ExecutionOptionDate(startDate, holdingPeriod.UnderlyingBond, startAiCashflow.Cashflows) == null ? 1.0 : 0.0; holdingPeriodResult["hasEndOption"] = ExecutionOptionDate(endDate, holdingPeriod.UnderlyingBond, endAiCashflow.Cashflows) == null ? 1.0 : 0.0; var isCalcStartCleanPrice = false; var isCalcEndCleanPrice = false; string functionType = ""; double inputPar1 = 0.0; double inputPar2 = 0.0; if (result.IsRequested(PricingRequest.NetAnnualizedYield)) { var startResult = CalcStartBond(startMarket, bondId, bond, holdingPeriodResult); var endResult = CalcEndBond(endMarket, bondId, bond, holdingPeriodResult); var startCleanPrice = _roundCleanPrice ? Math.Round(startResult.CleanPrice, 4, MidpointRounding.AwayFromZero) : startResult.CleanPrice; var endCleanPrice = _roundCleanPrice ? Math.Round(endResult.CleanPrice, 4, MidpointRounding.AwayFromZero) : endResult.CleanPrice; // Calc functionType = "AnnualYieldFromCleanPrice"; inputPar1 = startCleanPrice; inputPar2 = endCleanPrice; } else if (result.IsRequested(PricingRequest.CleanPrice) && market.MktQuote.Value[bondId].Item1 == PriceQuoteType.None) { if (market.MktQuote.Value.ContainsKey(startBondId)) { var startResult = CalcStartBond(startMarket, bondId, bond, holdingPeriodResult); var startCleanPrice = _roundCleanPrice ? Math.Round(startResult.CleanPrice, 4, MidpointRounding.AwayFromZero) : startResult.CleanPrice; var annualizedYield = market.MktQuote.Value[bondId].Item2; // Calc functionType = "EndCleanPriceFromAnnual"; inputPar1 = startCleanPrice; inputPar2 = annualizedYield; isCalcEndCleanPrice = true; } else if (market.MktQuote.Value.ContainsKey(endBondId)) { var endResult = CalcEndBond(endMarket, bondId, bond, holdingPeriodResult); var annualizedYield = market.MktQuote.Value[bondId].Item2; var endCleanPrice = _roundCleanPrice ? Math.Round(endResult.CleanPrice, 4, MidpointRounding.AwayFromZero) : endResult.CleanPrice; // Calc functionType = "StartCleanPriceFromAnnual"; inputPar1 = endCleanPrice; inputPar2 = annualizedYield; isCalcStartCleanPrice = true; } } else if (result.IsRequested(PricingRequest.CleanPrice) && market.MktQuote.Value.ContainsKey(bondId) && market.MktQuote.Value[bondId].Item1 == PriceQuoteType.NetPnl) { if (market.MktQuote.Value.ContainsKey(startBondId)) { var startResult = CalcStartBond(startMarket, bondId, bond, holdingPeriodResult); var netPnl = market.MktQuote.Value[bondId].Item2; var startCleanPrice = _roundCleanPrice ? Math.Round(startResult.CleanPrice, 4, MidpointRounding.AwayFromZero) : startResult.CleanPrice; // Calc functionType = "EndCleanPriceFromPnl"; inputPar1 = startCleanPrice; inputPar2 = netPnl; isCalcEndCleanPrice = true; } else if (market.MktQuote.Value.ContainsKey(endBondId)) { var endResult = CalcEndBond(endMarket, bondId, bond, holdingPeriodResult); var netPnl = market.MktQuote.Value[bondId].Item2; var endCleanPrice = _roundCleanPrice ? Math.Round(endResult.CleanPrice, 4, MidpointRounding.AwayFromZero) : endResult.CleanPrice; // Calc functionType = "StartCleanPriceFromPnl"; inputPar1 = endCleanPrice; inputPar2 = netPnl; isCalcStartCleanPrice = true; } } else { CalcStartBond(startMarket, bondId, bond, holdingPeriodResult); CalcEndBond(endMarket, bondId, bond, holdingPeriodResult); } // Calc yieldPricer.CalcAnnualizedYieldCleanPrice(functionType, inputPar1, inputPar2, holdingPeriodResult); if (isCalcStartCleanPrice) { // Calc startDate bond var mktQuote = new Dictionary <string, Tuple <PriceQuoteType, double> > { { bondId, Tuple.Create(PriceQuoteType.Clean, holdingPeriodResult["startCleanPrice"]) } }; var newMarket = startMarket.UpdateCondition(new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, mktQuote)); CalcStartBond(newMarket, bondId, bond, holdingPeriodResult); } if (isCalcEndCleanPrice) { // Calc endDate bond var mktQuote = new Dictionary <string, Tuple <PriceQuoteType, double> > { { bondId, Tuple.Create(PriceQuoteType.Clean, holdingPeriodResult["endCleanPrice"]) } }; var newMarket = endMarket.UpdateCondition(new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, mktQuote)); CalcEndBond(newMarket, bondId, bond, holdingPeriodResult); } ParseResult(psDict, bondId, holdingPeriodResult); result.ProductSpecific = psDict; var endValuation = DateTime.Now; result.CalcTimeInMilliSecond = (endValuation - beginValuation).TotalMilliseconds; return(result); }
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); }
public override IPricingResult Calculate(BinaryOption trade, IMarketCondition market, PricingRequest request) { var result = new PricingResult(market.ValuationDate, request); if (trade.BinaryOptionPayoffType == BinaryOptionPayoffType.CashOrNothing) { var factor = (double)BinaryOptionReplicationStrategy; var lowStrike = trade.Strike + Offset * (factor - 1.0) / 2.0; var highStrike = trade.Strike + Offset * (factor + 1.0) / 2.0; //if call, replicate by call spreads, //if put, replicate by put spreads var lowStrikeOption = new VanillaOption(trade.StartDate, trade.UnderlyingMaturityDate, trade.Exercise, trade.OptionType, lowStrike, trade.UnderlyingProductType, trade.Calendar, trade.DayCount, trade.PayoffCcy, trade.SettlementCcy, trade.ExerciseDates, trade.ObservationDates, trade.Notional); var highStrikeOption = new VanillaOption(trade.StartDate, trade.UnderlyingMaturityDate, trade.Exercise, trade.OptionType, highStrike, trade.UnderlyingProductType, trade.Calendar, trade.DayCount, trade.PayoffCcy, trade.SettlementCcy, trade.ExerciseDates, trade.ObservationDates, trade.Notional); var engine = new AnalyticalVanillaEuropeanOptionEngine(); var lowResult = engine.Calculate(lowStrikeOption, market, request); var highResult = engine.Calculate(highStrikeOption, market, request); var sign = trade.OptionType == OptionType.Call ? 1.0 : -1.0; factor = sign * trade.CashOrNothingAmount / Offset; //calc basic stuff if (result.IsRequested(PricingRequest.Pv)) { result.Pv = (lowResult.Pv - highResult.Pv) * factor; } if (AnalyticalOptionPricerUtil.isBasicPricing(result)) { result.Delta = (lowResult.Delta - highResult.Delta) * factor; result.DeltaCash = result.Delta * market.SpotPrices.Value.Values.First(); result.Gamma = (lowResult.Gamma - highResult.Gamma) * factor; result.GammaCash = result.Gamma * market.SpotPrices.Value.Values.First() * market.SpotPrices.Value.Values.First() / 100; result.Vega = (lowResult.Vega - highResult.Vega) * factor; result.Rho = (lowResult.Rho - highResult.Rho) * factor; result.Theta = (lowResult.Theta - highResult.Theta) * factor; } if (AnalyticalOptionPricerUtil.isHighOrderPricing(result)) { result.DDeltaDvol = (lowResult.DDeltaDvol - highResult.DDeltaDvol) * factor; result.DVegaDvol = (lowResult.DVegaDvol - highResult.DVegaDvol) * factor; result.DVegaDt = (lowResult.DVegaDt - highResult.DVegaDt) * factor; result.DDeltaDt = (lowResult.DDeltaDt - highResult.DDeltaDt) * factor; } } else if (trade.BinaryOptionPayoffType == BinaryOptionPayoffType.AssetOrNothing) { var binaryCfOption = new BinaryOption(trade.StartDate, trade.UnderlyingMaturityDate, trade.Exercise, trade.OptionType, trade.Strike, trade.UnderlyingProductType, BinaryOptionPayoffType.CashOrNothing, 1.0, trade.Calendar, trade.DayCount, trade.PayoffCcy, trade.SettlementCcy, trade.ExerciseDates, trade.ObservationDates); var binaryResult = Calculate(binaryCfOption, market, request); var vanillaOption = new VanillaOption(trade.StartDate, trade.UnderlyingMaturityDate, trade.Exercise, trade.OptionType, trade.Strike, trade.UnderlyingProductType, trade.Calendar, trade.DayCount, trade.PayoffCcy, trade.SettlementCcy, trade.ExerciseDates, trade.ObservationDates, trade.Notional); var engine = new AnalyticalVanillaEuropeanOptionEngine(); var vanillaResult = engine.Calculate(vanillaOption, market, request); var sign = trade.OptionType == OptionType.Call ? 1.0 : -1.0; if (result.IsRequested(PricingRequest.Pv)) { result.Pv = sign * vanillaResult.Pv + trade.Strike * binaryResult.Pv; } if (AnalyticalOptionPricerUtil.isBasicPricing(result)) { result.Delta = sign * vanillaResult.Delta + trade.Strike * binaryResult.Delta; result.DeltaCash = result.Delta * market.SpotPrices.Value.Values.First(); result.Gamma = sign * vanillaResult.Gamma + trade.Strike * binaryResult.Gamma; result.GammaCash = result.Gamma * market.SpotPrices.Value.Values.First() * market.SpotPrices.Value.Values.First() / 100; result.Vega = sign * vanillaResult.Vega + trade.Strike * binaryResult.Vega; result.Rho = sign * vanillaResult.Rho + trade.Strike * binaryResult.Rho; result.Theta = sign * vanillaResult.Theta + trade.Strike * binaryResult.Theta; } if (AnalyticalOptionPricerUtil.isHighOrderPricing(result)) { result.DDeltaDvol = sign * vanillaResult.DDeltaDvol + trade.Strike * binaryResult.DDeltaDvol; result.DVegaDvol = sign * vanillaResult.DVegaDvol + trade.Strike * binaryResult.DVegaDvol; result.DVegaDt = sign * vanillaResult.DVegaDt + trade.Strike * binaryResult.DVegaDt; result.DDeltaDt = sign * vanillaResult.DDeltaDt + trade.Strike * binaryResult.DDeltaDt; } } return(result); }
public override IPricingResult Calculate(RainbowOption trade, IMarketCondition market, PricingRequest request) { var result = new PricingResult(market.ValuationDate, request); var exerciseDate = trade.ExerciseDates.Last(); var maturityDate = trade.UnderlyingMaturityDate; //TODO: support timeIncrement var exerciseInYears = AnalyticalOptionPricerUtil.timeToMaturityFraction(market.ValuationDate, exerciseDate, trade); var maturityInYears = AnalyticalOptionPricerUtil.timeToMaturityFraction(market.ValuationDate, maturityDate, trade); var riskFreeRate = market.DiscountCurve.Value.ZeroRate(market.ValuationDate, exerciseDate); var ticker1 = trade.UnderlyingTickers[0]; var ticker2 = trade.UnderlyingTickers[1]; double dividendRate1, dividendRate2; if (AnalyticalOptionPricerUtil.isForwardFuturesOption(trade.UnderlyingProductType)) { dividendRate1 = riskFreeRate; dividendRate2 = riskFreeRate; } else { dividendRate1 = market.DividendCurves.Value[ticker1].ZeroRate(market.ValuationDate, exerciseDate); dividendRate2 = market.DividendCurves.Value[ticker2].ZeroRate(market.ValuationDate, exerciseDate); } var spot1 = market.SpotPrices.Value[ticker1]; var spot2 = market.SpotPrices.Value[ticker2]; var sigma1 = market.VolSurfaces.Value[ticker1].GetValue(exerciseDate, trade.Strikes[0], spot1); var sigma2 = market.VolSurfaces.Value[ticker2].GetValue(exerciseDate, trade.Strikes[0], spot2); var strike1 = trade.Strikes[0]; var strike2 = 0.0; if (trade.Strikes.Length > 1) { strike2 = trade.Strikes[1]; } //Note: correlation here is a scala number. can be a grid for multi-asset option var rho = market.Correlations.Value[ticker1].GetValue(exerciseDate, strike1); var calculator = new RainbowOptionCalculator(trade.OptionType, trade.RainbowType, strike1, strike2, trade.CashAmount, spot1, spot2, rho, sigma1, sigma2, exerciseInYears, riskFreeRate, dividendRate1, dividendRate2, trade.Notional); bool isExpired = trade.ExerciseDates.Last() < market.ValuationDate; bool isExpiredforTheta = trade.ExerciseDates.Last() <= market.ValuationDate; if (isExpired) { result.Pv = 0.0; result.Theta = 0.0; result.asset1Delta = 0.0; result.asset1DeltaCash = 0.0; result.asset2Delta = 0.0; result.asset2DeltaCash = 0.0; result.asset1PartialDelta = 0.0; result.asset2PartialDelta = 0.0; result.asset1Gamma = 0.0; result.asset1GammaCash = 0.0; result.asset2Gamma = 0.0; result.asset2GammaCash = 0.0; result.asset1Vega = 0.0; result.asset2Vega = 0.0; result.Rho = 0.0; result.Theta = 0.0; result.asset1DDeltaDt = 0.0; result.asset2DDeltaDt = 0.0; result.asset1DVegaDvol = 0.0; result.asset2DVegaDvol = 0.0; result.asset1DDeltaDvol = 0.0; result.asset2DDeltaDvol = 0.0; result.asset1DVegaDt = 0.0; result.asset2DVegaDt = 0.0; result.crossGamma = 0.0; result.crossVomma = 0.0; result.crossVanna1 = 0.0; result.crossVanna2 = 0.0; result.correlationVega = 0.0; } else { if (result.IsRequested(PricingRequest.Pv)) { result.Pv = calculator.Pv; } if (AnalyticalOptionPricerUtil.isBasicPricing(result)) { result.asset1Delta = calculator.asset1Delta; result.asset2Delta = calculator.asset2Delta; result.asset1DeltaCash = calculator.asset1Delta * spot1; result.asset2DeltaCash = calculator.asset2Delta * spot2; result.asset1PartialDelta = calculator.asset1PartialDelta; result.asset2PartialDelta = calculator.asset2PartialDelta; result.asset1Gamma = calculator.asset1Gamma; result.asset2Gamma = calculator.asset2Gamma; result.asset1GammaCash = calculator.asset1Gamma * spot1 * spot1 / 100; result.asset2GammaCash = calculator.asset2Gamma * spot2 * spot2 / 100; result.asset1Vega = calculator.asset1Vega; result.asset2Vega = calculator.asset2Vega; result.Rho = calculator.Rho; result.Theta = (isExpiredforTheta) ? 0.0 : calculator.Theta; } if (AnalyticalOptionPricerUtil.isHighOrderPricing(result)) { result.asset1DVegaDvol = calculator.asset1DVegaDvol; result.asset2DVegaDvol = calculator.asset2DVegaDvol; result.asset1DDeltaDvol = calculator.asset1DDeltaDvol; result.asset2DDeltaDvol = calculator.asset2DDeltaDvol; result.crossGamma = calculator.crossGamma; result.crossVomma = calculator.crossVomma; result.crossVanna1 = calculator.crossVanna1; result.crossVanna2 = calculator.crossVanna2; result.correlationVega = calculator.correlationVega; } if (isExpiredforTheta) { result.asset1DDeltaDt = result.asset2DDeltaDt = 0.0; result.asset1DVegaDt = result.asset2DVegaDt = 0.0; } else { result.asset1DDeltaDt = calculator.asset1DDeltaDt; result.asset2DDeltaDt = calculator.asset2DDeltaDt; result.asset1DVegaDt = calculator.asset1DVegaDt; result.asset2DVegaDt = calculator.asset2DVegaDt; } } 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); }