# rollingreturns

Period-over-period rolling returns or differences from prices

## Description

example

returns = rollingreturns(TT) calculates period-over-period (PoP) returns or differences from corresponding prices. For each date in timetable TT, the return represents the PoP return of the corresponding price compared to the price one Period earlier.

example

returns = rollingreturns(___,Name,Value) specifies options using one or more optional name-value pair arguments in addition to the input arguments in the previous syntax.

## Examples

collapse all

This example shows how to compute year-over-year rolling returns for five years of simulated daily prices and also includes business calendar awareness.

Simulate five years of daily prices and store the result in the timetable TT. Then, use isbusday to retain only data for New York Stock Exchange (NYSE) business dates.

rng(200,'twister')
time = (datetime(2014,1,1):caldays:datetime(2018,12,31))';
price = 100 + 0.1*(0:numel(time) - 1)'.*cumsum(randn(numel(time),1)/100);
price = round(price*100)/100;                 % Round prices to the nearest penny
TT = timetable(price,'RowTimes',time,'VariableNames',{'Prices'});
TT = TT(isbusday(TT.Properties.RowTimes),:);  % Retain only NYSE business days
Time        Prices
___________    ______

02-Jan-2014       100
03-Jan-2014       100
06-Jan-2014    100.01
07-Jan-2014    100.02
08-Jan-2014    100.02
09-Jan-2014    100.04
10-Jan-2014    100.06
13-Jan-2014    100.11
14-Jan-2014    100.12
15-Jan-2014    100.12

Use addBusinessCalendar to add NYSE business calendar awareness. The business calendar logic determines if the date of the previous period is a business date, and if it is not, then the most recent business day preceding that date is found. For example, since 21-May-2016 is a Saturday and 22-May-2016 is a Sunday, year-over-year prices for Monday 22-May-2017 are compared to Friday 20-May-2016.

Compute the year-over-year returns and display the last few prices and corresponding returns.

returns = rollingreturns(TT, 'Period', calyears);
tail([TT returns])
Time        Prices    Prices_Return_1y
___________    ______    ________________

19-Dec-2018    212.68        0.16941
20-Dec-2018    215.54        0.19024
21-Dec-2018    217.66        0.18648
24-Dec-2018    221.42        0.20882
26-Dec-2018    224.81        0.21473
27-Dec-2018    222.17        0.19897
28-Dec-2018    224.63        0.19142
31-Dec-2018    224.37        0.19206

Economic data is often reported on the last day of each month or quarter. So end-of-month ambiguities can arise when computing period-over-period returns for periods that exceed the periodicity at which data is reported.

Simulate five years of daily prices and store the result in the timetable TT.

rng(200,'twister')
time = (datetime(2014,1,1):caldays:datetime(2018,12,31))';
price = 100 + 0.1*(0:numel(time) - 1)'.*cumsum(randn(numel(time),1)/100);
price = round(price*100)/100;         % Round prices to the nearest penny
TT = timetable(price,'RowTimes',time,'VariableNames',{'Prices'});
Time        Prices
___________    ______

01-Jan-2014       100
02-Jan-2014       100
03-Jan-2014       100
04-Jan-2014       100
05-Jan-2014    100.01
06-Jan-2014    100.01
07-Jan-2014    100.02
08-Jan-2014    100.02
09-Jan-2014    100.04
10-Jan-2014    100.06

Create a new timetable by sampling TT on the last day of each month to mimic monthly reporting.

monthEndDates = dateshift(TT.Time(1):calmonths:TT.Time(end),'end','month');
TT = TT(monthEndDates,:);   % Sample TT at end-of-month dates
Time        Prices
___________    ______

31-Jan-2014    100.47
28-Feb-2014    100.93
31-Mar-2014       102
30-Apr-2014    102.28
31-May-2014    103.22
30-Jun-2014    103.92
31-Jul-2014     102.2
31-Aug-2014    104.79
30-Sep-2014    103.11
31-Oct-2014    105.29

Display a subset of the dates and compare a direct calculation of the dates in previous months to those shifted to the end of the month in which the previous period occurs.

dates = timerange(datetime(2016,2,29),datetime(2017,2,28),'month');
[TT.Time(dates) (TT.Time(dates) - calyears) dateshift(TT.Time(dates) - calyears,'end','month')]
ans = 13x3 datetime
29-Feb-2016   28-Feb-2015   28-Feb-2015
31-Mar-2016   31-Mar-2015   31-Mar-2015
30-Apr-2016   30-Apr-2015   30-Apr-2015
31-May-2016   31-May-2015   31-May-2015
30-Jun-2016   30-Jun-2015   30-Jun-2015
31-Jul-2016   31-Jul-2015   31-Jul-2015
31-Aug-2016   31-Aug-2015   31-Aug-2015
30-Sep-2016   30-Sep-2015   30-Sep-2015
31-Oct-2016   31-Oct-2015   31-Oct-2015
30-Nov-2016   30-Nov-2015   30-Nov-2015
31-Dec-2016   31-Dec-2015   31-Dec-2015
31-Jan-2017   31-Jan-2016   31-Jan-2016
28-Feb-2017   28-Feb-2016   29-Feb-2016

Examine these results and notice that the dates in the second and third columns of the last row differ. Specifically, when the current date in the first column is 28-Feb-2017 the dates in the second and third columns differ because 2016 is a leap year. More generally, the dates differ whenever the month of the previous period has more days than the current month for which returns are computed. In this example, end-of-months dates present the following ambiguity. When the current date of interest is 28-Feb-2017, should subtracting one calendar year produce 28-Feb-2016 or 29-Feb-2016?

The correct answer depends on the application, and both approaches are valid use cases. This problem is exacerbated, for example, when working with end-of-monthly price data and computing month-over-month returns. To address the end-of-month ambiguity, the rollingreturns function supports an EndOfMonth flag.

returns = rollingreturns(TT, 'Period', calyears, 'EndOfMonth', true);

The EndOfMonth flag ensures that the rollingreturns function uses the correct end-of-month date of each calendar month. In this example, the return on 28-Feb-2017 is correctly computed from the price reported 29-Feb-2016 rather than 28-Feb-2016.

[TT(dates,:) returns(dates,:)]
ans=13×2 timetable
Time        Prices    Prices_Return_1y
___________    ______    ________________

29-Feb-2016    135.59         0.21671
31-Mar-2016    138.47         0.25052
30-Apr-2016    131.44         0.11598
31-May-2016    129.34        0.083068
30-Jun-2016    133.86        0.077865
31-Jul-2016    132.78        0.046253
31-Aug-2016    140.32         0.11871
30-Sep-2016    136.52        0.087549
31-Oct-2016    141.27         0.10652
30-Nov-2016    140.76          0.1053
31-Dec-2016    135.96        0.057643
31-Jan-2017    129.52       0.0099025
28-Feb-2017    136.36       0.0056789

## Input Arguments

collapse all

Input timetable of prices, specified as a timetable. The timetable TT must satisfy the following conditions:

• All observations in TT must be associated with whole dates specified as datetimes with no HH:MM:SS time component (no time-of-day component).

• TT dates must be sorted in ascending order.

• TT must have no duplicate dates.

• Each variable in TT must contain either a single numeric vector or a numeric matrix of prices. For example, suppose TT contains three variables of daily prices.

Time        Price1    Price2         Prices
___________    ______    ______    ________________
24-Dec-2018    221.42    442.84    221.42    442.84
25-Dec-2018    220.62    441.24    220.62    441.24
26-Dec-2018    224.81    449.62    224.81    449.62
27-Dec-2018    222.17    444.34    222.17    444.34
28-Dec-2018    224.63    449.26    224.63    449.26
29-Dec-2018    225.36    450.72    225.36    450.72
30-Dec-2018    226.73    453.46    226.73    453.46
31-Dec-2018    224.37    448.74    224.37    448.74
The corresponding daily returns are formatted as three returns for the three price variables.
Time        Price1_Return    Price2_Return       Prices_Return
___________    _____________    _____________   _____________________
24-Dec-2018           NaN              NaN            NaN         NaN
25-Dec-2018     -0.003613        -0.003613      -0.003613   -0.003613
26-Dec-2018      0.018992         0.018992       0.018992    0.018992
27-Dec-2018     -0.011743        -0.011743      -0.011743   -0.011743
28-Dec-2018      0.011073         0.011073       0.011073    0.011073
29-Dec-2018      0.003249         0.003249       0.003249    0.003249
30-Dec-2018      0.006079         0.006079       0.006079    0.006079
31-Dec-2018     -0.010409        -0.010409      -0.010409   -0.010409

Note

Data Types: timetable

### Name-Value Arguments

Specify optional pairs of arguments as Name1=Value1,...,NameN=ValueN, where Name is the argument name and Value is the corresponding value. Name-value arguments must appear after other arguments, but the order of the pairs does not matter.

Before R2021a, use commas to separate each name and value, and enclose Name in quotes.

Example: returns = rollingreturns(TT,'Period',calweeks(1),'EndOfMonth',true,'Method','continuous')

Period to compute period-over-period returns, specified as the comma-separated pair consisting of 'EndMonthRule' and a scalar calendar duration (for example, caldays, calweeks, or calmonths).

The default is the time step defined in TT (TT.Properties.TimeStep), but only when TT.Properties.TimeStep is not NaN. If TT.Properties.TimeStep is NaN, then Period is required.

Data Types: double

End-of-month flag indicates whether the prices at the current date are compared to last date of month for the previous Period, specified as the comma-separated pair consisting of 'EndOfMonth' and a scalar logical value of true or false.

• If you set EndOfMonth to true (logical 1), meaning that the current prices are compared to end-of-month prices of the previous Period.

• If you set EndOfMonth to false (logical 0), meaning that the current prices are compared to prices recorded on the actual date of the previous Period.

Note

The EndOfMonth flag is intended to address end-of-month date calculations when computing the dates of a previous Period one or more months in the past.

For example, suppose you have monthly prices reported at the end of each month and want to compute year-over-year returns (that is, Period = calyears(1)). When the current date of interest is 28-Feb-2017, setting EndOfMonth = true (logical 1) ensures that returns computed for 28-Feb-2017 compare the prices on 28-Feb-2017 to those on 29-Feb-2016 rather than 28-Feb-2016.

Similarly, suppose you have monthly prices reported at the end of each month and want to compute month-over-month returns (that is, Period = calmonths(1)). When the current date of interest is 30-Apr-2020, setting EndOfMonth = true (logical 1) ensures that returns computed for 30-Apr-2020 compare the prices on 30-Apr-2020 to those on 31-Mar-2020 rather than 30-Mar-2020.

Data Types: logical

Method for computing returns from prices, specified as the comma-separated pair consisting of 'Method' and a scalar character vector.

• 'simple' — Compute simple (proportional) returns: R(t) = P(t)/P(t-period) - 1.

• 'continuous' — Compute continuous (logarithmic) returns: R(t) = log(P(t)/P(t-period)).

• 'difference' — Compute differences (period-over-period changes): R(t) = P(t) - P(t-period)

Data Types: char

## Output Arguments

collapse all

Period-over-period decimal returns or differences, returned as a timetable of the same size and format as the input argument TT. The returns or differences in row t are associated with the tth date in TT and represent the return or difference of the tth price P(t) relative to the price in the previous period P(t-period). If the date in the previous period is not found in TT, then the result is NaN to indicate a missing value.

Variable names in the output append _Return or _Difference to the variable names in TT for returns and differences, respectively, followed by the period used in the period-over-period results. For example, if TT has a variable named ABC and week-over-week returns are computed for a Period of calweeks(1), the corresponding output variable is named ABC_Returns_1w.

rollingreturns is an aggregation function in which the frequency at which prices are recorded must equal or exceed that at which returns or differences are computed. For example, daily prices can be used to compute daily, weekly, or monthly returns, but computing daily returns from weekly or monthly prices generally makes no sense.

## Algorithms

Period-over-period results are computed for every date in TT as follows:

1. For each date t in TT, the date t-period is computed.

• If date t-period is a business date, then this date is the date "one period ago"

• If date t-period is not a business date, then each calendar day preceding t-period is examined repeatedly until a business date is found, at which point this date is the date "one period ago," or the preceding date occurs prior to the first date in TT, at which point no previous business date exists in TT.

2. If the date "one period ago" is found in TT, then the corresponding price P(t-period) is recorded and the return R(t) is computed. However, if the date "one period ago" is not found in TT, then the previous price P(t-period) is assumed missing (that is, an implicit NaN), and the return R(t) = NaN.

3. The previous steps are repeated until the date t-period precedes the first date found in TT, at which point the algorithm terminates.

## Version History

Introduced in R2020b