Skip to content

TVIndicator Base Class API

levi edited this page Nov 6, 2025 · 2 revisions

TVIndicator Base Class API

Table of Contents

  1. Introduction
  2. Indicator Lifecycle Methods
  3. Configuration System
  4. Signal Generation and Drawing
  5. Registration System
  6. Implementation Examples
  7. Integration with TVEngine

Introduction

The TVIndicator base class serves as the foundation for all custom indicators in the PyTradingView framework. This abstract base class defines the interface and core functionality that all indicators must implement, providing a standardized approach to indicator development, configuration management, signal generation, and chart integration. The class implements the Abstract Base Class (ABC) pattern to enforce implementation of critical methods while providing helper methods for common operations.

Indicator Lifecycle Methods

The TVIndicator class defines a comprehensive lifecycle for indicator execution, with callback methods that are automatically invoked at specific points during the indicator's operation. These methods provide hooks for initialization, data processing, calculation, and cleanup.

Initialization and Setup

The on_init method is called when an indicator is initialized on a chart, receiving references to the widget, chart, and optional chart ID. This method sets up the indicator's configuration and internal state.

sequenceDiagram
participant Engine as TVEngine
participant Indicator as TVIndicator
participant Registry as IndicatorRegistry
Engine->>Registry : create_instance(name)
Registry->>Indicator : __init__()
Engine->>Indicator : on_init(widget, chart, chart_id)
Indicator->>Indicator : get_config()
Indicator->>Engine : Initialization complete
Loading

Data Processing Lifecycle

The indicator lifecycle includes several callback methods that are invoked during data processing:

  • on_data_loaded(df): Called when new market data is loaded, providing access to the DataFrame containing OHLC data
  • on_calculate_start(): Called before the calculation process begins
  • on_calculate_end(signals, drawables): Called after calculation completes, receiving the generated signals and drawable elements
  • on_draw_start(): Called before drawing operations begin
  • on_draw_end(): Called after drawing operations complete
  • on_destroy(): Called when the indicator is being destroyed, for cleanup operations

These methods provide visibility into the indicator's execution flow and enable debugging through optional debug output when the configuration's debug flag is enabled.

Configuration System

The configuration system in TVIndicator provides a comprehensive framework for managing indicator parameters, styles, and persistence. Configuration is managed through the IndicatorConfig class, which supports complex input types, style definitions, and runtime modifications.

Input Parameters

The configuration system supports various input parameter types through the InputType enumeration:

  • INTEGER: Integer values with optional min/max constraints
  • FLOAT: Floating-point values with optional min/max constraints
  • BOOLEAN: True/false values
  • STRING: Text strings
  • COLOR: Color values in hexadecimal format (#RGB or #RRGGBB)
  • OPTIONS: Dropdown options with label-value pairs
  • SOURCE: Data source selection (open, high, low, close, hl2, hlc3, ohlc4, volume)

Input parameters are defined using the InputDefinition dataclass, which includes properties for default values, validation rules, tooltips, and UI grouping.

Style Configuration

Style definitions are managed through the StyleDefinition class, which encapsulates visual properties:

  • color: Hexadecimal color code
  • line_width: Line width (0-10)
  • line_style: 0=solid, 1=dashed, 2=dotted
  • transparency: Transparency level (0-100)
  • visible: Whether the element is visible

Styles can be grouped in the UI and support validation to ensure values are within acceptable ranges.

Configuration Management

The TVIndicator class provides several methods for runtime configuration management:

  • get_config_dict(): Returns the current configuration as a dictionary
  • update_config(config_dict): Updates the entire configuration from a dictionary
  • update_input_value(input_id, value): Updates a single input parameter
  • update_style(style_id, **kwargs): Updates style properties

These methods automatically trigger recalculation when configuration changes, ensuring the indicator's output reflects the new settings.

classDiagram
class IndicatorConfig {
+name : str
+version : str
+description : str
+author : str
+enabled : bool
+debug : bool
+inputs : List[InputDefinition]
+styles : List[StyleDefinition]
+on_config_changed : Callable
+get_input_value(input_id) Any
+set_input_value(input_id, value) Tuple[bool, str]
+set_input_values(values) Tuple[bool, List[str]]
+get_style(style_id) StyleDefinition
+update_style(style_id, **kwargs) Tuple[bool, str]
+validate_all() Tuple[bool, List[str]]
+to_dict() Dict[str, Any]
+from_dict(data) IndicatorConfig
+reset_to_defaults() void
}
class InputDefinition {
+id : str
+display_name : str
+type : InputType
+default_value : Any
+value : Any
+tooltip : str
+options : List[InputOption]
+min_value : Union[int, float]
+max_value : Union[int, float]
+step : Union[int, float]
+visible : bool
+group : str
+validate() Tuple[bool, str]
+to_dict() Dict[str, Any]
}
class StyleDefinition {
+id : str
+display_name : str
+color : str
+line_width : int
+line_style : int
+transparency : int
+visible : bool
+group : str
+validate() Tuple[bool, str]
+to_dict() Dict[str, Any]
}
IndicatorConfig "1" -- "0..*" InputDefinition : contains
IndicatorConfig "1" -- "0..*" StyleDefinition : contains
InputDefinition "1" -- "0..*" InputOption : has options
Loading

Signal Generation and Drawing

The TVIndicator class provides a standardized approach to signal generation and visual representation on charts through data structures and drawing methods.

Signal Generation

Indicators generate trading signals through the calculate() method, which returns a tuple of TVSignal and TVDrawable objects. The TVSignal class represents trading signals with the following properties:

  • signal_type: 'buy', 'sell', or 'neutral'
  • timestamp: UNIX timestamp in seconds
  • price: Price value for the signal
  • metadata: Additional data as key-value pairs

Signals are designed to be independent of DataFrame indices, using timestamps for cross-chart compatibility and persistence.

Drawing Capabilities

Visual elements are represented by the TVDrawable class, which encapsulates drawable elements with:

  • points: List of (time, price) tuples representing coordinates
  • shape: TVSingleShape or TVMultipleShape object containing style information
  • metadata: Additional metadata

The drawing system supports both custom and default drawing logic:

  • If a subclass overrides the draw() method, that custom logic is used
  • Otherwise, the engine applies default drawing logic based on signal types and drawable elements

The base class provides helper methods for managing drawn entities:

  • add_drawn_entity(entity_id): Records a newly created entity
  • get_drawn_entities(): Returns all entity IDs created by the indicator
  • clear_all_drawings(): Asynchronously removes all drawn entities from the chart
classDiagram
class TVSignal {
+signal_type : str
+timestamp : int
+price : float
+metadata : Dict[str, Any]
}
class TVDrawable {
+points : List[Tuple[int, float]]
+shape : Any
+metadata : Dict[str, Any]
}
class TVIndicator {
+calculate(df) Tuple[List[TVSignal], List[TVDrawable]]
+draw(chart, df, signals, drawables) void
+add_drawn_entity(entity_id) void
+get_drawn_entities() List[str]
+clear_all_drawings() Coroutine
}
TVIndicator "1" -- "0..*" TVSignal : generates
TVIndicator "1" -- "0..*" TVDrawable : generates
Loading

Registration System

The indicator registration system enables discovery, management, and instantiation of custom indicators through the IndicatorRegistry class and @register_indicator decorator.

Indicator Registry

The IndicatorRegistry implements a singleton pattern to manage all registered indicator classes globally. Key features include:

  • Centralized storage of indicator classes
  • Support for enabling/disabling indicators
  • Runtime creation of indicator instances
  • Listing of registered and enabled indicators

The registry provides methods for:

  • register(indicator_class, name, enabled): Register a new indicator class
  • unregister(name): Remove an indicator from the registry
  • get(name): Retrieve an indicator class by name
  • create_instance(name): Create and return an instance of a registered indicator
  • list_all(): Return all registered indicator names
  • list_enabled(): Return all enabled indicator names

Registration Decorator

The @register_indicator decorator provides a convenient way to register indicators at definition time:

@register_indicator(name="MyCustomIndicator", enabled=True)
class MyCustomIndicator(TVIndicator):
    def get_config(self) -> IndicatorConfig:
        # Configuration definition
        pass
    
    def calculate(self, df: pd.DataFrame) -> Tuple[List[TVSignal], List[TVDrawable]]:
        # Calculation logic
        pass

When a class is decorated, it is automatically registered with the global IndicatorRegistry instance, making it available for instantiation by the TVEngine.

classDiagram
class IndicatorRegistry {
-_instance : IndicatorRegistry
-_indicators : Dict[str, Type[TVIndicator]]
-_enabled_indicators : Dict[str, bool]
+get_instance() IndicatorRegistry
+register(indicator_class, name, enabled) void
+unregister(name) bool
+get(name) Type[TVIndicator]
+create_instance(name) TVIndicator
+is_registered(name) bool
+is_enabled(name) bool
+enable(name) bool
+disable(name) bool
+list_all() List[str]
+list_enabled() List[str]
+clear() void
+get_info() Dict[str, Dict[str, Any]]
}
class TVIndicator {
<<abstract>>
+get_config() IndicatorConfig
+calculate(df) Tuple[List[TVSignal], List[TVDrawable]]
}
IndicatorRegistry "1" -- "0..*" TVIndicator : manages
register_indicator --> IndicatorRegistry : uses
Loading

Implementation Examples

Basic Indicator Implementation

A minimal indicator implementation requires overriding the abstract methods get_config() and calculate():

@register_indicator(name="SimpleMA")
class SimpleMAIndicator(TVIndicator):
    def get_config(self) -> IndicatorConfig:
        config = IndicatorConfig(
            name="Simple Moving Average",
            description="Simple moving average indicator",
            author="Developer"
        )
        
        # Add input parameter
        length_input = InputDefinition(
            id="length",
            display_name="Length",
            type=InputType.INTEGER,
            default_value=20,
            min_value=1,
            max_value=100
        )
        config.inputs.append(length_input)
        
        # Add style definition
        line_style = StyleDefinition(
            id="ma_line",
            display_name="MA Line",
            color="#FF0000",
            line_width=2
        )
        config.styles.append(line_style)
        
        return config
    
    def calculate(self, df: pd.DataFrame) -> Tuple[List[TVSignal], List[TVDrawable]]:
        # Get configuration values
        length = self._config.get_input_value("length")
        
        # Calculate moving average
        df['ma'] = df['close'].rolling(window=length).mean()
        
        # Generate signals (example: price crossing MA)
        signals = []
        for i in range(1, len(df)):
            if df['close'].iloc[i] > df['ma'].iloc[i] and df['close'].iloc[i-1] <= df['ma'].iloc[i-1]:
                signals.append(TVSignal(
                    signal_type='buy',
                    timestamp=int(df['time'].iloc[i]),
                    price=df['close'].iloc[i]
                ))
            elif df['close'].iloc[i] < df['ma'].iloc[i] and df['close'].iloc[i-1] >= df['ma'].iloc[i-1]:
                signals.append(TVSignal(
                    signal_type='sell',
                    timestamp=int(df['time'].iloc[i]),
                    price=df['close'].iloc[i]
                ))
        
        # Create drawable elements
        drawables = []
        style = self._config.get_style("ma_line")
        for i in range(len(df)):
            if not pd.isna(df['ma'].iloc[i]):
                drawables.append(TVDrawable(
                    points=[(df['time'].iloc[i], df['ma'].iloc[i])],
                    shape=style
                ))
        
        return signals, drawables

Advanced Indicator with Custom Drawing

For more complex visualizations, indicators can override the draw() method to implement custom drawing logic:

@register_indicator(name="CustomPattern")
class CustomPatternIndicator(TVIndicator):
    def get_config(self) -> IndicatorConfig:
        # Configuration setup
        pass
    
    def calculate(self, df: pd.DataFrame) -> Tuple[List[TVSignal], List[TVDrawable]]:
        # Complex calculation logic
        pass
    
    def draw(self, chart: TVChart, df: pd.DataFrame, signals: List[TVSignal], drawables: List[TVDrawable]) -> None:
        """
        Custom drawing implementation
        This method will be called instead of the default drawing logic
        """
        # Clear existing drawings
        asyncio.create_task(self.clear_all_drawings())
        
        # Custom drawing logic using TradingView API directly
        for signal in signals:
            if signal.signal_type == 'buy':
                # Custom buy arrow with special styling
                point = TVShapePoint(time=signal.timestamp, price=signal.price)
                arrow = TVArrowUp()
                arrow.overrides = {
                    "arrowColor": "#00FF00",
                    "color": "#00FF00",
                    "size": 2
                }
                entity_id = asyncio.create_task(chart.createShape(point=point, options=arrow))
                self.add_drawn_entity(entity_id.result())

Integration with TVEngine

The TVIndicator class integrates with the TVEngine through a modular architecture that handles indicator lifecycle management, configuration updates, and drawing operations.

Engine-Indicator Interaction

The TVEngine manages indicators through several mixins that handle different aspects of indicator operation:

  • TVEngineManager: Handles activation and deactivation of indicators
  • TVEngineConfig: Manages configuration updates and recalculation
  • TVEngineDrawing: Executes calculation and drawing operations
  • TVEngineRuntime: Handles initialization and event processing

When market data is loaded, the engine follows this sequence:

  1. Calls on_data_loaded() on each active indicator
  2. Invokes on_calculate_start() before calculation
  3. Calls the calculate() method to generate signals and drawables
  4. Calls on_calculate_end() after calculation completes
  5. Initiates drawing operations through on_draw_start() and the drawing system
  6. Calls on_draw_end() after drawing completes

Configuration Updates and Recalculation

When configuration changes occur, the system automatically handles recalculation through the recalculate_and_redraw() method:

flowchart TD
A[Configuration Update] --> B{Needs Recalculate?}
B --> |Yes| C[Set needs_recalculate flag]
C --> D[External Trigger]
D --> E[recalculate_and_redraw()]
E --> F[on_calculate_start()]
F --> G[calculate() method]
G --> H[on_calculate_end()]
H --> I[clear_all_drawings()]
I --> J{Custom draw method?}
J --> |Yes| K[Call custom draw()]
J --> |No| L[Apply default drawing logic]
K --> M[on_draw_end()]
L --> M
M --> N[mark_recalculate_done()]
N --> O[Update complete]
Loading

The update_config(), update_input_value(), and update_style() methods automatically set the needs_recalculate flag, which can be checked with needs_recalculate() and cleared with mark_recalculate_done().

Clone this wiki locally