Worked example · European calls · 0.9.0
From six quotes to an auditable terminal-price analysis
This example validates each quote, repairs the strip jointly, extracts a risk-neutral law, evaluates a covered-call terminal payoff, and then makes the measure change visible by comparing a separate physical scenario.
Educational probability analysis — not investment adviceStep 0
State forward, discount, maturity, and quote meaning
We use a forward of 102, a six-month discount factor exp(-0.04*0.5), and bid/ask calls from strikes 80 through 130. OptionObservation does not pretend to know a symbol, calendar, multiplier, settlement convention, or data source. Those facts must be resolved before constructing it.
OptionObservation[] quotes = {
new OptionObservation(80, true, 22.75, 23.15, 1.0),
new OptionObservation(90, true, 14.40, 14.75, 1.2),
new OptionObservation(100, true, 8.25, 8.55, 1.5),
new OptionObservation(110, true, 4.15, 4.45, 1.2),
new OptionObservation(120, true, 1.95, 2.20, 1.0),
new OptionObservation(130, true, 0.85, 1.05, 0.8)
};Step 1
Use implied volatility as a checked transformation
For every midpoint, ReferenceOptions.impliedBlackScholes first validates the discounted European no-arbitrage interval. A successful result still retains its final bracket and pricing residual. Invalid prices get a precise status instead of a plausible volatility.
Step 2
Enforce cross-strike constraints jointly
OptionCurve curve = OptionCurve.build(
forward, discount, maturity, quotes);
OptionCurve.Diagnostics d = curve.getDiagnostics();Single-quote IV checks cannot establish that calls decrease and remain convex across strikes. The builder converts puts through parity, projects undiscounted calls onto their bounds and a nondecreasing slope sequence in [-1,0], closes the right boundary, and reports how many quotes moved plus maximum and weighted-RMSE price residuals.
Step 3
Ask the inferred law directly
OptionImpliedDistribution q = curve.getDistribution();
double below90 = q.cumulative(90);
double above120 = q.cumulative(120, false, false);
double between = curve.strikeIntervalProbability(95, 115);
double median = q.quantile(0.5);A piecewise-linear convex call curve implies probability atoms where slopes change. The returned object declares those atoms rather than smoothing them away. Its atom masses normalize to one and support ordinary CDF, both tails, quantiles, exact point mass, partial moments, tail risk, and reproducible sampling.
Step 4
Translate a covered call into one bounded payoff
At expiry, one share minus one 110-strike call is min(S,110). Its risk-neutral undiscounted expectation is therefore forward - E[(S-110)+]. The chance that the terminal payoff is capped is Q(S>110). This is a terminal distribution calculation; it does not include entry price, early assignment, fees, tax, margin, path-dependent management, or execution.
double callPayoff = FinancialRisk.callPayoff(q, 110).valueOrThrow();
double coveredCall = forward - callPayoff;
double chanceCapped = q.cumulative(110, false, false);Step 5
Do not present risk-neutral probabilities as forecasts
The call strip identifies a discounted risk-neutral law, denoted Q. A forecasting model with an explicitly stated transition and observation process produces a physical/predictive P law. The example constructs a separate lognormal scenario and prints Q(S>120) next to P(S>120). A difference is expected; neither number should be silently relabeled.
Complete executable
Compile the exact worked analysis with the documentation catalog
Open WorkedOptionsTradingExample.java. The normal documentation check compiles it against the packaged JAR.
./gradlew compileDocumentationExamples
# after compiling, use build/documentation-examples and the JDistlib JAR
java examples.WorkedOptionsTradingExample