Skip to content

Commit 9c16fd5

Browse files
committed
Added progess indicator.
1 parent d7cd30f commit 9c16fd5

File tree

3 files changed

+155
-48
lines changed

3 files changed

+155
-48
lines changed

src/main/java/net/finmath/experiments/ui/ExperimentUI.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.concurrent.Future;
88
import java.util.concurrent.atomic.AtomicLong;
99
import java.util.function.BooleanSupplier;
10+
import java.util.function.DoubleConsumer;
1011

1112
import javafx.animation.PauseTransition;
1213
import javafx.application.Application;
@@ -21,6 +22,8 @@
2122
import javafx.scene.control.Button;
2223
import javafx.scene.control.CheckBox;
2324
import javafx.scene.control.Label;
25+
import javafx.scene.control.ProgressBar;
26+
import javafx.scene.control.ProgressIndicator;
2427
import javafx.scene.control.Slider;
2528
import javafx.scene.control.TextField;
2629
import javafx.scene.control.TitledPane;
@@ -52,6 +55,7 @@ public abstract class ExperimentUI extends Application {
5255
private final AtomicLong currentEpoch = new AtomicLong(0);
5356

5457
private final PauseTransition debounce = new PauseTransition(Duration.millis(300));
58+
private final ProgressBar progressIndicator = new ProgressBar();
5559
private final DecimalFormat df = new DecimalFormat("#.####");
5660

5761
private Parent content;
@@ -65,7 +69,7 @@ public ExperimentUI(List<Parameter> parameters) {
6569
*/
6670
abstract public String getTitle();
6771

68-
abstract public void runCalculation(BooleanSupplier isCancelled);
72+
abstract public void runCalculation(BooleanSupplier isCancelled, DoubleConsumer progress);
6973

7074
protected void onClose() {
7175
debounce.stop();
@@ -89,14 +93,23 @@ public void runCalculationAsync() {
8993

9094
BooleanSupplier isCancelled = () -> taskEpoch < currentEpoch.get();
9195

92-
Task<Double> task = new Task<>() {
96+
Task<Void> task = new Task<>() {
9397
@Override
94-
protected Double call() throws Exception {
95-
runCalculation(isCancelled);
96-
return 0.0;
98+
protected Void call() throws Exception {
99+
updateProgress(-1, 1);
100+
DoubleConsumer process = p -> { if(taskEpoch == currentEpoch.get()) this.updateProgress(p, 1.0); };
101+
runCalculation(isCancelled, process);
102+
updateProgress(1.0, 1.0);
103+
return null;
97104
}
98105
};
99106

107+
// Bind the progressIndicator to this task
108+
progressIndicator.progressProperty().unbind();
109+
progressIndicator.visibleProperty().unbind();
110+
progressIndicator.visibleProperty().bind(task.runningProperty());
111+
progressIndicator.progressProperty().bind(task.progressProperty());
112+
100113
currentJob = pool.submit(task);
101114
}
102115

@@ -157,9 +170,14 @@ public synchronized Parent getContent() {
157170
btnReset.setOnAction(e -> resetToDefaults());
158171
Button btnCompute = new Button("Calculate");
159172
btnCompute.setOnAction(e -> runCalculationAsync());
160-
buttons.getChildren().addAll(btnReset, btnCompute);
173+
174+
// progressIndicator.setPrefSize(18, 18);
175+
progressIndicator.setVisible(false);
176+
177+
buttons.getChildren().addAll(btnReset, btnCompute, progressIndicator);
161178
buttons.setAlignment(Pos.CENTER_LEFT);
162179

180+
163181
VBox vbox = new VBox(12, grid, buttons);
164182
vbox.setPadding(new Insets(14));
165183

src/main/java/net/finmath/experiments/ui/HestonModelGreeksAnalytic.java

Lines changed: 124 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
import java.util.List;
99
import java.util.Map;
1010
import java.util.function.BooleanSupplier;
11+
import java.util.function.DoubleConsumer;
1112
import java.util.function.DoubleUnaryOperator;
1213
import java.util.function.Function;
1314
import java.util.stream.Collectors;
1415

1516
import net.finmath.climateschool.utilities.ModelFactory;
1617
import net.finmath.climateschool.utilities.RandomOperators;
18+
import net.finmath.experiments.ui.parameter.BooleanParameter;
1719
import net.finmath.experiments.ui.parameter.DoubleParameter;
1820
import net.finmath.montecarlo.interestrate.TermStructureMonteCarloSimulationModel;
1921
import net.finmath.plots.DoubleToRandomVariableFunction;
@@ -39,7 +41,9 @@ public class HestonModelGreeksAnalytic extends ExperimentUI {
3941
private final DecimalFormat numberDigit2 = new DecimalFormat("#.00");
4042
private final DecimalFormat numberPercent1 = new DecimalFormat("#.0%");
4143

42-
Plot2D plot = null;
44+
Plot2D plotDelta = null;
45+
Plot2D plotGamma = null;
46+
Plot2D plotVega = null;
4347

4448
final static String RISK_FREE_RATE = "Risk Free Rate";
4549
final static String DIVIDEND_YIELD = "Dividend Yield";
@@ -61,13 +65,16 @@ public HestonModelGreeksAnalytic() {
6165
new DoubleParameter(V0, 0.0423, 0.0001, 2.0),
6266
new DoubleParameter(RHO, -0.4, -1.0, 1.0),
6367
new DoubleParameter(OPTION_MATURIY, 0.2, 0.01, 5.0),
64-
new DoubleParameter(OPTION_STRIKE, 50, 90, 250)
68+
new DoubleParameter(OPTION_STRIKE, 50, 90, 250),
69+
new BooleanParameter("Show Delta", true),
70+
new BooleanParameter("Show Gamma", false),
71+
new BooleanParameter("Show Vega", false)
6572
));
6673
}
6774

6875
public String getTitle() { return "Heston Model - Greeks (Analytic)"; }
6976

70-
public void runCalculation(BooleanSupplier isCancelled) {
77+
public void runCalculation(BooleanSupplier isCancelled, DoubleConsumer progress) {
7178
Map<String, Object> currentParameterSet = getExperimentParameters().stream().collect(Collectors.toMap(p -> p.getBindableValue().getName(), p -> p.getBindableValue().getValue()));
7279

7380
System.out.println("Calculation with Parameters: " + currentParameterSet);
@@ -82,6 +89,15 @@ public void runCalculation(BooleanSupplier isCancelled) {
8289
final double optionMaturity = (Double)currentParameterSet.get(OPTION_MATURIY);
8390
final double optionStrike = (Double)currentParameterSet.get(OPTION_STRIKE);
8491

92+
String titleSpec = "r="+numberPercent1.format(riskFreeRate) + ", q="+numberPercent1.format(dividendYield) +
93+
", 𝜅=" + numberDigit2.format(kappa) +
94+
", 𝜃=" + numberDigit2.format(theta) +
95+
", 𝜎=" + numberDigit2.format(sigma) +
96+
", v₀=" + numberDigit2.format(v0) +
97+
", 𝜌=" + numberDigit2.format(rho) +
98+
", T=" + numberDigit2.format(optionMaturity) +
99+
", K=" + numberDigit2.format(optionStrike);
100+
85101
DoubleUnaryOperator deltaFun = (stock) -> HestonModel.hestonOptionDelta(
86102
stock,
87103
riskFreeRate,
@@ -94,60 +110,133 @@ public void runCalculation(BooleanSupplier isCancelled) {
94110
optionMaturity,
95111
optionStrike);
96112

97-
DoubleUnaryOperator deltaFunBS = (stock) -> {
98-
return AnalyticFormulas.blackScholesOptionDelta(
113+
DoubleUnaryOperator gammaFun = (stock) -> HestonModel.hestonOptionGamma(
99114
stock,
100-
riskFreeRate-dividendYield,
101-
Math.sqrt(v0),
115+
riskFreeRate,
116+
dividendYield,
117+
kappa,
118+
theta,
119+
sigma,
120+
v0,
121+
rho,
102122
optionMaturity,
103-
optionStrike) * Math.exp(-dividendYield * optionMaturity); };
123+
optionStrike);
104124

105-
List<Plotable2D> plotables = List.of(
106-
new PlotableFunction2D(0, 300, 200, new Named<DoubleUnaryOperator>("Heston", deltaFun), new GraphStyle(null, new BasicStroke(), Color.blue)),
107-
new PlotableFunction2D(0, 300, 200, new Named<DoubleUnaryOperator>("Black Scholes", deltaFunBS), new GraphStyle(null, new BasicStroke(), Color.RED))
108-
);
125+
DoubleUnaryOperator vegaFun = (stock) -> HestonModel.hestonOptionVega1(
126+
stock,
127+
riskFreeRate,
128+
dividendYield,
129+
kappa,
130+
theta,
131+
sigma,
132+
v0,
133+
rho,
134+
optionMaturity,
135+
optionStrike);
109136

110-
String titleSpec = "r="+numberPercent1.format(riskFreeRate) + ", q="+numberPercent1.format(dividendYield) +
111-
", 𝜅=" + numberDigit2.format(kappa) +
112-
", 𝜃=" + numberDigit2.format(theta) +
113-
", 𝜎=" + numberDigit2.format(sigma) +
114-
", v₀=" + numberDigit2.format(v0) +
115-
", 𝜌=" + numberDigit2.format(rho) +
116-
", T=" + numberDigit2.format(optionMaturity) +
117-
", K=" + numberDigit2.format(optionStrike);
137+
DoubleUnaryOperator deltaFunBS = (stock) -> { return AnalyticFormulas.blackScholesOptionDelta(
138+
stock,
139+
riskFreeRate-dividendYield,
140+
Math.sqrt(v0),
141+
optionMaturity,
142+
optionStrike) * Math.exp(-dividendYield * optionMaturity);
143+
};
118144

119-
String nameOfGreek = "Delta";
145+
DoubleUnaryOperator gammaFunBS = (stock) -> { return AnalyticFormulas.blackScholesOptionGamma(
146+
stock,
147+
riskFreeRate-dividendYield,
148+
Math.sqrt(v0),
149+
optionMaturity,
150+
optionStrike) * Math.exp(-dividendYield * optionMaturity);
151+
};
152+
153+
DoubleUnaryOperator vegaFunBS = (stock) -> { return AnalyticFormulas.blackScholesOptionVega(
154+
stock,
155+
riskFreeRate-dividendYield,
156+
Math.sqrt(v0),
157+
optionMaturity,
158+
optionStrike) * Math.exp(-dividendYield * optionMaturity);
159+
};
120160

121161
synchronized(this) {
122162
if(!Thread.currentThread().isInterrupted() && !isCancelled.getAsBoolean()) {
123-
if(plot == null) {
124-
try {
125-
plot = new Plot2D(plotables);
126-
plot.setTitle("Heston vs Black Scholes Option " + nameOfGreek + "\n(" + titleSpec + ")").setXAxisLabel("Spot").setYAxisLabel(nameOfGreek);
127-
plot.setIsLegendVisible(true);
128-
// plot.setYRange(-0.02, 0.10);
129-
plot.show();
163+
if((Boolean)currentParameterSet.get("Show Delta"))
164+
plotDelta = plot(plotDelta, "Delta", titleSpec, deltaFunBS, deltaFun);
165+
else {
166+
if(plotDelta != null) {
167+
plotDelta.close();
168+
plotDelta = null;
130169
}
131-
catch(Exception e) {
132-
e.printStackTrace();
170+
}
171+
}
172+
if(!Thread.currentThread().isInterrupted() && !isCancelled.getAsBoolean()) {
173+
if((Boolean)currentParameterSet.get("Show Gamma"))
174+
plotGamma = plot(plotGamma, "Gamma", titleSpec, gammaFunBS, gammaFun);
175+
else {
176+
if(plotGamma != null) {
177+
plotGamma.close();
178+
plotGamma = null;
133179
}
180+
134181
}
182+
}
183+
if(!Thread.currentThread().isInterrupted() && !isCancelled.getAsBoolean()) {
184+
if((Boolean)currentParameterSet.get("Show Vega"))
185+
plotVega = plot(plotVega, "Vega", titleSpec, vegaFunBS, vegaFun);
135186
else {
136-
plot.setTitle("Heston vs Black Scholes Option " + nameOfGreek + "\n(" + titleSpec + ")").setXAxisLabel("Spot").setYAxisLabel(nameOfGreek);
137-
plot.update(plotables);
187+
if(plotVega != null) {
188+
plotVega.close();
189+
plotVega = null;
190+
}
138191
}
139192
}
140193
}
194+
195+
progress.accept(1.0);
196+
}
197+
198+
private Plot2D plot(Plot2D plot, String nameOfGreek, String titleSpec, DoubleUnaryOperator greekBlackScholes, DoubleUnaryOperator greekHeston) {
199+
List<Plotable2D> plotables = List.of(
200+
new PlotableFunction2D(0, 300, 200, new Named<DoubleUnaryOperator>("Heston", greekHeston), new GraphStyle(null, new BasicStroke(), Color.blue)),
201+
new PlotableFunction2D(0, 300, 200, new Named<DoubleUnaryOperator>("Black Scholes", greekBlackScholes), new GraphStyle(null, new BasicStroke(), Color.RED))
202+
);
203+
204+
if(plot == null) {
205+
try {
206+
plot = new Plot2D(plotables);
207+
plot.setTitle("Heston vs Black Scholes Option " + nameOfGreek + "\n(" + titleSpec + ")").setXAxisLabel("Spot").setYAxisLabel(nameOfGreek);
208+
plot.setIsLegendVisible(true);
209+
// plot.setYRange(-0.02, 0.10);
210+
plot.show();
211+
}
212+
catch(Exception e) {
213+
e.printStackTrace();
214+
}
215+
}
216+
else {
217+
plot.setTitle("Heston vs Black Scholes Option " + nameOfGreek + "\n(" + titleSpec + ")").setXAxisLabel("Spot").setYAxisLabel(nameOfGreek);
218+
plot.update(plotables);
219+
}
220+
221+
return plot;
141222
}
142223

143224
@Override
144225
protected void onClose() {
145226
synchronized (this) {
146227
super.onClose();
147228
System.out.println("Closing plot");
148-
if(plot != null) {
149-
plot.close();
150-
plot = null;
229+
if(plotDelta != null) {
230+
plotDelta.close();
231+
plotDelta = null;
232+
}
233+
if(plotGamma != null) {
234+
plotGamma.close();
235+
plotGamma = null;
236+
}
237+
if(plotVega != null) {
238+
plotVega.close();
239+
plotVega = null;
151240
}
152241
}
153242
}

src/main/java/net/finmath/experiments/ui/InterestRatesHullWhiteSimulationPathOfShortRate.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.List;
88
import java.util.Map;
99
import java.util.function.BooleanSupplier;
10+
import java.util.function.DoubleConsumer;
1011
import java.util.function.DoubleUnaryOperator;
1112
import java.util.function.Function;
1213
import java.util.stream.Collectors;
@@ -49,7 +50,7 @@ public InterestRatesHullWhiteSimulationPathOfShortRate() {
4950

5051
public String getTitle() { return "Hull White Model - Simulation of Interest Rate (Short Rate)"; }
5152

52-
public void runCalculation(BooleanSupplier isCancelled) {
53+
public void runCalculation(BooleanSupplier isCancelled, DoubleConsumer progress) {
5354
Map<String, Object> currentParameterSet = getExperimentParameters().stream().collect(Collectors.toMap(p -> p.getBindableValue().getName(), p -> p.getBindableValue().getValue()));
5455

5556
System.out.println("Calculation with Parameters: " + currentParameterSet);
@@ -90,11 +91,7 @@ public void runCalculation(BooleanSupplier isCancelled) {
9091
};
9192
};
9293

93-
final String titleSpec = "\u03c3="
94-
+String.format("%-10.3f",shortRateVolatility*100).trim() + "%, "
95-
+"a="
96-
+String.format("%-10.3f",shortRateMeanreversion*100).trim() + "%, "
97-
+ "";
94+
final String titleSpec = "\u03c3=" + numberPercent2.format(shortRateVolatility) + ", a=" + numberPercent2.format(shortRateMeanreversion);
9895

9996
int dotSize = 1;
10097
int numberOfPathsToShow = 50;
@@ -107,6 +104,7 @@ public void runCalculation(BooleanSupplier isCancelled) {
107104
} catch (Exception e) {
108105
valueSlices.add(Scalar.of(Double.NaN));
109106
}
107+
progress.accept((double)j/timeDiscretization.getNumberOfTimes()/2);
110108
}
111109

112110
List<Plotable2D> plotables = new ArrayList<Plotable2D>();
@@ -117,17 +115,19 @@ public void runCalculation(BooleanSupplier isCancelled) {
117115
series.add(new Point2D(time, valueSlices.get(j).get(i)));
118116
}
119117
plotables.add(new PlotablePoints2D("Scatter", series, new GraphStyle(new Rectangle(dotSize, dotSize), new BasicStroke(), null)));
118+
progress.accept(0.5+(double)i/numberOfPathsToShow/2);
120119
}
121120

122121
synchronized(this) {
123122
if(!Thread.currentThread().isInterrupted() && !isCancelled.getAsBoolean()) {
124123
if(plot == null) {
125124
plot = new Plot2D(plotables);
126125
plot.setTitle("Short Rate (" + titleSpec + ")").setXAxisLabel("time (years)").setYAxisLabel("Short Rate (r)");
127-
plot.setYRange(-0.02, 0.10);
126+
plot.setYRange(-0.02, 0.20);
128127
plot.show();
129128
}
130129
else {
130+
plot.setTitle("Short Rate (" + titleSpec + ")").setXAxisLabel("time (years)").setYAxisLabel("Short Rate (r)");
131131
plot.update(plotables);
132132
}
133133
}

0 commit comments

Comments
 (0)