TA with Talib
Compute industry-standard technical indicators using TA-Lib, the battle-tested C library with Python bindings that provides reliable, numerically stable, and highly efficient indicator calculations used in professional trading systems worldwide.
Technical Indicators Framework — TA-Lib
This notebook defines a standardized protocol for computing technical indicators using the TA-Lib library on OHLCV data. It covers the same indicator categories as Notebook 18 using TA-Lib's compiled C backend.
1. Dependency Installation
# TA-Lib requires the C library installed at the OS level first:
# Ubuntu/Debian: sudo apt-get install ta-lib
# macOS: brew install ta-lib
# Then install the Python wrapper:
!pip install TA-LibCollecting TA-Lib Downloading ta_lib-0.6.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (23 kB) Collecting build (from TA-Lib) Downloading build-1.5.0-py3-none-any.whl.metadata (5.7 kB) Requirement already satisfied: numpy in /usr/local/lib/python3.12/dist-packages (from TA-Lib) (2.0.2) Requirement already satisfied: packaging>=24.0 in /usr/local/lib/python3.12/dist-packages (from build->TA-Lib) (26.1) Collecting pyproject_hooks (from build->TA-Lib) Downloading pyproject_hooks-1.2.0-py3-none-any.whl.metadata (1.3 kB) Downloading ta_lib-0.6.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (4.1 MB) [2K [90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[0m [32m4.1/4.1 MB[0m [31m29.5 MB/s[0m eta [36m0:00:00[0m [?25hDownloading build-1.5.0-py3-none-any.whl (26 kB) Downloading pyproject_hooks-1.2.0-py3-none-any.whl (10 kB) Installing collected packages: pyproject_hooks, build, TA-Lib Successfully installed TA-Lib-0.6.8 build-1.5.0 pyproject_hooks-1.2.0
2. Library Imports
import warnings
warnings.filterwarnings("ignore")
import pandas as pd
import numpy as np
import talib
import plotly.graph_objects as go
from plotly.subplots import make_subplots3. What Is TA-Lib?
TA-Lib (Technical Analysis Library) is a widely used open-source library that implements over 200 technical indicators in compiled C code. The Python wrapper exposes these functions as simple array-in, array-out calls. Because the core is compiled C rather than Python, TA-Lib is significantly faster than pandas-based implementations — relevant for large datasets or live signal computation. The trade-off is that it requires the C library to be installed at the operating system level before the Python wrapper can be used.
4. Dummy Dataset
def generate_data(periods: int) -> pd.DataFrame:
"""Generates a larger synthetic OHLCV dataset with more realistic price fluctuations."""
start_date = pd.to_datetime("2024-01-01 00:00:00+00:00")
datetime_index = pd.date_range(start_date, periods=periods, freq="1min", tz="UTC")
price_data = []
last_close = 42000 # Starting price
volatility_scale = 0.005 # Controls the general magnitude of price changes
wick_deviation_scale = 0.002 # Controls how much wicks extend beyond body
for i in range(periods):
# Open price drifts slightly from the previous close
open_price = last_close + np.random.normal(0, last_close * volatility_scale * 0.1)
# Simulate a price change to determine the closing price
price_change = np.random.normal(0, last_close * volatility_scale)
close_price = open_price + price_change
# Determine the high and low of the candle body
body_high = max(open_price, close_price)
body_low = min(open_price, close_price)
# Simulate wicks extending beyond the body
# High wick should be above the body_high
high_wick_extension = np.abs(np.random.normal(0, last_close * wick_deviation_scale))
high_price = body_high + high_wick_extension
# Low wick should be below the body_low
low_wick_extension = np.abs(np.random.normal(0, last_close * wick_deviation_scale))
low_price = body_low - low_wick_extension
# Ensure OHLC integrity: High must be the absolute highest, Low the absolute lowest
high_price = max(high_price, open_price, close_price)
low_price = min(low_price, open_price, close_price)
# Ensure High is never less than Low
if high_price < low_price:
high_price, low_price = low_price, high_price # Swap if somehow invalid
# Ensure all values are positive integers
open_price = max(1, int(open_price))
high_price = max(1, int(high_price))
low_price = max(1, int(low_price))
close_price = max(1, int(close_price))
price_data.append({
"open": open_price,
"high": high_price,
"low": low_price,
"close": close_price
})
last_close = close_price # Update last_close for the next iteration
df_large = pd.DataFrame(price_data, index=datetime_index)
df_large.index.name = "datetime"
# Simulate volume with some fluctuation
df_large["volume"] = np.random.uniform(100.0, 500.0, periods)
return df_large
periods = 500 # Generate 500 data points
df_large = generate_data(periods)
df_large["datetime"] = df_large.index.to_series()
df_large = df_large.reset_index(drop=True)
open_ = df_large["open"].to_numpy(dtype=float)
high = df_large["high"].to_numpy(dtype=float)
low = df_large["low"].to_numpy(dtype=float)
close = df_large["close"].to_numpy(dtype=float)
volume = df_large["volume"].to_numpy(dtype=float)Code Logic
- TA-Lib functions accept numpy arrays, not pandas Series. Each OHLCV column is extracted as a
float64numpy array before being passed to TA-Lib functions.
5. Indicator Computation Function
def compute_indicators_talib(df: pd.DataFrame) -> pd.DataFrame:
df = df.copy()
o = df["open"].to_numpy(dtype=float)
h = df["high"].to_numpy(dtype=float)
l = df["low"].to_numpy(dtype=float)
c = df["close"].to_numpy(dtype=float)
v = df["volume"].to_numpy(dtype=float)
df["sma_10"] = talib.SMA(c, timeperiod=10)
df["ema_10"] = talib.EMA(c, timeperiod=10)
df["rsi_14"] = talib.RSI(c, timeperiod=14)
macd, signal, hist = talib.MACD(c, fastperiod=12, slowperiod=26, signalperiod=9)
df["macd"] = macd
df["macd_signal"] = signal
df["macd_hist"] = hist
upper, mid, lower = talib.BBANDS(c, timeperiod=10)
df["bb_upper"] = upper
df["bb_middle"] = mid
df["bb_lower"] = lower
df["atr_10"] = talib.ATR(h, l, c, timeperiod=10)
df["obv"] = talib.OBV(c, v)
slowk, slowd = talib.STOCH(h, l, c)
df["stoch_k"] = slowk
df["stoch_d"] = slowd
return df
df_indicators_large = compute_indicators_talib(df_large)
print("--- Indicators Output ---")
display(df_indicators_large.tail())
df_indicators_large.info()--- Indicators Output ---
| open | high | low | close | volume | datetime | sma_10 | ema_10 | rsi_14 | macd | macd_signal | macd_hist | bb_upper | bb_middle | bb_lower | atr_10 | obv | stoch_k | stoch_d | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 495 | 39615 | 40075 | 39560 | 39919 | 236.959469 | 2024-01-01 08:15:00+00:00 | 39182.2 | 39307.461538 | 69.650163 | 190.837604 | 113.086714 | 77.750890 | 39851.380275 | 39182.2 | 38513.019725 | 298.355099 | 5781.818667 | 89.939434 | 85.032844 |
| 496 | 39921 | 39983 | 39815 | 39868 | 289.310518 | 2024-01-01 08:16:00+00:00 | 39253.3 | 39409.377622 | 68.062363 | 225.236573 | 135.516686 | 89.719887 | 40037.810064 | 39253.3 | 38468.789936 | 285.319589 | 5492.508149 | 88.049788 | 89.628123 |
| 497 | 39889 | 39903 | 39750 | 39804 | 339.996727 | 2024-01-01 08:17:00+00:00 | 39355.6 | 39481.127145 | 66.028146 | 244.515121 | 157.316373 | 87.198748 | 40133.851990 | 39355.6 | 38577.348010 | 272.087630 | 5152.511422 | 79.977152 | 85.988791 |
| 498 | 39816 | 39910 | 39814 | 39894 | 164.902418 | 2024-01-01 08:18:00+00:00 | 39456.0 | 39556.194937 | 67.499209 | 264.012386 | 178.655575 | 85.356810 | 40227.097919 | 39456.0 | 38684.902081 | 255.478867 | 5317.413840 | 76.437253 | 81.488064 |
| 499 | 39876 | 40342 | 39683 | 40184 | 159.507031 | 2024-01-01 08:19:00+00:00 | 39579.0 | 39670.341312 | 71.744906 | 299.413236 | 202.807107 | 96.606128 | 40382.285752 | 39579.0 | 38775.714248 | 295.830980 | 5476.920872 | 75.765654 | 77.393353 |
<class 'pandas.core.frame.DataFrame'> RangeIndex: 500 entries, 0 to 499 Data columns (total 19 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 open 500 non-null int64 1 high 500 non-null int64 2 low 500 non-null int64 3 close 500 non-null int64 4 volume 500 non-null float64 5 datetime 500 non-null datetime64[ns, UTC] 6 sma_10 491 non-null float64 7 ema_10 491 non-null float64 8 rsi_14 486 non-null float64 9 macd 467 non-null float64 10 macd_signal 467 non-null float64 11 macd_hist 467 non-null float64 12 bb_upper 491 non-null float64 13 bb_middle 491 non-null float64 14 bb_lower 491 non-null float64 15 atr_10 490 non-null float64 16 obv 500 non-null float64 17 stoch_k 492 non-null float64 18 stoch_d 492 non-null float64 dtypes: datetime64[ns, UTC](1), float64(14), int64(4) memory usage: 74.3 KB
Code Logic
- All TA-Lib functions return numpy arrays of the same length as the input, with leading
NaNvalues where insufficient data exists to compute the indicator. - Multi-output functions (
MACD,BBANDS,STOCH) return multiple arrays via tuple unpacking, each assigned to its own column. - Indicator definitions match Notebook 18 — the library differs but the mathematical output is identical.
6. Data Visualization with Plotly
6.1 Candlestick Chart with Moving Averages (SMA, EMA)
fig = go.FigureWidget(data=[
go.Candlestick(
x=df_indicators_large["datetime"],
open=df_indicators_large['open'],
high=df_indicators_large['high'],
low=df_indicators_large['low'],
close=df_indicators_large['close'],
name='Price'
)
])
# Add SMA_10
fig.add_trace(go.Scatter(
x=df_indicators_large["datetime"],
y=df_indicators_large['sma_10'],
mode='lines',
name='SMA 10',
line=dict(color='blue', width=1)
))
# Add EMA_10
fig.add_trace(go.Scatter(
x=df_indicators_large["datetime"],
y=df_indicators_large['ema_10'],
mode='lines',
name='EMA 10',
line=dict(color='orange', width=1)
))
fig.update_layout(
title_text='Candlestick Chart with SMA and EMA',
xaxis_rangeslider_visible=False,
xaxis_title='Date',
yaxis_title='Price',
height=600,
yaxis=dict(autorange=True) # Ensure y-axis scales to visible candles
)
fig.show()6.2 Candlestick Chart with Bollinger Bands
fig = go.FigureWidget(data=[
go.Candlestick(
x=df_indicators_large["datetime"],
open=df_indicators_large['open'],
high=df_indicators_large['high'],
low=df_indicators_large['low'],
close=df_indicators_large['close'],
name='Price'
)
])
# Add Bollinger Bands
fig.add_trace(go.Scatter(
x=df_indicators_large["datetime"],
y=df_indicators_large['bb_lower'],
mode='lines',
name='BB Lower',
line=dict(color='red', width=1)
))
fig.add_trace(go.Scatter(
x=df_indicators_large["datetime"],
y=df_indicators_large['bb_middle'],
mode='lines',
name='BB Middle',
line=dict(color='green', width=1, dash='dot')
))
fig.add_trace(go.Scatter(
x=df_indicators_large["datetime"],
y=df_indicators_large['bb_upper'],
mode='lines',
name='BB Upper',
line=dict(color='red', width=1)
))
fig.update_layout(
title_text='Candlestick Chart with Bollinger Bands',
xaxis_rangeslider_visible=False,
xaxis_title='Date',
yaxis_title='Price',
height=600,
yaxis=dict(autorange=True) # Ensure y-axis scales to visible candles
)
fig.show()6.3 Relative Strength Index (RSI)
fig = make_subplots(
rows=2, cols=1,
shared_xaxes=True,
vertical_spacing=0.03,
row_heights=[0.7, 0.3]
)
# Candlestick chart
fig.add_trace(go.Candlestick(
x=df_indicators_large["datetime"],
open=df_indicators_large['open'],
high=df_indicators_large['high'],
low=df_indicators_large['low'],
close=df_indicators_large['close'],
name='Price'
), row=1, col=1)
# RSI chart
fig.add_trace(go.Scatter(
x=df_indicators_large["datetime"],
y=df_indicators_large['rsi_14'],
mode='lines',
name='RSI 14',
line=dict(color='purple', width=1)
), row=2, col=1)
# Add RSI overbought/oversold levels
fig.add_hline(y=70, line_dash="dot", line_color="red", row=2, col=1)
fig.add_hline(y=30, line_dash="dot", line_color="green", row=2, col=1)
fig.update_layout(
title_text='Candlestick Chart with RSI',
xaxis_rangeslider_visible=False,
xaxis_title='Date',
yaxis_title='Price',
height=800,
yaxis=dict(autorange=True) # Ensure y-axis scales to visible candles for candlestick
)
fig.update_yaxes(title_text="RSI", row=2, col=1)
fig.show()6.4 Moving Average Convergence Divergence (MACD)
fig = make_subplots(
rows=2, cols=1,
shared_xaxes=True,
vertical_spacing=0.03,
row_heights=[0.7, 0.3]
)
# Candlestick chart
fig.add_trace(go.Candlestick(
x=df_indicators_large["datetime"],
open=df_indicators_large['open'],
high=df_indicators_large['high'],
low=df_indicators_large['low'],
close=df_indicators_large['close'],
name='Price'
), row=1, col=1)
# MACD traces
fig.add_trace(go.Scatter(
x=df_indicators_large["datetime"],
y=df_indicators_large['macd'],
mode='lines',
name='MACD',
line=dict(color='blue', width=1)
), row=2, col=1)
fig.add_trace(go.Scatter(
x=df_indicators_large["datetime"],
y=df_indicators_large['macd_signal'],
mode='lines',
name='Signal',
line=dict(color='red', width=1)
), row=2, col=1)
fig.add_trace(go.Bar(
x=df_indicators_large["datetime"],
y=df_indicators_large['macd_hist'],
name='Histogram',
marker_color='green' # Green for positive, could add logic for red for negative
), row=2, col=1)
fig.update_layout(
title_text='Candlestick Chart with MACD',
xaxis_rangeslider_visible=False,
xaxis_title='Date',
yaxis_title='Price',
height=800,
yaxis=dict(autorange=True) # Ensure y-axis scales to visible candles for candlestick
)
fig.update_yaxes(title_text="MACD", row=2, col=1)
fig.show()6.5 Average True Range (ATR)
fig = make_subplots(
rows=2, cols=1,
shared_xaxes=True,
vertical_spacing=0.03,
row_heights=[0.7, 0.3]
)
# Candlestick chart
fig.add_trace(go.Candlestick(
x=df_indicators_large["datetime"],
open=df_indicators_large['open'],
high=df_indicators_large['high'],
low=df_indicators_large['low'],
close=df_indicators_large['close'],
name='Price'
), row=1, col=1)
# ATR chart
fig.add_trace(go.Scatter(
x=df_indicators_large["datetime"],
y=df_indicators_large['atr_10'],
mode='lines',
name='ATR 10',
line=dict(color='darkorange', width=1)
), row=2, col=1)
fig.update_layout(
title_text='Candlestick Chart with ATR',
xaxis_rangeslider_visible=False,
xaxis_title='Date',
yaxis_title='Price',
height=800,
yaxis=dict(autorange=True) # Ensure y-axis scales to visible candles for candlestick
)
fig.update_yaxes(title_text="ATR", row=2, col=1)
fig.show()6.6 On-Balance Volume (OBV)
fig = make_subplots(
rows=2, cols=1,
shared_xaxes=True,
vertical_spacing=0.03,
row_heights=[0.7, 0.3]
)
# Candlestick chart
fig.add_trace(go.Candlestick(
x=df_indicators_large["datetime"],
open=df_indicators_large['open'],
high=df_indicators_large['high'],
low=df_indicators_large['low'],
close=df_indicators_large['close'],
name='Price'
), row=1, col=1)
# OBV chart
fig.add_trace(go.Scatter(
x=df_indicators_large["datetime"],
y=df_indicators_large['obv'],
mode='lines',
name='OBV',
line=dict(color='brown', width=1)
), row=2, col=1)
fig.update_layout(
title_text='Candlestick Chart with OBV',
xaxis_rangeslider_visible=False,
xaxis_title='Date',
yaxis_title='Price',
height=800,
yaxis=dict(autorange=True) # Ensure y-axis scales to visible candles for candlestick
)
fig.update_yaxes(title_text="OBV", row=2, col=1)
fig.show()6.7 Stochastic Oscillator
fig = make_subplots(
rows=2, cols=1,
shared_xaxes=True,
vertical_spacing=0.03,
row_heights=[0.7, 0.3]
)
# Candlestick chart
fig.add_trace(go.Candlestick(
x=df_indicators_large["datetime"],
open=df_indicators_large['open'],
high=df_indicators_large['high'],
low=df_indicators_large['low'],
close=df_indicators_large['close'],
name='Price'
), row=1, col=1)
# Stochastic Oscillator chart
fig.add_trace(go.Scatter(
x=df_indicators_large["datetime"],
y=df_indicators_large['stoch_k'],
mode='lines',
name='%K',
line=dict(color='blue', width=1)
), row=2, col=1)
fig.add_trace(go.Scatter(
x=df_indicators_large["datetime"],
y=df_indicators_large['stoch_d'],
mode='lines',
name='%D',
line=dict(color='red', width=1)
), row=2, col=1)
# Add overbought/oversold levels for Stochastic
fig.add_hline(y=80, line_dash="dot", line_color="red", row=2, col=1)
fig.add_hline(y=20, line_dash="dot", line_color="green", row=2, col=1)
fig.update_layout(
title_text='Candlestick Chart with Stochastic Oscillator',
xaxis_rangeslider_visible=False,
xaxis_title='Date',
yaxis_title='Price',
height=800,
yaxis=dict(autorange=True) # Ensure y-axis scales to visible candles for candlestick
)
fig.update_yaxes(title_text="Stochastic", row=2, col=1)
fig.show()Conclusion
This notebook demonstrates how to effectively compute and visualize various technical indicators using the TA-Lib library, a powerful tool for financial analysis. By leveraging TA-Lib's compiled C backend, we achieved efficient calculations on OHLCV data. The visualizations provided a clear understanding of each indicator's behavior relative to price action, highlighting their utility in identifying potential market trends, momentum, volatility, and overbought/oversold conditions.
Key takeaways include:
- Efficiency: TA-Lib's performance advantage, especially with large datasets.
- Comprehensive Indicators: The ability to calculate a wide range of popular technical indicators.
- Visualization: The importance of plotting indicators alongside price data for meaningful analysis.
This framework can be extended for further analysis, strategy development, and backtesting in financial markets.