Options and Replication
Question: what should you pay today for the right to buy an investment next year, if you can walk away when buying would be unfavorable?
A forward is like a binding purchase agreement. An option is more like paying for a reservation: you pay for the choice, then decide whether to use it. The reservation payment is not refunded just because you decide not to buy.
In D.1, the buyer had an obligation. In D.2, we followed daily settlement and collateral. Here we introduce a premium: the price paid to acquire an option. It is different from the collateral supporting a futures position.
Our invented investment costs £100 per unit today, pays no income, and has a nonnegative price. Each option covers one unit, expires in one year, and has a £100 strike price. The strike is the fixed price at which the holder can buy or sell. We use European options, exercisable only at expiry; “European” describes exercise timing, not geography. An American option permits earlier exercise and needs additional analysis.
We express exercise benefits as cash-equivalent payoffs. With physical settlement, a call holder pays the strike and receives the asset; its net exercise benefit is the asset's value minus the strike. A cash-settled contract instead pays the specified difference. For our cash-flow calculations, assume cash settlement, no fees or default, fractional asset holdings and short selling, and borrowing and lending at the same 5% effective annual rate. These assumptions make the replication arithmetic transparent.
Run cells in order. The final-price widget also runs independently. All prices are teaching inputs, not market observations.
import json
import numpy as np
import pandas as pd
spot_today = 100.0
strike = 100.0
annual_rate = 0.05
years = 1.0
growth = (1 + annual_rate) ** years2
3
4
5
6
7
8
9
1. A Call Buys a Choice; a Put Buys a Different Choice
A call gives its holder the right to buy at the strike. A put gives its holder the right to sell at the strike. The buyer is long the option. The seller, or writer, is short and must meet the contractual obligation when exercise occurs. The seller does not share the buyer's choice to walk away.
Let
At a final price of £120, the call lets you buy something worth £120 for £100:
| Final asset price | Long call payoff | Long put payoff | Short call payoff | Short put payoff |
|---|---|---|---|---|
| £80 | £0 | £20 | £0 | −£20 |
| £100 | £0 | £0 | £0 | £0 |
| £120 | £20 | £0 | −£20 | £0 |
An option is in the money when immediate exercise would have a positive benefit, at the money when spot equals strike, and out of the money when exercise would be unfavorable. At expiry, a £120 spot makes this call in the money and this put out of the money. At exactly £100 neither has a positive exercise benefit. Before expiry, a zero immediate exercise benefit does not imply a zero option price: future outcomes can still make the right valuable.
Read the Kink Before Running the Code
The horizontal axis below is a possible final asset price, not elapsed time. The first graph shows the holder's cash payoff. The call is flat until £100, then gains £1 for every £1 rise. The put falls toward zero as the final price approaches £100, then stays at zero. The second graph reflects the same obligations from the writer's side. The point where the slope changes is a kink.
np.maximum applies the “choose the larger value” calculation at every price in the array. The code computes the table before generating the two graphs.
final_prices = np.arange(0., 201., 1.)
call_payoff = np.maximum(final_prices - strike, 0.)
put_payoff = np.maximum(strike - final_prices, 0.)
check_prices = np.array([80., 100., 120.])
payoff_table = pd.DataFrame({
"Final asset (GBP)": check_prices,
"Long call (GBP)": np.maximum(check_prices - strike, 0.),
"Long put (GBP)": np.maximum(strike - check_prices, 0.),
"Short call (GBP)": -np.maximum(check_prices - strike, 0.),
"Short put (GBP)": -np.maximum(strike - check_prices, 0.),
})
print(payoff_table.to_string(index=False))
payoff_charts = {}
for label, sign in [("Holder", 1), ("Writer", -1)]:
payoff_charts["optionsPayoff" + label] = {
"type": "line", "title": label + ": Cash at Expiry Before Premium Costs",
"xAxis": {"type": "value", "name": "Final asset price (GBP/unit)"},
"yAxisName": "Option payoff for one unit (GBP)",
"series": {
"Call": np.column_stack([final_prices, sign * call_payoff]).tolist(),
"Put": np.column_stack([final_prices, sign * put_payoff]).tolist(),
},
}
print("\n<ECHARTS_DATA>" + json.dumps(payoff_charts, allow_nan=False))2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
The writer's negative payoff is a payment to the holder. It is not yet the writer's profit, because the writer received a premium at entry.
Experiment: change only strike to 110.0 in the setup cell, rerun it and this cell, and predict the payments at £120. The call pays £10; the put pays zero. Then restore strike = 100.0 and rerun both cells before continuing.
Check: at a final price of £90, which right has a positive exercise benefit? The put pays £10, the call zero; the short put pays £10. A negative number inside maximum is replaced by zero for the holder, not charged to the holder.
2. Payoff Is Not Profit
An option's premium is paid today. Its payoff arrives in one year. Compare them on one date, as we did for futures cash flows.
For this example, use a call premium
At 5%, the call premium's maturity equivalent is
With effective annual rate
For the put at £80, this is
The writer's financed profit is the negative of the holder's under the same ideal funding rate: premium grown to maturity minus the payoff owed. We exclude collateral costs from this option example; actual short-option margin and funding can change the result.
Find the Break-Even Prices
Break-even means zero financed profit, not zero payoff. For the call, solve
The following graphs shift the payoff lines by the financed premiums. At £100 both long options lose their respective premium costs. The zero line lets you distinguish a positive payment from a positive profit.
call_premium = 250 / 21
put_premium = 50 / 7
call_cost_at_expiry = call_premium * growth
put_cost_at_expiry = put_premium * growth
call_profit = call_payoff - call_cost_at_expiry
put_profit = put_payoff - put_cost_at_expiry
print(f"Call premium today: {call_premium:.6f} GBP; financed cost: {call_cost_at_expiry:.2f} GBP")
print(f"Put premium today: {put_premium:.6f} GBP; financed cost: {put_cost_at_expiry:.2f} GBP")
print(f"Call break-even: {strike + call_cost_at_expiry:.2f} GBP/unit")
print(f"Put break-even: {strike - put_cost_at_expiry:.2f} GBP/unit")
profit_charts = {}
for label, sign in [("Holder", 1), ("Writer", -1)]:
profit_charts["optionsProfit" + label] = {
"type": "line", "title": label + ": Include the Premium and One Year of Funding",
"xAxis": {"type": "value", "name": "Final asset price (GBP/unit)"},
"yAxisName": "Financed profit at expiry for one unit (GBP)",
"series": {
"Call": np.column_stack([final_prices, sign * call_profit]).tolist(),
"Put": np.column_stack([final_prices, sign * put_profit]).tolist(),
"Zero profit": [[0., 0.], [200., 0.]],
},
"seriesOptions": {"Zero profit": {"lineStyle": {"type": "dashed"}}},
}
print("\n<ECHARTS_DATA>" + json.dumps(profit_charts, allow_nan=False))2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
For a nonnegative asset price, the long call's maximum financed loss is £12.50, while its upside has no finite bound. The long put's maximum financed loss is £7.50 and its maximum financed gain is £92.50, reached if the asset becomes worthless. The short call's maximum gain is £12.50 but its loss has no finite bound. The short put's maximum gain is £7.50 and its maximum loss is £92.50. The graph stops at £200 for readability; it does not cap the asset price or the short call's loss.
Experiment: in this cell, temporarily set call_premium = 14.0, keeping everything else fixed. The funded cost becomes £14.70 and break-even becomes £114.70. Payoff is unchanged. This is a transaction-cost what-if, not the model price we will derive. Restore 250 / 21 before continuing.
Check: if the call finishes at £105, should the holder forgo a £5 payoff because the funded premium was £12.50? No. Taking the £5 leaves a £7.50 loss; forgoing it leaves a £12.50 loss. The premium is a past payment at the exercise decision.
3. Reproduce a Call with the Asset and a Loan
We now ask what the right is worth today. For pricing only, impose a small model: next year the asset can be £80 or £120, and no other price. Both states are possible. These invented states are not a forecast or a claim that real prices take only two values.
The call pays £0 in the down state and £20 in the up state. Can an asset holding plus a cash account make exactly those two payments? Such a portfolio is a replication. If it can be traded under our assumptions, a different call price would allow buying the cheaper payment stream and selling the more expensive identical stream.
Step 1: Match the Difference Between Outcomes
The asset's outcome difference is
Let
Subscripts
Step 2: Subtract the Unwanted £40
Half a unit is worth £40 in the down state and £60 in the up state. We need £0 and £20. Subtract £40 from both outcomes by borrowing an amount today that requires £40 repayment at expiry.
Let
The portfolio costs
| Position | Value today | Down state: £80 | Up state: £120 |
|---|---|---|---|
| Half a unit of the asset | £50.000000 | £40 | £60 |
| Borrowing account | −£38.095238 | −£40 | −£40 |
| Net replicating portfolio | £11.904762 | £0 | £20 |
The initial asset purchase is a £50 cash outflow, partly funded by £38.095238 of borrowing proceeds. That leaves £11.904762 to supply. Negative cash-account value is a liability, not another asset. No rebalancing is required between the two dates in this one-step model.
The graph below uses two discrete state labels rather than a smooth curve. Compare the net portfolio and option bars within each state; the separate asset and cash bars explain how the match is built.
state_prices = np.array([80., 120.])
call_states = np.maximum(state_prices - strike, 0.)
call_delta = (call_states[1] - call_states[0]) / (state_prices[1] - state_prices[0])
call_cash_today = (call_states[0] - call_delta * state_prices[0]) / growth
replicated_call_price = call_delta * spot_today + call_cash_today
call_replication = call_delta * state_prices + call_cash_today * growth
print(f"Asset units: {call_delta:.6f}; cash account today: {call_cash_today:.6f} GBP")
print(f"Replicated call price today: {replicated_call_price:.6f} GBP")
print(pd.DataFrame({"Asset (GBP)": call_delta * state_prices,
"Cash (GBP)": np.full(2, call_cash_today * growth),
"Net portfolio (GBP)": call_replication, "Call (GBP)": call_states},
index=["Down: 80", "Up: 120"]).to_string())
print("\n<ECHARTS_DATA>" + json.dumps({"optionsCallReplication": {
"type": "bar", "title": "Half a Unit Minus a 40 GBP Repayment",
"xAxis": {"type": "category", "name": "Model state", "data": ["Down: 80", "Up: 120"]},
"yAxisName": "Maturity value for one call (GBP)",
"series": {"Asset holding": (call_delta * state_prices).tolist(),
"Cash account": [float(call_cash_today * growth)] * 2,
"Net portfolio": call_replication.tolist(), "Call payoff": call_states.tolist()},
}}, allow_nan=False))2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Experiment: change the up outcome from £120 to £140 in state_prices and rerun this cell. The call pays £40 in that state, so delta becomes [80., 120.] and rerun before continuing. Altering a possible future state changes the pricing model; moving the eventual realized price along a payoff graph does not reprice the original purchase.
Check: suppose this call is offered at £13 while the replication still costs £11.904762. Sell the call and buy the replication. You receive £1.095238 net today, and the portfolio meets the call obligation in either state. Investing that initial surplus gives £1.15 at maturity. The argument assumes both trades, including the short option and borrowing, can be executed at the stated terms.
Where the model stops: at a final price of £100, the static portfolio pays
4. Reproduce a Put with a Short Holding and Cash
The put pays £20 at £80 and zero at £120. Its state difference is
A negative holding means short selling half a unit: borrow it, sell it today, and later buy it back to return it. This brings in £50 today. Its maturity liability is £40 in the down state or £60 in the up state.
Add a cash account that pays £60 in one year. It costs
In the same notation,
put_states = np.maximum(strike - state_prices, 0.)
put_delta = (put_states[1] - put_states[0]) / (state_prices[1] - state_prices[0])
put_cash_today = (put_states[0] - put_delta * state_prices[0]) / growth
replicated_put_price = put_delta * spot_today + put_cash_today
put_replication = put_delta * state_prices + put_cash_today * growth
print(f"Asset units: {put_delta:.6f}; cash account today: {put_cash_today:.6f} GBP")
print(f"Replicated put price today: {replicated_put_price:.6f} GBP")
print(pd.DataFrame({"Short asset (GBP)": put_delta * state_prices,
"Cash (GBP)": np.full(2, put_cash_today * growth),
"Net portfolio (GBP)": put_replication, "Put (GBP)": put_states},
index=["Down: 80", "Up: 120"]).to_string())
print("\n<ECHARTS_DATA>" + json.dumps({"optionsPutReplication": {
"type": "bar", "title": "A 60 GBP Cash Receipt Covers the Short Asset",
"xAxis": {"type": "category", "name": "Model state", "data": ["Down: 80", "Up: 120"]},
"yAxisName": "Maturity value for one put (GBP)",
"series": {"Short asset": (put_delta * state_prices).tolist(),
"Cash account": [float(put_cash_today * growth)] * 2,
"Net portfolio": put_replication.tolist(), "Put payoff": put_states.tolist()},
}}, allow_nan=False))2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Experiment: for two identical puts, double each holding: short one unit and invest £114.285714. Initial net cost is £14.285714, and the final net payments are £40 or £0. Doubling quantity doubles cost and payoff; it does not change the per-unit strike. Real option contracts may cover many units, so check the multiplier before applying these amounts.
Check: why is the cash deposit £57.142857 rather than the put premium £7.142857? Because the short asset supplies £50 of the deposit. The premium is the net portfolio cost, not the value of its cash component.
5. Pricing Weights Are Not a Forecast
We have priced both rights without choosing the actual chance of an up move. Replication depends on traded prices and matching payoffs. A different expression packages the same calculation as a discounted weighted average.
Choose a weight
Subtract 80 from both sides:
The call price is
We require
For comparison, suppose your real-world up probability is
The graph varies the assumed real-world probability on its horizontal axis. It shows expected financed profit, not option price. It crosses zero at 62.5%, which is the pricing weight for this particular model.
pricing_weight = (spot_today * growth - state_prices[0]) / (state_prices[1] - state_prices[0])
weighted_call_price = (pricing_weight * call_states[1] + (1 - pricing_weight) * call_states[0]) / growth
weighted_put_price = (pricing_weight * put_states[1] + (1 - pricing_weight) * put_states[0]) / growth
real_up_probabilities = np.linspace(0., 1., 101)
expected_call_profit = (real_up_probabilities * call_states[1]
+ (1 - real_up_probabilities) * call_states[0] - call_cost_at_expiry)
print(f"Pricing up weight: {pricing_weight:.3f}; down weight: {1 - pricing_weight:.3f}")
print(f"Discounted weighted call: {weighted_call_price:.6f} GBP; put: {weighted_put_price:.6f} GBP")
for probability in [0.5, 0.625, 0.8]:
expected_payoff = probability * call_states[1] + (1 - probability) * call_states[0]
print(f"Real up probability {probability:.1%}: expected payoff {expected_payoff:.2f} GBP; "
f"expected financed profit {expected_payoff - call_cost_at_expiry:.2f} GBP")
print("\n<ECHARTS_DATA>" + json.dumps({"optionsForecastProfit": {
"type": "line", "title": "Your Probability Changes Expected Profit, Not This Replication Cost",
"xAxis": {"type": "value", "name": "Assumed up probability (%)"},
"yAxisName": "Expected financed call profit at expiry (GBP)",
"series": {"Expected profit": np.column_stack([100 * real_up_probabilities, expected_call_profit]).tolist(),
"Zero profit": [[0., 0.], [100., 0.]]},
"seriesOptions": {"Zero profit": {"lineStyle": {"type": "dashed"}}},
}}, allow_nan=False))2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Experiment: replace the printed probability 0.8 with 0.7. Expected call payoff becomes £14 and expected financed profit £1.50. The pricing weight and option price remain unchanged.
Check: at a real-world up probability of 62.5%, does zero expected financed profit mean no risk? No. The actual financed profit is still −£12.50 or +£7.50, depending on the state. Only their weighted average is zero.
6. Put–Call Parity: Two Ways to Buy the Same Final Value
Consider two packages with the same strike, expiry, underlying, and one-unit quantity:
- Package A: one call plus cash invested today to pay £100 at expiry.
- Package B: one put plus one unit of the asset.
At £80, A pays
Equal future payments require equal current prices under the stated trading assumptions. This is put–call parity for European options on a non-income-paying asset:
Our numbers give
The first graph overlays the two packages; the dashed line lies on the solid line because they are equal. The second compares long call plus short put with a forward's signed maturity payment. The identity
strike_present_value = strike / growth
package_a = call_payoff + strike
package_b = put_payoff + final_prices
synthetic_forward = call_payoff - put_payoff
print(f"Call plus discounted strike: {call_premium + strike_present_value:.6f} GBP")
print(f"Put plus asset: {put_premium + spot_today:.6f} GBP")
print(f"Call minus put today: {call_premium - put_premium:.6f} GBP")
print(f"Asset minus discounted strike: {spot_today - strike_present_value:.6f} GBP")
print("\n<ECHARTS_DATA>" + json.dumps({
"optionsParityPackages": {
"type": "line", "title": "Call Plus Cash and Put Plus Asset Pay the Same Amount",
"xAxis": {"type": "value", "name": "Final asset price (GBP/unit)"},
"yAxisName": "Package value at expiry (GBP)",
"series": {"Call + cash": np.column_stack([final_prices, package_a]).tolist(),
"Put + asset": np.column_stack([final_prices, package_b]).tolist()},
"seriesOptions": {"Put + asset": {"lineStyle": {"type": "dashed"}}},
},
"optionsSyntheticForward": {
"type": "line", "title": "Buying a Call and Selling a Put Restores the Obligation",
"xAxis": {"type": "value", "name": "Final asset price (GBP/unit)"},
"yAxisName": "Maturity payoff before entry cost (GBP)",
"series": {"Call - put": np.column_stack([final_prices, synthetic_forward]).tolist(),
"Forward at K = 100": np.column_stack([final_prices, final_prices - strike]).tolist()},
"seriesOptions": {"Forward at K = 100": {"lineStyle": {"type": "dashed"}}},
},
}, allow_nan=False))2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
The synthetic £100-delivery-price forward costs £4.761905 today. It is not the zero-cost new forward from D.1: today's fair one-year forward delivery price is £105. Financing the synthetic position's initial cost gives £5 at maturity, so its financed profit is
Experiment: if a market call quote were £13 and the put quote stayed £7.142857, Package A would cost £108.238095 and B £107.142857. Sell A and buy B; the initial surplus is £1.095238, and expiry payments cancel for every final price. This parity comparison does not require the two-state assumption, but it still requires the same contract terms and executable trades.
Check: with the call at £11.904762, spot £100, and discounted strike £95.238095, recover the put price:
7. Check Price Bounds Before Trusting a Quote
Even without specifying two states, some prices cannot be consistent with these European contracts and our assumptions. A call cannot cost more than the asset: owning the asset supplies at least the call's final payoff when prices are nonnegative. A put cannot cost more than cash that will pay the strike, since its payoff never exceeds the strike.
Parity and nonnegative option prices also give lower bounds:
For this example, the call must lie between £4.761905 and £100, and the put between £0 and £95.238095. Our derived prices lie within these intervals. The bounds are broad checks, not a substitute for a pricing model. The call's lower bound is not always its immediate exercise benefit: these European rights cannot be exercised early.
bound_table = pd.DataFrame({
"Lower bound (GBP)": [max(0., spot_today - strike_present_value), max(0., strike_present_value - spot_today)],
"Model price (GBP)": [replicated_call_price, replicated_put_price],
"Upper bound (GBP)": [spot_today, strike_present_value],
}, index=["Call", "Put"])
print(bound_table.to_string(float_format=lambda value: f"{value:.6f}"))
print("Both model prices satisfy the bounds:", bool(
((bound_table.iloc[:, 0] <= bound_table.iloc[:, 1])
& (bound_table.iloc[:, 1] <= bound_table.iloc[:, 2])).all()))2
3
4
5
6
7
8
9
Experiment: test a £3 call quote. It is below the £4.761905 lower bound. Sell one asset, buy the call for £3, and invest £95.238095 to have £100 at expiry. You retain £1.761905 today. At expiry, the call plus cash is worth at least the asset you owe, in every state. The lower bound detects the inconsistency without choosing an up probability.
Check: a £12 call satisfies the bounds. Does that prove it is the fair price? No. It passes a necessary check, but the specified two-state model gives £11.904762, and real-market valuation requires a suitable model and trading inputs.
8. Experiment: Move the Final Price, Keep the Purchased Options Fixed
Keep the £100 strike, one-year horizon, 5% rate, and original premiums fixed. Move only the final asset price. This is a payoff/profit scenario experiment, not a change to today's spot price or a new pricing model.
Each graph shows a blue payoff line and a purple financed-profit line for one long option. Large outlined markers show the selected amounts: an orange diamond for payoff and a magenta square for profit. Their vertical separation is the financed premium: £12.50 for the call and £7.50 for the put. The slider includes £92.50 and £112.50 so you can inspect both break-even points exactly.
The helper repeats the visible payoff and funding formulas. Prices between and beyond £80 and £120 illustrate the contracts' payments; they are not additional states in the model used to derive the original premiums.
import ipywidgets as widgets
import PortfolioOptimizationKit as pok
widget_instance = widgets.interactive(
pok.show_option_payoff_echart,
final_spot=widgets.FloatSlider(value=120., min=0., max=200., step=2.5,
description="Final GBP", continuous_update=False),
)
widget_instance2
3
4
5
6
7
8
9
Try it: predict each row, then move the slider.
| Final price | Call payoff | Call financed profit | Put payoff | Put financed profit |
|---|---|---|---|---|
| £80 | £0 | −£12.50 | £20 | £12.50 |
| £92.50 | £0 | −£12.50 | £7.50 | £0 |
| £100 | £0 | −£12.50 | £0 | −£7.50 |
| £110 | £10 | −£2.50 | £0 | −£7.50 |
| £112.50 | £12.50 | £0 | £0 | −£7.50 |
| £120 | £20 | £7.50 | £0 | −£7.50 |
Check: why are both long-option profits negative at £100? Neither right produces an exercise benefit, but both premiums have been paid and financed. “No exercise” does not mean “no loss.”
9. Worked Exercise: Change the Strike to £110
Return to the original two-state pricing model: £100 today, £80 or £120 in one year, and 5% effective annual financing. Change the common option strike to £110.
- Find the call and put payoffs in each state.
- Find each option's asset holding, signed cash account today, and premium.
- Verify put–call parity. Does its payoff identity still hold at a hypothetical final price of £100?
- Calculate both financed break-even prices. Is the call's positive £10 up-state payoff entirely profit?
Hint
For each right, divide the up-minus-down payoff difference by £40. Then use the down-state payoff to solve for the cash account. Discount that cash amount before calculating today's portfolio cost. Retain full precision until displaying results.
Worked answer
The call pays £0 or £10. Hold 0.25 units and borrow £19.047619, which requires £20 repayment. Its price is
The put pays £30 or £0. Short 0.75 units and invest £85.714286 to receive £90. Its price is
Both parity packages cost £110.714286 today. Call minus put costs −£4.761905, equal to
The financed premiums are £6.25 and £11.25. Call break-even is £116.25, put break-even £98.75. The call's up-state payoff of £10 leaves £3.75 financed profit.
exercise_strike = 110.0
exercise_states = np.array([80., 120.])
exercise_growth = 1.05
exercise_rows = []
for name, payments in [("Call", np.maximum(exercise_states - exercise_strike, 0.)),
("Put", np.maximum(exercise_strike - exercise_states, 0.))]:
units = (payments[1] - payments[0]) / (exercise_states[1] - exercise_states[0])
cash_today = (payments[0] - units * exercise_states[0]) / exercise_growth
premium = units * 100 + cash_today
funded_premium = premium * exercise_growth
break_even = exercise_strike + funded_premium if name == "Call" else exercise_strike - funded_premium
exercise_rows.append([name, *payments, units, cash_today, premium, break_even])
exercise_results = pd.DataFrame(exercise_rows, columns=[
"Option", "Down payoff (GBP)", "Up payoff (GBP)", "Asset units", "Cash today (GBP)",
"Premium (GBP)", "Break-even (GBP/unit)",
]).set_index("Option")
print(exercise_results.to_string(float_format=lambda value: f"{value:.6f}"))
exercise_call = exercise_results.loc["Call", "Premium (GBP)"]
exercise_put = exercise_results.loc["Put", "Premium (GBP)"]
print(f"Parity difference: {exercise_call - exercise_put:.6f} GBP")
print(f"Asset minus discounted strike: {100 - exercise_strike / exercise_growth:.6f} GBP")
print(f"Call financed profit in up state: {10 - exercise_call * exercise_growth:.2f} GBP")2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Chapter Summary
- The holder buys a right; the writer accepts the corresponding obligation. Strike, premium, payoff, and financed profit are different quantities.
- Exercise decisions compare the asset with the strike. Profit also includes the premium and its funding or opportunity cost.
- A two-state option can be replicated by an asset holding and a signed cash account. The portfolio's net cost gives its model price.
- Pricing weights reproduce that cost. Real-world probabilities answer a different question: what outcomes and profits you expect.
- European put–call parity is an all-price payoff identity under matched terms. A static two-state replication has a narrower scope.
- Option price bounds detect some inconsistent quotes, but do not determine a unique price.
Next is D.4 — Random Motion and Itô's Lemma, currently planned. We will introduce changes, slopes, and curvature with small numerical examples, then build the tools needed to move beyond a two-state model.
