Building Trading Features from Technical Indicators
Summary
This document describes a dataframe wrapper that adds groups of technical analysis features from price and volume columns. Its feature set covers volume measures such as on-balance volume and volume-weighted average price; volatility bands and range measures; trend indicators including moving averages and MACD; momentum indicators; and daily and cumulative returns. A combined function applies the groups together, while separate functions allow callers to add selected categories.
The wrapper uses configurable input column names, an optional output prefix, and a fill option. A vectorized setting omits some indicators that are not included in that mode, so the resulting feature set depends on the setting. The excerpt is implementation documentation rather than a trading study: it supplies no market data, predictive evidence, trading rules, or performance results. These indicators can serve as inputs for research, but their presence alone does not establish that they predict returns or improve a strategy; users would need to validate them for their markets and avoid look-ahead bias.
Key ideas
- The wrapper adds volume, volatility, trend, momentum, and return features to a dataframe.
- Separate functions let users add a particular indicator category or apply all categories together.
- Input columns, output prefixes, missing-value handling, and vectorization affect how features are generated.
- The document gives no evidence that any included indicator has predictive value or trading performance.
Tags
Full text
# wrapper.py
```py
"""
.. module:: wrapper
:synopsis: Wrapper of Indicators.
.. moduleauthor:: Dario Lopez Padial (Bukosabino)
"""
import pandas as pd
from ta.momentum import (
AwesomeOscillatorIndicator,
KAMAIndicator,
PercentagePriceOscillator,
PercentageVolumeOscillator,
ROCIndicator,
RSIIndicator,
StochasticOscillator,
StochRSIIndicator,
TSIIndicator,
UltimateOscillator,
WilliamsRIndicator,
)
from ta.others import (
CumulativeReturnIndicator,
DailyLogReturnIndicator,
DailyReturnIndicator,
)
from ta.trend import (
MACD,
ADXIndicator,
AroonIndicator,
CCIIndicator,
DPOIndicator,
EMAIndicator,
IchimokuIndicator,
KSTIndicator,
MassIndex,
PSARIndicator,
SMAIndicator,
STCIndicator,
TRIXIndicator,
VortexIndicator,
)
from ta.volatility import (
AverageTrueRange,
BollingerBands,
DonchianChannel,
KeltnerChannel,
UlcerIndex,
)
from ta.volume import (
AccDistIndexIndicator,
ChaikinMoneyFlowIndicator,
EaseOfMovementIndicator,
ForceIndexIndicator,
MFIIndicator,
NegativeVolumeIndexIndicator,
OnBalanceVolumeIndicator,
VolumePriceTrendIndicator,
VolumeWeightedAveragePrice,
)
def add_volume_ta(
df: pd.DataFrame,
high: str,
low: str,
close: str,
volume: str,
fillna: bool = False,
colprefix: str = "",
vectorized: bool = False,
) -> pd.DataFrame:
"""Add volume technical analysis features to dataframe.
Args:
df (pandas.core.frame.DataFrame): Dataframe base.
high (str): Name of 'high' column.
low (str): Name of 'low' column.
close (str): Name of 'close' column.
volume (str): Name of 'volume' column.
fillna(bool): if True, fill nan values.
colprefix(str): Prefix column names inserted
vectorized(bool): if True, use only vectorized functions indicators
Returns:
pandas.core.frame.DataFrame: Dataframe with new features.
"""
# Accumulation Distribution Index
df[f"{colprefix}volume_adi"] = AccDistIndexIndicator(
high=df[high], low=df[low], close=df[close], volume=df[volume], fillna=fillna
).acc_dist_index()
# On Balance Volume
df[f"{colprefix}volume_obv"] = OnBalanceVolumeIndicator(
close=df[close], volume=df[volume], fillna=fillna
).on_balance_volume()
# Chaikin Money Flow
df[f"{colprefix}volume_cmf"] = ChaikinMoneyFlowIndicator(
high=df[high], low=df[low], close=df[close], volume=df[volume], fillna=fillna
).chaikin_money_flow()
# Force Index
df[f"{colprefix}volume_fi"] = ForceIndexIndicator(
close=df[close], volume=df[volume], window=13, fillna=fillna
).force_index()
# Ease of Movement
indicator_eom = EaseOfMovementIndicator(
high=df[high], low=df[low], volume=df[volume], window=14, fillna=fillna
)
df[f"{colprefix}volume_em"] = indicator_eom.ease_of_movement()
df[f"{colprefix}volume_sma_em"] = indicator_eom.sma_ease_of_movement()
# Volume Price Trend
df[f"{colprefix}volume_vpt"] = VolumePriceTrendIndicator(
close=df[close], volume=df[volume], fillna=fillna
).volume_price_trend()
# Volume Weighted Average Price
df[f"{colprefix}volume_vwap"] = VolumeWeightedAveragePrice(
high=df[high],
low=df[low],
close=df[close],
volume=df[volume],
window=14,
fillna=fillna,
).volume_weighted_average_price()
if not vectorized:
# Money Flow Indicator
df[f"{colprefix}volume_mfi"] = MFIIndicator(
high=df[high],
low=df[low],
close=df[close],
volume=df[volume],
window=14,
fillna=fillna,
).money_flow_index()
# Negative Volume Index
df[f"{colprefix}volume_nvi"] = NegativeVolumeIndexIndicator(
close=df[close], volume=df[volume], fillna=fillna
).negative_volume_index()
return df
def add_volatility_ta(
df: pd.DataFrame,
high: str,
low: str,
close: str,
fillna: bool = False,
colprefix: str = "",
vectorized: bool = False,
) -> pd.DataFrame:
"""Add volatility technical analysis features to dataframe.
Args:
df (pandas.core.frame.DataFrame): Dataframe base.
high (str): Name of 'high' column.
low (str): Name of 'low' column.
close (str): Name of 'close' column.
fillna(bool): if True, fill nan values.
colprefix(str): Prefix column names inserted
vectorized(bool): if True, use only vectorized functions indicators
Returns:
pandas.core.frame.DataFrame: Dataframe with new features.
"""
# Bollinger Bands
indicator_bb = BollingerBands(
close=df[close], window=20, window_dev=2, fillna=fillna
)
df[f"{colprefix}volatility_bbm"] = indicator_bb.bollinger_mavg()
df[f"{colprefix}volatility_bbh"] = indicator_bb.bollinger_hband()
df[f"{colprefix}volatility_bbl"] = indicator_bb.bollinger_lband()
df[f"{colprefix}volatility_bbw"] = indicator_bb.bollinger_wband()
df[f"{colprefix}volatility_bbp"] = indicator_bb.bollinger_pband()
df[f"{colprefix}volatility_bbhi"] = indicator_bb.bollinger_hband_indicator()
df[f"{colprefix}volatility_bbli"] = indicator_bb.bollinger_lband_indicator()
# Keltner Channel
indicator_kc = KeltnerChannel(
close=df[close], high=df[high], low=df[low], window=10, fillna=fillna
)
df[f"{colprefix}volatility_kcc"] = indicator_kc.keltner_channel_mband()
df[f"{colprefix}volatility_kch"] = indicator_kc.keltner_channel_hband()
df[f"{colprefix}volatility_kcl"] = indicator_kc.keltner_channel_lband()
df[f"{colprefix}volatility_kcw"] = indicator_kc.keltner_channel_wband()
df[f"{colprefix}volatility_kcp"] = indicator_kc.keltner_channel_pband()
df[f"{colprefix}volatility_kchi"] = indicator_kc.keltner_channel_hband_indicator()
df[f"{colprefix}volatility_kcli"] = indicator_kc.keltner_channel_lband_indicator()
# Donchian Channel
indicator_dc = DonchianChannel(
high=df[high], low=df[low], close=df[close], window=20, offset=0, fillna=fillna
)
df[f"{colprefix}volatility_dcl"] = indicator_dc.donchian_channel_lband()
df[f"{colprefix}volatility_dch"] = indicator_dc.donchian_channel_hband()
df[f"{colprefix}volatility_dcm"] = indicator_dc.donchian_channel_mband()
df[f"{colprefix}volatility_dcw"] = indicator_dc.donchian_channel_wband()
df[f"{colprefix}volatility_dcp"] = indicator_dc.donchian_channel_pband()
if not vectorized:
# Average True Range
df[f"{colprefix}volatility_atr"] = AverageTrueRange(
close=df[close], high=df[high], low=df[low], window=10, fillna=fillna
).average_true_range()
# Ulcer Index
df[f"{colprefix}volatility_ui"] = UlcerIndex(
close=df[close], window=14, fillna=fillna
).ulcer_index()
return df
def add_trend_ta(
df: pd.DataFrame,
high: str,
low: str,
close: str,
fillna: bool = False,
colprefix: str = "",
vectorized: bool = False,
) -> pd.DataFrame:
"""Add trend technical analysis features to dataframe.
Args:
df (pandas.core.frame.DataFrame): Dataframe base.
high (str): Name of 'high' column.
low (str): Name of 'low' column.
close (str): Name of 'close' column.
fillna(bool): if True, fill nan values.
colprefix(str): Prefix column names inserted
vectorized(bool): if True, use only vectorized functions indicators
Returns:
pandas.core.frame.DataFrame: Dataframe with new features.
"""
# MACD
indicator_macd = MACD(
close=df[close], window_slow=26, window_fast=12, window_sign=9, fillna=fillna
)
df[f"{colprefix}trend_macd"] = indicator_macd.macd()
df[f"{colprefix}trend_macd_signal"] = indicator_macd.macd_signal()
df[f"{colprefix}trend_macd_diff"] = indicator_macd.macd_diff()
# SMAs
df[f"{colprefix}trend_sma_fast"] = SMAIndicator(
close=df[close], window=12, fillna=fillna
).sma_indicator()
df[f"{colprefix}trend_sma_slow"] = SMAIndicator(
close=df[close], window=26, fillna=fillna
).sma_indicator()
# EMAs
df[f"{colprefix}trend_ema_fast"] = EMAIndicator(
close=df[close], window=12, fillna=fillna
).ema_indicator()
df[f"{colprefix}trend_ema_slow"] = EMAIndicator(
close=df[close], window=26, fillna=fillna
).ema_indicator()
# Vortex Indicator
indicator_vortex = VortexIndicator(
high=df[high], low=df[low], close=df[close], window=14, fillna=fillna
)
df[f"{colprefix}trend_vortex_ind_pos"] = indicator_vortex.vortex_indicator_pos()
df[f"{colprefix}trend_vortex_ind_neg"] = indicator_vortex.vortex_indicator_neg()
df[f"{colprefix}trend_vortex_ind_diff"] = indicator_vortex.vortex_indicator_diff()
# TRIX Indicator
df[f"{colprefix}trend_trix"] = TRIXIndicator(
close=df[close], window=15, fillna=fillna
).trix()
# Mass Index
df[f"{colprefix}trend_mass_index"] = MassIndex(
high=df[high], low=df[low], window_fast=9, window_slow=25, fillna=fillna
).mass_index()
# DPO Indicator
df[f"{colprefix}trend_dpo"] = DPOIndicator(
close=df[close], window=20, fillna=fillna
).dpo()
# KST Indicator
indicator_kst = KSTIndicator(
close=df[close],
roc1=10,
roc2=15,
roc3=20,
roc4=30,
window1=10,
window2=10,
window3=10,
window4=15,
nsig=9,
fillna=fillna,
)
df[f"{colprefix}trend_kst"] = indicator_kst.kst()
df[f"{colprefix}trend_kst_sig"] = indicator_kst.kst_sig()
df[f"{colprefix}trend_kst_diff"] = indicator_kst.kst_diff()
# Ichimoku Indicator
indicator_ichi = IchimokuIndicator(
high=df[high],
low=df[low],
window1=9,
window2=26,
window3=52,
visual=False,
fillna=fillna,
)
df[f"{colprefix}trend_ichimoku_conv"] = indicator_ichi.ichimoku_conversion_line()
df[f"{colprefix}trend_ichimoku_base"] = indicator_ichi.ichimoku_base_line()
df[f"{colprefix}trend_ichimoku_a"] = indicator_ichi.ichimoku_a()
df[f"{colprefix}trend_ichimoku_b"] = indicator_ichi.ichimoku_b()
# Schaff Trend Cycle (STC)
df[f"{colprefix}trend_stc"] = STCIndicator(
close=df[close],
window_slow=50,
window_fast=23,
cycle=10,
smooth1=3,
smooth2=3,
fillna=fillna,
).stc()
if not vectorized:
# Average Directional Movement Index (ADX)
indicator_adx = ADXIndicator(
high=df[high], low=df[low], close=df[close], window=14, fillna=fillna
)
df[f"{colprefix}trend_adx"] = indicator_adx.adx()
df[f"{colprefix}trend_adx_pos"] = indicator_adx.adx_pos()
df[f"{colprefix}trend_adx_neg"] = indicator_adx.adx_neg()
# CCI Indicator
df[f"{colprefix}trend_cci"] = CCIIndicator(
high=df[high],
low=df[low],
close=df[close],
window=20,
constant=0.015,
fillna=fillna,
).cci()
# Ichimoku Visual Indicator
indicator_ichi_visual = IchimokuIndicator(
high=df[high],
low=df[low],
window1=9,
window2=26,
window3=52,
visual=True,
fillna=fillna,
)
df[f"{colprefix}trend_visual_ichimoku_a"] = indicator_ichi_visual.ichimoku_a()
df[f"{colprefix}trend_visual_ichimoku_b"] = indicator_ichi_visual.ichimoku_b()
# Aroon Indicator
indicator_aroon = AroonIndicator(
high=df[high], low=df[low], window=25, fillna=fillna
)
df[f"{colprefix}trend_aroon_up"] = indicator_aroon.aroon_up()
df[f"{colprefix}trend_aroon_down"] = indicator_aroon.aroon_down()
df[f"{colprefix}trend_aroon_ind"] = indicator_aroon.aroon_indicator()
# PSAR Indicator
indicator_psar = PSARIndicator(
high=df[high],
low=df[low],
close=df[close],
step=0.02,
max_step=0.20,
fillna=fillna,
)
# df[f'{colprefix}trend_psar'] = indicator.psar()
df[f"{colprefix}trend_psar_up"] = indicator_psar.psar_up()
df[f"{colprefix}trend_psar_down"] = indicator_psar.psar_down()
df[f"{colprefix}trend_psar_up_indicator"] = indicator_psar.psar_up_indicator()
df[
f"{colprefix}trend_psar_down_indicator"
] = indicator_psar.psar_down_indicator()
return df
def add_momentum_ta(
df: pd.DataFrame,
high: str,
low: str,
close: str,
volume: str,
fillna: bool = False,
colprefix: str = "",
vectorized: bool = False,
) -> pd.DataFrame:
"""Add trend technical analysis features to dataframe.
Args:
df (pandas.core.frame.DataFrame): Dataframe base.
high (str): Name of 'high' column.
low (str): Name of 'low' column.
close (str): Name of 'close' column.
volume (str): Name of 'volume' column.
fillna(bool): if True, fill nan values.
colprefix(str): Prefix column names inserted
vectorized(bool): if True, use only vectorized functions indicators
Returns:
pandas.core.frame.DataFrame: Dataframe with new features.
"""
# Relative Strength Index (RSI)
df[f"{colprefix}momentum_rsi"] = RSIIndicator(
close=df[close], window=14, fillna=fillna
).rsi()
# Stoch RSI (StochRSI)
indicator_srsi = StochRSIIndicator(
close=df[close], window=14, smooth1=3, smooth2=3, fillna=fillna
)
df[f"{colprefix}momentum_stoch_rsi"] = indicator_srsi.stochrsi()
df[f"{colprefix}momentum_stoch_rsi_k"] = indicator_srsi.stochrsi_k()
df[f"{colprefix}momentum_stoch_rsi_d"] = indicator_srsi.stochrsi_d()
# TSI Indicator
df[f"{colprefix}momentum_tsi"] = TSIIndicator(
close=df[close], window_slow=25, window_fast=13, fillna=fillna
).tsi()
# Ultimate Oscillator
df[f"{colprefix}momentum_uo"] = UltimateOscillator(
high=df[high],
low=df[low],
close=df[close],
window1=7,
window2=14,
window3=28,
weight1=4.0,
weight2=2.0,
weight3=1.0,
fillna=fillna,
).ultimate_oscillator()
# Stoch Indicator
indicator_so = StochasticOscillator(
high=df[high],
low=df[low],
close=df[close],
window=14,
smooth_window=3,
fillna=fillna,
)
df[f"{colprefix}momentum_stoch"] = indicator_so.stoch()
df[f"{colprefix}momentum_stoch_signal"] = indicator_so.stoch_signal()
# Williams R Indicator
df[f"{colprefix}momentum_wr"] = WilliamsRIndicator(
high=df[high], low=df[low], close=df[close], lbp=14, fillna=fillna
).williams_r()
# Awesome Oscillator
df[f"{colprefix}momentum_ao"] = AwesomeOscillatorIndicator(
high=df[high], low=df[low], window1=5, window2=34, fillna=fillna
).awesome_oscillator()
# Rate Of Change
df[f"{colprefix}momentum_roc"] = ROCIndicator(
close=df[close], window=12, fillna=fillna
).roc()
# Percentage Price Oscillator
indicator_ppo = PercentagePriceOscillator(
close=df[close], window_slow=26, window_fast=12, window_sign=9, fillna=fillna
)
df[f"{colprefix}momentum_ppo"] = indicator_ppo.ppo()
df[f"{colprefix}momentum_ppo_signal"] = indicator_ppo.ppo_signal()
df[f"{colprefix}momentum_ppo_hist"] = indicator_ppo.ppo_hist()
# Percentage Volume Oscillator
indicator_pvo = PercentageVolumeOscillator(
volume=df[volume], window_slow=26, window_fast=12, window_sign=9, fillna=fillna
)
df[f"{colprefix}momentum_pvo"] = indicator_pvo.pvo()
df[f"{colprefix}momentum_pvo_signal"] = indicator_pvo.pvo_signal()
df[f"{colprefix}momentum_pvo_hist"] = indicator_pvo.pvo_hist()
if not vectorized:
# KAMA
df[f"{colprefix}momentum_kama"] = KAMAIndicator(
close=df[close], window=10, pow1=2, pow2=30, fillna=fillna
).kama()
return df
def add_others_ta(
df: pd.DataFrame,
close: str,
fillna: bool = False,
colprefix: str = "",
) -> pd.DataFrame:
"""Add others analysis features to dataframe.
Args:
df (pandas.core.frame.DataFrame): Dataframe base.
close (str): Name of 'close' column.
fillna(bool): if True, fill nan values.
colprefix(str): Prefix column names inserted
Returns:
pandas.core.frame.DataFrame: Dataframe with new features.
"""
# Daily Return
df[f"{colprefix}others_dr"] = DailyReturnIndicator(
close=df[close], fillna=fillna
).daily_return()
# Daily Log Return
df[f"{colprefix}others_dlr"] = DailyLogReturnIndicator(
close=df[close], fillna=fillna
).daily_log_return()
# Cumulative Return
df[f"{colprefix}others_cr"] = CumulativeReturnIndicator(
close=df[close], fillna=fillna
).cumulative_return()
return df
def add_all_ta_features(
df: pd.DataFrame,
open: str, # noqa
high: str,
low: str,
close: str,
volume: str,
fillna: bool = False,
colprefix: str = "",
vectorized: bool = False,
) -> pd.DataFrame:
"""Add all technical analysis features to dataframe.
Args:
df (pandas.core.frame.DataFrame): Dataframe base.
open (str): Name of 'open' column.
high (str): Name of 'high' column.
low (str): Name of 'low' column.
close (str): Name of 'close' column.
volume (str): Name of 'volume' column.
fillna(bool): if True, fill nan values.
colprefix(str): Prefix column names inserted
vectorized(bool): if True, use only vectorized functions indicators
Returns:
pandas.core.frame.DataFrame: Dataframe with new features.
"""
df = add_volume_ta(
df=df,
high=high,
low=low,
close=close,
volume=volume,
fillna=fillna,
colprefix=colprefix,
vectorized=vectorized,
)
df = add_volatility_ta(
df=df,
high=high,
low=low,
close=close,
fillna=fillna,
colprefix=colprefix,
vectorized=vectorized,
)
df = add_trend_ta(
df=df,
high=high,
low=low,
close=close,
fillna=fillna,
colprefix=colprefix,
vectorized=vectorized,
)
df = add_momentum_ta(
df=df,
high=high,
low=low,
close=close,
volume=volume,
fillna=fillna,
colprefix=colprefix,
vectorized=vectorized,
)
df = add_others_ta(df=df, close=close, fillna=fillna, colprefix=colprefix)
return df
```Shown in full with attribution under the source's licence. Licence: MIT
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.