Value of a cash flow stream over time¶
Problem¶
Write a function that values a stream of cash flows at any evaluation date, then apply it to a three-year bond paying 5, 5 and 105, valued at \(t = 0, 1, 2, 3\): first with a flat 5% rate, then with a rate for each payment date.
Method¶
The value at time \(t\) of flows \(F_k\) paid at dates \(s_k\) discounts the flows after \(t\) and compounds those before it:
\[
V(t) = \sum_k F_k\,(1 + i_k)^{-(s_k - t)}
\]
The function accepts either one rate (flat curve) or one rate per payment date, and stops with a message if the curve does not match the schedule.
Code¶
clear all, clc
% Point 1
% see F_es46 (local function below)
% Point 2
F = [5, 5, 105]; % parameters of the problem
i = 0.05;
s = [1, 2, 3];
t = [0, 1, 2, 3];
V = F_es46(F, i, s, t)
% output : V = 100.0000
% 105.0000
% 110.2500
% 115.7625
% value of the flow at each evaluation time t
% Point 3
i_tilde = [0.037, 0.042, 0.051];
V_tilde = F_es46(F, i_tilde, s, t)
% Point 4
% save('nomefile.txt', 'V', 'V_tilde', '-ascii');
function [V_tF] = F_es46(F, i, s, t)
% INPUTS: F := flow (cash flow amounts)
% i := vector of interest rates (fixed or variable)
% s := the schedule (payment dates)
% t := times of evaluation
% OUTPUT: cf_vector := vector of discounted cash flows
% Note that:
% - F and s must have the same length!
% - cf_vector and t must have the same length!
% - i could be a scalar (flat) or a vector (floating); in the second instance
% must have the same length of s and F
V_tF = zeros(length(t), 1); % preallocation (one row per evaluation time)
% force F to be arow vector and S to be a column one
F = reshape(F, 1, length(F));
s = reshape(s, length(s), 1);
control = length(i);
if control ~= length(s) && control ~= 1 % only a flat curve (one rate) or a non-flat curve (one rate per date) is accepted
disp('%%%%% The supplied yield curve is not coherent');
return; % this command will abort the function
end
for k = 1:length(t) % for each evaluation time
v = (1 + i)'.^(-(s - t(k))); % discount factor vector
% the exponent discounts flows after t(k) and compounds flows before it
V_tF(k) = F * v; % value of the cash flow evaluated at time t(k)
% t(k) = 0 gives the present value; t(k) = 2 the value two years from now
end
end
import numpy as np
# %% function F_es46 (in Python a function must be defined before it is used)
def F_es46(F, i, s, t):
# INPUTS: F := flow (cash flow amounts)
# i := interest rate: a number (flat) or a vector (one rate per date of s)
# s := the schedule (payment dates)
# t := times of evaluation
# OUTPUT: V_tF := value of the flow at each evaluation time
F = np.array(F)
i = np.array(i)
s = np.array(s)
V_tF = np.zeros(len(t)) # preallocation
if i.size != len(s) and i.size != 1: # only a flat curve or one rate per date is accepted
print('%%%%% The supplied yield curve is not coherent')
return
for k in range(len(t)): # for each evaluation time (k = 0, 1, ..., len(t)-1)
v = (1 + i) ** (-(s - t[k])) # discount factor vector: discounts flows after t(k), compounds those before
V_tF[k] = F @ v # value of the flow at time t(k)
return V_tF
# %% Point 2
F = [5, 5, 105] # parameters of the problem
i = 0.05
s = [1, 2, 3]
t = [0, 1, 2, 3]
V = F_es46(F, i, s, t)
print("V =", V)
# value of the flow at each time t: 100, 105, 110.25, 115.7625
# %% Point 3
i_tilde = [0.037, 0.042, 0.051]
V_tilde = F_es46(F, i_tilde, s, t)
print("V_tilde =", V_tilde)
Results¶
| Evaluation time | Flat 5% | Rates 3.7%, 4.2%, 5.1% |
|---|---|---|
| 0 | 100.0000 | 99.8709 |
| 1 | 105.0000 | 104.8554 |
| 2 | 110.2500 | 110.0899 |
| 3 | 115.7625 | 115.5868 |
Takeaways¶
- With a flat 5% rate a 5% coupon bond is worth exactly par today, and its value grows at 5% a year: 100, 105, 110.25, 115.76.
- The same function covers present value (\(t = 0\)) and future value (\(t\) after the last flow): the sign of the exponent does the work.
- With several rates, each flow keeps the rate of its own payment date whatever the evaluation date. That is the simplification the exercise asks for; a fully consistent valuation at \(t > 0\) would use forward rates.