Docs
Open Studio

US examples

Price deviation & ROE filter

On this page4

A momentum strategy with a fundamental guard rail: buy the strongest 60-day gainers, but only from profitable companies. Pure momentum can load up on names that surged on hype; requiring a positive ROE steers the portfolio toward stocks whose price momentum is backed by actual earnings.

Strategy logic

  1. Universe: companies with a market capitalization above $1 billion.
  2. 60-day momentum: rank them by their price change over the last 60 trading days and take the top 30.
  3. ROE filter: of those, keep only stocks with a positive ROE (Return on Equity), which drops companies that are currently unprofitable.
  4. Rebalance monthly.

Code

strategy.py
from finlab import data
from finlab.backtest import sim
from finlab.markets.us import USMarket

data.set_market('us')

close = data.get('price:adj_close')
close = close[close.index.dayofweek < 5]  # remove weekends
market_value = data.get('etl:us_market_value')

# Compute ROE from financial statements, positive equity only
net_income = data.get('us_income_statement:net_income')
equity = data.get('us_balance_sheet:total_stockholders_equity')
roe = net_income / equity[equity > 0]

# Top 30 by 60-day momentum among stocks above $1 billion market cap,
# filtered by positive ROE
momentum = close / close.shift(60)
position = momentum[market_value > 1e9].is_largest(30) & (roe > 0)

# Backtest with monthly (M) rebalancing
report = sim(position, resample='M', market=USMarket(), fee_ratio=0.001, tax_ratio=0)
report.display()

How to adjust it

  • close / close.shift(60): the 60-day price ratio (1 + the return over 60 trading days). It captures about one quarter of momentum, between short-term noise and long-term mean reversion, and avoids the reversals that plague very short-term momentum.
  • market_value > 1e9: limits the universe to companies above $1 billion. See the warning below before loosening it.
  • is_largest(30): the 30 stocks with the highest 60-day returns. Concentrating on the top performers tilts the portfolio toward momentum.
  • equity[equity > 0]: leaves ROE empty for companies with negative equity, where a loss would otherwise divide into a positive ROE.
  • roe > 0: a simple profitability screen. It avoids speculative or distressed companies whose price surges may come from short squeezes or speculative trading rather than improving fundamentals.
  • us_income_statement:net_income and us_balance_sheet:total_stockholders_equity: ROE is computed from these statement fields directly, because point-in-time ROE is not available as a pre-computed field.
  • resample='M': monthly rebalancing follows how often new financial data arrives, gives positions time to play out and keeps transaction costs reasonable.
↑↓ Move Enter Open Esc Close