How do I slice a list?

Slice a list with x[start:stop], and turn the last few closes into a moving average.

I take part of a list with a slice, written x[start:stop]. It gives me the items from start up to stop, and the item at stop is left out.

Leave start out and the slice begins at the first item. Leave stop out and it runs to the end. Negative positions count backwards, so x[-5:] is the last five items.

In this lesson I slice a list of closing prices, then use slices to compute a 5-day and a 3-day moving average.

Step 1. The smallest slice

Seven closes. Positions run 0, 1, 2, 3, 4, 5, 6 from the front, and -1, -2, -3 and so on from the back.

closes = [100, 102, 101, 105, 110, 108, 112]

print(closes[0])              # -> 100                          <- one item, the first
print(closes[-1])             # -> 112                          <- one item, the last

print(closes[2:5])            # -> [101, 105, 110]              <- positions 2, 3, 4
print(closes[:3])             # -> [100, 102, 101]              <- from the start
print(closes[3:])             # -> [105, 110, 108, 112]         <- to the end
print(closes[-5:])            # -> [101, 105, 110, 108, 112]    <- the last five
print(closes[1:-1])           # -> [102, 101, 105, 110, 108]    <- everything but the two ends
100
112
[101, 105, 110]
[100, 102, 101]
[105, 110, 108, 112]
[101, 105, 110, 108, 112]
[102, 101, 105, 110, 108]

The item at stop is left out, so closes[2:5] stops at position 4 and holds three items. stop - start is the number of items you get.

print(len(closes[2:5]))       # -> 3
print(len(closes[:3]))        # -> 3

split = closes[:3] + closes[3:]
print(split == closes)        # -> True   <- [:3] and [3:] split the list in two
3
3
True

Step 2. A 5-day and a 3-day moving average

A moving average is the mean of the last few closes. The slice picks the window, and sum adds it up. Dividing by the window length gives the mean.

closes = [185.40, 187.20, 184.90, 188.10, 190.50,
          189.30, 192.40, 195.10, 193.80, 197.20]

print(len(closes))            # -> 10
print(closes[-5:])            # -> [189.3, 192.4, 195.1, 193.8, 197.2]
print(closes[-3:])            # -> [195.1, 193.8, 197.2]

ma5 = sum(closes[-5:]) / 5
ma3 = sum(closes[-3:]) / 3

print(round(ma5, 2))          # -> 193.56
print(round(ma3, 2))          # -> 195.37
print(ma3 > ma5)              # -> True    <- the short average sits above the long one
10
[189.3, 192.4, 195.1, 193.8, 197.2]
[195.1, 193.8, 197.2]
193.56
195.37
True

The two windows share the last three closes and differ in the two older ones.

Now compare the latest close with an average of the closes before it. closes[-6:-1] starts six from the end and stops one from the end, so the last close is not inside its own average.

latest = closes[-1]
prior5 = closes[-6:-1]
avg5 = sum(prior5) / len(prior5)

print(latest)                 # -> 197.2
print(prior5)                 # -> [190.5, 189.3, 192.4, 195.1, 193.8]
print(round(avg5, 2))         # -> 192.22
print(latest > avg5)          # -> True
197.2
[190.5, 189.3, 192.4, 195.1, 193.8]
192.22
True

Step 3. A slice that runs past the end

Indexing past the end raises an error. Slicing past the end returns whatever exists.

short_history = [100, 102, 101]

print(short_history[-5:])              # -> [100, 102, 101]
print(len(short_history[-5:]))         # -> 3    <- asked for 5, got 3

window = short_history[-5:]
print(sum(window) / 5)                 # -> 60.6    <- three closes divided by 5
print(sum(window) / len(window))       # -> 101.0   <- three closes divided by 3
[100, 102, 101]
3
60.6
101.0

Dividing by len(window) uses the number of closes the slice returned.

Your turn

From the ten-day closes in Step 2, print the three middle closes (positions 4, 5 and 6) and their average, rounded to two decimals.

middle = closes[4:7]

print(middle)                                    # -> [190.5, 189.3, 192.4]
print(round(sum(middle) / len(middle), 2))       # -> 190.73