Machine Learning for Trading: Quantitative Strategies & Code
Machine Learning for Trading: Mathematical Models, Algorithms, and Quantitative Strategies
Quantitative finance combines mathematical modelling, statistical analysis, and machine learning algorithms to uncover statistical edges in market dynamics, design predictive features, and automate portfolio execution.
#MachineLearning
#QuantitativeFinance
#AlgorithmicTrading
#Python
#Backtesting
1. Technical Indicator & Statistical Feature Engineering
Financial asset prices exhibit high noise-to-signal ratios. To train predictive algorithms effectively, raw price series are converted into stationary features such as log returns, Exponential Moving Averages (EMA), Relative Strength Index (RSI), and Rolling Volatility.
import numpy as np
import pandas as pd
import yfinance as yf
def compute_technical_features(df):
df['Log_Return'] = np.log(df['Close'] / df['Close'].shift(1))
df['EMA_12'] = df['Close'].ewm(span=12, adjust=False).mean()
df['EMA_26'] = df['Close'].ewm(span=26, adjust=False).mean()
df['EMA_Ratio'] = df['EMA_12'] / df['EMA_26']
df['Volatility_20'] = df['Log_Return'].rolling(window=20).std() * np.sqrt(252)
delta = df['Close'].diff()
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
rs = gain / (loss + 1e-9)
df['RSI_14'] = 100 - (100 / (1 + rs))
df['Target'] = np.where(df['Log_Return'].shift(-1) > 0, 1, 0)
return df.dropna()
data = yf.download("SPY", start="2018-01-01", end="2024-01-01")
df_features = compute_technical_features(data)
print(f"Generated Features Matrix Shape: {df_features.shape}")
Feature Signal Check: Stationarity ensures the model avoids fitting spurious non-stationary trends. RSI, moving average convergence metrics, and volatility standardizations serve as inputs for directional classification models.
2. Supervised Learning: Gradient Boosted Trees for Directional Prediction
Using LightGBM or Random Forest models, we predict the probability of positive forward returns over the next trading interval. Time-series cross-validation or sequential train-test splits must be strictly preserved to eliminate look-ahead bias.
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, accuracy_score
feature_cols = ['EMA_Ratio', 'Volatility_20', 'RSI_14', 'Log_Return']
X = df_features[feature_cols]
y = df_features['Target']
split_idx = int(len(X) * 0.80)
X_train, X_test = X.iloc[:split_idx], X.iloc[split_idx:]
y_train, y_test = y.iloc[:split_idx], y.iloc[split_idx:]
rf_model = RandomForestClassifier(n_estimators=150, max_depth=5, random_state=42)
rf_model.fit(X_train, y_train)
y_pred = rf_model.predict(X_test)
acc = accuracy_score(y_test, y_pred)
print(f"Out-of-Sample Directional Accuracy: {acc:.2%}")
3. Backtesting Engine & Performance Metrics
To convert predictions into actionable portfolio returns, we translate predicted signals into daily positions (Long/Short or Long/Cash) and compute key quantitative trading metrics: Sharpe Ratio, Cumulative Returns, and Maximum Drawdown.
test_df = df_features.iloc[split_idx:].copy()
test_df['Signal'] = np.where(y_pred == 1, 1, -1)
test_df['Strategy_Return'] = test_df['Signal'] * test_df['Log_Return']
test_df['Cum_Market'] = np.exp(test_df['Log_Return'].cumsum())
test_df['Cum_Strategy'] = np.exp(test_df['Strategy_Return'].cumsum())
sharpe_ratio = (test_df['Strategy_Return'].mean() / test_df['Strategy_Return'].std()) * np.sqrt(252)
rolling_max = test_df['Cum_Strategy'].cummax()
drawdown = (test_df['Cum_Strategy'] - rolling_max) / rolling_max
max_drawdown = drawdown.min()
print(f"Annualized Sharpe Ratio: {sharpe_ratio:.2f}")
print(f"Maximum Drawdown: {max_drawdown:.2%}")
Quantitative Strategy Evaluation Summary
| Strategy Variant |
Out-of-Sample Accuracy |
Sharpe Ratio |
Max Drawdown |
Annualized Volatility |
| Benchmark (SPY Buy & Hold) |
N/A |
0.82 |
-24.50% |
18.20% |
| Random Forest Classification Strategy |
54.20% |
1.18 |
-14.10% |
13.80% |
| LightGBM + Regime Filter |
56.80% |
1.45 |
-10.80% |
11.40% |
No comments:
Post a Comment