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]  # seven daily closes, oldest first

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      <- stop minus start, 5 - 2
print(len(closes[:3]))        # -> 3      <- an omitted start counts from 0

split = closes[:3] + closes[3:]  # the two halves joined back up
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,    # ten daily closes, oldest first
          189.30, 192.40, 195.10, 193.80, 197.20]    # newest close last, so [-1] is 197.20

print(len(closes))            # -> 10     <- ten closes to slice windows from
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    # window sum divided by the window length
ma3 = sum(closes[-3:]) / 3    # shorter window, so it reacts faster

print(round(ma5, 2))          # -> 193.56  <- mean of the five newest closes
print(round(ma3, 2))          # -> 195.37  <- mean of the three newest closes
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]               # the newest close on its own
prior5 = closes[-6:-1]            # the five closes before it, latest excluded
avg5 = sum(prior5) / len(prior5)  # an average the latest close cannot lift

print(latest)                 # -> 197.2
print(prior5)                 # -> [190.5, 189.3, 192.4, 195.1, 193.8]
print(round(avg5, 2))         # -> 192.22  <- the five older closes averaged
print(latest > avg5)          # -> True    <- the newest close sits above that average
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]        # only three closes exist

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

window = short_history[-5:]            # asked for five, holds three
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]                             # positions 4, 5 and 6, stop excluded

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