Checkout finmath-experiments from git and run maven (mvn
or mvnw
) from
its directory. This will start a JShell.
See Getting Started for details.
The following will value a Bermudan option with two exercise dates \( T_{1} \) and \( T_{2} \), with the right to receive either \( S(T_{1})-K_{1} \) in \( T_{1} \) or \( S(T_{2})-K_{2} \) in \( T_{2} \) or nothing.
Since we assume a Black-Scholes model, the second option can be valued analytically using the Black-Scholes formula, and hence, the exercise criteria for the first exercise date is known analytically.
We calculate the numeraire relative Bermudan exercise value \( U(T)/N(T) \) where \( T \) is the optimal exercise time - a stochastic time being either \( T_{1} \) or \( T_{2} \). And \( U(T)[\omega] \) is given by
Form this, the Bermudan value is given by the unconditional expectation \[ N(0) \mathrm{E}\Big( \frac{U(T)}{N(T)} \Big) \text{.} \]
In addition, we value the super-optimal exercise using 'perfect foresight' where the super-optimal exercise criteria uses the future on-path value. This strategy is wrong. We consider it for illustration.
import java.awt.Color;
import java.awt.Rectangle;
import java.util.List;
import net.finmath.functions.AnalyticFormulas;
import net.finmath.montecarlo.BrownianMotion;
import net.finmath.montecarlo.BrownianMotionFromMersenneRandomNumbers;
import net.finmath.montecarlo.assetderivativevaluation.MonteCarloAssetModel;
import net.finmath.montecarlo.assetderivativevaluation.models.BlackScholesModel;
import net.finmath.montecarlo.process.EulerSchemeFromProcessModel;
import net.finmath.montecarlo.process.MonteCarloProcess;
import net.finmath.plots.GraphStyle;
import net.finmath.plots.Plot;
import net.finmath.plots.Plot2D;
import net.finmath.plots.Plotable2D;
import net.finmath.plots.PlotablePoints2D;
import net.finmath.stochastic.RandomVariable;
import net.finmath.time.TimeDiscretizationFromArray;
double initialValue = 100.0;
double riskFreeRate = 0.05;
double volatility = 0.30;
double initialTime = 0.0;
double timeHorizon = 2.0;
double dt = 1.0;
int numberOfPaths = 5000;
int seed = 3141;
double maturity1 = 1.0;
double strike1 = 120.0;
double maturity2 = 2.0;
double strike2 = 140.0;
BlackScholesModel blackScholesModel = new BlackScholesModel(initialValue, riskFreeRate, volatility);
BrownianMotion bm = new BrownianMotionFromMersenneRandomNumbers(new TimeDiscretizationFromArray(initialTime, (int)Math.round(timeHorizon/dt), dt), 1, numberOfPaths, seed);
MonteCarloProcess process = new EulerSchemeFromProcessModel(blackScholesModel, bm);
MonteCarloAssetModel model = new MonteCarloAssetModel(process);
RandomVariable stockInT2 = model.getAssetValue(maturity2, 0); // S(T2)
RandomVariable stockInT1 = model.getAssetValue(maturity1, 0); // S(T1)
RandomVariable valueOption2InT2 = stockInT2.sub(strike2).floor(0.0); // max(S(T2)-K,0)
RandomVariable valueOption1InT1 = stockInT1.sub(strike1).floor(0.0); // max(S(T1)-K,0)
RandomVariable valueOption2InT1 = AnalyticFormulas.blackScholesOptionValue(stockInT1, riskFreeRate, volatility, maturity2-maturity1, strike2);
// Convert all time T-values to numeraire relative values by dividing by N(T)
RandomVariable valueRelativeOption2InT2 = valueOption2InT2.div(model.getNumeraire(maturity2));
RandomVariable valueRelativeOption1InT1 = valueOption1InT1.div(model.getNumeraire(maturity1));
RandomVariable valueRelativeOption2InT1 = valueOption2InT1.div(model.getNumeraire(maturity1));
RandomVariable bermudanPathwiseValueAdmissible = valueRelativeOption2InT1.sub(valueRelativeOption1InT1)
.choose(valueRelativeOption2InT2, valueRelativeOption1InT1);
RandomVariable bermudanPathwiseValueForesight = valueRelativeOption2InT2.sub(valueRelativeOption1InT1)
.choose(valueRelativeOption2InT2, valueRelativeOption1InT1);
var bermudanValueWithForsight = bermudanPathwiseValueForesight.mult(model.getNumeraire(0.0)).getAverage();
var bermudanValue = bermudanPathwiseValueAdmissible.mult(model.getNumeraire(0.0)).getAverage();
Run in JShell, this prints out the values
bermudanValueWithForsight ==> 11.904281948764233
bermudanValue ==> 9.209824685660006
That is, the value of the Bermudan option is ≈ 9.21. Performing super-optimal exercise
with an non-admissible strategy results in a value of ≈ 11.9.
The following code has to be run after Experiment 1 in the same JShell, since it requires initialization of variables from there.
The following code will create a scatter plot illustrating the optimal exercise of a Bermudan option with two exercise dates \( T_{1} \) and \( T_{2} \), with the right to receive either \( S(T_{1})-K_{1} \) in \( T_{1} \) or \( S(T_{2})-K_{2} \) in \( T_{2} \) or nothing.
import java.awt.Color;
import java.awt.Rectangle;
import java.util.List;
import net.finmath.plots.GraphStyle;
import net.finmath.plots.Plot;
import net.finmath.plots.Plot2D;
import net.finmath.plots.Plotable2D;
import net.finmath.plots.PlotablePoints2D;
import net.finmath.stochastic.RandomVariable;
import net.finmath.time.TimeDiscretizationFromArray;
/*
* Plot the stuff
*/
Plotable2D continuationValues = PlotablePoints2D.of("Continuation Value max(S(T\u2082)-K\u2082, 0)", stockInT1, valueRelativeOption2InT2, new GraphStyle(new Rectangle(-1,-1,2,2), null, Color.RED));
Plotable2D exerciseValues = PlotablePoints2D.of("Underlying 1 (S(T\u2081)-K\u2081)", stockInT1, valueRelativeOption1InT1, new GraphStyle(new Rectangle(-3,-3,5,5), null, Color.GREEN));
Plotable2D expectedContinuationValue = PlotablePoints2D.of("Black Scholes Value of Option on S(T\u2082)-K\u2082", stockInT1, valueRelativeOption2InT1, new GraphStyle(new Rectangle(-3,-3,5,5),null, Color.BLUE));
Plotable2D bermudanPathwiseValueAdmissiblePlot = PlotablePoints2D.of("Bermudan exercise value", stockInT1, bermudanPathwiseValueAdmissible, new GraphStyle(new Rectangle(-1,-1,2,2), null, new Color(0.0f, 0.66f, 0.0f)));
Plotable2D bermudanPathwiseValueForesightPlot = PlotablePoints2D.of("Exercise value with foresight", stockInT1, bermudanPathwiseValueForesight, new GraphStyle(new Rectangle(-1,-1,2,2), null, new Color(0.50f, 0.50f, 0.0f)));
Plot plotBermudanExercise = new Plot2D(
List.of(
bermudanPathwiseValueAdmissiblePlot,
continuationValues,
exerciseValues,
expectedContinuationValue)
)
.setYRange(-5, 150)
.setIsLegendVisible(true)
.setXAxisLabel("S(T\u2081)")
.setYAxisLabel("V\u2081(T\u2081), V\u2082(T\u2081), V\u2082(T\u2082)")
.setTitle("Time T\u2081 and T\u2082 values related to a Bermudan option with exercises in T\u2081 and T\u2082.");
plotBermudanExercise.show();
The plot shows simulations points for different simulation paths (scenarios) ω of the following points
The following code creates a similar plot as in the previous Experiment 2, but uses 'perfect foresight' for the exercise decision, by 'looking into the future'. This strategy is wrong. We plot it for illustration.
The code just differs in a single line, that we plot
bermudanPathwiseValueForesightPlot
instead of
bermudanPathwiseValueAdmissiblePlot
.
The code has to be run after Experiment 1 or 2 in the same JShell, since it requires initialization of variables from there.
import java.awt.Color;
import java.awt.Rectangle;
import java.util.List;
import net.finmath.plots.GraphStyle;
import net.finmath.plots.Plot;
import net.finmath.plots.Plot2D;
import net.finmath.plots.Plotable2D;
import net.finmath.plots.PlotablePoints2D;
import net.finmath.stochastic.RandomVariable;
import net.finmath.time.TimeDiscretizationFromArray;
/*
* Plot the stuff
*/
Plotable2D continuationValues = PlotablePoints2D.of("Continuation Value max(S(T\u2082)-K\u2082, 0)", stockInT1, valueRelativeOption2InT2, new GraphStyle(new Rectangle(-1,-1,2,2), null, Color.RED));
Plotable2D exerciseValues = PlotablePoints2D.of("Underlying 1 (S(T\u2081)-K\u2081)", stockInT1, valueRelativeOption1InT1, new GraphStyle(new Rectangle(-3,-3,5,5), null, Color.GREEN));
Plotable2D expectedContinuationValue = PlotablePoints2D.of("Black Scholes Value of Option on S(T\u2082)-K\u2082", stockInT1, valueRelativeOption2InT1, new GraphStyle(new Rectangle(-3,-3,5,5),null, Color.BLUE));
Plotable2D bermudanPathwiseValueAdmissiblePlot = PlotablePoints2D.of("Bermudan exercise value", stockInT1, bermudanPathwiseValueAdmissible, new GraphStyle(new Rectangle(-1,-1,2,2), null, new Color(0.0f, 0.66f, 0.0f)));
Plotable2D bermudanPathwiseValueForesightPlot = PlotablePoints2D.of("Exercise value with foresight", stockInT1, bermudanPathwiseValueForesight, new GraphStyle(new Rectangle(-1,-1,2,2), null, new Color(0.50f, 0.50f, 0.0f)));
Plot plotBermudanExerciseWithForesight = new Plot2D(
List.of(
bermudanPathwiseValueForesightPlot,
continuationValues,
exerciseValues,
expectedContinuationValue)
)
.setYRange(-5, 150)
.setIsLegendVisible(true)
.setXAxisLabel("S(T\u2081)")
.setYAxisLabel("V\u2081(T\u2081), V\u2082(T\u2081), V\u2082(T\u2082)")
.setTitle("Time T\u2081 and T\u2082 values related to a Bermudan option with exercises in T\u2081 and T\u2082.");
plotBermudanExerciseWithForesight.show();
plotBermudanExerciseWithForesight.saveAsSVG(new File("BermudanExerciseForesight.svg"),900,600);
The plot shows simulations points for different simulation paths (scenarios) ω of the following points
We have created a valuation of a Bermudan option in a Black-Scholes model and illustrated the effect of using a wrong exercise strategy using perfect foresight.
In this example, the admissible exercise strategy was known analytically, since the Bermuan had only two exercise dates and the model allowed for an analytic valuation of the European option.