/// <summary> /// Calculate vector valuation profile and vector realised cash profile. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { PreValue(factors); CalcUtils.CreateDealProfilesIfRequired(valuationResults, fItems, factors); double paySign = fSwaptionDeal.Payer_Receiver == PayerReceiver.Payer ? +1 : -1; double buySign = fSwaptionDeal.Buy_Sell == BuySell.Buy ? +1 : -1; bool isCashSettled = fSwaptionDeal.Settlement_Style == SettlementType.Cash; bool isPhysicallySettled = fSwaptionDeal.Settlement_Style == SettlementType.Physical; bool cashRequired = !valuationResults.Cash.Ignore; TimeGridIterator tgi = new TimeGridIterator(fT); PVProfiles result = valuationResults.Profile; using (IntraValuationDiagnosticsHelper.StartDeal(fIntraValuationDiagnosticsWriter, Deal)) { using (var outerCache = Vector.Cache(factors.NumScenarios)) { Vector pv = outerCache.Get(); Vector exerciseWeight = outerCache.GetClear(); Vector cash = cashRequired ? outerCache.GetClear() : null; // For a cash settled swaption, Settlement amount to be paid on Settlement Date. Vector settlementCash = isCashSettled ? outerCache.GetClear() : null; VectorEngine.For(tgi, () => { // Work out the PV if (tgi.Date < fSwaptionDeal.Option_Expiry_Date) { ValueBeforeExpiry(pv, factors, isCashSettled, tgi); } else { ValueOnOrAfterExpiry(pv, exerciseWeight, settlementCash, cash, factors, isCashSettled, isPhysicallySettled, cashRequired, tgi, paySign); } result.AppendVector(tgi.Date, buySign * pv * fFxRate.Get(tgi.T)); if (cashRequired) { valuationResults.Cash.Accumulate(fFxRate, tgi.Date, buySign * cash); } }); } result.Complete(fT); } }
/// <summary> /// Generic Deal Valuation - derived objects override the ValueFn() method. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { PreValue(factors); var deal = (CalendarSpreadOption)Deal; var tgi = new TimeGridIterator(fT); PVProfiles result = valuationResults.Profile; CashAccumulators accumulator = valuationResults.Cash; fTimeToSettlement = CalcUtils.DaysToYears(deal.Settlement_Date - factors.BaseDate); fPeriodEnd1 = CalcUtils.DaysToYears(deal.Period_End_1 - factors.BaseDate); fPeriodEnd2 = CalcUtils.DaysToYears(deal.Period_End_2 - factors.BaseDate); // The time to expiry is the time to the end of the first expiring contract. fTimeToExpiry = CalcUtils.DaysToYears(deal.Period_End_1 - factors.BaseDate); using (var cache = Vector.Cache(factors.NumScenarios)) { EnergySpreadOptionPricer pricer1 = GetEnergyOptionPricer(fForwardSample, fReferencePrice1, fReferenceVol1, factors.BaseDate, fPeriodEnd1, deal.Period_Start_1, deal.Period_End_1, deal.Realized_Average_1, deal.Realized_Average_Date_1, cache); EnergySpreadOptionPricer pricer2 = GetEnergyOptionPricer(fForwardSample, fReferencePrice2, fReferenceVol2, factors.BaseDate, fPeriodEnd2, deal.Period_Start_2, deal.Period_End_2, deal.Realized_Average_2, deal.Realized_Average_Date_2, cache); Vector pv = cache.Get(); Vector realizedPrice1 = cache.Get(); Vector realizedPrice2 = cache.Get(); Vector unrealizedPrice1 = cache.Get(); Vector unrealizedPrice2 = cache.Get(); Vector vol1 = cache.Get(); Vector vol2 = cache.Get(); Vector correlation = cache.Get(); Vector discountFactor = cache.Get(); // Calculate correlation between forward prices for two different (dereferenced) maturities (e.g. Apr2012 and Jan2013). TDate forwardDate1 = fReferencePrice1.GetPriceDate(deal.Period_End_1, 0); TDate forwardDate2 = fReferencePrice2.GetPriceDate(deal.Period_End_2, 0); fCorrelations.GetValue(correlation, tgi.T, forwardDate1, forwardDate2); while (tgi.Next()) { pv.Clear(); if (tgi.T <= fTimeToExpiry) { // Get unrealised reference forward prices (which also age the pricer to time t and update the realized average at time t) pricer1.GetUnrealizedPrice(tgi.T, unrealizedPrice1); pricer2.GetUnrealizedPrice(tgi.T, unrealizedPrice2); // Get the realized averages for both reference prices. pricer1.GetRealizedPrice(realizedPrice1); pricer2.GetRealizedPrice(realizedPrice2); int numSamples1 = pricer1.GetNumSamples(); int numRealizedSamples1 = pricer1.GetNumRealizedSamples(); int numUnrealizedSamples1 = pricer1.GetNumUnrealizedSamples(); int numSamples2 = pricer2.GetNumSamples(); int numRealizedSamples2 = pricer2.GetNumRealizedSamples(); int numUnrealizedSamples2 = pricer2.GetNumUnrealizedSamples(); // Modify the strike Vector kStar = cache.Get(deal.Strike_Price - realizedPrice1 * numRealizedSamples1 / numSamples1 + realizedPrice2 * numRealizedSamples2 / numSamples2); Vector refPriceStar1 = cache.Get(unrealizedPrice1 * numUnrealizedSamples1 / numSamples1); Vector refPriceStar2 = cache.Get(unrealizedPrice2 * numUnrealizedSamples2 / numSamples2); // Get ATM volatilities of the forward price at different maturities (given as time in years with respect to base date). pricer1.GetVol(tgi.T, unrealizedPrice2, vol1); pricer2.GetVol(tgi.T, unrealizedPrice1, vol2); // value is intrinsic - pricing with volatility 0 and realized price if there are no future sample. if (numUnrealizedSamples1 == 0) { vol1.Clear(); } // value is intrinsic - pricing with volatility 0 and realized price if there are no future sample. if (numUnrealizedSamples2 == 0) { vol2.Clear(); } // CSO pricing: exp(-rT) * E{max(F1(T) - F2(T) - K, 0)} // For the European CSO, we set multiplier1 = sign, multiplier2 = -sign, constant = -sign * strike, // where sign = +1 for a call and -1 for a put in the SpreadOptionBS function. double rootTime = Math.Sqrt(fTimeToExpiry - tgi.T); Vector volStar1 = cache.Get(vol1 * rootTime); Vector volStar2 = cache.Get(vol2 * rootTime); PricingFunctions.SpreadOptionBS(pv, fCallPutSign, -fCallPutSign, cache.Get(-fCallPutSign * kStar), fCallPutSign, refPriceStar1, refPriceStar2, kStar, volStar1, volStar2, correlation); // The option itself cannot be worth less than zero (for long positions). // However, due to that the Bjerksund & Stensland is a type of non-optimal exercise, it is possible to end up with negative PV via // its pricing formula. pv.AssignMax(pv, 0.0); // Discount payment made at settlement date pv.MultiplyBy(fScale * fDiscountRate.Get(tgi.T, fTimeToSettlement)); } // Cash settlement at settlement date if (tgi.T == fTimeToSettlement && accumulator != null && !accumulator.Ignore) { accumulator.Accumulate(fFxPayoffRate, tgi.Date, pv); } result.AppendVector(tgi.Date, pv * fFxPayoffRate.Get(tgi.T)); } result.Complete(fT); } }
/// <summary> /// Value the deal from given base date, price factors and time grid. /// </summary> public void Value(PVProfiles pvResults, CashAccumulators cashResults, double baseDate, IInterestRate discountRate, IInterestRate forecastRate1, IInterestRate forecastRate2, IFxRate fxRate, TimeGrid timeGrid, int numScenarios) { var tgi = new TimeGridIterator(timeGrid); var deal = (FloatingInterestCashflowInterpolatedDeal)Deal; bool hasRate1 = deal.HasRate1(); bool hasRate2 = deal.HasRate2(); double scale = deal.Buy_Sell == BuySell.Buy ? +deal.Principal : -deal.Principal; double tPay = CalcUtils.DaysToYears(fPaymentDate - baseDate); double tReset = CalcUtils.DaysToYears(deal.Reset_Date - baseDate); double tRateStart = CalcUtils.DaysToYears(deal.Rate_Start_Date - baseDate); double tRateEnd1 = hasRate1 ? CalcUtils.DaysToYears(fRate1EndDate - baseDate) : 0.0; double tRateEnd2 = hasRate2 ? CalcUtils.DaysToYears(fRate2EndDate - baseDate) : 0.0; double tRateEnd12 = tRateEnd2 - tRateEnd1; // Time from rate 1 end date to rate 2 end date. double tAccrualEnd = CalcUtils.DaysToYears(deal.Accrual_End_Date - baseDate); double interpCoefficient = Math.Abs(tRateEnd12) >= CalcUtils.MinTime ? (tAccrualEnd - tRateEnd1) / tRateEnd12 : 0.0; // Coefficient used to calculate interpolated rate. VectorEngine.For(tgi, () => { using (var cache = Vector.Cache(numScenarios)) { Vector pv = cache.Get(); if (tgi.Date <= fPaymentDate && fPaymentDate > fCutoffDate) { Vector interpRate = cache.GetClear(); Vector rate1 = cache.GetClear(); Vector rate2 = cache.GetClear(); if (hasRate1) { if (fKnownResetRate1.HasValue) { rate1.Assign(fKnownResetRate1.Value); } else { InterestRateUtils.LiborRate(rate1, forecastRate1, tgi.T, tReset, tRateStart, tRateEnd1, fRate1YearFraction); } } if (hasRate2) { if (fKnownResetRate2.HasValue) { rate2.Assign(fKnownResetRate2.Value); } else { InterestRateUtils.LiborRate(rate2, forecastRate2, tgi.T, tReset, tRateStart, tRateEnd2, fRate2YearFraction); } } if (hasRate1 && hasRate2) { if (Math.Abs(tRateEnd12) >= CalcUtils.MinTime) { interpRate.Assign(rate1 + interpCoefficient * (rate2 - rate1)); } else { interpRate.Assign(0.5 * rate1 + 0.5 * rate2); } } else { interpRate.Assign(hasRate1 ? rate1 : rate2); } // Round the calculated rate, regardless whether the valuation date is before or after the reset date. CFFloatingInterestList.RoundRateTo(deal.Interpolated_Rate_Rounding, interpRate); pv.Assign(scale * (interpRate + deal.Margin) * fAccrualYearFraction); CFFixedList.RoundCashflow(pv, Cashflow_Rounding); if (tgi.Date < fPaymentDate) { pv.MultiplyBy(discountRate.Get(tgi.T, tPay)); } else if (tgi.Date == fPaymentDate) { cashResults.Accumulate(fxRate, fPaymentDate, pv); } } else { pv.Clear(); } pvResults.AppendVector(tgi.Date, pv * fxRate.Get(tgi.T)); } }); // After maturity pvResults.Complete(timeGrid); }
/// <summary> /// Calculate a valuation profile for a range of scenarios. /// </summary> public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes) { IAssetPrice price = GetAssetPrice(factors); PVProfiles result = valuationResults.Profile; double scale = fDeal.Units * (fDeal.Buy_Sell == BuySell.Buy ? +1 : -1); var tgi = new TimeGridIterator(fT); VectorEngine.For(tgi, () => result.AppendVector(tgi.Date, scale * price.Get(tgi.T))); result.Complete(fT); CashAccumulators cashAccumulators = valuationResults.Cash; double endDate = Deal.EndDate(); if (!cashAccumulators.Ignore && endDate <= fT.fHorizon) { double tEnd = CalcUtils.DaysToYears(endDate - factors.BaseDate); IFxRate fxRate = factors.GetInterface <IFxRate>(fDeal.Currency); cashAccumulators.Accumulate(fxRate, endDate, scale * price.Get(tEnd) / fxRate.Get(tEnd)); } }