Skip to content

Value of a cash flow stream over time

Course exercise 46 · Part I

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.