Files
ai/backend.old/src/indicator/tv_mapping.py
2026-03-11 18:47:11 -04:00

361 lines
11 KiB
Python

"""
Mapping layer between TA-Lib indicators and TradingView indicators.
This module provides bidirectional conversion between our internal TA-Lib-based
indicator representation and TradingView's indicator system.
"""
from typing import Dict, Any, Optional, Tuple, List
import logging
logger = logging.getLogger(__name__)
# Mapping of TA-Lib indicator names to TradingView indicator names
# Only includes indicators that are present in BOTH systems (inner join)
# Format: {talib_name: tv_name}
TALIB_TO_TV_NAMES = {
# Overlap Studies (14)
"SMA": "Moving Average",
"EMA": "Moving Average Exponential",
"WMA": "Weighted Moving Average",
"DEMA": "DEMA",
"TEMA": "TEMA",
"TRIMA": "Triangular Moving Average",
"KAMA": "KAMA",
"MAMA": "MESA Adaptive Moving Average",
"T3": "T3",
"BBANDS": "Bollinger Bands",
"MIDPOINT": "Midpoint",
"MIDPRICE": "Midprice",
"SAR": "Parabolic SAR",
"HT_TRENDLINE": "Hilbert Transform - Instantaneous Trendline",
# Momentum Indicators (21)
"RSI": "Relative Strength Index",
"MOM": "Momentum",
"ROC": "Rate of Change",
"TRIX": "TRIX",
"CMO": "Chande Momentum Oscillator",
"DX": "Directional Movement Index",
"ADX": "Average Directional Movement Index",
"ADXR": "Average Directional Movement Index Rating",
"APO": "Absolute Price Oscillator",
"PPO": "Percentage Price Oscillator",
"MACD": "MACD",
"MFI": "Money Flow Index",
"STOCH": "Stochastic",
"STOCHF": "Stochastic Fast",
"STOCHRSI": "Stochastic RSI",
"WILLR": "Williams %R",
"CCI": "Commodity Channel Index",
"AROON": "Aroon",
"AROONOSC": "Aroon Oscillator",
"BOP": "Balance Of Power",
"ULTOSC": "Ultimate Oscillator",
# Volume Indicators (3)
"AD": "Chaikin A/D Line",
"ADOSC": "Chaikin A/D Oscillator",
"OBV": "On Balance Volume",
# Volatility Indicators (3)
"ATR": "Average True Range",
"NATR": "Normalized Average True Range",
"TRANGE": "True Range",
# Price Transform (4)
"AVGPRICE": "Average Price",
"MEDPRICE": "Median Price",
"TYPPRICE": "Typical Price",
"WCLPRICE": "Weighted Close Price",
# Cycle Indicators (5)
"HT_DCPERIOD": "Hilbert Transform - Dominant Cycle Period",
"HT_DCPHASE": "Hilbert Transform - Dominant Cycle Phase",
"HT_PHASOR": "Hilbert Transform - Phasor Components",
"HT_SINE": "Hilbert Transform - SineWave",
"HT_TRENDMODE": "Hilbert Transform - Trend vs Cycle Mode",
# Statistic Functions (9)
"BETA": "Beta",
"CORREL": "Pearson's Correlation Coefficient",
"LINEARREG": "Linear Regression",
"LINEARREG_ANGLE": "Linear Regression Angle",
"LINEARREG_INTERCEPT": "Linear Regression Intercept",
"LINEARREG_SLOPE": "Linear Regression Slope",
"STDDEV": "Standard Deviation",
"TSF": "Time Series Forecast",
"VAR": "Variance",
}
# Total: 60 indicators supported in both systems
# Custom indicators (TradingView indicators implemented in our backend)
CUSTOM_TO_TV_NAMES = {
"VWAP": "VWAP",
"VWMA": "VWMA",
"HMA": "Hull Moving Average",
"SUPERTREND": "SuperTrend",
"DONCHIAN": "Donchian Channels",
"KELTNER": "Keltner Channels",
"CMF": "Chaikin Money Flow",
"VORTEX": "Vortex Indicator",
"AO": "Awesome Oscillator",
"AC": "Accelerator Oscillator",
"CHOP": "Choppiness Index",
"MASS": "Mass Index",
}
# Combined mapping (TA-Lib + Custom)
ALL_BACKEND_TO_TV_NAMES = {**TALIB_TO_TV_NAMES, **CUSTOM_TO_TV_NAMES}
# Total: 72 indicators (60 TA-Lib + 12 Custom)
# Reverse mapping
TV_TO_TALIB_NAMES = {v: k for k, v in TALIB_TO_TV_NAMES.items()}
TV_TO_CUSTOM_NAMES = {v: k for k, v in CUSTOM_TO_TV_NAMES.items()}
TV_TO_BACKEND_NAMES = {v: k for k, v in ALL_BACKEND_TO_TV_NAMES.items()}
def get_tv_indicator_name(talib_name: str) -> str:
"""
Convert TA-Lib indicator name to TradingView indicator name.
Args:
talib_name: TA-Lib indicator name (e.g., 'RSI')
Returns:
TradingView indicator name
"""
return TALIB_TO_TV_NAMES.get(talib_name, talib_name)
def get_talib_indicator_name(tv_name: str) -> Optional[str]:
"""
Convert TradingView indicator name to TA-Lib indicator name.
Args:
tv_name: TradingView indicator name
Returns:
TA-Lib indicator name or None if not mapped
"""
return TV_TO_TALIB_NAMES.get(tv_name)
def convert_talib_params_to_tv_inputs(
talib_name: str,
talib_params: Dict[str, Any]
) -> Dict[str, Any]:
"""
Convert TA-Lib parameters to TradingView input format.
Args:
talib_name: TA-Lib indicator name
talib_params: TA-Lib parameter dictionary
Returns:
TradingView inputs dictionary
"""
tv_inputs = {}
# Common parameter mappings
param_mapping = {
"timeperiod": "length",
"fastperiod": "fastLength",
"slowperiod": "slowLength",
"signalperiod": "signalLength",
"nbdevup": "mult", # Standard deviations for upper band
"nbdevdn": "mult", # Standard deviations for lower band
"fastlimit": "fastLimit",
"slowlimit": "slowLimit",
"acceleration": "start",
"maximum": "increment",
"fastk_period": "kPeriod",
"slowk_period": "kPeriod",
"slowd_period": "dPeriod",
"fastd_period": "dPeriod",
"matype": "maType",
}
# Special handling for specific indicators
if talib_name == "BBANDS":
# Bollinger Bands
tv_inputs["length"] = talib_params.get("timeperiod", 20)
tv_inputs["mult"] = talib_params.get("nbdevup", 2)
tv_inputs["source"] = "close"
elif talib_name == "MACD":
# MACD
tv_inputs["fastLength"] = talib_params.get("fastperiod", 12)
tv_inputs["slowLength"] = talib_params.get("slowperiod", 26)
tv_inputs["signalLength"] = talib_params.get("signalperiod", 9)
tv_inputs["source"] = "close"
elif talib_name == "RSI":
# RSI
tv_inputs["length"] = talib_params.get("timeperiod", 14)
tv_inputs["source"] = "close"
elif talib_name in ["SMA", "EMA", "WMA", "DEMA", "TEMA", "TRIMA"]:
# Moving averages
tv_inputs["length"] = talib_params.get("timeperiod", 14)
tv_inputs["source"] = "close"
elif talib_name == "STOCH":
# Stochastic
tv_inputs["kPeriod"] = talib_params.get("fastk_period", 14)
tv_inputs["dPeriod"] = talib_params.get("slowd_period", 3)
tv_inputs["smoothK"] = talib_params.get("slowk_period", 3)
elif talib_name == "ATR":
# ATR
tv_inputs["length"] = talib_params.get("timeperiod", 14)
elif talib_name == "CCI":
# CCI
tv_inputs["length"] = talib_params.get("timeperiod", 20)
else:
# Generic parameter conversion
for talib_param, value in talib_params.items():
tv_param = param_mapping.get(talib_param, talib_param)
tv_inputs[tv_param] = value
logger.debug(f"Converted TA-Lib params for {talib_name}: {talib_params} -> TV inputs: {tv_inputs}")
return tv_inputs
def convert_tv_inputs_to_talib_params(
tv_name: str,
tv_inputs: Dict[str, Any]
) -> Tuple[Optional[str], Dict[str, Any]]:
"""
Convert TradingView inputs to TA-Lib parameters.
Args:
tv_name: TradingView indicator name
tv_inputs: TradingView inputs dictionary
Returns:
Tuple of (talib_name, talib_params)
"""
talib_name = get_talib_indicator_name(tv_name)
if not talib_name:
logger.warning(f"No TA-Lib mapping for TradingView indicator: {tv_name}")
return None, {}
talib_params = {}
# Reverse parameter mappings
reverse_mapping = {
"length": "timeperiod",
"fastLength": "fastperiod",
"slowLength": "slowperiod",
"signalLength": "signalperiod",
"mult": "nbdevup", # Use same for both up and down
"fastLimit": "fastlimit",
"slowLimit": "slowlimit",
"start": "acceleration",
"increment": "maximum",
"kPeriod": "fastk_period",
"dPeriod": "slowd_period",
"smoothK": "slowk_period",
"maType": "matype",
}
# Special handling for specific indicators
if talib_name == "BBANDS":
# Bollinger Bands
talib_params["timeperiod"] = tv_inputs.get("length", 20)
talib_params["nbdevup"] = tv_inputs.get("mult", 2)
talib_params["nbdevdn"] = tv_inputs.get("mult", 2)
talib_params["matype"] = 0 # SMA
elif talib_name == "MACD":
# MACD
talib_params["fastperiod"] = tv_inputs.get("fastLength", 12)
talib_params["slowperiod"] = tv_inputs.get("slowLength", 26)
talib_params["signalperiod"] = tv_inputs.get("signalLength", 9)
elif talib_name == "RSI":
# RSI
talib_params["timeperiod"] = tv_inputs.get("length", 14)
elif talib_name in ["SMA", "EMA", "WMA", "DEMA", "TEMA", "TRIMA"]:
# Moving averages
talib_params["timeperiod"] = tv_inputs.get("length", 14)
elif talib_name == "STOCH":
# Stochastic
talib_params["fastk_period"] = tv_inputs.get("kPeriod", 14)
talib_params["slowd_period"] = tv_inputs.get("dPeriod", 3)
talib_params["slowk_period"] = tv_inputs.get("smoothK", 3)
talib_params["slowk_matype"] = 0 # SMA
talib_params["slowd_matype"] = 0 # SMA
elif talib_name == "ATR":
# ATR
talib_params["timeperiod"] = tv_inputs.get("length", 14)
elif talib_name == "CCI":
# CCI
talib_params["timeperiod"] = tv_inputs.get("length", 20)
else:
# Generic parameter conversion
for tv_param, value in tv_inputs.items():
if tv_param == "source":
continue # Skip source parameter
talib_param = reverse_mapping.get(tv_param, tv_param)
talib_params[talib_param] = value
logger.debug(f"Converted TV inputs for {tv_name}: {tv_inputs} -> TA-Lib {talib_name} params: {talib_params}")
return talib_name, talib_params
def is_indicator_supported(talib_name: str) -> bool:
"""
Check if a TA-Lib indicator is supported in TradingView.
Args:
talib_name: TA-Lib indicator name
Returns:
True if supported
"""
return talib_name in TALIB_TO_TV_NAMES
def get_supported_indicators() -> List[str]:
"""
Get list of supported TA-Lib indicators.
Returns:
List of TA-Lib indicator names
"""
return list(TALIB_TO_TV_NAMES.keys())
def get_supported_indicator_count() -> int:
"""
Get count of supported indicators.
Returns:
Number of indicators supported in both systems (TA-Lib + Custom)
"""
return len(ALL_BACKEND_TO_TV_NAMES)
def is_custom_indicator(indicator_name: str) -> bool:
"""
Check if an indicator is a custom implementation (not TA-Lib).
Args:
indicator_name: Indicator name
Returns:
True if custom indicator
"""
return indicator_name in CUSTOM_TO_TV_NAMES
def get_backend_indicator_name(tv_name: str) -> Optional[str]:
"""
Get backend indicator name from TradingView name (TA-Lib or custom).
Args:
tv_name: TradingView indicator name
Returns:
Backend indicator name or None if not mapped
"""
return TV_TO_BACKEND_NAMES.get(tv_name)