Showing posts with label ML. Show all posts
Showing posts with label ML. Show all posts

Friday, September 25, 2026

Machine Learning for trading

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): # Calculate Log Returns df['Log_Return'] = np.log(df['Close'] / df['Close'].shift(1)) # Exponential Moving Averages (EMA) Ratio 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'] # Rolling Volatility (20-day annualized) df['Volatility_20'] = df['Log_Return'].rolling(window=20).std() * np.sqrt(252) # Relative Strength Index (RSI) 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)) # Target Variable: Next Period Return Direction (1 for Up, 0 for Down) df['Target'] = np.where(df['Log_Return'].shift(-1) > 0, 1, 0) return df.dropna() # Fetch historical ETF data 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 # Define Feature Set & Target feature_cols = ['EMA_Ratio', 'Volatility_20', 'RSI_14', 'Log_Return'] X = df_features[feature_cols] y = df_features['Target'] # Strict Time-Based Train/Test Split (80% Train, 20% Test) 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:] # Train Random Forest Model rf_model = RandomForestClassifier(n_estimators=150, max_depth=5, random_state=42) rf_model.fit(X_train, y_train) # Out-of-Sample Predictions 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.

# Generate Position Signals (+1 for Bullish, -1 for Bearish) test_df = df_features.iloc[split_idx:].copy() test_df['Signal'] = np.where(y_pred == 1, 1, -1) # Calculate Strategy Returns (Signal * Next Period Return) test_df['Strategy_Return'] = test_df['Signal'] * test_df['Log_Return'] # Cumulative Growth test_df['Cum_Market'] = np.exp(test_df['Log_Return'].cumsum()) test_df['Cum_Strategy'] = np.exp(test_df['Strategy_Return'].cumsum()) # Metrics Computation 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%

Featured Post

3rd Place at the BAKE Blogger Awards: Thank You for the Support!

3rd Place at the BAKE Blogger Awards: Thank You for the Support! ...

Meet the Authors
Zacharia Nyambu’s blog features multiple contributors with clear activity status.
Active ✔
πŸ§‘‍πŸ’»
Zacharia Nyambu
Lead Author
Inactive ✖
πŸ‘©‍πŸ’»
Linda Bahati
Co‑Author
Inactive ✖
πŸ‘¨‍πŸ’»
Jefferson Mwangolo
Co‑Author
Inactive ✖
πŸ‘©‍πŸŽ“
Florence Wavinya
Guest Author
Inactive ✖
πŸ‘©‍πŸŽ“
Esther Njeri
Guest Author
Inactive ✖
πŸ‘©‍πŸŽ“
Clemence Mwangolo
Guest Author

Followers