A common beginner mistake is feeding raw close prices into a model and expecting it to find patterns. Price levels are non-stationary — a model trained on EUR/USD at 1.05 has no idea what to do at 1.20. Feature engineering is the process of transforming raw data into stable, comparable, predictive inputs.
Categories of Useful Features
- ▸Return-based — log returns over various horizons (1-bar, 5-bar, 20-bar) instead of price levels.
- ▸Volatility-based — realized volatility, ATR ratios, Bollinger Band width.
- ▸Momentum/trend — moving average spreads, RSI, rate-of-change normalized by volatility.
- ▸Cross-sectional — an asset's return rank relative to a peer basket (used heavily in equity quant).
- ▸Microstructure — bid-ask spread, order book imbalance, volume-weighted metrics.
- ▸Calendar/seasonality — day-of-week, month-end, options-expiry proximity flags.
Normalization Matters More Than the Raw Formula
The same feature computed two different ways can behave completely differently across volatility regimes. A raw 20-day price change of 3 points means something different in a low-vol period than a high-vol one. Normalizing by recent volatility (a z-score) makes the feature comparable across time.
z = (x − rolling_mean(x, n)) / rolling_std(x, n)Worked Example
If a 20-day price change averages 0 with a rolling standard deviation of 1.5 points, a raw move of 3 points gives z = 2.0 — a two-standard-deviation event, regardless of whether it happened in a calm or volatile month. This is what makes the feature usable across regimes and across different instruments.
Computing a rolling mean or standard deviation using the full dataset (including future bars) instead of only past data will make backtests look excellent and live results collapse instantly. Every feature must be computable using only information available strictly before the bar it labels.
Feature Decay
Most features have a shelf life. A momentum signal that worked from 2015-2019 may lose predictive power once enough participants trade against it, or once market microstructure shifts (e.g. tick size changes, new participants). Track feature performance on a rolling basis and be willing to retire features whose correlation with forward returns has decayed.
def zscore_feature(series, window=20):
roll_mean = series.rolling(window).mean().shift(1)
roll_std = series.rolling(window).std().shift(1)
return (series - roll_mean) / roll_std # shift(1) avoids lookaheadThe Bottom Line
Good feature design is where the intellectual work of quant trading actually lives — far more than model selection. A simple model on well-designed features consistently beats a sophisticated model on lazy, leaky ones.