What ML Can and Can't Do in Trading
Machine learning is genuinely useful in trading, but most beginners drastically overestimate what it can do. The most common failure mode: someone takes a year of price data, trains a neural network to predict tomorrow's return, gets 90% in-sample accuracy, gets 51% out-of-sample, deploys it anyway, and loses money. The model wasn't bad - it was applied in a way that financial data won't sustain.
This tutorial walks through ML for trading the way it actually works at top systematic funds: with rigorous feature engineering, careful validation methodology, realistic expectations about edge sizes, and explicit attention to look-ahead bias and overfitting.
For broader ML-in-finance context, see our machine learning finance guide. For the methodology questions that come up in interviews, see our quant research interview questions.
Step 1: Pick a Realistic Problem
Bad framings:
- "Predict tomorrow's S&P 500 direction." (Edge if real is tiny; competition is fierce)
- "Predict the next big crash." (Vanishing positive examples; impossible to validate)
- "Find the best stock to buy this year." (Wrong horizon; wrong objective)
Good framings:
- "Predict next-week return for each US small-cap stock, ranked cross-sectionally."
- "Predict 20-day realised volatility from intraday volume patterns."
- "Detect a pre-defined regime (high-vol vs low-vol) for adaptive strategy switching."
The good framings have:
- Many independent observations (cross-sectional + time series)
- Specific, measurable target
- Reasonable signal-to-noise ratio for the chosen horizon
Step 2: Feature Engineering
Categories of features in financial ML
- Price-based: Returns over various windows, momentum, mean reversion, volatility, correlation with index
- Volume-based: Dollar volume, turnover, volume-vs-average ratio, intraday volume patterns
- Cross-sectional: Z-scores of metrics across sector/universe peers
- Calendar: Day-of-week, day-of-month, days-to-earnings, days-to-month-end
- Fundamental (if you have data): Valuation ratios, growth rates, quality metrics, ESG scores
- Alternative data: News sentiment, search trends, satellite imagery, credit card spending
import pandas as pd import numpy as np def engineer_features(prices, volumes): """Compute basic features from OHLCV data.""" features = pd.DataFrame(index=prices.index) returns = prices.pct_change() # Momentum features['mom_5d'] = returns.rolling(5).sum() features['mom_20d'] = returns.rolling(20).sum() features['mom_60d'] = returns.rolling(60).sum() # Volatility features['vol_20d'] = returns.rolling(20).std() features['vol_60d'] = returns.rolling(60).std() # Mean reversion features['zscore_20d'] = (prices - prices.rolling(20).mean()) / prices.rolling(20).std() # Volume features['volume_ratio'] = volumes / volumes.rolling(20).mean() # Calendar features['day_of_week'] = prices.index.dayofweek return features
Cardinal rule: avoid look-ahead bias
Every feature must use only information available at time t. Common subtle violations:
- Using rolling().mean() without lagging - you've used today's price to predict today
- Using market cap (today's price × shares outstanding) - same issue
- Using "industry average earnings" computed across the full sample - leaks future info
- Using sector membership (which can change) - might use future categorisation
Fix: lag every feature by at least one period. features = features.shift(1).
Step 3: Define the Target
What are you predicting?
# Next-week return target = prices.pct_change(5).shift(-5) # 5-day forward return # Or: cross-sectional rank within universe target_rank = target.rank(axis=1, pct=True)
Considerations:
- Holding period: Match the rebalance frequency of your eventual strategy
- Risk-adjustment: Sometimes Sharpe is the right target, not return
- Win-rate vs magnitude: Predicting direction (binary) is different from predicting magnitude
Step 4: Cross-Validation Methodology
Why standard K-fold is wrong
K-fold randomly shuffles observations across folds. For time series, this leaks future information into training. A model can "predict" something that wouldn't have been predictable in real time.
What to use instead
Walk-forward validation (also called time-series cross-validation):
def walk_forward_split(data, train_size=252*3, test_size=252): """Yield train/test splits respecting time order.""" splits = [] n = len(data) start = 0 while start + train_size + test_size <= n: train_end = start + train_size test_end = train_end + test_size splits.append((data.iloc[start:train_end], data.iloc[train_end:test_end])) start += test_size # advance by test_size, no overlap return splits splits = walk_forward_split(features_target_combined) for train, test in splits: model = fit_model(train) predictions = model.predict(test) evaluate(predictions, test['target'])
For each fold: train only on past data; predict on next chunk; advance.
Step 5: Model Selection
Where to start
For tabular financial data with 100s-1000s of features and 1000s-100,000s of observations:
- Linear regression with regularisation (lasso, ridge, elastic net) - the strongest baseline
- Gradient boosted trees (XGBoost, LightGBM) - often beat linear by a meaningful margin if you have enough data
- Random forest - good baseline, harder to overfit than XGBoost
- Neural networks - sometimes useful but high overfitting risk; usually overkill for tabular financial data
For sequence-heavy or alternative data:
- LSTMs / Transformers for sentiment, news, time-series of features
- CNNs for image/satellite data
Avoid
- Deep neural networks for small data. With <10K observations and few hundred features, deep nets typically don't beat XGBoost.
- Black-box ensembling without validation. Stacking 50 models that all overfit slightly creates a model that overfits more.
Example: XGBoost baseline
import xgboost as xgb from sklearn.metrics import roc_auc_score def fit_predict(train_df, test_df, target_col): model = xgb.XGBRegressor( n_estimators=100, max_depth=4, learning_rate=0.05, reg_alpha=0.1, reg_lambda=0.1 ) feature_cols = [c for c in train_df.columns if c != target_col] model.fit(train_df[feature_cols], train_df[target_col]) return model.predict(test_df[feature_cols])
The conservative hyperparameters (low max_depth, modest n_estimators, regularisation) reduce overfitting risk.
Step 6: Convert Predictions to a Trading Strategy
A model that predicts return doesn't directly tell you how much to invest. You need:
Signal-to-position mapping
def signal_to_position(predictions, percentile_long=0.9, percentile_short=0.1): """Long top decile, short bottom decile.""" cutoff_long = predictions.quantile(percentile_long, axis=1) cutoff_short = predictions.quantile(percentile_short, axis=1) positions = pd.DataFrame(index=predictions.index, columns=predictions.columns, data=0.0) for date in predictions.index: positions.loc[date, predictions.loc[date] >= cutoff_long.loc[date]] = 1 positions.loc[date, predictions.loc[date] <= cutoff_short.loc[date]] = -1 # Normalise so portfolio is dollar-neutral positions = positions.div(positions.abs().sum(axis=1).replace(0, 1), axis=0) return positions
Realistic backtest
def backtest(positions, returns, transaction_cost_bps=10): """Backtest with transaction costs.""" pnl = (positions.shift(1) * returns).sum(axis=1) # Transaction costs on position changes turnover = (positions - positions.shift(1)).abs().sum(axis=1) / 2 cost = turnover * (transaction_cost_bps / 10000) net_pnl = pnl - cost return net_pnl
Real transaction costs are usually higher than 10 bps round-trip for less-liquid stocks. Be conservative.
Step 7: Evaluate Honestly
The metrics that matter (more than R² or accuracy):
- Out-of-sample Sharpe ratio - the headline. Sharpe < 1 is questionable; > 2 is suspicious in normal markets
- Drawdown - max peak-to-trough loss
- Turnover - how often you trade; affects costs
- Capacity - what's the dollar volume you can absorb?
- Decay rate - how does the strategy perform if you lag your decisions by 1 day, 1 week?
If Sharpe ratio > 3 in your backtest, assume something is wrong. Real edges in equities at horizons of days-to-weeks rarely exceed Sharpe 1.5-2. Sharpe > 3 usually indicates look-ahead bias, survivorship bias, or some other methodology error.
Common Pitfalls
1. Look-ahead bias (the #1 killer)
Your features incorporate information not available at the prediction time. See Step 2.
2. Survivorship bias
Backtesting on current S&P 500 constituents from 2000 - the companies that survived. Use point-in-time index constituents.
3. Selection bias from too many trials
If you tried 100 strategies and the best one has Sharpe 2 in backtest, the expected forward Sharpe is much lower. Apply multiple testing correction.
4. Transaction costs underestimated
Default backtest fees rarely match reality. Add 50-100% to your assumed transaction cost.
5. Capacity ignored
A strategy that works at 100M because impact eats the alpha.
6. Regime dependence
Your model trained on 2020-2024 may not work in 2026 if the market regime has shifted. Always validate on the most recent unseen window.
7. Overfitting to specific stocks
A model that needs Tesla and Nvidia to work won't generalise. Test on a held-out universe.
For deeper coverage of these pitfalls in interview-prep format, see our quant research interview questions (questions 21-25).
Step 8: Deploy
Once you have an out-of-sample-validated strategy:
- Paper trade for a month - even validated strategies can have implementation issues
- Start small in live trading - 5-10% of intended capital
- Monitor against backtest - track whether live performance matches expected
- Have explicit kill criteria - if drawdown exceeds X or live-vs-backtest divergence exceeds Y, halt
For execution infrastructure, see:
- Backtesting platforms comparison - QuantConnect is good for ML strategies because of multi-asset data
- Best brokers for algo trading - IBKR or Alpaca for US equities
- Interactive Brokers API tutorial
Further Reading
- Advances in Financial Machine Learning (Marcos Lopez de Prado) - the canonical text on ML for finance done right
- Machine Learning for Asset Managers (Lopez de Prado) - shorter, more applied follow-up
- The Elements of Statistical Learning (Hastie, Tibshirani, Friedman) - underlying ML methodology
- Pattern Recognition and Machine Learning (Bishop) - rigorous ML reference
For specific strategy implementations:
- Pairs trading tutorial - simpler stat arb baseline
- Statistical arbitrage strategies - broader stat arb context
- Machine learning finance guide - broader ML applications
For research methodology:
Skip the £25k programme - try the alternative
Master's programmes are slow and expensive. Quantt is a self-paced alternative built around the actual skills firms hire for: Python, mathematics, derivatives pricing, options Greeks and live trading-system design. 50+ courses, interactive coding tests, and the same end-state as a one-year MFE - in your evenings.
No prerequisites - start at any level