Model solutions to Homework Set 4
Math 456
Topics in Financial Mathematics
Prof. Wickerhauser
Read Chapters 7 and 8 of the textbook, "Binomial Models In Finance" by
John van der Hoek and Robert J. Elliott
NOTE: When asked to produce a spreadsheet, you may instead implement
the model in Octave or another system. For full credit you must
translate the algorithm into a computer program and produce output
from several examples. Include your code so that the grader can see
and reproduce your work.
Do the following exercise from the textbook Chapter 7.4, p.106:
Exercise 7.11.
Instead of a spreadsheet, modify the Octave programs "americanCallPut.m"
and "europeanCallPut.m" from the class website Example Programs.
Download these files into the current folder for the Octave session. The
parameters from Example 5.1 are S(0)=S=80, u=1.5, d=0.5, R=1.1, N=3, and
K=80, which are not the parameters in the example codes, so use the
following modifications:
function [Call, Put, EarlyC, EarlyP] = americanCallPut71(S, K, R, u, d, N)
% Octave/Matlab function to price American Call and Put options using
% the Cox-Ross-Rubinstein (CRR) binomial pricing model.
%
% Inputs:
% S = stock price
% K = strike price
% R = risk-free return over one step
% u = up factor, must be >1
% d = down factor, must be <1
% N = height of the binomial tree
% Output:
% Call = price of the call option at all gridpoints (n,j).
% Put = price of the put option at all gridpoints (n,j).
% EarlyC = 1 if early Call exercise is optimal, else 0
% EarlyP = 1 if early Put exercise is optimal, else 0
%
% Example 7.1, p.98 of the text, using Example 5.1 parameters:
% S=$80
% K=$80 (at the money call and put),
% R=1.1 ( 110% risk-free return)
% u = 1.5 (150% up factor)
% d = 0.5 (50% down factor)
% N=3
% [aCa, aPu, earlyC, earlyP] = americanCallPut71(80, 80, 1.1, 1.5, 0.5, 3)
%
% M.V.Wickerhauser, 2019-10-20
%
p0 = (R - d) / (u - d); % risk-neutral up probability
p1 = 1 - p0; % risk-neutral down probability
% Initial output Call and Put matrices of all zeros:
Call = zeros(N+1,N+1);
Put = zeros(N+1,N+1);
EarlyC = zeros(N+1,N+1); % Early Call exercise optimal?
EarlyP = zeros(N+1,N+1); % Early Pur exercise optimal?
% Set the terminal values at time N, gridpoints (N,0),...,(N,N):
for j = 0:N
Call(N+1,j+1) = max( S * u^j * d^(N-j) - K, 0 );
Put(N+1,j+1) = max( K - S * u^j * d^(N-j), 0 );
end
% Use backward induction to price earlier grid values:
for n = (N-1):(-1):0 % grid times n={N-1, N-2, ..., -1, 0}
for j = 0:n % states j={0,1,...,n} at time n
% Binomial pricing model value
binvalCall = (p0*Call(n+2,j+2) + p1*Call(n+2,j+1))/R;
binvalPut = (p0*Put(n+2,j+2) + p1*Put(n+2,j+1))/R;
% Exercise value, American style options
exerciseCall = S*u^j*d^(n-j) - K;
exercisePut = K - S*u^j*d^(n-j);
% Price at this node is the larger:
Call(n+1,j+1) = max(binvalCall, exerciseCall);
Put(n+1,j+1) = max(binvalPut, exercisePut);
% Record 1 if early exercise is chosen, else 0
EarlyC(n+1,j+1) = exerciseCall>binvalCall;
EarlyP(n+1,j+1) = exercisePut>binvalPut;
end
end
return; % Prices in matrices Call and Put are now fully defined.
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [Call, Put] = europeanCallPut71(S, K, R, u, d, N)
% Octave/Matlab function to price European Call and Put options using
% the Cox-Ross-Rubinstein (CRR) binomial pricing model.
%
% Inputs:
% S = stock price
% K = strike price
% R = risk-free return over
% u = up factor, must be >1
% d = down factor, must be <1
% N = height of the binomial tree
% Output:
% Call = price of the call option at all gridpoints (n,j).
% Put = price of the put option at all gridpoints (n,j).
%
% Example 7.1, p.98 of the text, using Example 5.1 parameters:
% S=$80
% K=$80 (at the money call and put),
% R=1.1 ( 110% risk-free return)
% u = 1.5 (150% up factor)
% d = 0.5 (50% down factor)
% N=3
% [eCa, ePu] = europeanCallPut71(80, 80, 1.1, 1.5, 0.5, 3)
%
% M.V.Wickerhauser, 2019-10-20
%
p0 = (R - d) / (u - d); % risk-neutral up probability
p1 = 1 - p0; % risk-neutral down probability
% Initial output Call and Put matrices of all zeros:
Call = zeros(N+1,N+1);
Put = zeros(N+1,N+1);
% Set the terminal values at time T, gridpoints (N,0),...,(N,N):
for j = 0:N
Call(N+1,j+1) = max( S * u^j * d^(N-j) - K, 0 );
Put(N+1,j+1) = max( K - S * u^j * d^(N-j), 0 );
end
% Use backward induction to price earlier grid values:
for n = N-1:-1:0
for j = 0:n
% Binomial pricing model value
Call(n+1,j+1) = (p0*Call(n+2,j+2) + p1*Call(n+2,j+1))/R;
Put(n+1,j+1) = (p0*Put(n+2,j+2) + p1*Put(n+2,j+1))/R;
end
end
return; % Prices in matrices Call and Put are now fully defined.
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% The example calls in the functions above are:
[eCa, ePu] = europeanCallPut71(80, 80, 1.1, 1.5, 0.5, 3)
[aCa, aPu, earlyC, earlyP] = americanCallPut71(80, 80, 1.1, 1.5, 0.5, 3)
% To find the early-exercise premium:
aPu(1,1) % American put price: $18.51240
ePu(1,1) % European put price: $34.07964
aPu(1,1)-ePu(1,1) % Early exercise premium: $4.3276
% See where early exercise was optimal:
earlyC
%ans =
%
% 0 0 0 0
% 0 0 0 0
% 0 0 0 0
% 0 0 0 0
%
% ... which shows that early Call exercise is never optimal
earlyP
%ans =
%
% 0 0 0 0
% 1 0 0 0
% 1 1 0 0
% 0 0 0 0
% ... which shows that early Put exercise was optimal at (1,0), (2,0),
% and (2,1) as claimed.
Exercise 7.12. Create a spreadsheet.
Again, modify the example program "americanCallPut.m" to keep track of
the nodes where early exercise is optimal:
function [Call, Put, EarlyC, EarlyP] = americanCallPut72(T, S, K, r, sigma, N)
% Octave/Matlab function to price American Call and Put options using
% the Cox-Ross-Rubinstein (CRR) binomial pricing model.
%
% Inputs:
% T = expiration time
% S = stock price
% K = strike price
% r = risk-free yield
% sigma = volatility; must be >0
% N = height of the binomial tree
% Output:
% Call = price of the call option at all gridpoints (n,j).
% Put = price of the put option at all gridpoints (n,j).
% EarlyC = 1 if early Call exercise is optimal, else 0
% EarlyP = 1 if early Put exercise is optimal, else 0
%
% Example 7.2, p.98 of the text, using Example 5.1 parameters:
% T=1
% S=$100
% K=$100 (at the money call and put),
% r=0.1 ( 10% risk-free interest)
% sigma = 0.15 (15% volatility)
% N=10
% [aCa, aPu, earlyC, earlyP] = americanCallPut72(1,100,100, 0.10,0.15, 10)
%
% M.V.Wickerhauser, 2019-10-20
%
deltaT = T/N; % small time increment
R = exp(r*deltaT); % risk-free return over time deltaT
u = exp(sigma * sqrt(deltaT)); % up factor, must be >1
d = 1/u; % down factor, must be <1
p0 = (R - d) / (u - d); % risk-neutral up probability
p1 = 1 - p0; % risk-neutral down probability
% Initial output Call and Put matrices of all zeros:
Call = zeros(N+1,N+1);
Put = zeros(N+1,N+1);
EarlyC = zeros(N+1,N+1); % Early Call exercise optimal?
EarlyP = zeros(N+1,N+1); % Early Pur exercise optimal?
% Set the terminal values at time N, gridpoints (N,0),...,(N,N):
for j = 0:N
Call(N+1,j+1) = max( S * u^j * d^(N-j) - K, 0 );
Put(N+1,j+1) = max( K - S * u^j * d^(N-j), 0 );
end
% Use backward induction to price earlier grid values:
for n = (N-1):(-1):0 % grid times n={N-1, N-2, ..., -1, 0}
for j = 0:n % states j={0,1,...,n} at time n
% Binomial pricing model value
binvalCall = (p0*Call(n+2,j+2) + p1*Call(n+2,j+1))/R;
binvalPut = (p0*Put(n+2,j+2) + p1*Put(n+2,j+1))/R;
% Exercise value, American style options
exerciseCall = S*u^j*d^(n-j) - K;
exercisePut = K - S*u^j*d^(n-j);
% Price at this node is the larger:
Call(n+1,j+1) = max(binvalCall, exerciseCall);
Put(n+1,j+1) = max(binvalPut, exercisePut);
% Record 1 if early exercise is chosen, else 0
EarlyC(n+1,j+1) = exerciseCall>binvalCall;
EarlyP(n+1,j+1) = exercisePut>binvalPut;
end
end
return; % Prices in matrices Call and Put are now fully defined.
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Run this with the parameters from Example 7.2:
[aCa, aPu, earlyC, earlyP] = americanCallPut72(1,100,100, 0.10,0.15, 10)
aCa(1,1) % Call option price: 11.50713
aPu(1,1) % Put option price: 3.07621
% From the all-zero array earlyC we see that it is never optimal to
% exercise the Call option early.
% From the nonzeros in the array earlyP we see that it optimal to
% exercise the Put option early at (2,0), (3,0), (4,0), (4,1), (5,0),
% (5,1), (6,0), (6,1), (6,2), (7,0), (7,1), (7,2), (8,0), (8,1),
% (8,2), (8,3), (9,0), (9,1), (9,2), (9,3), (9,4).
% The highest-state early-exercise-optimal nodes at each time form the
% early exercise frontier (2,0), (3,0), (4,1), (5,1), (6,2), (7,2),
% (8,3), and (9,4).
earlyP
% 0 0 0 0 0 0 0 0 0 0 0
% 0 0 0 0 0 0 0 0 0 0 0
% 1 0 0 0 0 0 0 0 0 0 0
% 1 0 0 0 0 0 0 0 0 0 0
% 1 1 0 0 0 0 0 0 0 0 0
% 1 1 0 0 0 0 0 0 0 0 0
% 1 1 1 0 0 0 0 0 0 0 0
% 1 1 1 0 0 0 0 0 0 0 0
% 1 1 1 1 0 0 0 0 0 0 0
% 1 1 1 1 1 0 0 0 0 0 0
% 0 0 0 0 0 0 0 0 0 0 0
Exercise 7.13.
(a) To prove that
V(n,j) >= V(n+1,j)+ S(n+1,j)-S(n,j),
follow the hint and use proof by induction.
BASE CASE: Verify for n=N-1.
Start with the exercise value at expiry
V(N,j) = plus(K-S(N,j))
so
V(N,j)+S(N,j)-S(N-1,j) =
= { K-S(N-1,j), if K>S(N,j),
or
{ S(N,j)-S(N-1,j), otherwise.
Now S(N,j)=d*S(N-1,j) with downfactor d<1, and S(N-1,j)>0, so
S(N,j)-S(N-1,j) = (d-1)*S(N-1,j) < 0.
Also
K-S(N-1,j) =< plus(K-S(N-1,j)) % X =< plus(X) always
and
plus(K-S(N-1,j)) >= 0 > S(N,j)-S(N-1,j).
Thus in either case we have
V(N,j)+S(N,j)-S(N-1,j) =< plus(K-S(N-1,j))
Finally, note that
plus(K-S(N-1,j)) =< V(N-1,j)
since V(n,j) is worth at least the exercise value at (n,j).
Conclude that
V(N,j)+S(N,j)-S(N-1,j) =< V(N-1,j).
INDUCTIVE STEP: If true for n, then true for n-1.
First check the binomial value:
V(n-1,j) >= binomialValue(n-1,j)
= [p*V(n,j+1) + (1-p)*V(n,j)]/R % binomial value with p=pi(n-1,j)
>= [p*(V(n+1,j+1)+S(n+1,j+1)-S(n,j+1))
+ (1-p)*(V(n+1,j)+S(n+1,j)-S(n,j))]/R % inductive hypothesis
= [p*(V(n+1,j+1)+(1-p)*(V(n+1,j)]/R % rearrange the terms...
+ [p*S(n+1,j+1)+(1-p)*S(n+1,j)]/R % ...this part is S(n,j)
- [p*S(n,j+1)+(1-p)*S(n,j)]/R % ...and this is S(n-1,j)
= [p*(V(n+1,j+1)+(1-p)*(V(n+1,j)]/R + S(n,j)-S(n-1,j)
= binomialValue(n,j) + S(n,j)-S(n-1,j).
Then check the exercise value:
V(n-1,j) >= exerciseValue(n-1,j)
= plus[K - S(n-1,j)] % exercise value at (n-1,j)
= plus[K - S(n,j)+S(n,j) - S(n-1,j)] % add then subtract same
>= plus[K - S(n,j)] + S(n,j)-S(n-1,j) % since S(n,j)-S(n-1,j)<0
= exerciseValue(n,j) + S(n,j)-S(n-1,j)
Conclude that
V(n-1,j) >= max{binomialValue(n,j),exerciseValue(n,j)} + S(n,j)-S(n-1,j)
= V(n,j) + S(n,j)-S(n-1,j)
which is the formula (at n-1) that we wished to prove.
(b) To prove that if k>j, then
V(n,k) >= V(n,j)+S(n,j)-S(n,k),
follow the hint and use proof by induction.
First note that since u>1>d>0, then k S0*u^j*d^(n-k) > S0*u^j*d^(n-j) = S(n,j).
so S(n,k)>S(n,j) and thus S(n,j)-S(n,k)<0 for every n.
BASE CASE: Verify for n=N.
Suppose k= plus[K- S(N,j)] + S(N,j)-S(N,k) % since S(N,j)-S(N,k)<0
= V(N,j) + S(N,j)-S(N,k) % definition of V(N,j)
Conclude that the formula holds for n=N.
INDUCTIVE STEP: If true for n, then true for n-1.
First check the binomial value:
V(n-1,k) >= binomialValue(n-1,k)
= [p*V(n,k+1) + (1-p)*V(n,k)]/R % binomial value with p=pi(n-1,k)
>= [p*(V(n,j+1)+(1-p)*(V(n,j)]/R % inductive hypothesis
= binomialValue(n-1,j) % backward pricing formula
>= binomialValue(n-1,j) + S(n-1,j)-S(n-1,k). % S(n-1,j)-S(n-1,k)<0
Then check the exercise value:
V(n-1,k) >= exerciseValue(n-1,k)
= plus[K - S(n-1,k)] % exercise value at (n-1,k)
= plus[K - S(n-1,j)+S(n-1,j) - S(n-1,k)] % add then subtract same
>= plus[K - S(n-1,j)] + S(n-1,j)-S(n-1,k) % S(n-1,j)-S(n-1,k)<0
= exerciseValue(n-1,j) + S(n-1,j)-S(n-1,k)
Conclude that
V(n-1,k) >= max{binomialValue(n-1,j),exerciseValue(n-1,j)} + S(n-1,j)-S(n-1,k)
= V(n-1,j) + S(n-1,j)-S(n-1,k)
which is the formula (at n-1) that we wished to prove.
(c) Early exercise of the Put is not optimal at (n+1,j) if and only if
V(n+1,j) >= plus(K - S(n+1,j)) >= K - S(n+1,j),
or equivalently if and only if
V(n+1,j)+S(n+1,j) >= K.
Apply part a to compute
V(n,j)+S(n,j) >= V(n+1,j)+S(n+1,j) >= K.
Conclude that Early exercise of the Put is not optimal at (n,j).
(d) Early exercise of the Put is not optimal at (n,j) if and only if
V(n,j) >= plus(K - S(n,j)) >= K - S(n,j),
or equivalently if and only if
V(n,j)+S(n,j) >= K.
Apply part b to compute that if k>j, then
V(n,k)+S(n,k) >= V(n,j)+S(n,j) >= K.
Conclude that Early exercise of the Put is not optimal at (n,k) for k>j.
(e) This is the contrapositive of part d, hence it is true since d is true.
It may also be proved directly: If early exercise ocurs at (n,j), then
V(n,j) = plus(K - S(n,j)) = K - S(n,j) >0,
or equivalently
V(n,j)+S(n,j) = K.
Apply part b (with j and k interchanged) to compute that if k= V(n,k)+S(n,k)
so K-S(n,k)>= V(n,k). Since V(n,k) is nonnegative, this implies that
K-S(n,k)>=0,
so
V(n,k) =< K-S(n,k) = plus(K-S(n,k)),
from which we must conclude that V(n,k) = plus(K-S(n,k)) since V(n,k)
cannot be less than this early exercise Put price at (n,k). Hence
early exercise is optimal at (n,k) as well for all k0,
or equivalently
V(n,j)+S(n,j) = K.
Apply part a to compute that
K = V(n,j)+S(n,j) >= V(n+1,j)+S(n+1,j)
so K-S(n+1,j)>= V(n+1,j). Since V(n+1,j) is nonnegative, this implies that
K-S(n+1,j)>=0,
so
V(n+1,j) =< K-S(n+1,j) = plus(K-S(n+1,j)),
from which we must conclude that V(n+1,j) = plus(K-S(n+1,j)) since V(n+1,j)
cannot be less than this early exercise Put price at (n+1,j). Hence
early exercise is optimal at (n+1,j) as well as at (n,j).
Exercise 7.14. Create a spreadsheet.
To verify Example 7.4, use the following Octave code:
function Call = upandoutCall(T, S, K, B, r, sigma, N)
% Octave/Matlab function to price up and out barrier Call options using
% the Cox-Ross-Rubinstein (CRR) binomial pricing model.
% Inspired by the WikiPedia example code at
% https://en.wikipedia.org/wiki/Binomial_options_pricing_model
%
% Inputs:
% T = expiration time
% S = stock price
% K = strike price
% B = barrier price
% r = risk-free yield
% sigma = volatility; must be >0
% N = height of the binomial tree
% Output:
% Call = price of the option at all gridpoints (n,j).
%
% NOTE: since Octave array indices start at 1, the option
% price at time 0 is stored at Call(1,1).
%
% Example:
% T=1 year
% S=$100
% K=$80
% B=$120
% r=0.05
% sigma = 0.20
% N=4
% uoCa = upandoutCall(1, 100, 80, 120, 0.05, 0.20, 4)
%
% M.V.Wickerhauser, 2019-10-22
%
deltaT = T/N; % small time increment
R = exp(r*deltaT); % risk-free return over time deltaT
up = exp(sigma * sqrt(deltaT)); % up factor, must be >1
down = 1/up; % down factor, must be <1
p0 = (R - down) / (up - down); % risk-neutral up probability
p1 = 1 - p0; % risk-neutral down probability
% Initial output matrix of all zeros:
Call = zeros(N+1,N+1);
% Set the terminal values at time T, gridpoints (N,0),...,(N,N):
for j = 0:N
SNj = S * up^j * down^(N-j); % asset price at (N,j)
if(SNj < B) % then barrier is not exceeded
Call(N+1,j+1) = max( SNj - K, 0 );
else % barrier exceeded, so option vanishes
Call(N+1,j+1) = 0;
end
end
% Use backward induction to price earlier grid values:
for n = (N-1):(-1):0 % grid times n={N-1, N-2, ..., -1, 0}
for j = 0:n % states j={0,1,...,n} at time n
% Test the barrier
Snj = S * up^j * down^(n-j); % asset price at (n,j)
if(Snj < B) % then barrier is not exceeded
% ...so use the binomial pricing model value
Call(n+1,j+1) = (p0*Call(n+2,j+2) + p1*Call(n+2,j+1))/R;
else % barrier exceeded, so option vanishes
Call(n+1,j+1) = 0;
end
end
end
return; % Prices in matrix Call are now fully defined.
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Run it with the Example 7.4 parameters as follows:
uoCa = upandoutCall(1, 100, 80, 120, 0.05, 0.20, 4)
%uoCa =
% 6.25583 0.00000 0.00000 0.00000 0.00000
% 8.34760 4.60448 0.00000 0.00000 0.00000
% 6.55012 10.08758 0.00000 0.00000 0.00000
% 0.99484 11.47752 9.12900 0.00000 0.00000
% 0.00000 1.87308 20.00000 0.00000 0.00000
uoCa(1,1) % price of the option at time 0: $6.26
Exercise 7.15. Create a spreadsheet.
To verify Example 7.5, use the following Octave code:
function [Exotic, Vanilla] = upandinCall(T, S, K, B, r, sigma, N)
% Octave/Matlab function to price up and in barrier Call options using
% the Cox-Ross-Rubinstein (CRR) binomial pricing model.
% Inspired by the WikiPedia example code at
% https://en.wikipedia.org/wiki/Binomial_options_pricing_model
%
% Inputs:
% T = expiration time
% S = stock price
% K = strike price
% B = barrier price
% r = risk-free yield
% sigma = volatility; must be >0
% N = height of the binomial tree
% Output:
% Exotic = price of the option at all gridpoints (n,j).
% Vanilla = price of the vanilla European Call option.
%
% NOTE: since Octave array indices start at 1, the option
% price at time 0 is stored at index (1,1).
%
% Example:
% T=1 year
% S=$100
% K=$80
% B=$120
% r=0.05
% sigma = 0.20
% N=4
% [uiCa, eCa] = upandinCall(1, 100, 80, 120, 0.05, 0.20, 4)
%
% M.V.Wickerhauser, 2019-10-22
%
deltaT = T/N; % small time increment
R = exp(r*deltaT); % risk-free return over time deltaT
up = exp(sigma * sqrt(deltaT)); % up factor, must be >1
down = 1/up; % down factor, must be <1
p0 = (R - down) / (up - down); % risk-neutral up probability
p1 = 1 - p0; % risk-neutral down probability
% Initial output matrix of all zeros:
Exotic = zeros(N+1,N+1);
Vanilla = zeros(N+1,N+1);
% Set the terminal values at time T, gridpoints (N,0),...,(N,N):
for j = 0:N
SNj = S * up^j * down^(N-j); % asset price at (N,j)
Vanilla(N+1,j+1) = max( SNj - K, 0 ); % plain European Call
if(SNj < B) % barrier is not exceeded, so no Exotic
Exotic(N+1,j+1) = 0;
else % barrier exceeded, so Exotic is created
Exotic(N+1,j+1) = Vanilla(N+1,j+1);
end
end
% Use backward induction to price earlier grid values:
for n = (N-1):(-1):0 % grid times n={N-1, N-2, ..., -1, 0}
for j = 0:n % states j={0,1,...,n} at time n
% Price the Vanilla option in all cases:
Vanilla(n+1,j+1) = (p0*Vanilla(n+2,j+2)+p1*Vanilla(n+2,j+1))/R;
% Test the barrier
Snj = S * up^j * down^(n-j); % asset price at (n,j)
if(Snj < B) % barrier is not exceeded, so use Exotic
% ...by the backward binomial pricing model
Exotic(n+1,j+1) = (p0*Exotic(n+2,j+2) + p1*Exotic(n+2,j+1))/R;
else % barrier exceeded, so use Vanilla
Exotic(n+1,j+1) = Vanilla(n+1,j+1);
end
end
end
return; % Prices in matrices Exotic and Vanilla are now fully defined.
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Run it with the Example 7.4 parameters as follows:
[uiCa, eCa] = upandinCall(1, 100, 80, 120, 0.05, 0.20, 4)
%uiCa =
% 18.20874 0.00000 0.00000 0.00000 0.00000
% 6.31385 28.85706 0.00000 0.00000 0.00000
% 0.00000 11.88763 44.11548 0.00000 0.00000
% 0.00000 0.00000 22.38186 55.97966 0.00000
% 0.00000 0.00000 0.00000 42.14028 69.18247
%eCa =
% 24.46457 0.00000 0.00000 0.00000 0.00000
% 14.66144 33.46154 0.00000 0.00000 0.00000
% 6.55012 21.97521 44.11548 0.00000 0.00000
% 0.99484 11.47752 31.51087 55.97966 0.00000
% 0.00000 1.87308 20.00000 42.14028 69.18247
Notice that the Exotic Up-and-In Call is $18.21 while the Vanilla
European Call is $24.46. However, these are not the results mentioned
in Example 7.5, pp.101-102. Those are obtained with a strike price of
K=$95 (mentioned on p.101) but all else the same, as follows:
[uiCa, eCa] = upandinCall(1, 100, 95, 120, 0.05, 0.20, 4)
%uiCa =
% 12.03008 0.00000 0.00000 0.00000 0.00000
% 4.06641 19.15540 0.00000 0.00000 0.00000
% 0.00000 7.65618 29.48583 0.00000 0.00000
% 0.00000 0.00000 14.41495 41.16599 0.00000
% 0.00000 0.00000 0.00000 27.14028 54.18247
%eCa =
% 13.49942 0.00000 0.00000 0.00000 0.00000
% 5.99785 20.26199 0.00000 0.00000 0.00000
% 1.41048 10.08051 29.48583 0.00000 0.00000
% 0.00000 2.65564 16.69720 41.16599 0.00000
% 0.00000 0.00000 5.00000 27.14028 54.18247
Here the Exotic Up-and-In Call is $12.03 while the Vanilla
European Call is $13.50, more or less as claimed.
Exercise 7.16. Create a spreadsheet.
In Section 7.3, example 7.10, the Up-And-Out Call is modified by
including a refund of the premium if the option knocks out by hitting
the barrier. This may be named "up and out call with rebate."
Alternatively, an "up and out call with partial rebate" refunds alpha
of the premium, 00
% N = height of the binomial tree
% Output:
% Call = price of the option at all gridpoints (n,j).
%
% NOTE: since Octave array indices start at 1, the option
% price at time 0 is stored at Call(1,1).
%
% Example:
% T=1 year
% S=$100
% K=$80
% B=$120
% refund=$17
% r=0.05
% sigma = 0.20
% N=4
% uorCa = upandoutrebateCall(1, 100, 80, 120, 17, 0.05, 0.20, 4)
%
% M.V.Wickerhauser, 2019-10-30
%
deltaT = T/N; % small time increment
R = exp(r*deltaT); % risk-free return over time deltaT
up = exp(sigma * sqrt(deltaT)); % up factor, must be >1
down = 1/up; % down factor, must be <1
p0 = (R - down) / (up - down); % risk-neutral up probability
p1 = 1 - p0; % risk-neutral down probability
% Initial output matrix of all zeros:
Call = zeros(N+1,N+1);
% Set the terminal values at time T, gridpoints (N,0),...,(N,N):
for j = 0:N
SNj = S * up^j * down^(N-j); % asset price at (N,j)
if(SNj < B) % then barrier is not exceeded
Call(N+1,j+1) = max( SNj - K, 0 );
else % barrier exceeded, so option vanishes
Call(N+1,j+1) = refund; % ...but there is a refund
end
end
% Use backward induction to price earlier grid values:
for n = (N-1):(-1):0 % grid times n={N-1, N-2, ..., -1, 0}
for j = 0:n % states j={0,1,...,n} at time n
% Test the barrier
Snj = S * up^j * down^(n-j); % asset price at (n,j)
if(Snj < B) % then barrier is not exceeded
% ...so use the binomial pricing model value
Call(n+1,j+1) = (p0*Call(n+2,j+2) + p1*Call(n+2,j+1))/R;
else % barrier exceeded, so option vanishes
Call(n+1,j+1) = refund; % ...but there is a refund
end
end
end
return; % Prices in matrix Call are now fully defined.
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Now iterate, replacing "refund" with the price of the option, until
the price of the option with "refund" equals "refund":
X=[0]; % initial guess: zero price, zero refund
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 6.2558
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 8.8762
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 9.9739
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 10.434
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 10.626
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 10.707
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 10.741
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 10.755
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 10.761
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 10.763
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 10.764
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 10.765
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 10.765
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 10.765
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 10.765
X=upandoutrebateCall(1,100,80,120,X(1,1),0.05,0.20,4); X(1,1) % 10.765
...and we have convegence to the value $10.765 for the up and out call
option with rebate.
If we iterate using alpha*X(1,1) as the refund, we will converge to
the price of the up and out call option with partial rebate:
alpha=0.5; % partial rebate, 50 percent of the price
X=[0]; % initial guess is zero price, zero refund
X=upandoutrebateCall(1,100,80,120,alpha*X(1,1),0.05,0.20,4); X(1,1) % 6.2558
X=upandoutrebateCall(1,100,80,120,alpha*X(1,1),0.05,0.20,4); X(1,1) % 7.5660
X=upandoutrebateCall(1,100,80,120,alpha*X(1,1),0.05,0.20,4); X(1,1) % 7.8404
X=upandoutrebateCall(1,100,80,120,alpha*X(1,1),0.05,0.20,4); X(1,1) % 7.8979
X=upandoutrebateCall(1,100,80,120,alpha*X(1,1),0.05,0.20,4); X(1,1) % 7.9100
X=upandoutrebateCall(1,100,80,120,alpha*X(1,1),0.05,0.20,4); X(1,1) % 7.9125
X=upandoutrebateCall(1,100,80,120,alpha*X(1,1),0.05,0.20,4); X(1,1) % 7.9130
X=upandoutrebateCall(1,100,80,120,alpha*X(1,1),0.05,0.20,4); X(1,1) % 7.9131
X=upandoutrebateCall(1,100,80,120,alpha*X(1,1),0.05,0.20,4); X(1,1) % 7.9131
X=upandoutrebateCall(1,100,80,120,alpha*X(1,1),0.05,0.20,4); X(1,1) % 7.9131
X=upandoutrebateCall(1,100,80,120,alpha*X(1,1),0.05,0.20,4); X(1,1) % 7.9131
...and it appears that with a half-rebate the up and out call option
with partial rebate should cost $7.9131.
Exercise 7.17. Create a spreadsheet.
The following Octave function prices the booster option:
function Boo = boosterOption(T, S, L, H, r, sigma, N)
% Octave/Matlab function to price booster options using
% the Cox-Ross-Rubinstein (CRR) binomial pricing model.
% Inspired by the WikiPedia example code at
% https://en.wikipedia.org/wiki/Binomial_options_pricing_model
%
% Inputs:
% T = expiration time
% S = stock price
% L = low barrier price
% H = high barrier price
% r = risk-free yield
% sigma = volatility; must be >0
% N = height of the binomial tree
% Output:
% Boo = price of the option at all gridpoints (n,j).
%
% NOTE: since Octave array indices start at 1, the option
% price at time 0 is stored at Boo(1,1).
%
% Example:
% T=1 year
% S=$100
% L=$85
% B=$115
% r=0.05
% sigma = 0.20
% N=4
% boo = boosterOption(1, 100, 85, 115, 0.05, 0.20, 4)
%
% M.V.Wickerhauser, 2019-10-22
%
deltaT = T/N; % small time increment
R = exp(r*deltaT); % risk-free return over time deltaT
up = exp(sigma * sqrt(deltaT)); % up factor, must be >1
down = 1/up; % down factor, must be <1
p0 = (R - down) / (up - down); % risk-neutral up probability
p1 = 1 - p0; % risk-neutral down probability
% Initial output matrix of all zeros:
Boo = zeros(N+1,N+1);
% Set the terminal values at time T, gridpoints (N,0),...,(N,N):
for j = 0:N
SNj = S * up^j * down^(N-j); % asset price at (N,j)
if(SNj < H) % less than high barrier
if(L < SNj )
Boo(N+1,j+1) = N; % since N=time spent between barriers
end
end
end
% Set the barrier values at times 0< n < N, gridpoints (n,0),...,(n,n):
for n=1:N-1 % n=0 case is unneeded
for j=0:n
Snj = S * up^j * down^(n-j); % asset price at (n,j)
if(Snj >= H || Snj <= L )
Boo(n+1,j+1) = n;
end
end
end
% Use backward induction to price any zero grid values:
for n = (N-1):(-1):0 % grid times n={N-1, N-2, ..., -1, 0}
for j = 0:n % states j={0,1,...,n} at time n
if (Boo(n+1,j+1)==0) % then fill this by backward induction
Boo(n+1,j+1) = (p0*Boo(n+2,j+2) + p1*Boo(n+2,j+1))/R;
end
end
end
return; % Prices in matrix Boo are now fully defined.
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Run with the suggested parameters with sigma = 0.15,0.20,0.25,0.30:
b15=boosterOption(1, 100, 85, 115, 0.05, 0.15, 4)
b20=boosterOption(1, 100, 85, 115, 0.05, 0.20, 4)
b25=boosterOption(1, 100, 85, 115, 0.05, 0.25, 4)
b30=boosterOption(1, 100, 85, 115, 0.05, 0.30, 4)
%b15 =
% 2.54046 0.00000 0.00000 0.00000 0.00000
% 2.98239 2.25682 0.00000 0.00000 0.00000
% 3.49302 2.65571 2.00000 0.00000 0.00000
% 3.00000 3.95031 1.71824 3.00000 0.00000
% 0.00000 4.00000 4.00000 0.00000 0.00000
b20 =
% 1.92127 0.00000 0.00000 0.00000 0.00000
% 1.94300 1.94753 0.00000 0.00000 0.00000
% 2.00000 1.93947 2.00000 0.00000 0.00000
% 3.00000 2.12451 1.82580 3.00000 0.00000
% 0.00000 0.00000 4.00000 0.00000 0.00000
%b25 =
% 1.92521 0.00000 0.00000 0.00000 0.00000
% 1.94841 1.95036 0.00000 0.00000 0.00000
% 2.00000 1.94781 2.00000 0.00000 0.00000
% 3.00000 2.05011 1.90020 3.00000 0.00000
% 0.00000 0.00000 4.00000 0.00000 0.00000
%b30 =
% 1.57938 0.00000 0.00000 0.00000 0.00000
% 2.20899 1.00000 0.00000 0.00000 0.00000
% 2.00000 2.46947 2.00000 0.00000 0.00000
% 3.00000 1.99231 3.00000 3.00000 0.00000
% 0.00000 0.00000 4.00000 0.00000 0.00000
COMMENT: The price of the booster option seems to decrease as
volatility increases.
EXPLANATION: With increasing volatility, the underlying asset price
may be expected to spend less time inside the interval [L,H] and
thus will have lower expected payoff.
APPLICATION: A booster option may be used to hedge a combination of an
American Call with strike H and an American Put with strike L. It
will pay off in case neither the Call nor the Put yield a profit.
Exercise 7.19. Create a spreadsheet.
Modify the up-and-out option Octave code to impose time limits (only
two small changes are needed):
function Call = partialUpandoutCall(T, S, K, B, r, sigma, N, N0)
% Octave/Matlab function to price up and out barrier Call options using
% the Cox-Ross-Rubinstein (CRR) binomial pricing model.
% Inspired by the WikiPedia example code at
% https://en.wikipedia.org/wiki/Binomial_options_pricing_model
%
% Inputs:
% T = expiration time
% S = stock price
% K = strike price
% B = barrier price
% r = risk-free yield
% sigma = volatility; must be >0
% N = height of the binomial tree
% N0 = first barrier test time; must satisfy 0 < N0 < N
% Output:
% Call = price of the option at all gridpoints (n,j).
%
% NOTE: since Octave array indices start at 1, the option
% price at time 0 is stored at Call(1,1).
%
% Example:
% T=1 year
% S=$100
% K=$80
% B=$120
% r=0.05
% sigma = 0.20
% N=4
% N0=3
% puoCa = partialUpandoutCall(1, 100, 80, 120, 0.05, 0.20, 4, 3)
%
% M.V.Wickerhauser, 2019-10-22
%
deltaT = T/N; % small time increment
R = exp(r*deltaT); % risk-free return over time deltaT
up = exp(sigma * sqrt(deltaT)); % up factor, must be >1
down = 1/up; % down factor, must be <1
p0 = (R - down) / (up - down); % risk-neutral up probability
p1 = 1 - p0; % risk-neutral down probability
% Initial output matrix of all zeros:
Call = zeros(N+1,N+1);
% Set the terminal values at time T, gridpoints (N,0),...,(N,N):
for j = 0:N
SNj = S * up^j * down^(N-j); % asset price at (N,j)
if(SNj < B) % then barrier is not exceeded
Call(N+1,j+1) = max( SNj - K, 0 );
else % barrier exceeded, so option vanishes
Call(N+1,j+1) = 0;
end
end
% Use backward induction to price earlier grid values:
for n = (N-1):(-1):0 % grid times n={N-1, N-2, ..., -1, 0}
for j = 0:n % states j={0,1,...,n} at time n
% Test the barrier
Snj = S * up^j * down^(n-j); % asset price at (n,j)
if(n < N0 || Snj < B) % too early, or barrier is not exceeded
% ...so use the binomial pricing model value
Call(n+1,j+1) = (p0*Call(n+2,j+2) + p1*Call(n+2,j+1))/R;
else % time after N0 and barrier exceeded, so option vanishes
Call(n+1,j+1) = 0;
end
end
end
return; % Prices in matrix Call are now fully defined.
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Run this with the Example 7.4 parameters and compare the result to the
full up-and-out call pricing:
puoCa = partialUpandoutCall(1, 100, 80, 120, 0.05, 0.20, 4, 3)
%puoCa =
% 7.43131 0.00000 0.00000 0.00000 0.00000
% 8.34760 6.81765 0.00000 0.00000 0.00000
% 6.55012 10.08758 4.16694 0.00000 0.00000
% 0.99484 11.47752 9.12900 0.00000 0.00000
% 0.00000 1.87308 20.00000 0.00000 0.00000
uoCa = upandoutCall(1, 100, 80, 120, 0.05, 0.20, 4)
%uoCa =
% 6.25583 0.00000 0.00000 0.00000 0.00000
% 8.34760 4.60448 0.00000 0.00000 0.00000
% 6.55012 10.08758 0.00000 0.00000 0.00000
% 0.99484 11.47752 9.12900 0.00000 0.00000
% 0.00000 1.87308 20.00000 0.00000 0.00000
The partial up-and-out call is more expensive, $7.43 versus $6.26 for
the earlier up-and-out call which is easier to kill.
Do the following exercises from the textbook Chapter 8.6, p.118:
An Octave program to compute exotic path-dependent options with
parameters from Example 8.1 is on the class website at aro.txt:
% Average Rate Options (ARO)
%
% Multiperiod binomial model to price an average rate option
%
%
% M.V.Wickerhauser
% 2019-10-17
% 2019-10-18 - fixed the FSPut and FSCall mistake.
% Example 8.1, p.112 of the textbook
% Given
S0=100; % Initial stock price
r=0.05; % risk-free interest rate over T
sigma=0.20; % volatility
T=1; % unit period
N=4; % number of sub periods in the tree
% Computed
K=S0; % Strike price for at-the-money options
dt=T/N; % sub period
R=exp(r*dt); % risk-free return over dt
u=exp(sigma*sqrt(dt)); % up factor
d=1/u; % down factor
pi_up= (R-d)/(u-d); % risk-neutral up probability
pi_down= 1-pi_up; % risk-neutral down probability
%%% Non recombining trees for the binomial pricing model
% Asset prices
S=0*(1:(2*2^N-1)); % non recombining tree of stock prices
S(1)=S0; % spot price at time n=0, state j=0
for n=0:(N-1) % recursive current times up to N-1
for j=2^n:(2*2^n -1) % state indexes at the current time n
S(2*j) = S(j)*d; % next down state at time n+1
S(2*j+1) = S(j)*u; % next up state at time n+1
end
end
% ...now S(1),...,S(2*2^N-1) contain the non recombining asset tree
% Path-dependent averages
% ...compute these recursively as on p.111
A=0*(1:(2*2^N-1)); % non recombining tree of averages
% Compute partial sums recursively along each path:
A(1)=S(1); % initial summand at time n=0, state j=0
for n=0:(N-1) % recursive current times up to N-1
for j=2^n:(2*2^n -1) % state indexes at the current time n
A(2*j) = A(j)+S(2*j); % next partial sum at time n+1
A(2*j+1) = A(j)+S(2*j+1); % next partial sum at time n+1
end
end
% Divide the partial sums by the number n+1 of summands to get averages:
for n=1:N % recursive current times up to N
for j=2^n:(2*2^n -1) % state indexes at the current time n
A(j) = A(j)/(n+1); % n+1 summands for these indexes
end
end
% ...now A(1),...,A(2*2^N-1) contain the non recombining averages tree
% Path-dependent maximums and minimums
% ...compute these recursively as on p.113
MaxS=0*(1:(2*2^N-1)); % non recombining tree of maximums
MinS=0*(1:(2*2^N-1)); % non recombining tree of minimums
% Compute maximums and minimums recursively along each path:
MaxS(1)=S(1); % initial maximum at time n=0, state j=0
MinS(1)=S(1); % initial minimum at time n=0, state j=0
for n=0:(N-1) % recursive current times up to N-1
for j=2^n:(2*2^n -1) % state indexes at the current time n
MaxS(2*j) = max(MaxS(j),S(2*j)); % next maximums at time n+1
MaxS(2*j+1) = max(MaxS(j),S(2*j+1));
MinS(2*j) = min(MinS(j),S(2*j)); % next minimums at time n+1
MinS(2*j+1) = min(MinS(j),S(2*j+1));
end
end
% ...now MaxS() and MinS() contain the non recombining max and min tree
%%% Various path-dependent options
% Asian options = average rate options. Compute these by backwardization
AsianCall=0*(1:(2*2^N-1)); % non recombining tree of option prices
AsianPut =0*(1:(2*2^N-1)); % non recombining tree of option prices
% Initialize with the payoffs at expiry:
for j=2^N:(2*2^N -1) % state indexes at expiry n=N
AsianCall(j) = max(0,A(j)-K); % call payoff
AsianPut(j) = max(0,K-A(j)); % put payoff
end
% Backward induction
for n=(N-1):-1:0 % recursive previous times down to 0
for j=2^n:(2*2^n -1) % state indexes at the previous time n
AsianCall(j) = (pi_up*AsianCall(2*j+1) + pi_down*AsianCall(2*j))/R;
AsianPut(j) = (pi_up*AsianPut(2*j+1) + pi_down*AsianPut(2*j))/R;
end
end
% ...now AsianCall(1) and AsianPut(1) contain the option prices at n=0.
AsianCall(1)
AsianPut(1)
% Floating strike options. Compute these by backwardization
FSCall=0*(1:(2*2^N-1)); % non recombining tree of option prices
FSPut =0*(1:(2*2^N-1)); % non recombining tree of option prices
% Initialize with the payoffs at expiry:
for j=2^N:(2*2^N -1) % state indexes at expiry n=N
FSCall(j) = max(0,S(j)-A(j)); % call payoff
FSPut(j) = max(0,A(j)-S(j)); % put payoff
end
% Backward induction
for n=(N-1):-1:0 % recursive previous times down to 0
for j=2^n:(2*2^n -1) % state indexes at the previous time n
FSCall(j) = (pi_up*FSCall(2*j+1) + pi_down*FSCall(2*j))/R;
FSPut(j) = (pi_up*FSPut(2*j+1) + pi_down*FSPut(2*j))/R;
end
end
% ...now FSCall(1) and FSPut(1) contain the option prices at n=0.
FSCall(1)
FSPut(1)
% Lookback options
LBCall=0*(1:(2*2^N-1)); % non recombining tree of option prices
LBPut =0*(1:(2*2^N-1)); % non recombining tree of option prices
% Initialize with the payoffs at expiry:
for j=2^N:(2*2^N -1) % state indexes at expiry n=N
LBCall(j) = S(j)-MinS(j); % call payoff
LBPut(j) = MaxS(j)-S(j); % put payoff
end
% Backward induction
for n=(N-1):-1:0 % recursive previous times down to 0
for j=2^n:(2*2^n -1) % state indexes at the previous time n
LBCall(j) = (pi_up*LBCall(2*j+1) + pi_down*LBCall(2*j))/R;
LBPut(j) = (pi_up*LBPut(2*j+1) + pi_down*LBPut(2*j))/R;
end
end
% ...now LBCall(1) and LBPut(1) contain the option prices at n=0.
LBCall(1)
LBPut(1)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Run these commands, when aro.txt is in the current folder for your
Octave session, using:
source("aro.txt")
which produces:
ans = 5.6661 % AsianCall(1)
ans = 3.2428 % AsianPut(1)
ans = 5.7432 % FSCall(1)
ans = 3.2894 % FSPut(1)
ans = 13.758 % LBCall(1)
ans = 9.6589 % LBPut(1)
Exercise 8.5. Create a spreadsheet.
Use the values
ans = 5.6661 % AsianCall(1)
ans = 3.2428 % AsianPut(1)
Compare with the vanilla at-the-money European Call with the same
parameters as the exotic option:
[vCall, vPut] = europeanCallPut(T,S0,S0,r,0,sigma, N);vCall(1,1),vPut(1,1)
This gives
ans = 9.9705 % vCall(1,1)
ans = 5.0935 % vPut(1,1)
This exposes several typographical errors in the text.
Exercise 8.6. Create a spreadsheet.
Use the values
ans = 13.758 % LBCall(1)
ans = 9.6589 % LBPut(1)
These agree with the lookback Put and Call option prices for Examples
8.2 and 8.3 on p.114 of the textbook.
Exercise 8.7. Create a spreadsheet.
NOTE: there is an important typo on p.115 of the textbook. The
formulas for A1 and A4 should be
A1(n+1,j+1) = ((n+1)*A1(n,j)+S(n+1,j))/(n+2); % down average
A4(n+1,j) = ((n+1)*A4(n,j)+S(n+1,j+1))/(n+2); % up average
for j=0,...,n. The book's error is to have an up average for A1 and a
down average for A4. This results in M(down) < A1 in some cases, and
M(up)> A4 in other cases, violating the condition
A1 =< M(up) =< A4 and A1 =< M(down) =< A4
which is needed to find lambda in later steps.
Octave code to implement Example 8.4 on p.115 of the textbook is in
the file "HullWhite.txt" linked under EXAMPLE PROGRAMS at the class
web page. Run it with the parameters of this example by copying the
file into the Octave functions folder and then using the Octave command
source("HullWhite.txt")
It yields
Asian Call option price: $4.11282 = C1(1,1)=C2(1,1)=C3(1,1)=C4(1,1)
Asian Put option price: $2.88545 = P1(1,1)=P2(1,1)=P3(1,1)=P4(1,1)
Note that all four Call trees give the same price at time 0, but these
prices do not agree with the textbook value on p.118. Likewise, all
four Put trees give the same price at time 0.
There is a discrepancy with the call-put parity formula:
C1(1,1)-P1(1,1) = 1.2274
whereas using annual interest rate r=0.05 to get R=1.0050 for one
period dt=T/N=0.1 years gives a risk free return of 1.025 to expiry at
T=0.5 years, and then
S0 - K/(1.025) = 2.4390
Also, the code computes C^2(4,2) = $0.27813, which is the value in
C2(4+1,2+1) in the program output. This does not agree with the
textbook value on p.116.
An Octave function to price the Asian Call option with a
non-recombining tree is in the file "asianCallPut.m" linked under
EXAMPLE PROGRAMS at the class web page. Called with the parameters of
this example, it yields the following:
[aroC, aroP]=asianCallPut(0.5,100,100,0.05,0.2,10)
% aroC = 3.8272
% aroP = 2.5973
aroC-aroP % ans = 1.2299
These prices differ from the Hull and White prices, but the difference
agrees to within one penny.
REMARK: Assuming that the Call-Put parity formula holds, and using
Call-Put=$1.23, we may compute the hypothetical risk-free return to
expiry as
R = K/(S0-[Call-Put]) = $100/($100-$1.23) = 1.0125
for these example values.
Exercise 8.8. Create a spreadsheet.
The ladder option price may be computed using a modified Octave code
for lookback options:
function LOpt = ladderOption(T,S0,K,L,r,sigma,N)
%
% Multiperiod binomial model to price a ladder option
%
%
% M.V.Wickerhauser
% 2019-10-31
%
% Example:
% T=1; unit period
% S0=100; Initial stock price
% K=S0; Strike price, at-the-money
% L= [110,120,140]; Ladder, 3 prices above K, increasing
% r=0.05; risk-free interest rate over T
% sigma=0.20; volatility
% N=4; number of sub periods in the tree
%
% LOpt = ladderOption(T,S0,K,L,r,sigma,N)
dt=T/N; % sub period
R=exp(r*dt); % risk-free return over dt
u=exp(sigma*sqrt(dt)); % up factor
d=1/u; % down factor
pi_up= (R-d)/(u-d); % risk-neutral up probability
pi_down= 1-pi_up; % risk-neutral down probability
%%% Non recombining trees for the binomial pricing model
% Asset prices
S=0*(1:(2*2^N-1)); % non recombining tree of stock prices
S(1)=S0; % spot price at time n=0, state j=0
for n=0:(N-1) % recursive current times up to N-1
for j=2^n:(2*2^n -1) % state indexes at the current time n
S(2*j) = S(j)*d; % next down state at time n+1
S(2*j+1) = S(j)*u; % next up state at time n+1
end
end
% ...now S(1),...,S(2*2^N-1) contain the non recombining asset tree
% Path-dependent maximums
% ...compute these recursively as on p.113
MaxS=0*(1:(2*2^N-1)); % non recombining tree of maximums
% Compute maximums recursively along each path:
MaxS(1)=S(1); % initial maximum at time n=0, state j=0
for n=0:(N-1) % recursive current times up to N-1
for j=2^n:(2*2^n -1) % state indexes at the current time n
MaxS(2*j) = max(MaxS(j),S(2*j)); % next maximums at time n+1
MaxS(2*j+1) = max(MaxS(j),S(2*j+1));
end
end
% ...now MaxS() contains the non recombining maximums tree
%%%% Ladder option
Lad=0*(1:(2*2^N-1)); % non recombining tree of option prices
% Initialize with the payoffs at expiry:
for j=2^N:(2*2^N -1) % state indexes at expiry n=N
k=length(L); % number of ladder levels, must be >1
if(MaxS(j)