← Back to Blog
TUTORIALS

pandas-ta: A Complete Indicator Library for Crypto Backtesting

June 27, 2026 · 8 min read · LMEX.AI

Writing your own RSI in pandas takes 4 lines. Writing the next 50 indicators takes a week. By the time you've implemented Bollinger Bands, MACD, Stochastic, Ichimoku, and a dozen oscillators, you've spent more time on indicator code than strategy logic. pandas-ta gives you 130+ indicators in a single import, all properly vectorised, all consistent with TradingView's calculations.


This article walks through using pandas-ta for crypto strategy research, the indicators worth knowing, and where the library's defaults don't match what you'd expect.


Why pandas-ta


Three reasons it's worth learning over rolling your own:


**Speed.** Native pandas operations under the hood. A full set of 20 indicators on 10,000 candles runs in under a second. Hand-rolled equivalents are often 5-10× slower because they accidentally use loops where pandas has vectorised primitives.


**Correctness.** Subtle indicator math errors are common in hand-rolled code. RSI's Wilder smoothing versus EMA smoothing changes signals materially. MACD with the wrong signal line period gives plausible-looking but wrong results. pandas-ta matches TradingView's calculations exactly, which matters when comparing backtest signals to chart signals.


**Composability.** Every indicator returns a series or DataFrame that integrates cleanly with the rest of your pipeline. No glue code, no shape mismatches.


Setup


Install via pip:


pip install pandas-ta

Basic usage on a DataFrame from CCXT:


import ccxt
import pandas as pd
import pandas_ta as ta

exchange = ccxt.lmex()
candles = exchange.fetch_ohlcv('BTC-PERP', '1h', limit=500)
df = pd.DataFrame(candles, columns=['ts', 'open', 'high', 'low', 'close', 'volume'])
df['ts'] = pd.to_datetime(df['ts'], unit='ms')
df = df.set_index('ts')

# Add indicators
df['rsi_14'] = ta.rsi(df['close'], length=14)
df['atr_14'] = ta.atr(df['high'], df['low'], df['close'], length=14)
df['sma_50'] = ta.sma(df['close'], length=50)

# Bollinger Bands return a DataFrame
bb = ta.bbands(df['close'], length=20, std=2)
df = df.join(bb)

# MACD returns a DataFrame
macd = ta.macd(df['close'], fast=12, slow=26, signal=9)
df = df.join(macd)

The column naming convention is `INDICATOR_PARAMS` — `RSI_14`, `BBL_20_2.0` (lower band), `BBM_20_2.0` (middle), `MACD_12_26_9`, `MACDh_12_26_9` (histogram), and so on. Slightly verbose but disambiguates clean across parameter sets.


Indicators worth knowing


For crypto strategies specifically, these are the high-utility ones:


Trend identification:

  • `ema()` — Exponential MA. The standard trend filter.
  • `adx()` — Average Directional Index. Trend strength on a 0-100 scale.
  • `supertrend()` — Trend-following indicator combining ATR with directional logic.
  • `ichimoku()` — Full Ichimoku Cloud system. Multi-timeframe trend view in one indicator set.

  • Momentum:

  • `rsi()` — Standard 14-period RSI. Overbought/oversold and divergence detection.
  • `macd()` — MACD with signal line and histogram. Trend-following momentum.
  • `stoch()` — Stochastic oscillator. Faster than RSI; better for short timeframes.

  • Volatility:

  • `atr()` — Average True Range. Foundation for stop placement and position sizing.
  • `bbands()` — Bollinger Bands. Mean reversion and breakout signals.
  • `kc()` — Keltner Channels. Like Bollinger Bands but using ATR instead of standard deviation.

  • Volume:

  • `obv()` — On-Balance Volume. Cumulative volume in direction of price.
  • `vwap()` — Volume-Weighted Average Price. Daily resets by default.
  • `mfi()` — Money Flow Index. Volume-weighted RSI.

  • These cover 90% of indicator needs for crypto strategy research.


    A complete strategy pipeline


    Bringing it together — a mean-reversion signal generator using multiple indicators for confirmation:


    import pandas_ta as ta
    
    def generate_mean_reversion_signals(df):
        # Volatility regime
        df['atr'] = ta.atr(df['high'], df['low'], df['close'], length=14)
        df['atr_pct'] = df['atr'] / df['close']
        
        # Bollinger Bands for entry levels
        bb = ta.bbands(df['close'], length=20, std=2)
        df = df.join(bb)
        
        # RSI for overbought/oversold
        df['rsi'] = ta.rsi(df['close'], length=14)
        
        # ADX for regime filter
        adx = ta.adx(df['high'], df['low'], df['close'], length=14)
        df = df.join(adx)
        
        # Long entry: price below lower BB, RSI < 30, ADX < 20 (ranging regime)
        df['long_entry'] = (
            (df['close'] < df['BBL_20_2.0']) &
            (df['rsi'] < 30) &
            (df['ADX_14'] < 20)
        )
        
        # Short entry: opposite
        df['short_entry'] = (
            (df['close'] > df['BBU_20_2.0']) &
            (df['rsi'] > 70) &
            (df['ADX_14'] < 20)
        )
        
        # Exit: price returns to middle band
        df['exit'] = abs(df['close'] - df['BBM_20_2.0']) / df['close'] < 0.002
        
        return df

    Each indicator is calculated in one line. The strategy logic is readable. Adding new conditions (volume confirmation, MACD divergence) is a one-line addition.


    Combining with vectorbt


    pandas-ta and vectorbt complement each other well — pandas-ta generates the signals, vectorbt runs the backtest:


    import vectorbt as vbt
    
    df = generate_mean_reversion_signals(df)
    
    pf = vbt.Portfolio.from_signals(
        df['close'],
        entries=df['long_entry'],
        exits=df['exit'],
        short_entries=df['short_entry'],
        short_exits=df['exit'],
        init_cash=10000,
        fees=0.0006,
        slippage=0.0005,
        freq='1H',
    )
    
    print(pf.stats())

    End-to-end: from raw OHLCV to a fully backtested strategy in ~30 lines, with realistic transaction costs and proper signal mechanics.


    Where the defaults bite


    A few pandas-ta quirks that catch new users:


    **EMA initialisation.** pandas-ta's EMA starts from the first available data point and ramps up over N periods. For accurate signals, drop the first 2-3× length data points before evaluating. Otherwise early signals are based on under-warm indicator values.


    **VWAP resets.** By default, `ta.vwap()` resets daily at midnight UTC. This is what TradingView does. If your strategy uses session-relative VWAP (resetting at market open, for example), pass `anchor='D'` or override with custom logic.


    **RSI calculation.** pandas-ta uses Wilder's smoothing by default, matching TradingView. Some libraries (notably older versions of TA-Lib) use simple moving averages, which gives slightly different values. If you're cross-referencing with another tool, verify which version it uses.


    **ADX warm-up period.** ADX needs roughly 2× length bars to stabilise. Signals based on ADX in the first 30-40 bars of any series are unreliable.


    Frequently Asked Questions


    Q: pandas-ta vs TA-Lib?

    TA-Lib is older, more battle-tested, and harder to install (C library dependency). pandas-ta is pure Python, easier to install, and has more indicators. For crypto retail use, pandas-ta is the better default. For institutional production code, TA-Lib's stability still wins.


    Q: Can I write custom indicators?

    Yes — pandas-ta has a clean extension API. Define a function that takes pandas Series inputs and returns a Series, register it with `ta.indicators` or just use it directly in your pipeline. Custom indicators behave like the built-ins.


    Q: Does it handle multi-timeframe analysis?

    Not natively — you resample your DataFrame to the desired timeframe first, then run indicators. The library is timeframe-agnostic, which is the right design but requires the user to manage timeframe state.


    Q: How fast is it on large datasets?

    1M candles, full indicator suite: ~10-30 seconds depending on which indicators. Faster than hand-rolled by 5-10× because of vectorisation. Still slow enough that parameter sweeps need to be batched intelligently — use vectorbt for that.


    Related Articles


    → Backtesting Crypto Strategies with vectorbt: A Complete Guide
    → Backtesting Your LMEX Trading Bot in Python: A Practical Guide
    → Building a Crypto Perpetuals Trading Bot in Python: Complete Guide
    ← All ArticlesBuild a Bot →