public static Date GetFxSpotDate(Date startDate, DayGap settlement, CurrencyCode fgnCcy, CurrencyCode domCcy, ICalendar fgnCalendar, ICalendar domCalendar) { //中国外汇交易中心产品指引(外汇市场)V1.8 58页 var foreignSpotDate = settlement.Get(fgnCalendar, startDate); var domesticSpotDate = settlement.Get(domCalendar, startDate); var usdCalendar = CalendarImpl.Get(Calendar.Usd); if (fgnCcy == CurrencyCode.USD) { if (!usdCalendar.IsHoliday(domesticSpotDate)) { return(domesticSpotDate); } } if (domCcy == CurrencyCode.USD) { if (!usdCalendar.IsHoliday(foreignSpotDate)) { return(foreignSpotDate); } } var guessSettlementDate = foreignSpotDate > domesticSpotDate ? foreignSpotDate : domesticSpotDate; if (usdCalendar.IsHoliday(guessSettlementDate) || fgnCalendar.IsHoliday(guessSettlementDate) || domCalendar.IsHoliday(guessSettlementDate)) { //find next biz day for foreign currency and domestic currency while (fgnCalendar.IsHoliday(guessSettlementDate) || domCalendar.IsHoliday(guessSettlementDate)) { guessSettlementDate = new Term("1D").Next(guessSettlementDate); } return(guessSettlementDate); } return(guessSettlementDate); }
/// <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 double GetFixingRate(IYieldCurve fixingCurve, ICalendar resetCalendar, IDayCount dayCount, Date accStartDate, Date accEndDate, ITerm resetTerm, ITerm fixingTenor, DayGap fixingToResetGap, Stub resetStub, BusinessDayConvention resetBda, double couponSpread, double capRate, double floorRate, out CfCalculationDetail[] resetDetails, IDictionary <Date, double> historicalRate = null, FloatingCouponCalcType frnCalc = FloatingCouponCalcType.SimpleFrn, double stepWiseCompensationCoupon = 0.0, double multiplier = 1.0) { var details = new List <CfCalculationDetail>(); var resetPriods = resetTerm == null ? new List <Date> { accStartDate, accEndDate } : new Schedule(accStartDate, accEndDate, resetTerm, resetStub, resetCalendar, resetBda).ToList(); for (var i = 0; i < resetPriods.Count - 1; ++i) { var resetStartDate = resetPriods[i]; var resetEndDate = resetPriods[i + 1]; if (resetEndDate > accEndDate) { resetEndDate = accEndDate; } var fixingStartDate = fixingToResetGap.Get(resetCalendar, resetStartDate); var forwardCompound = frnCalc == FloatingCouponCalcType.ZzFrn ? Compound.Continuous : IndexType.ForwardCompound(); details.Add(new CfCalculationDetail( resetStartDate, resetEndDate, fixingStartDate, multiplier * GetResetRate(fixingCurve, fixingStartDate, resetCalendar, fixingTenor, AverageDays, historicalRate, forwardCompound, IndexType.DayCount()) + stepWiseCompensationCoupon, dayCount.CalcDayCountFraction(resetStartDate, resetEndDate, accStartDate, accEndDate), resetStartDate < (fixingCurve == null ? fixingStartDate : fixingCurve.ReferenceDate))); } resetDetails = details.ToArray(); var totalDcf = resetDetails.Sum(x => x.FixingDcf); double couponRate; switch (CouponCompound) { case CouponCompound.Compounded: couponRate = (resetDetails.Select(x => { var coupon = FilterRate(x.FixingRate + couponSpread, capRate, floorRate); return(1.0 + coupon * x.FixingDcf); }) .Aggregate(1.0, (current, v) => current * v) - 1.0) / totalDcf; return(couponRate); case CouponCompound.Simple: couponRate = resetDetails.Select(x => { var coupon = FilterRate(x.FixingRate + couponSpread, capRate, floorRate); return(coupon * x.FixingDcf); }).Sum() / totalDcf; return(couponRate); default: throw new PricingLibraryException("Unknow type of coupon compund type" + CouponCompound); } }