Skip to content

Indicators

Supports TA-Lib and pandas_ta with hundreds of technical indicators. Calculate thousands of stocks over long time periods with a single line, suitable for individual stock analysis and machine learning.

Installing TA-Lib

For more parameters and functions, see the TA-Lib official documentation.

Computing RSI for All Stocks

Use data.indicator() to compute RSI (timeperiod=14) for every stock in the active market:

from finlab import data

data.set_market('us')
rsi = data.indicator('RSI', timeperiod=14)
rsi[['AAPL', 'MSFT', 'NVDA']].tail()

The result is a DataFrame indexed by date with one column per ticker. The first 14 days are NaN because there is not enough data yet. By default the indicator uses unadjusted prices; pass adjust_price=True for adjusted prices, or resample='W' for weekly bars.

Technical Indicators with Two Time Series (Stochastic Oscillator KD)

Use data.indicator('STOCH') to compute the Stochastic Oscillator (KD values), which returns two DataFrames (K and D).

from finlab import data

data.set_market('us')
k, d = data.indicator('STOCH')
k

Stock Screening Using KD Values

List stocks where K > D on the most recent day:

signal = (k > d).iloc[-1]
signal[signal].index  # tickers with K above D on the latest date

(k > d).iloc[-1] is a boolean Series indexed by ticker for the latest date.

Indicators Beyond the Classic Set

data.indicator() looks up TA-Lib first, by upper-case name. Newer TA-Lib releases include indicators such as SuperTrend, which returns the trend line and its direction (1 up, -1 down):

from finlab import data

data.set_market('us')
supertrend, direction = data.indicator('SUPERTREND')

When TA-Lib has no function of that name, a lower-case name is computed with pandas_ta if it is installed (it may be slower).

Custom Indicators (Pandas Operations)

Build custom indicators directly with Pandas operations, no TA-Lib required:

Price & Volume Indicators

from finlab import data

data.set_market('us')
close = data.get('price:adj_close')
volume = data.get('price:volume')
revenue = data.get('us_income_statement:revenue')  # quarterly

# Momentum: N-day return
momentum_20 = close / close.shift(20) - 1

# Moving average deviation
ma60 = close.average(60)
bias = (close - ma60) / ma60

# Price-volume divergence: price at high but volume shrinking
price_high = close == close.rolling(60).max()
volume_low = volume < volume.average(20)
divergence = price_high & volume_low

# Quarterly revenue growth
rev_yoy = revenue / revenue.shift(4) - 1  # year-over-year
rev_qoq = revenue / revenue.shift(1) - 1  # quarter-over-quarter
rev_trend = (
    revenue.rolling(2).mean() / revenue.rolling(4).mean() - 1
)  # last 2 vs last 4 quarters

Factor Standardization with .cs

eps = data.get('us_income_statement:eps_diluted')
net_income = data.get('us_income_statement:net_income')
equity = data.get('us_balance_sheet:total_stockholders_equity')

raw_close = data.get('price:close')  # unadjusted price for valuation
pe = raw_close / eps.rolling(4).sum()  # trailing-12-month P/E
roe = net_income / equity[equity > 0]  # quarterly ROE, positive equity only

# Multi-factor composite: standardize then equal-weight sum
score = roe.cs.zscore() + (-pe).cs.zscore()  # higher ROE, lower PE = better

# Select top 10% by composite score
position = score.cs.rank() > 0.9

Sector Neutralization with .sector

# Problem: raw ROE selection overweights high-ROE sectors (e.g., technology)
# Solution: within-sector standardization selects "sector standouts"
profile = data.get('us_company_profile')
sectors = dict(zip(profile['symbol'], profile['sector']))

roe_neutral = roe.sector(sectors).zscore()  # sector-neutralized
rev_neutral = rev_yoy.sector(sectors).zscore()  # revenue growth also neutralized

# Composite factor
score = roe_neutral + rev_neutral
position = score.cs.rank() > 0.8

# Or use within-sector ranking directly
position = roe.sector(sectors).rank() > 0.7  # top 30% ROE within each sector

Without a mapping, .sector uses the Taiwan industry classification; for U.S. stocks always pass one.

Weight Construction with .weight

from finlab.backtest import sim

position = score.cs.rank() > 0.8

# Refined weights: inverse volatility + turnover limit
weights = position.weight.inverse_volatility(
    window=60, price=close
).weight.limit_turnover(max_turnover=0.3)

report = sim(weights, resample='Q')

inverse_volatility() reads Taiwan prices unless you pass price. weight.cap_industry() caps Taiwan industry weights and is Taiwan market only.

Full accessor documentation

See finlab.dataframe reference for all .cs, .sector, and .weight methods.