What Is Momentum? Measure Price Change with Python

Recent AAPL candlesticks with SMA 20 and EMA 20 above a ten-day momentum panel built with Python

In the moving-average lessons, we kept asking one kind of question:

Where is the average price level?

We took several prices, combined them, and made a smoother line.

Now we are going to ask a different question:

How much did price change?

That small change in the question takes us from moving averages to momentum.

1. Start with the Idea We Already Know

A moving average transforms several prices into an average.

prices
   ↓
average them
   ↓
smoother price level

That helps us see the broad direction without reacting to every small move.

But averaging is only one way to transform price.

We can also compare two prices directly.

price now
-
price before
=
price change

That is the basic idea behind momentum.

2. Begin with One Tiny Example

Imagine a stock closed at:

10 trading days ago: 100
today:               106

The change is:

106 - 100 = +6

So 10-day momentum is +6.

If today's Close were 96 instead:

96 - 100 = -4

Then 10-day momentum would be -4.

Nothing mysterious happened. We only subtracted one old price from one new price.

3. The Momentum Formula

We can write the idea as:

Momentum now
=
Price now
-
Price N periods ago

If we choose:

N = 10

each momentum value compares today's Close with the Close 10 trading rows earlier.

This gives us three simple cases:

Momentum > 0
→ price is higher than N periods ago

Momentum < 0
→ price is lower than N periods ago

Momentum = 0
→ price is unchanged from N periods ago

The zero line therefore has a natural meaning.

4. Moving Average and Momentum Ask Different Questions

This is the most important idea in this lesson.

Moving Average

many prices
→ average
→ smoother level


Momentum

two prices
→ difference
→ change

Both calculations start from the same Close prices.

In our chart, we will keep SMA 20 and EMA 20 on the price panel so you can keep the moving-average idea in view while we add momentum below it.

same Close prices
     ↓
SMA 20 / EMA 20
→ smoother level

same Close prices
     ↓
Momentum 10
→ price change

But the transformation changes what we can see.

This is a useful way to think about every technical indicator:

same raw data
+
different calculation
=
different view of the market

5. Build Momentum from Scratch

We only need one new Learning Block.

def momentum(values, lookback):
    momentum_values = [None] * lookback

    for i in range(lookback, len(values)):
        price_now = float(values[i])
        price_before = float(values[i - lookback])

        change = price_now - price_before
        momentum_values.append(change)

    return momentum_values

Read the loop slowly.

take the current price
        ↓
find the price N rows before
        ↓
subtract
        ↓
save the change
        ↓
move to the next row

That is the entire calculation.

6. Why Do the First Values Return None?

Suppose we want 10-day momentum.

On the first row, there is no price from 10 rows earlier.

The same is true for rows 2 through 10.

So the code starts with:

momentum_values = [None] * lookback

The missing values are not errors.

They mean:

not enough past data yet

You already saw the same idea when we built a Simple Moving Average.

7. Reuse the Blocks We Already Know

We do not need to rebuild the moving-average or candlestick code.

We already learned:

simple_moving_average()
exponential_moving_average()
draw_candlesticks()

So Phase 3-9 reuses those Frozen Learning Blocks without changing them.

KNOWN BLOCK
simple_moving_average()
        ↓
SMA 20

KNOWN BLOCK
exponential_moving_average()
        ↓
EMA 20

KNOWN BLOCK
draw_candlesticks()
        ↓
price candles

NEW BLOCK
momentum()
        ↓
Momentum 10

This gives us a useful comparison.

upper panel
→ candles + SMA 20 + EMA 20

lower panel
→ Momentum 10

We are not throwing away the moving-average ideas. We are placing a new transformation next to them.

8. Build the Smallest Reusable Plot Block

For the first time, our chart needs more than one panel.

We could write the full plt.subplots(...) command every time. But we already know something about the future of this course:

today
2 panels

later
3 panels
4 panels
...
7 panels

So this is a good moment to make one very small plotting Building Block:

def create_stacked_panels(
    panel_count,
    height_ratios,
    figure_width=12,
    figure_height=9,
    figure_dpi=100,
):
    fig, axes = plt.subplots(
        panel_count,
        1,
        figsize=(figure_width, figure_height),
        dpi=figure_dpi,
        sharex=True,
        gridspec_kw={
            "height_ratios": height_ratios,
        },
    )

    return fig, axes

This function does not draw candles. It does not calculate SMA, EMA, Momentum, RSI, or MACD.

Its job is much smaller:

make empty panels
        ↓
stack them vertically
        ↓
make them share the same x-axis
        ↓
return the figure and axes

Think of it as building the empty rooms before we decide what to put inside each room.

What Does panel_count Mean?

panel_count tells Matplotlib how many vertical plotting areas we want.

In this lesson:

panel_count = 2

so Matplotlib gives us two axes:

axes[0]
→ upper panel

axes[1]
→ lower panel

We give them names so the code becomes easier to read:

ax_price = axes[0]
ax_momentum = axes[1]

Now the variable names tell us what each room is for.

What Does height_ratios Mean?

We do not want the price panel and momentum panel to have the same height.

Price needs more room, so we use:

height_ratios = [3, 1]

Read this as:

upper panel  → 3 parts
lower panel  → 1 part

The exact pixel height is not important. The ratio between the panels is what matters.

Why Is the Second Number in plt.subplots() Equal to 1?

plt.subplots(
    panel_count,
    1,
    ...
)

The first number means rows. The second number means columns.

We want panels stacked from top to bottom, not side by side.

2 rows × 1 column

[ price    ]
[ momentum ]

Later, if panel_count = 4, the same idea becomes:

4 rows × 1 column

[ price     ]
[ momentum  ]
[ RSI       ]
[ ATR       ]

Why Use sharex=True?

Every panel represents the same trading dates.

So we want one horizontal time position to mean the same date in every panel.

sharex=True

same x position
     ↓
same date
     ↓
all panels line up vertically

This becomes more useful as the number of indicators grows.

If a price event happens at one x position, you can look straight down and inspect Momentum, RSI, MACD, or another indicator at the same time.

What Are fig and axes?

Matplotlib returns two useful objects:

fig
→ the entire figure

axes
→ the individual plotting panels

The figure is the whole page. Each axis is one panel inside that page.

That is why our function returns both:

return fig, axes

We need axes to draw inside each panel, and we need fig later when we adjust spacing or save the entire image.

How Will This Grow Later?

The useful part is that the function itself does not need to know which indicators we add.

Three panels could be:

fig, axes = create_stacked_panels(
    panel_count=3,
    height_ratios=[3, 1, 1],
)

axes[0] → price
axes[1] → momentum
axes[2] → RSI

Four panels could be:

fig, axes = create_stacked_panels(
    panel_count=4,
    height_ratios=[3, 1, 1, 1],
)

axes[0] → price
axes[1] → momentum
axes[2] → RSI
axes[3] → ATR

And one day, seven panels could still use exactly the same Building Block:

fig, axes = create_stacked_panels(
    panel_count=7,
    height_ratios=[3, 1, 1, 1, 1, 1, 1],
)

The layout block stays small. We only add new calculations and new drawing instructions around it.

That is exactly what a Frozen Learning Block should do: solve one small problem well, then stay familiar while the rest of the program grows.

9. Why Put Momentum Below Price?

Price and raw momentum use different units on the vertical axis.

So we keep them in two panels:

top
→ OHLC candlesticks
→ SMA 20
→ EMA 20

bottom
→ Momentum 10

The two panels use the same dates.

That makes it easy to ask:

What was price doing
when momentum moved above zero?

What was price doing
when momentum moved below zero?

When did momentum become larger
or smaller?

Notice the wording.

We are observing. We are not creating a buy or sell rule yet.

10. Read the Chart in the Right Order

When the chart opens, start with price.

1. Read the candles.
2. Look at SMA 20 and EMA 20.
3. Ask what they say about the smoother price level.
4. Move down to the momentum panel.
5. Check whether momentum is positive or negative.
6. Look at how large the change is.
7. Ask which old price is being compared.

The last question matters because momentum always depends on the lookback.

11. The Lookback Changes the Question

If:

lookback = 5

we ask:

How much did price change
over roughly one trading week?

If:

lookback = 20

we ask about a much longer comparison.

So changing the lookback does not merely change a chart setting.

It changes the time distance between the two prices being compared.

12. Momentum Is Not a Prediction

Suppose 10-day momentum is strongly positive.

We can say:

price now
is much higher than
10 trading days ago

We cannot automatically say:

price must rise tomorrow

Momentum is calculated from prices that already happened.

past price
+
current price
↓
measured change

Whether that change contains a useful trading edge is a later research question.

13. One Important Limitation of Raw Momentum

Raw momentum is measured in price units.

For example:

Stock A
100 → 105
momentum = +5

Stock B
1,000 → 1,005
momentum = +5

The raw momentum number is the same.

But the size of the move relative to each starting price is very different.

That gives us the next question:

How can we express price change
relative to the old price?

That idea leads naturally to Rate of Change.

14. Try One Small Experiment

Start with:

momentum_lookback_days = 10

Then try:

momentum_lookback_days = 5

and:

momentum_lookback_days = 20

Run the file again each time and ask:

Does the zero-crossing date change?

Which setting reacts to shorter moves?

Which setting stays positive or negative longer?

Which old price is each value comparing?

The goal is not to find the best number.

The goal is to understand what the lookback means.

Check Your Understanding

You are ready to move on if you can explain these ideas in your own words:

  • A moving average summarizes price level, while momentum measures price change.
  • Raw momentum is current price minus the price N periods ago.
  • Positive momentum means price is higher than N periods ago.
  • Negative momentum means price is lower than N periods ago.
  • The first N momentum values are unavailable because there is not enough history yet.
  • The lookback changes which two points in time are being compared.
  • Momentum describes a completed price change; it does not automatically predict the next move.
  • Raw momentum is measured in price units, which makes cross-price comparisons difficult.
  • create_stacked_panels() creates reusable vertically stacked axes with one shared time axis.
  • The same plot block can grow from 2 panels to 3, 4, or more by changing panel_count and height_ratios.

What You Just Learned

Close prices
     ↓
choose a lookback
     ↓
Price now - Price before
     ↓
Momentum
     ↓
positive / zero / negative
     ↓
measure price change

If one idea stays in your head after this lesson, let it be this:

A moving average asks where price has been. Momentum asks how price is changing.

And now we have a new limitation to solve.

A five-dollar move does not mean the same thing for every price level.

The next logical step is to turn the change into a relative number: a percentage.