Basic Prerequisites: none beyond basic Python.
1. Intuition & Practical Objective
This page builds the why of the Python quant stack with no performance background needed. The objective is one idea: Python is a scripting language that gets its speed by delegating whole-array work to two invisible C runtimes - NumPy and pandas - and a quant researcher’s entire craft is learning to write expressions that trigger those C runtimes instead of per-element interpreted loops.
Start with the dumbest question: why is Python slow at math at all? When Python executes for i in range(n): s += a[i], every single iteration pays overhead that has nothing to do with the math: the interpreter fetches the loop variable, looks up a and __getitem__, creates a Python int/float object for each intermediate, and does the addition as an object operation. That is a few hundred nanoseconds per element, for work the CPU itself does in a few nanoseconds. Over a million elements the overhead is not a detail - it is the whole story.
The stack’s answer is to stop looping in Python. A NumPy array is a single contiguous block of typed memory, and 2.0*x + 1.0 tells NumPy to run a compiled C loop over the whole block in one call. The Python code you write is thin - one line - but the work happens at C speed.
Three “aha”s, mirroring the arc of the folder:
- Python is the steering wheel; NumPy/pandas are the engine. You write Python to describe the computation; the heavy lifting happens in C, batch by batch. Slow Python is almost never the math - it is the parts of your code that force per-element interpretation.
- The same gap that slows you down is the opportunity. Because (measured in 02 · NumPy Vectorization), a well-vectorized line is typically 1–2 orders of magnitude faster than the obvious Python loop. Speed is a property of how you write the loop boundary, not of the hardware.
- Backtests are arrays, not days. A strategy over bars is a handful of whole-array transforms (returns, signals, positions, compounded equity) - see 02 for the algebra. Once you see a backtest as array math, the whole stack becomes coherent.
Where the stack sits. The research layer feeds the compiled production core (High-Performance C++ for Trading) and the event-driven engine (Event-Driven Backtesting Engines). The data arrives via Data Sources & Corporate Data. This folder is the “how do I compute on this data quickly and correctly” layer.
2. Mathematical Ground Truth & Derivations
The overhead model. For an array operation on elements, vectorization replaces interpreted dispatches (each ~, a few hundred ns) with one compiled C pass (each element ~, a few ns):
For large , is negligible and the ratio collapses to
That ratio is the number that explains the entire design of the stack. It is also why the stack fails in exactly one way: the moment a computation drops back into per-element interpreted Python (an apply, a manual for over rows, an object-dtype column), the term returns and the gain evaporates.
The compounding-of-returns identity (why a backtest is array math). With position-adjusted log returns, compounded equity satisfies
so the “portfolio of a lifetime of trades” is np.exp(np.log1p(rstar).cumsum()) - a constant number of C passes regardless of . This is the mathematical backbone of vectorized backtesting (Hilpisch, Python for Algorithmic Trading, ch. on vectorized backtesting).
3. Computational Implementation - your first taste of the stack
This is a working, stdlib+numpy walk-through that computes the returns and compounded equity of a synthetic price path - the exact shape of a minimal vectorized backtest, and the seed of everything in the folder.
The log-returns identity holds to floating-point precision (final equity equals final price because both are the same exp(cumsum(log(1+r))) chain) - and the entire computation is a handful of vectorized passes, not a 5,000-iteration Python loop. That is the stack’s whole promise in one cell.
4. Failure Modes & First-Principles Breakdowns
- “Python is slow” is a self-fulfilling prophecy. The slowness is your loop placement, not the language. Writing per-element loops for things NumPy can do whole-array throws away the 50–150× gap on purpose.
- The stack is only as fast as its slowest drop. One
apply-over-rows or oneobjectcolumn silently re-introduces the interpreted-per-element cost across the whole pipeline - often without a visible error (see 03 · pandas Pitfalls). - Syntactic clarity ≠ computational clarity.
df[df.x>0]['y'] = 5looks like an assignment but operates on a copy. What reads as one line is not always what executes (the chained-assignment trap, 03). - Vectorization has a memory price. Whole-array work means materializing whole arrays; a grid search over millions of parameter combos can blow RAM (see 06 · Advanced Extensions).
5. References
- McKinney, Python for Data Analysis (3rd ed., 2022)
- Hilpisch, Python for Algorithmic Trading (2020)
- Gorelick & Ozsvald, High Performance Python (2nd ed., 2020)
6. Connected Graph Bridges
- Base: Numerical Methods · Quantitative Research
- Continue: 02 · NumPy Vectorization · Index Hub