Docs
Open Studio

US examples

ML example strategy

On this page5

Use machine learning for stock selection: an XGBoost model, integrated through qlib, predicts each stock's future return, and a technical trend filter turns the predictions into a portfolio. It is an introductory example for investors who want to see the full machine-learning workflow end to end.

Strategy logic

  1. Features: price-to-book (PB) and price-to-earnings (PE) of S&P 500 stocks, sampled weekly.
  2. Label: the forward 12-week return, the price change over the next 12 weeks from each observation.
  3. Train and predict: train on data before 2020-01-01 and predict everything after it.
  4. Trend filter: only stocks whose price is above their 60-day moving average are eligible.
  5. Portfolio: hold the 10 eligible stocks with the highest predicted return, rebalanced quarterly.

Code

Install the Qlib extra before importing finlab.ml.qlib. It includes XGBoost and the other dependencies used below:

pip install 'finlab[qlib]'
ml_strategy.py
from finlab import data
from finlab.ml import feature as mlf
from finlab.ml import label as mll
import finlab.ml.qlib as q
from finlab.backtest import sim
from finlab.markets.us import USMarket

data.set_market('us')

# Features: price-to-book and price-to-earnings of S&P 500 stocks
with data.us_universe(index='SP500'):
    price = data.get('price:close')
    market_value = data.get('etl:us_market_value')
    equity = data.get('us_balance_sheet:total_stockholders_equity')
    eps = data.get('us_income_statement:eps_diluted')

ttm_eps = eps.rolling(4).sum()  # Trailing-12-month EPS
features = mlf.combine(
    {
        # Ratios only where the denominator is positive
        'pb': market_value / equity[equity > 0],  # Price-to-book
        'pe': price / ttm_eps[ttm_eps > 0],  # Price-to-earnings
    },
    resample='W',
)

# Label: forward 12-week return
label = mll.return_percentage(features.index, resample='W', period=12)

# Train before 2020-01-01, predict after
is_train = features.index.get_level_values('datetime') < '2020-01-01'

X_train, y_train = features[is_train], label[is_train]
X_test = features[~is_train]

model = q.XGBModel()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)

# Top 10 predictions among stocks above their 60-day moving average
close = data.get('price:adj_close')

position = y_pred[close > close.average(60)].is_largest(10)

report = sim(position, resample='Q', market=USMarket(), fee_ratio=0.001, tax_ratio=0)
report.display()

How each part works

  • Features: PB and PE are computed from daily market value and unadjusted price, and from quarterly statements aligned to each SEC filing date. These value metrics capture how the market prices a company relative to its fundamentals. feature.combine() assembles the feature matrix with weekly resampling. The resulting features DataFrame has a MultiIndex of (datetime, instrument) and one column per feature, so each row is one stock at one point in time; look at it with features.head().
  • Label: the label uses future prices that would not be known at the time of prediction. This is intentional: the model learns to map current features to future outcomes. In the backtest, predictions are made only on out-of-sample data to avoid look-ahead bias.
  • Train/test split: everything before 2020-01-01 is training data and everything after is the test set. Unlike traditional machine learning, where random splitting is acceptable, financial time series must be split chronologically to prevent data leakage, so the model never sees future data during training. The qlib XGBModel wrapper handles XGBoost training and prediction.
  • Predictions: y_pred is a FinlabDataFrame with one row per date and one column per stock, holding the model's predicted future return for each stock and date.
  • Trend filter: close > close.average(60) serves two purposes. It reduces exposure to downtrending stocks, since even when the model predicts high returns, a stock in a downtrend may keep falling before the prediction materializes. And it improves risk-adjusted returns: pairing fundamental or ML signals with a trend filter is a standard quant technique.
  • Rebalancing: resample='Q' (quarterly) matches the prediction horizon (12 weeks is about one quarter). The portfolio is rebuilt each quarter from fresh predictions, so the model adapts to changing markets while turnover stays limited.

How to adjust it

  • resample='W' (features): weekly feature sampling balances granularity with noise reduction.

  • period=12 (label): the 12-week forward return is the prediction target.

  • resample='Q' (backtest): quarterly portfolio rebalancing.

  • is_largest(10): a concentrated portfolio of the top 10 predictions.

  • fee_ratio=0.001: a 0.1% commission.

  • tax_ratio=0: US stock trades carry no transaction tax.

  • More features: PB and PE are kept minimal for this introduction. In practice, expand the feature set with:

    • growth metrics: revenue growth, earnings growth (us_income_statement:revenue, us_income_statement:net_income);
    • profitability: ROE, operating margin (us_income_statement:net_income / us_balance_sheet:total_stockholders_equity, us_income_statement:operating_income / us_income_statement:revenue);
    • technical indicators: RSI, MACD, moving average ratios;
    • momentum: 6-month and 12-month price returns.

    More features can improve predictive power but also raise the risk of overfitting. Cross-validation and feature importance analysis are essential when scaling up.

↑↓ Move Enter Open Esc Close