SigmaField
Mathematical Foundations for Quant Finance
0 of 256 lessons
      1. 3.3.1 Linear systems and Gaussian eliminationConcept · 30 min
      2. 3.3.2 Rank and the shape of the solution setConcept · 30 min
      3. 3.3.3 Practice set: linear systemsPractice set · 20 min
      4. 3.3.4 Case study: replicating a payoff, and complete marketsCase study · 35 min

Level 3, Linear algebra 3.3 Linear systems Lesson 4 of 4

Case studyLesson 3.3.4 in Level 3, Linear algebra. About 35 minutes.

Case study: replicating a payoff, and complete markets

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 K=100K = 100 pays max⁡(S−100,0)\max(S - 100, 0), where SS 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 max⁡(100−S,0)\max(100 - S, 0): 20, 0 and 0. Its price is the question.

Both options expire at the end of the year, when the bond pays.

assetdownmiddleupprice today
bond1110.97
stock8010012598.75
call, strike 10000256.75
put, strike 1002000?

Payoffs as a matrix

Put each asset's payoffs in a column, one row per state. This is the payoff matrix DD, with the bond, the stock and the call as its columns:

D=(180011000112525)D = \begin{pmatrix} 1 & 80 & 0 \\ 1 & 100 & 0 \\ 1 & 125 & 25 \end{pmatrix}

A portfolio holds h1h_1 bonds, h2h_2 shares and h3h_3 calls: the holdings vector h\mathbf{h}. 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: DhD\mathbf{h} is a combination of the asset columns. Its price today is p⋅h\mathbf{p} \cdot \mathbf{h}, the dot product (lesson 3.1.2) with the prices p=(0.97,98.75,6.75)\mathbf{p} = (0.97, 98.75, 6.75).

Rule: Payoff and price of a portfolio

payoff in each state=Dhprice today=p⋅h\begin{gathered} \text{payoff in each state} = D\mathbf{h} \\[4pt] \text{price today} = \mathbf{p} \cdot \mathbf{h} \end{gathered}

DD has one row per state and one column per asset. h\mathbf{h} holds the units of each asset, negative when short, and p\mathbf{p} holds today's prices.

Copying the put

To replicate the put, find holdings whose payoff equals the put's in every state: Dh=(20,0,0)D\mathbf{h} = (20, 0, 0). That is a linear system of three equations in three unknowns, and elimination (lesson 3.3.1) solves it.

stepoperationmultipliernew row of DDnew right-hand side
1R2−R1R_2 - R_110, 20, 00,\ 20,\ 0−20-20
2R3−R1R_3 - R_110, 45, 250,\ 45,\ 25−20-20
3R3−94R2R_3 - \frac{9}{4}R_245÷20=9445 \div 20 = \frac{9}{4}0, 0, 250,\ 0,\ 2525

Back substitution gives h3=25÷25=1h_3 = 25 \div 25 = 1, then h2=−20÷20=−1h_2 = -20 \div 20 = -1, then h1=20−80h2=100h_1 = 20 - 80h_2 = 100. So: buy 100 bonds, sell one share short, buy one call.

Check it in each state: down 100−80+0=20100 - 80 + 0 = 20, middle 100−100+0=0100 - 100 + 0 = 0 and up 100−125+25=0100 - 125 + 25 = 0. 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 10−1510^{-15}, and adding 0.0 turns a −0.0-0.0 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.

⌘/Ctrl + Enter runs the cell

One payoff, one price

The replicating portfolio costs 100×0.97−98.75+6.75=5.00100 \times 0.97 - 98.75 + 6.75 = 5.00 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.

Check yourself
The put trades at $5.40. Which trade locks in a profit?

Put–call parity

The holdings were K=100K = 100 bonds, −1-1 share and +1+1 call. Write B=0.97B = 0.97 for the bond price, S0=98.75S_0 = 98.75 for the stock price today, and CC and PP for the call and put prices. Then the price of the put is P=KB−S0+CP = KB - S_0 + C.

Rule: Put–call parity

C−P=S0−KBC - P = S_0 - KB

A call minus a put with the same strike and date is worth the stock minus KK bonds. Here 6.75−5.00=1.756.75 - 5.00 = 1.75, and 98.75−0.97×100=1.7598.75 - 0.97 \times 100 = 1.75.

It does not depend on the three states. For every final price SS, max⁡(S−K,0)−S+K=max⁡(K−S,0)\max(S - K, 0) - S + K = \max(K - S, 0): 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 max⁡(S−K,0)−S+K=max⁡(K−S,0)\max(S - K, 0) - S + K = \max(K - S, 0) at five final prices, three of them outside the model's states.

⌘/Ctrl + Enter runs the cell

State prices

A state price ψs\psi_s (psi) is today's price of $1 paid in state ss only, and nothing in the others. If you know the three, any payoff x\mathbf{x} costs ψ⋅x\boldsymbol{\psi} \cdot \mathbf{x}: 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 ψ1+ψ2+ψ3=0.97\psi_1 + \psi_2 + \psi_3 = 0.97; the stock gives 80ψ1+100ψ2+125ψ3=98.7580\psi_1 + 100\psi_2 + 125\psi_3 = 98.75; the call gives 25ψ3=6.7525\psi_3 = 6.75. These are the columns of DD dotted with ψ\boldsymbol{\psi}, so the system uses the transpose of lesson 3.2.3.

Rule: State prices

D⊤ψ=pprice of a payoff x=ψ⋅x\begin{gathered} D^\top \boldsymbol{\psi} = \mathbf{p} \\[4pt] \text{price of a payoff } \mathbf{x} = \boldsymbol{\psi} \cdot \mathbf{x} \end{gathered}

Row jj of D⊤ψ=pD^\top \boldsymbol{\psi} = \mathbf{p} prices asset jj. Solving gives ψ=(0.25,0.45,0.27)\boldsymbol{\psi} = (0.25, 0.45, 0.27), and the put costs 0.25×20=5.000.25 \times 20 = 5.00, 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 D⊤ψ=pD^\top\boldsymbol{\psi} = \mathbf{p}. Then the put is priced with ψ\boldsymbol{\psi}.

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.

⌘/Ctrl + Enter runs the cell

Complete markets

A market is complete when every payoff can be replicated: Dh=xD\mathbf{h} = \mathbf{x} has a solution for every x\mathbf{x}. By lesson 3.3.2, that needs a pivot in every row of DD: the rank of DD must equal the number of states.

Here DD 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, −1-1 share and 2 calls, at a price of 11.75: the put's 5.00 plus the call's 6.75.

Rule: Complete market

rank⁡D=number of states\operatorname{rank} D = \text{number of states}

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 D2D_2 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 [D2∣call][D_2 \mid \text{call}]:

(180011000112525)\left(\begin{array}{cc|c} 1 & 80 & 0 \\ 1 & 100 & 0 \\ 1 & 125 & 25 \end{array}\right)

The steps R2−R1R_2 - R_1, R3−R1R_3 - R_1 and R3−94R2R_3 - \frac{9}{4}R_2, as before, give

(180002000025)\left(\begin{array}{cc|c} 1 & 80 & 0 \\ 0 & 20 & 0 \\ 0 & 0 & 25 \end{array}\right)

The last row reads 0=250 = 25, so there is no solution. The rank of D2D_2 is 2 and the rank of [D2∣call][D_2 \mid \text{call}] is 3.

The second panel of the figure above shows why. A portfolio of aa bonds and bb shares pays a+bSa + bS, a straight line in SS. 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 ψ3=t\psi_3 = t. Elimination gives the line of solutions

ψ=(−0.0875+1.25t1.0575−2.25tt)\boldsymbol{\psi} = \begin{pmatrix} -0.0875 + 1.25t \\ 1.0575 - 2.25t \\ t \end{pmatrix}

All three are positive only for tt between 0.07 and 0.47. The call costs 25t25t, 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 S−100S - 100, which −100-100 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 DD is complete and D2D_2 is not. np.linalg.solve refuses D2D_2 because it is not square.

The loop puts three values of tt 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.

⌘/Ctrl + Enter runs the cell
Check yourself
A market has four states and three assets. Can it be complete?

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 −0.0125-0.0125.

Scaled by 80, it is 400 bonds, short 4 shares and long 4 calls. It pays 80, 0 and 0, and costs −1-1: you are paid $1.00 to take it.

⌘/Ctrl + Enter runs the cell
Exercise

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.

  1. replicate(D, prices, target) returns (h, price): the holdings with D @ h equal to target in every state, and their price today.
  2. state_prices(D, prices) returns the state prices ψ\boldsymbol{\psi} with D⊤ψ=pD^\top\boldsymbol{\psi} = \mathbf{p}. If any is 0 or negative, raise ValueError: 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 DD has one row per state and one column per asset. Holdings h\mathbf{h} pay DhD\mathbf{h} and cost p⋅h\mathbf{p} \cdot \mathbf{h} today; a negative holding is short.
  • Replicating a payoff x\mathbf{x} means solving Dh=xD\mathbf{h} = \mathbf{x}. The put with strike 100 is 100 bonds, −1-1 share and 1 call, and by the law of one price it costs $5.00.
  • Put–call parity: C−P=S0−KBC - P = S_0 - KB, here 6.75−5.00=98.75−976.75 - 5.00 = 98.75 - 97. It holds at every final price, not only in the model's states.
  • State prices solve D⊤ψ=pD^\top\boldsymbol{\psi} = \mathbf{p}: here (0.25,0.45,0.27)(0.25, 0.45, 0.27). Any payoff x\mathbf{x} costs ψ⋅x\boldsymbol{\psi} \cdot \mathbf{x}; positive state prices mean no arbitrage.
  • A market is complete when rank⁡D\operatorname{rank} D 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.

Free further reading

Optional. This lesson covers what you need; these add more examples and practice. All are free to read.