Turn a vector of closing prices into daily returns, first with a loop and then in one line, and compound those returns into a single figure.
A return is today’s price over yesterday’s price, minus one. Three prices give two returns, because the first price has no day before it.
In this lesson I compute returns twice on the same closes: once with a loop, so every piece of the arithmetic is on the page, and once with a single line of R. Then I compound them into the return for the whole stretch.
Step 1. The loop, so the arithmetic is visible
numeric(k) makes a vector of k zeros. I make it the right size up front and fill each slot in turn, rather than growing the vector as I go.
seq_along(closes) gives the positions 1 to n, and putting [-1] after it drops the first of those positions. So the loop runs over 2 upward. Position 1 is skipped: there is nothing before it to compare against, and on a single price the loop does not run at all.
closes <-c(100, 110, 99) # three closing prices in ordern <-length(closes) # how many prices I havereturns <-numeric(n -1) # room for n - 1 returns, zeros for nowfor (i inseq_along(closes)[-1]) { # i = 2, 3 returns[i -1] <- (closes[i] - closes[i -1]) / closes[i -1] # today minus yesterday, over yesterday}print(returns) # -> [1] 0.1 -0.1
[1] 0.1 -0.1
print(length(closes)) # -> [1] 3
[1] 3
print(length(returns)) # -> [1] 2
[1] 2
The price went 100 to 110, a gain of 10%, then 110 to 99, a fall of 10%. Return i - 1 sits in slot i - 1, so the returns vector is one shorter than the prices vector.
Step 2. The same thing in one line
diff(x) returns the gaps between neighbouring elements: x[2] - x[1], x[3] - x[2], and so on. head(x, -1) returns every element except the last, which is exactly the list of yesterdays. Divide one by the other and you have every return at once.
I switch to five closes here, picked so you can check each return in your head.
closes <-c(100, 110, 99, 99, 108.9) # five closes, easy numbers on purposeprint(diff(closes)) # -> [1] 10.0 -11.0 0.0 9.9
[1] 10.0 -11.0 0.0 9.9
print(head(closes, -1)) # -> [1] 100 110 99 99
[1] 100 110 99 99
diff() gave four gaps and head(closes, -1) gave four denominators, so the division lines up element by element.
r <-diff(closes) /head(closes, -1) # every return in one lineprint(r) # -> [1] 0.1 -0.1 0.0 0.1
[1] 0.1 -0.1 0.0 0.1
print(round(r *100, 2)) # -> [1] 10 -10 0 10
[1] 10 -10 0 10
Up 10%, down 10%, flat, up 10%.
Now run the Step 1 loop on these same five closes and compare. all.equal() checks two numeric vectors match to within floating point tolerance, which is what you want here rather than ==.
loop_r <-numeric(length(closes) -1) # same preallocation as Step 1for (i inseq_along(closes)[-1]) { # same loop, 2 to 5 loop_r[i -1] <- (closes[i] - closes[i -1]) / closes[i -1] # same arithmetic}print(loop_r) # -> [1] 0.1 -0.1 0.0 0.1
[1] 0.1 -0.1 0.0 0.1
print(all.equal(loop_r, r)) # -> [1] TRUE
[1] TRUE
Same four numbers. The one line replaces the loop, and from here on I use the one line.
Step 3. Compound the returns into one number
To get the return over the whole stretch, multiply the growth factors 1 + r together and take the starting 1 back off. prod() multiplies a vector down to a single number.
total <-prod(1+ r) -1# 1.1 * 0.9 * 1.0 * 1.1, minus 1print(total) # -> [1] 0.089
[1] 0.089
That figure has to equal the last close over the first close, minus 1, because everything in between cancels.
closes[length(closes)] is how you reach the last element in R. A negative index drops elements instead of counting from the end, as [-1] did to the positions in Step 1, so closes[-1] would give you the last four prices, not the last one.
Now add the same four returns instead of multiplying them.
added <-sum(r) # the four returns added, not compoundedprint(round(c(compounded = total, added = added) *100, 4))
compounded added
8.9 10.0
# -> compounded added# -> 8.9 10.0
The prices went from 100 to 108.9, a move of 8.9%. prod(1 + r) - 1 gives 8.9%. sum(r) gives 10%. Simple returns compound, so use prod(1 + r) - 1.
Your turn
Take closes <- c(50, 55, 55, 44). Build the returns with diff() and head(), compound them, and check the result against the last close over the first. How many returns do four prices give?