/// <summary> /// Prepares the portfolios by splitting products with early exercise into /// the products they become after exercise and the product that produces their /// cashflows until exercise. /// </summary> /// <param name="portfolioIn">The user supplied portfolio.</param> /// <param name="fwdValueDates">The extra forward value dates.</param> private void PreparePortfolios(Product[] portfolioIn, Date[] fwdValueDates) { allDates = fwdValueDates.Select(date => new Date(date)).ToList(); allDates.Add(valueDate); allTrades = new List <Product>(); originalTrades = new List <int>(); postExerciseTrades = new Dictionary <int, List <int> >(); // Add the orginal trades int counter = 0; foreach (Product product in portfolioIn) { allTrades.Add(product.Clone()); originalTrades.Add(counter); ProductWithEarlyExercise option = product as ProductWithEarlyExercise; if (option != null) { List <int> subList = new List <int>(); postExerciseTrades.Add(counter, subList); counter++; allDates.AddRange(option.GetExerciseDates()); List <Product> postExProducts = option.GetPostExProducts(); foreach (Product postExProduct in postExProducts) { allTrades.Add(postExProduct); subList.Add(counter); counter++; } } else { counter++; } } allDates = allDates.Distinct().ToList(); allDates.Sort(); }
/// <summary> /// Replaces the no exercise cashflows for the product at position <paramref name="key"/> with the cashflows based on /// an estimated optimal exercise policy. /// </summary> /// <param name="key">The postion in <see cref="allTrades"/> of the product to be updated.</param> private void ApplyEarlyExercise(int key) { // Get pathwise regressed values of post exercise products. ProductWithEarlyExercise option = allTrades[key] as ProductWithEarlyExercise; List <Date> exDates = option.GetExerciseDates(); double[,] postExRegressedValues = new double[N, exDates.Count]; for (int i = 0; i < exDates.Count; i++) { // perform regression on each exercise date int postExerciseProductInd = postExerciseTrades[key][option.GetPostExProductAtDate(exDates[i])]; double[] fitted = PerformRegression(exDates[i], simulatedCFs, simulatedRegs, new List <int> { postExerciseProductInd }); postExRegressedValues.SetColumn(i, fitted); } // Iterate backwards // initially the stoppping time on all paths is infinity (actually the year 3000) Date[] optimalStop = new Date[N]; Date finalDate = new Date(3000, 1, 1); for (int i = 0; i < N; i++) { optimalStop[i] = finalDate; } for (int exDateCount = exDates.Count - 1; exDateCount >= 0; exDateCount--) { Date exDate = exDates[exDateCount]; // Optimal flows are underlying product up to the stopping time, then the post exercise product flows afterwards double[] pvOptimalCFs = Vector.Zeros(N); for (int pathCount = 0; pathCount < N; pathCount++) { if (optimalStop[pathCount] < finalDate) { int exProductInd = postExerciseTrades[key][option.GetPostExProductAtDate(optimalStop[pathCount])]; foreach (Cashflow cf in simulatedCFs.GetCFs(exProductInd, pathCount)) { if (cf.date > optimalStop[pathCount]) { pvOptimalCFs[pathCount] += cf.amount; } } } foreach (Cashflow cf in simulatedCFs.GetCFs(key, pathCount)) { if (cf.date > valueDate && cf.date <= optimalStop[pathCount]) { pvOptimalCFs[pathCount] += cf.amount; } } } // update optimal stopping times double[] optimalCV = simulatedRegs.FitCFs(exDate, pvOptimalCFs); for (int pathCount = 0; pathCount < N; pathCount++) { if (option.IsLongOptionality(exDate) && optimalCV[pathCount] < postExRegressedValues[pathCount, exDateCount]) { optimalStop[pathCount] = exDate; } else if (!option.IsLongOptionality(exDate) && optimalCV[pathCount] > postExRegressedValues[pathCount, exDateCount]) { optimalStop[pathCount] = exDate; } } } // All stopping times have been found so now we can update the cashflows. // The cashflows are continuation flows up to the exercise date then cashflows from the // exercise product after that. List <Cashflow>[] newCFs = new List <Cashflow> [N]; for (int pathCount = 0; pathCount < N; pathCount++) { newCFs[pathCount] = new List <Cashflow>(); int exProductInd = postExerciseTrades[key][option.GetPostExProductAtDate(optimalStop[pathCount])]; foreach (Cashflow cf in simulatedCFs.GetCFs(exProductInd, pathCount)) { if (cf.date > optimalStop[pathCount]) { newCFs[pathCount].Add(cf); } } foreach (Cashflow cf in simulatedCFs.GetCFs(key, pathCount)) { if (cf.date > valueDate && cf.date <= optimalStop[pathCount]) { newCFs[pathCount].Add(cf); } } } simulatedCFs.Update(key, newCFs); }