Beispiel #1
0
        /// <summary>
        /// Calculate valuation profiles.
        /// </summary>
        public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes)
        {
            base.Value(valuationResults, factors, baseTimes);

            var accruedResults = valuationResults.Results <AccruedInterest>();

            if (accruedResults == null)
            {
                return;
            }

            using (var cache = Vector.Cache(factors.NumScenarios))
            {
                Vector accruedInterest = cache.Get();
                var    deal            = (CFEquityFloatingInterestListDeal)Deal;
                var    tgi             = new TimeGridIterator(fT);

                var equityParams = new EquityCashflowParams((EquityPrice)fEquity, (EquityPriceVol)fEquityVol, fEquityFXRate, fEquityPayoffFXRate, deal.GetEquityPayoffType(), fEquityQuantoCompo, null, null);

                while (tgi.Next())
                {
                    deal.Cashflows.CalculateAccrual(accruedInterest, equityParams, factors.BaseDate, tgi.Date, accruedResults.AccrueFromToday, deal.GetHolidayCalendar(), deal.Accrual_Day_Count, fForecastRate, fFxRate);
                    accruedResults.SetValue(tgi.Date, fBuySellSign * accruedInterest);
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Calculate valuation profiles.
        /// </summary>
        public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes)
        {
            base.Value(valuationResults, factors, baseTimes);

            var deal = (CFFixedInterestListDeal)Deal;

            CalculateMetrics(valuationResults, factors, deal);

            var accruedResults = valuationResults.Results <AccruedInterest>();

            if (accruedResults == null)
            {
                return;
            }

            var tgi = new TimeGridIterator(fT);

            while (tgi.Next())
            {
                double accruedInterest = deal.Cashflows.CalculateAccrual(tgi.Date, accruedResults.AccrueFromToday, fDeal.GetHolidayCalendar());
                accruedResults.SetValue(tgi.Date, fBuySellSign * accruedInterest);
            }
        }
Beispiel #3
0
        /// <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);
            }
        }
Beispiel #4
0
        /// <summary>
        /// Vector valuation function.
        /// </summary>
        public override void Value(ValuationResults valuationResults, PriceFactorList factors, BaseTimeGrid baseTimes)
        {
            PreValue(factors);

            TimeGridIterator         tgi         = new TimeGridIterator(fT);
            PVProfiles               result      = valuationResults.Profile;
            CashAccumulators         accumulator = valuationResults.Cash;
            DealCreditLinkedNoteBase deal        = (DealCreditLinkedNoteBase)fDeal;

            ISurvivalProb SP = GetSurvivalProbability(factors);
            RecoveryRate  RR = GetRecoveryRate(factors);
            CreditRating  CR = GetCreditRating(factors);

            double tEffective    = CalcUtils.DaysToYears(deal.Effective_Date - factors.BaseDate);
            double scale         = (deal.Buy_Sell == BuySell.Buy) ? +deal.Notional_Amount : -deal.Notional_Amount;
            double purchasePrice = Percentage.PercentagePoint * deal.Price;
            double couponRate    = (deal.Coupon_Type == InterestRateType.Fixed) ? Percentage.PercentagePoint * deal.Coupon_Rate : 0.0;
            double couponSpread  = (deal.Coupon_Type == InterestRateType.Fixed) ? 0.0 : BasisPoint.BasisPointValue * deal.Coupon_Spread;
            double indexTenor    = (deal.Index_Tenor > 0.0) ? deal.Index_Tenor : deal.Coupon_Interval;

            using (var cache = Vector.Cache(factors.NumScenarios))
            {
                bool[] hasDefaulted       = (Respect_Default == YesNo.Yes) ? new bool[factors.NumScenarios] : null;
                Vector defaultTime        = (Respect_Default == YesNo.Yes) ? cache.Get() : null;
                Vector historicalRecovery = (Respect_Default == YesNo.Yes) ? cache.GetClear() : null;

                if (hasDefaulted != null && CR != null)
                {
                    DefaultTime(defaultTime, CR);
                }

                Vector npv      = cache.Get();
                Vector cash     = cache.Get();
                Vector pStart   = cache.Get();
                Vector pEnd     = cache.Get();
                Vector amount   = cache.Get();
                Vector recovery = cache.Get();
                Vector dfLast   = cache.Get();
                Vector df       = cache.Get();

                cash.Clear();
                var defaultedBeforeTheBaseDate = Respect_Default == YesNo.Yes &&
                                                 CreditRating.DefaultedBeforeBaseDate(CR, factors.BaseDate);
                while (tgi.Next())
                {
                    if (defaultedBeforeTheBaseDate)
                    {
                        npv.Clear();
                        result.AppendVector(tgi.Date, npv);
                        break;
                    }

                    if (!deal.Principal_Guaranteed && Respect_Default == YesNo.Yes)
                    {
                        RealizedRecoveryRate(recovery, RR, tgi.T);
                    }

                    // Assume defaults are rare and start by valuing under all scenarios without realized defaults

                    // Value of principal repayment
                    SurvivalProbability(pEnd, factors, SP, tgi.T, fT.fLast);
                    fDiscountRate.GetValue(dfLast, tgi.T, fT.fLast);
                    if (deal.Principal_Guaranteed)
                    {
                        npv.Assign(dfLast);
                    }
                    else
                    {
                        npv.Assign(dfLast * pEnd);
                    }
                    if (accumulator != null && tgi.T == fT.fLast)
                    {
                        cash.Assign(npv);
                    }

                    // Value of coupons
                    for (int i = PayDates.Count - 1; i >= 0 && tgi.Date <= PayDates[i]; --i)
                    {
                        double tPay      = CalcUtils.DaysToYears(PayDates[i] - factors.BaseDate);
                        double tReset    = CalcUtils.DaysToYears(ResetDates[i] - factors.BaseDate);
                        double tPrevious = Math.Max(tgi.T, tReset);
                        SurvivalProbability(pStart, factors, SP, tgi.T, tPrevious);

                        if (deal.Coupon_Type == InterestRateType.Floating)
                        {
                            // Forecast a coupon, add the spread
                            InterestRateUtils.LiborRate(amount, fForecastRate, tgi.T, tReset, tReset, tReset + indexTenor, deal.Index_Day_Count);
                            amount.Add(couponSpread);
                            amount.MultiplyBy(Accruals[i]);
                        }
                        else
                        {
                            // Fixed coupon
                            amount.Assign(couponRate * Accruals[i]);
                        }

                        // The value of the coupon if no default
                        npv.Add(amount * fDiscountRate.Get(tgi.T, tPay) * pEnd);
                        if (accumulator != null && tgi.T == tPay)
                        {
                            cash.Assign(amount);
                        }

                        // The recovery value on default - assume guaranteed principal paid at end, recovery paid immediately
                        if (!deal.Principal_Guaranteed)
                        {
                            npv.Add(fDiscountRate.Get(tgi.T, 0.5 * (tPay + tPrevious)) * (pStart - pEnd) * PricingRecoveryRate(SP));
                        }

                        pEnd.DestructiveAssign(pStart);
                    }

                    // Now check for realized default scenario by scenario, overwriting NPV and cash as appropriate
                    if (Respect_Default == YesNo.Yes && defaultTime != null)
                    {
                        if (tgi.T < tEffective)
                        {
                            fDiscountRate.GetValue(df, tgi.T, tEffective);
                        }

                        for (int i = 0; i < npv.Count; ++i)
                        {
                            if (defaultTime[i] > tgi.T)
                            {
                                continue;
                            }

                            if (deal.Principal_Guaranteed)
                            {
                                npv[i] = dfLast[i];   // full principal paid at maturity
                            }
                            else
                            {
                                if (!hasDefaulted[i])
                                {
                                    historicalRecovery[i] = recovery[i];   // record the historical recovery rate
                                }
                                if (tgi.T < tEffective)
                                {
                                    npv[i] = df[i] * historicalRecovery[i];   // The discounted recovery value of the principal will be paid out on the effective date
                                }
                                else if (tgi.T == tEffective || !hasDefaulted[i])
                                {
                                    npv[i] = historicalRecovery[i];           // The full recovery amount is paid out
                                }
                                else
                                {
                                    npv[i] = 0.0;                             // default is in the past but we are after effective date; settlement has already occurred.
                                }
                            }

                            hasDefaulted[i] = true;
                        }
                    }

                    // Value of purchase price
                    if (tgi.T < tEffective)
                    {
                        npv.Add(-purchasePrice * fDiscountRate.Get(tgi.T, tEffective));
                    }
                    else if (tgi.T == tEffective)
                    {
                        npv.Add(-purchasePrice);
                        if (accumulator != null)
                        {
                            cash.Add(-purchasePrice);
                        }
                    }

                    result.AppendVector(tgi.Date, scale * npv * fFxRate.Get(tgi.T));
                    if (accumulator != null)
                    {
                        accumulator.Accumulate(fFxRate, tgi.Date, scale * cash);
                    }
                }

                // After maturity
                result.Complete(fT);
            }
        }
        /// <summary>
        /// Aggregate the valuation profile onto a set of result curves to support result partitioning.
        /// </summary>
        protected override void ProcessResults(ValuationResults valResults, DealPartitionAssociations assoc, PriceFactorList factors, BaseTimeGrid baseTimes, ValuationOptions options, int partition)
        {
            var pvProfiles          = valResults.Results <PVProfiles>();
            var addOnProfiles       = valResults.Results <AddOnProfiles>();
            var positiveMtmProfiles = valResults.Results <PositiveMtmProfiles>();

            Debug.Assert(addOnProfiles != null, "No Add-On profiles. Cannot proceed with valuation.");
            Debug.Assert(positiveMtmProfiles != null, "No Positive mtM profiles. Cannot proceed with valuation.");

            fT = Deal.ValuationGrid(factors, baseTimes, Deal.EndDate());
            var tgi = new TimeGridIterator(fT);

            var nettedExposure     = new PVProfiles(factors.NumScenarios);
            var collateralExposure = new PVProfiles(factors.NumScenarios);

            var addOnsProfile  = new PVProfiles(factors.NumScenarios);
            var mtmTermProfile = new PVProfiles(factors.NumScenarios);

            DealBaselNettingCollateralSet nettingSetDeal = Deal as DealBaselNettingCollateralSet;

            bool collateralised = nettingSetDeal.Collateralised == YesNo.Yes;

            using (var cache = Vector.Cache(factors.NumScenarios))
            {
                Vector sumMtm         = cache.Get();
                Vector sumPositiveMtm = cache.Get();
                Vector addOns         = cache.Get();
                Vector netGrossRatio  = cache.Get();
                Vector value          = cache.Get();
                Vector term1          = cache.Get();
                Vector term2          = cache.Get();

                // Collateral related vectors.
                Vector mtmTermStart = cache.Get();
                Vector addOnHp      = cache.Get();

                // Loop to get the netting set exposure.
                while (tgi.Next())
                {
                    sumMtm.Clear();
                    sumPositiveMtm.Clear();
                    addOns.Clear();
                    value.Clear();

                    double date = tgi.Date;

                    // For MtM Plus Add-On deals PV profiles represents the sum of the MtM profile and Add-On profile.
                    // Subtract the Add-On profile to recover the MtM profile before flooring.
                    sumMtm.Assign(VectorMath.Max(pvProfiles[date] - addOnProfiles[date], 0.0));

                    addOns.Assign(addOnProfiles[date]);
                    sumPositiveMtm.Assign(positiveMtmProfiles[date]);

                    netGrossRatio.AssignConditional(sumPositiveMtm > 0, sumMtm / sumPositiveMtm, 0.0);

                    netGrossRatio.MultiplyBy(this.fNetGrossRatioCorrelation);
                    netGrossRatio.Add(1 - this.fNetGrossRatioCorrelation);

                    term2.AssignProduct(addOns, netGrossRatio);

                    term1.Assign(VectorMath.Max(sumMtm, 0.0));

                    value.AssignSum(term1, term2);

                    nettedExposure.AppendVector(date, value);

                    if (collateralised)
                    {
                        mtmTermProfile.AppendVector(date, term1);
                        addOnsProfile.AppendVector(date, term2);
                    }
                }

                nettedExposure.Complete(this.fT);

                var exposureResults = valResults.Results <Exposure>();
                if (exposureResults != null)
                {
                    exposureResults.Assign(nettedExposure);
                }

                // Collateral cases.
                if (collateralised)
                {
                    mtmTermProfile.Complete(this.fT);
                    addOnsProfile.Complete(this.fT);

                    double date = factors.BaseDate;

                    mtmTermProfile.GetValue(mtmTermStart, date);
                    addOnsProfile.GetValue(addOnHp, date + nettingSetDeal.Holding_Period);

                    // Assume we have post haircut collateral.
                    double collateral = nettingSetDeal.Balance;
                    double threshold  = nettingSetDeal.Threshold;

                    tgi.Reset();

                    // Loop to get the netting set exposure.
                    while (tgi.Next())
                    {
                        bool inHoldingPeriod = tgi.T < CalcUtils.DaysToYears(nettingSetDeal.Holding_Period);

                        CollateralBasel3(mtmTermStart, collateral, addOnHp, threshold, inHoldingPeriod, tgi.Date,
                                         nettedExposure,
                                         collateralExposure);
                    }

                    collateralExposure.Complete(this.fT);

                    if (exposureResults != null)
                    {
                        exposureResults.Assign(collateralExposure);
                    }
                }
            }

            if (options.PartitionCollateralMode != PartitionCollateralMode.Suppress_Collateral_And_Flooring || partition < options.NumTotalPartitions)
            {
                valResults.FloorResult(assoc.AggregationMode, options);
            }

            CollateralPlugIn.CollateralBalancesContainer coProfiles = valResults.Results <CollateralPlugIn.CollateralBalancesContainer>();

            // Store collateral information according to diagnostic collection rules.
            if (coProfiles != null)
            {
                coProfiles.StoreInformation(this);
            }
        }
Beispiel #6
0
        /// <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 cashAccumulator = valuationResults.Cash;
            double           baseDate        = factors.BaseDate;

            CallableStructuredDeal deal = (CallableStructuredDeal)fDeal;

            int buySellSign = deal.Buy_Sell == BuySell.Buy ? +1 : -1;
            int callPutSign = deal.Option_Type == OptionType.Call ? 1 : -1;

            InterestRateOptionPricer optionPricer = CreateOptionPricer(factors);

            CalcUtils.CreateDealProfilesIfRequired(valuationResults, fItems, factors);

            bool needRating = Respect_Default == YesNo.Yes && !string.IsNullOrEmpty(deal.Issuer);

            using (var cache = Vector.Cache(factors.NumScenarios))
            {
                Vector exercised      = cache.GetClear(); // vector taking value 0 or 1 indicating exercise before tgi.date
                Vector exercisedToday = cache.Get();      // vector taking value 0 or 1 indicating exercise at tgi.date

                Vector optionPv = cache.Get();
                Vector pv       = cache.Get();
                Vector cash     = cache.Get();
                Vector settlementDateAtExercise = cache.GetClear();
                Vector defaultDate = needRating ? cache.Get(CalcUtils.DateTimeMaxValueAsDouble) : null;

                var defaultedBeforeBaseDate = needRating &&
                                              CreditRating.DefaultedBeforeBaseDate(fCreditRating, baseDate);

                while (tgi.Next())
                {
                    if (defaultedBeforeBaseDate)
                    {
                        pv.Clear();
                        result.AppendVector(tgi.Date, pv);
                        break;
                    }

                    if (needRating)
                    {
                        UpdateDefaultDate(fCreditRating, tgi.Date, tgi.T, defaultDate);
                    }

                    double val;
                    bool   allExercised = exercised.AllElementsTheSame(out val) && val == 1.0;

                    if (deal.Settlement_Style == SettlementType2.Physical)
                    {
                        // Calculate value of option (option value is zero after last exercise date)
                        if (!allExercised)
                        {
                            optionPricer.Value(baseDate, tgi.Date, optionPv, exercised, exercisedToday, settlementDateAtExercise, defaultDate);
                        }

                        // Calculate value of underlying cashflows after settlementDateAtExercise
                        pv.Clear();
                        cash.Clear();
                        InterestRateOptionPricer.ValueDeals(fItems, pv, cash, baseDate, tgi.Date, settlementDateAtExercise, defaultDate, fDiscountRate, fForecastRate, fRepoRate, fInterestRateVol, fInterestYieldVol, fSurvivalProb, fRecoveryRate);
                        pv.MultiplyBy(callPutSign);
                        cash.MultiplyBy(callPutSign);

                        if (!allExercised)
                        {
                            // If exercised today the cashflow is the value of the option minus the value of the physically settled part
                            // Else if already exercised, cash is the unnderlying cash.
                            // Else (before exercise) there is no cash.
                            cash.AssignConditional(exercisedToday, optionPv - pv, exercised * cash);

                            // If already exercised, pv is the unnderlying pv.
                            // Else (before exercise or exercised today), pv is the option pv.
                            pv.AssignConditional(exercised, pv, optionPv);
                            pv.AssignConditional(exercisedToday, optionPv, pv);
                        }
                    }
                    else
                    {
                        if (allExercised)
                        {
                            // Already exercised on all scenarios
                            result.AppendZeroVector(tgi.Date);
                            continue;
                        }

                        if (deal.Settlement_Style == SettlementType2.Cash)
                        {
                            // Calculate value of option
                            optionPricer.Value(baseDate, tgi.Date, pv, exercised, exercisedToday, settlementDateAtExercise, defaultDate);

                            // If exercised today then option pv is settled today, otherwise there is no cash
                            cash.AssignProduct(pv, exercisedToday);
                        }
                        else // Embedded option (callable or puttable)
                        {
                            // Calculate underlying value
                            pv.Clear();
                            cash.Clear();
                            InterestRateOptionPricer.ValueDeals(fItems, pv, cash, baseDate, tgi.Date, null, defaultDate, fDiscountRate, fForecastRate, fRepoRate, fInterestRateVol, fInterestYieldVol, fSurvivalProb, fRecoveryRate);

                            // Calculate value of option
                            optionPricer.Value(baseDate, tgi.Date, optionPv, exercised, exercisedToday, settlementDateAtExercise, defaultDate);

                            // Add or subtract value of embedded option
                            pv.AddProduct(-callPutSign, optionPv);

                            // Option payoff is Max(callPutSign * (underlyingPv - accruedInterest - discountedFee), 0)
                            // Callable/puttable payoff on exercise is
                            // underlyingPv - callPutSign * (callPutSign * (underlyingPv - accruedInterest - discountedFee))
                            // = accruedInterest + discountedFee

                            // Set pv and cash to zero if already exercised.
                            // If exercised today then the pv is settled today.
                            pv.AssignConditional(exercised, exercisedToday * pv, pv);
                            cash.AssignConditional(exercised, exercisedToday * pv, cash);
                        }
                    }

                    pv.MultiplyBy(buySellSign);
                    cash.MultiplyBy(buySellSign);
                    result.AppendVector(tgi.Date, fFxRate.Get(tgi.T) * pv);
                    cashAccumulator.Accumulate(fFxRate, tgi.Date, cash);
                }
            }

            result.Complete(fT);
        }