/// <summary> /// Generate CTD dates and set CTD coupon rate and conversion factor. /// </summary> protected void GenerateCTD(double baseDate, double issueDate, double maturityDate, double couponInterval, double firstCouponDate, double penultimateCouponDate, DayCount dayCount, IHolidayCalendar calendar, double couponRate, double conversionFactor) { if (conversionFactor <= 0.0) { return; // No CTD details or details invalid } BondFutureOption deal = (BondFutureOption)fDeal; // Validation of settlement date not done for CTD details on price factor if (deal.Settlement_Date >= maturityDate) { throw new AnalyticsException("Settlement date must be before cheapest-to-deliver maturity date."); } DateGenerationResults dateGenerationResults = deal.GetDateGenerationResults(issueDate, maturityDate, couponInterval, firstCouponDate, penultimateCouponDate, dayCount, calendar); fPayDates = dateGenerationResults.PayDates; fAccruals = dateGenerationResults.AccrualYearFractions; fIssueDate = issueDate; fMaturityDate = maturityDate; fCouponInterval = couponInterval; fCouponRate = couponRate; fConversionFactor = conversionFactor; fAccrual = PricingFunctions.AccruedInterest(deal.Settlement_Date, fIssueDate, fPayDates, fAccruals, fCouponRate, 1.0, null); double strike = PriceTransform(deal.Strike); double tSettle = CalcUtils.DaysToYears(deal.Settlement_Date - baseDate); double tMaturity = CalcUtils.DaysToYears(fMaturityDate - baseDate); fStrikeYield = PricingFunctions.BondYieldFromPrice(tSettle, tMaturity, couponRate, couponInterval, strike); }
/// <summary> /// Calculate forward price, discount factor and volatility /// </summary> protected override void PriceAndVolatility(double baseDate, double valueDate, Vector forwardPrice, Vector discountFactor, Vector volatility) { BondFutureOption deal = (BondFutureOption)fDeal; double t = CalcUtils.DaysToYears(valueDate - baseDate); double tSettle = CalcUtils.DaysToYears(deal.Settlement_Date - baseDate); double tMaturity = CalcUtils.DaysToYears(fMaturityDate - baseDate); fDiscountRate.GetValue(discountFactor, t, tSettle); if (volatility != null) { double tExpiry = deal.GetTimeToExpiry(baseDate); if (tExpiry > t) { // Calculate price volatility using (var cache = Vector.CacheLike(forwardPrice)) { Vector macaulayDuration = cache.Get(); Vector yield = cache.Get(); Vector yieldStrike = cache.Get(fStrikeYield); // Calculate forwrad price, yield and adjusted duration using simple bond price functions PricingFunctions.BondForwardPriceAndAdjustedMacaulayDuration(forwardPrice, macaulayDuration, t, tSettle, tMaturity, fCouponRate, fCouponInterval, discountFactor, fDiscountRate, fSurvivalProb); PricingFunctions.BondYieldFromPrice(yield, tSettle, tMaturity, fCouponRate, fCouponInterval, forwardPrice); // Calculate Modified Duration from Macaulay Duration. Vector modifiedDuration = cache.Get(); PricingFunctions.GetModifiedDuration(modifiedDuration, macaulayDuration, yield, fCouponInterval); // Get yield volatility fInterestYieldVol.GetValue(volatility, t, yield, yieldStrike, tExpiry, tMaturity - tSettle); // Convert (normal) yield vol to lognormal price vol volatility.MultiplyBy(modifiedDuration); if (fInterestYieldVol.GetDistributionType() == ProbabilityDistribution.Lognormal) { // Convert lognormal yield vol to lognormal price vol. volatility.MultiplyBy(yield); } } } else { volatility.Clear(); } } // Recalculate forward price using fAccruals double accrual, cash; PricingFunctions.BondPrice(forwardPrice, out accrual, out cash, baseDate, valueDate, deal.Settlement_Date, fIssueDate, fMaturityDate, 1.0, fCouponRate, fPayDates, fAccruals, fDiscountRate, null, null, 0.0, fSurvivalProb, 1.0); BondFutureValuation.AdjustForDefault(baseDate, valueDate, forwardPrice, deal.Expiry_Date, Respect_Default == YesNo.Yes && !string.IsNullOrEmpty(deal.Issuer), fUnderlyingIsAlive, fHistoricalRecovery, fDefaultTime, fDiscountRate, fRecoveryRate); forwardPrice.AssignQuotient(forwardPrice, discountFactor); if (!fRepoIsDiscount) { fRepoRate.GetValue(discountFactor, t, tSettle); } }
/// <summary> /// Calculate valuation profiles. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { PreValue(factors); TimeGridIterator tgi = new TimeGridIterator(fT); PVProfiles result = valuationResults.Profile; CashAccumulators cashAccumulators = valuationResults.Cash; BondOptionDeal deal = (BondOptionDeal)Deal; double baseDate = factors.BaseDate; double notional = deal.Notional; double interval = deal.Coupon_Interval; double buySign = (deal.Buy_Sell == BuySell.Buy) ? +1 : -1; double paySign = (deal.Option_Type == OptionType.Call) ? +1 : -1; double coupon = Percentage.PercentagePoint * deal.Coupon_Rate; double tExpiry = CalcUtils.DaysToYears(deal.Expiry_Date - baseDate); double tMaturity = CalcUtils.DaysToYears(deal.Bond_Maturity_Date - baseDate); IInterestYieldVol interestYieldVol = InterestVolBase.GetYieldVol(factors, deal.Yield_Volatility, fCurrency); if ((deal.Amortisation) != null && (deal.Amortisation.Count > 0)) { notional = deal.Amortisation.GetPrincipal(notional, deal.Expiry_Date); } bool respectDefault = Respect_Default == YesNo.Yes && fCreditRating != null; using (IntraValuationDiagnosticsHelper.StartDeal(fIntraValuationDiagnosticsWriter, Deal)) { using (var pricerCache = Vector.Cache(factors.NumScenarios)) { Vector defaultTime = null; Vector bondIsAlive = null; Vector historicalRecovery = null; if (respectDefault) { defaultTime = pricerCache.Get(); bondIsAlive = pricerCache.Get(1.0); historicalRecovery = pricerCache.GetClear(); fCreditRating.DefaultTime(defaultTime); } var defaultedBeforeBaseDate = respectDefault && CreditRating.DefaultedBeforeBaseDate(fCreditRating, baseDate); VectorEngine.For(tgi, () => { using (IntraValuationDiagnosticsHelper.StartValuation(fIntraValuationDiagnosticsWriter, tgi.Date)) { using (var cache = Vector.Cache(factors.NumScenarios)) { Vector optionValue = cache.GetClear(); Vector stdDev = cache.Get(); // Std.Dev of Price Vector stdDevYield = cache.Get(); //Std.Dev of Yield Vector price = cache.Get(); Vector yield = cache.Get(); Vector macaulayDuration = cache.Get(); Vector bondValue = cache.Get(); Vector df = cache.Get(); Vector dfr = fRepoIsDiscount ? null : cache.Get(); if (defaultedBeforeBaseDate) { result.AppendVector(tgi.Date, optionValue); return(LoopAction.Break); } // This BondPrice function returns the value of the bond cashflows after ExpiryDate, including accrual, discounted back to T.date double accrual, cash; PricingFunctions.BondPrice(bondValue, out accrual, out cash, baseDate, tgi.Date, deal.Expiry_Date, deal.Issue_Date, deal.Bond_Maturity_Date, notional, coupon, fPayDates, fAccruals, fDiscountRate, deal.Amortisation, fPrincipals, fFinalPrincipal, fSurvivalProb, +1.0); // Now check scenario by scenario for defaults, overwriting bondValue as necessary if (respectDefault) { AdjustBondValueForDefault(notional, tExpiry, bondValue, bondIsAlive, historicalRecovery, defaultTime, tgi.T, fDiscountRate, fRecoveryRate); } // convert price and duration to forward (tExpiry) basis if (tgi.Date == deal.Expiry_Date) { optionValue.Assign(buySign * VectorMath.Max(0.0, paySign * (bondValue - notional * fStrike))); cashAccumulators.Accumulate(fFxRate, tgi.Date, optionValue); } else { fDiscountRate.GetValue(df, tgi.T, tExpiry); if (fRepoIsDiscount) { dfr = df; } else { fRepoRate.GetValue(dfr, tgi.T, tExpiry); } // Need yield and duration to convert yield vol to price vol. PricingFunctions.BondForwardPriceAndAdjustedMacaulayDuration(price, macaulayDuration, tgi.T, tExpiry, tMaturity, coupon, interval, df, fDiscountRate, fSurvivalProb); PricingFunctions.BondYieldFromPrice(yield, tExpiry, tMaturity, coupon, interval, price); // Calculate Modified Duration from Macaulay Duration. Vector modifiedDuration = cache.GetClear(); PricingFunctions.GetModifiedDuration(modifiedDuration, macaulayDuration, yield, interval); // Calculate Std.Dev of Yield and Price interestYieldVol.GetStdDev(stdDevYield, tgi.T, yield, fStrikeYield, tExpiry, tMaturity - tExpiry); stdDev.Assign(modifiedDuration * stdDevYield); if (interestYieldVol.GetDistributionType() == ProbabilityDistribution.Lognormal) { stdDev.MultiplyBy(yield); } price.AssignQuotient(bondValue, df); PricingFunctions.BlackFunction(optionValue, deal.Option_Type, price, notional * fStrike, stdDev); optionValue.MultiplyBy(buySign * dfr); if (fIntraValuationDiagnosticsWriter.Level > IntraValuationDiagnosticsLevel.None) { // Add Intra-valuation Diagnostics using (var volatilitiesAtDateStore = IntraValuationDiagnosticsHelper.CreateVolatilitiesAtDateStore(fIntraValuationDiagnosticsWriter, factors.NumScenarios)) using (var volatilitiesYieldAtDateStore = IntraValuationDiagnosticsHelper.CreateVolatilitiesAtDateStore(fIntraValuationDiagnosticsWriter, factors.NumScenarios)) { volatilitiesAtDateStore.Add(tgi.Date, tgi.TimeGrid.fEndDate, stdDev); volatilitiesYieldAtDateStore.Add(tgi.Date, tgi.TimeGrid.fEndDate, stdDevYield); IntraValuationDiagnosticsHelper.AddBondOptionProperties(fIntraValuationDiagnosticsWriter, price, dfr, bondValue, accrual, volatilitiesAtDateStore, volatilitiesYieldAtDateStore); IntraValuationDiagnosticsHelper.AddCashflowsPV(fIntraValuationDiagnosticsWriter, optionValue); } } } result.AppendVector(tgi.Date, fFxRate.Get(tgi.T) * optionValue); return(LoopAction.Continue); } } }); } result.Complete(fT); } }
/// <summary> /// Returns the yield implied by strike price. /// </summary> public double GetStrikeYield(ref double strike, double tExpiry, double tMaturity, DateList payDates, double[] accruals) { strike = DirtyStrike(strike, payDates, accruals); return(PricingFunctions.BondYieldFromPrice(tExpiry, tMaturity, Percentage.PercentagePoint * Coupon_Rate, Coupon_Interval, strike)); }