A market with three states
Take a market that lasts one year and can end in one of three states: down, middle or up. The numbers are invented to keep the arithmetic small. Real markets have far more than three outcomes; the desk notes at the end come back to that.
- The bond pays $1 in every state and costs 0.97 today.
- The stock ends at 80, 100 or 125 and costs 98.75 today.
- The call with strike pays , where is the stock's final price (case study 1.1.5). That is 0, 0 and 25. It costs 6.75.
- The put with the same strike pays : 20, 0 and 0. Its price is the question.
Both options expire at the end of the year, when the bond pays.
| asset | down | middle | up | price today |
|---|---|---|---|---|
| bond | 1 | 1 | 1 | 0.97 |
| stock | 80 | 100 | 125 | 98.75 |
| call, strike 100 | 0 | 0 | 25 | 6.75 |
| put, strike 100 | 20 | 0 | 0 | ? |
Payoffs as a matrix
Put each asset's payoffs in a column, one row per state. This is the payoff matrix , with the bond, the stock and the call as its columns:
A portfolio holds bonds, shares and calls: the holdings vector . A negative holding is a short position: you sell the asset today, receive its price, and owe its payoff at the end.
In each state the portfolio pays the sum of its holdings times their payoffs. That is the column picture of lesson 3.3.1: is a combination of the asset columns. Its price today is , the dot product (lesson 3.1.2) with the prices .
Rule: Payoff and price of a portfolio
has one row per state and one column per asset. holds the units of each asset, negative when short, and holds today's prices.
Copying the put
To replicate the put, find holdings whose payoff equals the put's in every state: . That is a linear system of three equations in three unknowns, and elimination (lesson 3.3.1) solves it.
| step | operation | multiplier | new row of | new right-hand side |
|---|---|---|---|---|
| 1 | 1 | |||
| 2 | 1 | |||
| 3 | 25 |
Back substitution gives , then , then . So: buy 100 bonds, sell one share short, buy one call.
Check it in each state: down , middle and up . The portfolio pays exactly what the put pays.
Try it
np.linalg.solve finds the holdings, and D @ h gives back the put's payoffs. np.round(…, 4) hides rounding of about , and adding 0.0 turns a into 0.0.
The price can print with rounding in the last digit, such as 4.999999999999999, so the cell also rounds it to cents.
One payoff, one price
The replicating portfolio costs today. The law of one price says two positions with the same payoff in every state must cost the same. So the put is worth $5.00.
Suppose it traded at $5.40. Sell the put for 5.40 and buy the portfolio for 5.00, keeping 0.40 today. At the end, the portfolio pays exactly what you owe on the put, in every state.
That is an arbitrage: a profit today with nothing to pay later. Traders would keep doing it until the price came back to 5.00.
At $4.60 the trade runs the other way. Buy the put and take the opposite holdings: sell 100 bonds (borrowing 97), buy a share and sell a call.
Put–call parity
The holdings were bonds, share and call. Write for the bond price, for the stock price today, and and for the call and put prices. Then the price of the put is .
Rule: Put–call parity
A call minus a put with the same strike and date is worth the stock minus bonds. Here , and .
It does not depend on the three states. For every final price , : above the strike the call cancels the falling line, below it the call pays nothing. So parity holds in any market where the bond, the stock and the two options trade, whatever the stock can do.
Payoffs at the end of the year. 100 bonds minus a share pay a straight line; the call adds a kink, and the sum is the put at every price. Bonds and shares alone pay only straight lines, and none passes through the call's three payoffs.
Try it
The first line checks parity. The loop compares the two sides of at five final prices, three of them outside the model's states.
State prices
A state price (psi) is today's price of $1 paid in state only, and nothing in the others. If you know the three, any payoff costs : so much in each state, at that state's price.
Each traded asset must obey this, so its price gives one equation. The bond gives ; the stock gives ; the call gives . These are the columns of dotted with , so the system uses the transpose of lesson 3.2.3.
Rule: State prices
Row of prices asset . Solving gives , and the put costs , as replication found.
The state prices add up to 0.97, the price of $1 in every state: the bond. Divided by 0.97 they become 0.2577, 0.4639 and 0.2784, positive numbers that add up to 1.
The market prices assets as if these were the chances of the states. They are called risk-neutral probabilities, and the Derivatives Modelling course builds option pricing on them.
All three state prices are positive here. A set of prices allows no arbitrage exactly when it has positive state prices; the Derivatives Modelling course proves it. A negative state price is a warning, as the desk notes below show.
Try it
D.T is the transpose, so the first line solves . Then the put is priced with .
np.linalg.solve(D, np.eye(3)) solves for three payoffs at once, one per column: $1 in each state. Each column of H is a portfolio; the one for the up state is 0.04 calls. Their prices, p @ H, are the state prices again.
Complete markets
A market is complete when every payoff can be replicated: has a solution for every . By lesson 3.3.2, that needs a pivot in every row of : the rank of must equal the number of states.
Here has rank 3 and there are 3 states, so the market is complete. A straddle, a call plus a put (case study 1.1.5), pays 20, 0 and 25. Solving gives 100 bonds, share and 2 calls, at a price of 11.75: the put's 5.00 plus the call's 6.75.
Rule: Complete market
Then every payoff has a replicating portfolio and a single price. With fewer assets than states, or with assets that copy each other, some payoffs cannot be copied.
Without the call
Now suppose only the bond and the stock trade. Their payoff matrix has 3 rows and 2 columns, so its rank is at most 2: a tall system (lesson 3.3.2). To copy the call, eliminate on :
The steps , and , as before, give
The last row reads , so there is no solution. The rank of is 2 and the rank of is 3.
The second panel of the figure above shows why. A portfolio of bonds and shares pays , a straight line in . The call's three payoffs, 0, 0 and 25, do not lie on one line: the line through two of them misses the third.
What the bond and the stock do fix
Bond and stock still give two pricing equations for three state prices. By lesson 3.3.2 that leaves one free variable: take . Elimination gives the line of solutions
All three are positive only for between 0.07 and 0.47. The call costs , so any call price between 1.75 and 11.75 is free of arbitrage, and the market's 6.75 is one of them. The bond and the stock bound the call's price; they do not fix it.
The lower bound has a meaning. 1.75 is the price of the payoff , which bonds and 1 share replicate, and the call never pays less than that. Case study 3.4.3 adds a fund to the bond and the stock, and asks whether it fills the gap.
Try it
The ranks show that is complete and is not. np.linalg.solve refuses because it is not square.
The loop puts three values of into the line of state prices. At 0.07 and 0.47 one state price reaches 0, and the call's price reaches a bound.
Try it
With the call at 1.50, the state prices have a negative entry. The portfolio that pays $1 in the down state then costs .
Scaled by 80, it is 400 bonds, short 4 shares and long 4 calls. It pays 80, 0 and 0, and costs : you are paid $1.00 to take it.
Replication and state prices
Write two functions. In both, D has one row per state and one column per asset, and prices holds today's price of each asset.
replicate(D, prices, target)returns(h, price): the holdings withD @ hequal totargetin every state, and their price today.state_prices(D, prices)returns the state prices with . If any is 0 or negative, raiseValueError: the prices allow an arbitrage.
Both raise ValueError unless D is square and its rank equals the number of states, so that the answer exists and is unique. The tests use this case study's market and a two-state market. They also try call quotes of $1.50 and $12.00, the bond and stock alone, and a repeated asset.
See the tests
import numpy as np
# columns: bond, stock, call with strike 100; rows: the states down, middle, up
D = [[1, 80, 0], [1, 100, 0], [1, 125, 25]]
PRICES = [0.97, 98.75, 6.75]
PUT = [20, 0, 0]
def test_put_holdings():
"""The put is copied by 100 bonds, −1 share and 1 call"""
h, _ = replicate(D, PRICES, PUT)
expect_close(h, [100, -1, 1], what="holdings that copy the put")
def test_put_price():
"""The put costs 5.00"""
_, price = replicate(D, PRICES, PUT)
expect_close(price, 5.0, what="price of the put")
def test_straddle():
"""The straddle, paying 20, 0 and 25, needs 2 calls and costs 11.75"""
h, price = replicate(D, PRICES, [20, 0, 25])
expect_close(h, [100, -1, 2], what="holdings that copy the straddle")
expect_close(price, 11.75, what="price of the straddle")
def test_bond_copies_itself():
"""The bond's own payoff gives holdings (1, 0, 0) and its price, 0.97"""
h, price = replicate(D, PRICES, [1, 1, 1])
expect_close(h, [1, 0, 0], what="holdings that copy the bond", abs_tol=1e-12)
expect_close(price, 0.97, what="price of the bond")
def test_state_prices():
"""The state prices are 0.25, 0.45 and 0.27"""
expect_close(state_prices(D, PRICES), [0.25, 0.45, 0.27], what="state prices")
def test_two_states():
"""Two states (stock 80 or 125): the call is 0.5556 shares and −44.44 bonds, and costs 11.75"""
h, price = replicate([[1, 80], [1, 125]], [0.97, 98.75], [0, 25])
expect_close(h, [-80 * 25 / 45, 25 / 45], what="holdings that copy the call")
expect_close(price, 11.75, what="price of the call")
def test_call_too_cheap():
"""A call quoted at 1.50 makes the down state price negative: ValueError"""
try:
state_prices(D, [0.97, 98.75, 1.50])
except ValueError:
return
raise AssertionError("expected ValueError: the state prices are -0.0125, 0.9225 and 0.06")
def test_call_too_dear():
"""A call quoted at 12.00 makes the middle state price negative: ValueError"""
try:
state_prices(D, [0.97, 98.75, 12.0])
except ValueError:
return
raise AssertionError("expected ValueError: the state prices are 0.5125, -0.0225 and 0.48")
def test_incomplete():
"""With only the bond and the stock, the call cannot be copied: ValueError"""
try:
replicate([[1, 80], [1, 100], [1, 125]], [0.97, 98.75], [0, 0, 25])
except ValueError:
return
raise AssertionError("expected ValueError: two assets cannot cover three states")
def test_repeated_asset():
"""A second bond, paying 2 in every state, adds nothing new: ValueError"""
try:
replicate([[1, 80, 2], [1, 100, 2], [1, 125, 2]], [0.97, 98.75, 1.94], PUT)
except ValueError:
return
raise AssertionError("expected ValueError: the payoff matrix has rank 2, below 3 states")
Summary card
- A payoff matrix has one row per state and one column per asset. Holdings pay and cost today; a negative holding is short.
- Replicating a payoff means solving . The put with strike 100 is 100 bonds, share and 1 call, and by the law of one price it costs $5.00.
- Put–call parity: , here . It holds at every final price, not only in the model's states.
- State prices solve : here . Any payoff costs ; positive state prices mean no arbitrage.
- A market is complete when equals the number of states. With only the bond and the stock (rank 2, three states), the call cannot be replicated. Its price is only bounded, between 1.75 and 11.75.