Quant500 Academy

6. NumPy: arrays and vectorized computation on prices

Level 2: Python from Scratch for Finance

Python lists are flexible, but they have a problem for quantitative work: they are slow for numerical computation and do not support direct mathematical operations between them. If you have two lists of prices and want to subtract them element by element, Python will not let you write list1 - list2 directly — you would have to write a loop. NumPy solves exactly that problem.

import numpy as np

prices = np.array([182.52, 184.10, 179.88, 181.45, 183.20])
type(prices)   # numpy.ndarray

A NumPy array behaves like a mathematical vector: arithmetic operations are applied element by element automatically, with no explicit loop needed — this is called vectorization, and it is why NumPy is fast: internally it runs the computation in compiled C code, not in interpreted Python.

prices_yesterday = np.array([180.00, 183.50, 178.00, 180.90, 182.00])

difference = prices - prices_yesterday                     # element-wise subtraction, with no loop
returns = (prices - prices_yesterday) / prices_yesterday   # each day's percentage return, vectorized

To create arrays without typing every number by hand, NumPy offers several generator functions. arange(start, stop, step) generates a numerical sequence, just like range() but returning an array directly:

days = np.arange(0, 10, 1)       # [0, 1, 2, ..., 9]

linspace(start, stop, count) generates an exact number of evenly spaced points between two values — very useful, for example, for generating a continuous time axis for a simulation:

horizon = np.linspace(0, 1, 252)   # 252 points between 0 and 1: the trading days of a year

zeros and ones create arrays filled with zeros or ones, typically used to initialize a structure you will later fill with the results of a simulation:

simulation_results = np.zeros(1000)   # space reserved for 1000 results, all at 0 for now

And diag creates a diagonal matrix from a list of values — useful, for example, when building a covariance matrix where you only care about the individual variances:

variances = np.diag([0.04, 0.09, 0.01])   # 3x3 diagonal matrix with those three variances

Indexing a NumPy array works just as it does with lists, but with extra power: you can select elements using a condition directly, with no loop and no list comprehension. This is called boolean indexing, and it is one of the most used tools in financial analysis.

daily_returns = np.array([0.02, -0.01, 0.005, -0.03, 0.015])

losing_days = daily_returns[daily_returns < 0]   # array([-0.01, -0.03]) - only the negative ones
mask = daily_returns < 0
daily_returns[mask].mean()   # the average return of the losing days

NumPy also comes with the most basic descriptive statistics you will be computing constantly on a return series already built in:

daily_returns.mean()   # average return
daily_returns.std()    # standard deviation (volatility)
daily_returns.max()    # the best day
daily_returns.min()    # the worst day
daily_returns.cumsum() # cumulative sum, useful for seeing the cumulative return day by day

The general rule when working with NumPy is: if you find yourself writing a for loop to walk through an array doing an element-by-element calculation, there is almost always a vectorized way to do the same thing, and it will be considerably faster once the data series has thousands or millions of prices instead of five.

The rest of Quant500

What you are learning, applied to the index every day:

The model portfolio, weekly in your inbox

Every Saturday, its week in one minute: how it went, the best and the worst, and which insiders bought with their own money. Plus, the day the model is rebuilt, which stocks come in and which go out. A simulation, not advice. At most one email a week, no ads, no account; one-click unsubscribe. Made by one person: Pol Gracia.

A model portfolio with simulated results, not a personalized recommendation. Your email is used for this only: privacy.

Behind the Academy and the rest of Quant500 there is one person: Pol Gracia Serrahima.