Skip to content

Drawing Elements and Visualization

levi edited this page Nov 6, 2025 · 2 revisions

Drawing Elements and Visualization

Table of Contents

  1. Introduction
  2. TVDrawable Data Structure
  3. Shape Objects: TVSingleShape and TVMultipleShape
  4. Default Drawing System
  5. Custom draw() Method Override
  6. Shapes Library Overview
  7. Example: False Breakout Indicator
  8. Entity Management and Drawing Persistence
  9. Performance Optimization
  10. Best Practices for Visual Clarity
  11. Conclusion

Introduction

This document provides a comprehensive guide to drawing elements and visualization in the PyTradingView framework. It covers the core data structures, drawing systems, shape libraries, and best practices for creating effective trading indicators. The focus is on the TVDrawable structure, shape objects, default and custom drawing mechanisms, and practical implementation examples using the false breakout indicator.

TVDrawable Data Structure

The TVDrawable class is the fundamental data structure for representing drawable elements in the PyTradingView framework. It encapsulates both positional data and visual styling information in a single object.

The core components of TVDrawable are:

  • points: A list of (timestamp, price) tuples that define the position of the drawable element on the chart
  • shape: An instance of TVSingleShape or TVMultipleShape that contains all styling information
  • metadata: Optional additional data for the drawable element

The design decision to use timestamps instead of bar indices provides several advantages:

  1. API alignment: Direct compatibility with TradingView's API without conversion
  2. Performance advantage: 40% performance improvement in drawing operations and 99% less memory usage
  3. Data independence: Not dependent on DataFrame structure, enabling cross-chart usage
  4. Persistence-friendly: Timestamps can be directly serialized and deserialized

For single-point graphics like arrows, the points array contains exactly one (timestamp, price) tuple. For multi-point graphics like trend lines, it contains two or more tuples defining the line's endpoints.

classDiagram
class TVDrawable {
+List[Tuple[int, float]] points
+Any shape
+Dict[str, Any] metadata
}
TVDrawable --> TVSingleShape : "contains"
TVDrawable --> TVMultipleShape : "contains"
Loading

Shape Objects: TVSingleShape and TVMultipleShape

Shape objects in PyTradingView are divided into two main categories: TVSingleShape for single-point elements and TVMultipleShape for multi-point elements. Both inherit from the base TVBaseShape class, which provides common properties and methods.

TVSingleShape

TVSingleShape represents graphical elements that require only a single point for positioning, such as arrows, flags, and icons. Each specific shape type is implemented as a subclass with predefined styling options. Common single shapes include:

  • TVArrowUp, TVArrowDown: Directional arrows for buy/sell signals
  • TVHorizontalLine, TVVerticalLine: Single-axis reference lines
  • TVFlagMark: Flag markers for important events
  • TVPriceLabel, TVPriceNote: Price annotations

TVMultipleShape

TVMultipleShape represents graphical elements that require multiple points for definition, such as lines, channels, and geometric shapes. These shapes are created using two or more (timestamp, price) coordinates. Common multi-point shapes include:

  • TVTrendLine: Trend lines connecting two price points
  • TVParallelChannel: Parallel channels with multiple reference lines
  • TVFibRetracement: Fibonacci retracement levels
  • TVRectangle, TVCircle: Geometric shapes for area highlighting

Both shape types follow a consistent pattern where styling information is encapsulated within the shape object itself, eliminating the need for separate style configuration.

classDiagram
class TVBaseShape {
+Optional[str] text
+Optional[bool] lock
+Optional[bool] disable_selection
+Optional[dict] overrides
+Optional[str] z_order
+Optional[str] owner_study_id
}
class TVSingleShape {
+TVSingleShapeType shape
+Optional[str] owner_study_id
}
class TVMultipleShape {
+TVMultipleShapeType shape
}
TVBaseShape <|-- TVSingleShape
TVBaseShape <|-- TVMultipleShape
TVSingleShape <|-- TVArrowUp
TVSingleShape <|-- TVHorizontalLine
TVMultipleShape <|-- TVTrendLine
TVMultipleShape <|-- TVParallelChannel
Loading

Default Drawing System

The PyTradingView framework provides an automatic default drawing system that renders signals and drawables based on configuration without requiring custom implementation. This system is activated when an indicator does not override the draw() method.

The default drawing logic follows these steps:

  1. Clear all previously drawn graphics using clear_all_drawings()
  2. Draw buy/sell signals as arrows based on the signal type
  3. Render drawable elements according to their shape type and configuration
  4. Apply styling from the indicator's configuration
  5. Track entity IDs for persistence and cleanup

Signal rendering automatically converts TVSignal objects into appropriate visual elements:

  • Buy signals are rendered as green upward arrows
  • Sell signals are rendered as red downward arrows
  • Neutral signals are not drawn

Drawable elements are rendered based on their shape type:

  • Single-point shapes use createShape() method
  • Multi-point shapes use createMultipointShape() method
  • Anchor shapes use createAnchoredShape() method

The system automatically handles timestamp conversion and applies styling from the shape object's overrides property.

sequenceDiagram
participant Indicator
participant Engine
participant Chart
Indicator->>Engine : calculate() returns signals and drawables
Engine->>Engine : _default_draw()
Engine->>Engine : clear_all_drawings()
loop For each signal
Engine->>Chart : createShape() with arrow options
Chart-->>Engine : entity_id
Engine->>Indicator : add_drawn_entity(entity_id)
end
loop For each drawable
Engine->>Chart : createShape/createMultipointShape()
Chart-->>Engine : entity_id
Engine->>Indicator : add_drawn_entity(entity_id)
end
Engine->>Indicator : on_draw_end()
Loading

Custom draw() Method Override

While the default drawing system handles most use cases, indicators can override the draw() method for custom visualization logic. This approach provides complete control over the rendering process but requires more implementation effort.

The framework determines whether to use custom or default drawing by checking if the indicator's draw method is the same as the base class implementation:

has_custom_draw = indicator.__class__.draw is not TVIndicator.draw

Custom drawing should be used when:

  • Complex rendering logic is required beyond simple shapes
  • Dynamic styling based on market conditions
  • Special animation or interaction effects
  • Combining multiple elements in a coordinated way
  • Performance optimization for complex drawings

When overriding draw(), developers must:

  1. Handle all drawing operations manually
  2. Call add_drawn_entity() for each created element
  3. Implement proper error handling
  4. Consider performance implications

The custom draw method receives the chart object, data DataFrame, signals, and drawables as parameters, allowing access to all necessary information for rendering.

flowchart TD
Start([Indicator Drawing]) --> CheckCustom["Check if draw() is overridden"]
CheckCustom --> |Yes| CustomDraw["Execute custom draw() method"]
CheckCustom --> |No| DefaultDraw["Use default drawing system"]
CustomDraw --> HandleErrors["Implement error handling"]
DefaultDraw --> ClearGraphics["clear_all_drawings()"]
ClearGraphics --> DrawSignals["Draw signals as arrows"]
DrawSignals --> DrawElements["Draw drawable elements"]
DrawElements --> TrackEntities["add_drawn_entity() for each element"]
TrackEntities --> Complete["Drawing completed"]
HandleErrors --> Complete
Loading

Shapes Library Overview

The PyTradingView framework provides an extensive library of shapes through the shapes module, offering a wide range of annotation and analysis tools for trading indicators.

Trend Lines and Channels

  • TVTrendLine: Basic trend lines connecting two price points
  • TVParallelChannel: Parallel channels with configurable width
  • TVRegressionTrend: Linear regression trend lines
  • TVFibChannel: Fibonacci price channels
  • TVGannbox: Gann box analysis tools

Arrows and Markers

  • TVArrowUp, TVArrowDown: Directional arrows for signals
  • TVArrowLeft, TVArrowRight: Horizontal direction indicators
  • TVArrowMarker: Custom arrow markers
  • TVFlagMark: Flag markers for events
  • TVLongPosition, TVShortPosition: Position indicators

Geometric Shapes

  • TVRectangle, TVRotatedRectangle: Rectangular areas
  • TVCircle, TVEllipse: Circular and elliptical shapes
  • TVTriangle: Triangular patterns
  • TVPath, TVPolyline: Custom paths and polylines
  • TVCurve, TVDoubleCurve: Curved lines

Advanced Analysis Tools

  • TVFibRetracement: Fibonacci retracement levels
  • TVFibExtension: Fibonacci extension levels
  • TVFibSpeedResistFan: Fibonacci speed resistance fans
  • TVPitchfork, TVSchiffPitchfork: Andrews pitchfork variants
  • TVElliottImpulseWave: Elliott wave analysis tools

Annotation and Text

  • TVText, TVNote: Text annotations
  • TVPriceLabel, TVPriceNote: Price-specific labels
  • TVCallout, TVBalloon: Callout bubbles
  • TVComment: Comment boxes
  • TVSticker, TVEmoji: Visual stickers and emojis

Each shape type includes default styling that can be customized through overrides, providing both ease of use and flexibility for specific visualization needs.

graph TD
Shapes[Shapes Library] --> Trend[Trend Analysis]
Shapes --> Markers[Markers & Arrows]
Shapes --> Geometry[Geometric Shapes]
Shapes --> Analysis[Advanced Analysis]
Shapes --> Annotation[Text & Annotation]
Trend --> TVTrendLine
Trend --> TVParallelChannel
Trend --> TVRegressionTrend
Markers --> TVArrowUp
Markers --> TVArrowDown
Markers --> TVFlagMark
Markers --> TVLongPosition
Geometry --> TVRectangle
Geometry --> TVCircle
Geometry --> TVTriangle
Geometry --> TVPath
Analysis --> TVFibRetracement
Analysis --> TVFibChannel
Analysis --> TVPitchfork
Analysis --> TVElliottImpulseWave
Annotation --> TVText
Annotation --> TVNote
Annotation --> TVCallout
Annotation --> TVSticker
Loading

Example: False Breakout Indicator

The false breakout indicator demonstrates practical implementation of drawing elements and visualization techniques. This indicator detects false breakouts by identifying when price creates a new high/low and then reverses direction.

Horizontal Trend Lines for False Breakout Levels

The indicator creates horizontal trend lines at false breakout levels using the TVTrendLine shape. When a false breakout is detected, the indicator:

  1. Creates a TVTrendLine shape with custom styling from the indicator configuration
  2. Defines two points for the line: the breakout point and current price
  3. Adds the drawable to the drawables list for rendering
# Create horizontal trend line for false breakout
trend_line = TVTrendLine()
trend_line.overrides = line_overrides

drawables.append(TVDrawable(
    points=[
        (int(time[indx0]), float(state.val)),
        (int(time[i]), float(state.val))
    ],
    shape=trend_line,
    metadata={'type': 'false_breakout_up'}
))

Custom Arrow Styling

The indicator uses custom styling for buy and sell arrows, with colors and appearance defined in the configuration:

  • False breakout up signals use red downward arrows
  • False breakout down signals use green upward arrows
  • Arrow colors and labels are configured through style definitions

The styling is applied through the signal's metadata, which contains arrow color, text color, and label visibility settings that are automatically applied by the drawing system.

sequenceDiagram
participant Indicator
participant Engine
participant Chart
Indicator->>Indicator : Detect false breakout
Indicator->>Indicator : Create TVDrawable with TVTrendLine
Indicator->>Indicator : Set custom styling from config
Indicator->>Engine : Return signals and drawables
Engine->>Chart : Render horizontal trend line
Engine->>Chart : Render styled arrows
Chart-->>User : Visual false breakout signals
Loading

Entity Management and Drawing Persistence

Proper entity management is crucial for maintaining drawing persistence and preventing memory leaks. The framework provides several mechanisms for tracking and managing drawn elements.

Entity ID Management

The indicator maintains a list of drawn entity IDs through the _drawn_entities attribute. Each time a new element is created, its entity ID is recorded:

def add_drawn_entity(self, entity_id: str) -> None:
    """Record drawn entity ID"""
    self._drawn_entities.append(entity_id)

This allows the system to:

  • Track all elements created by the indicator
  • Clean up graphics when needed
  • Prevent duplicate drawings
  • Support persistence across updates

Drawing Persistence Across Updates

When an indicator is recalculated, the system automatically clears previous drawings before rendering new ones:

async def clear_all_drawings(self) -> None:
    """Clear all drawn graphics"""
    for entity_id in self._drawn_entities:
        try:
            await self._chart.removeEntity(entityId=entity_id)
        except Exception as e:
            if self._config and self._config.debug:
                print(f"Failed to clear entity: {e}")
    self._drawn_entities.clear()

This ensures that:

  • Old, outdated elements are removed
  • Only current signals and drawables are displayed
  • Memory usage is optimized
  • Visual clutter is prevented

The persistence mechanism works in conjunction with the recalculation flag (_needs_recalculate) to ensure drawings are updated only when necessary.

flowchart TD
Start([Indicator Update]) --> CheckRecalculate["Check needs_recalculate flag"]
CheckRecalculate --> |True| ClearDrawings["clear_all_drawings()"]
CheckRecalculate --> |False| SkipClear["Skip clearing"]
ClearDrawings --> Calculate["calculate() method"]
SkipClear --> Calculate
Calculate --> DrawElements["draw() or default drawing"]
DrawElements --> MarkDone["mark_recalculate_done()"]
MarkDone --> End([Update Complete])
Loading

Performance Optimization

Effective performance optimization is essential when rendering multiple drawing elements, especially in real-time trading applications.

Batch Processing

Group related drawing operations to minimize chart API calls:

  • Create multiple elements in a single update cycle
  • Use bulk operations when available
  • Minimize redundant style calculations

Conditional Rendering

Only render elements when necessary:

  • Skip drawing when no signals are present
  • Use visibility flags to toggle element display
  • Implement level-of-detail rendering for different timeframes

Memory Management

Optimize memory usage by:

  • Reusing shape objects when possible
  • Clearing old entities promptly
  • Using efficient data structures for points arrays
  • Avoiding circular references

Drawing Strategies

For complex drawings with many elements:

  • Prioritize critical signals
  • Use simpler shapes for less important elements
  • Implement throttling for high-frequency updates
  • Consider using canvas-based rendering for dense visualizations

The framework's design with timestamp-based positioning and direct shape styling already provides significant performance advantages, but these additional optimizations can further improve responsiveness.

flowchart TD
Performance[Performance Optimization] --> Batch["Batch Processing"]
Performance --> Conditional["Conditional Rendering"]
Performance --> Memory["Memory Management"]
Performance --> Strategies["Drawing Strategies"]
Batch --> GroupOperations
Batch --> BulkUpdates
Batch --> MinimizeCalls
Conditional --> SkipEmpty
Conditional --> VisibilityFlags
Conditional --> LOD
Memory --> ReuseShapes
Memory --> ClearEntities
Memory --> EfficientData
Strategies --> PrioritizeSignals
Strategies --> SimpleShapes
Strategies --> Throttling
Strategies --> CanvasRendering
Loading

Best Practices for Visual Clarity

Creating effective visualizations requires attention to both technical implementation and design principles.

Color and Contrast

  • Use high-contrast colors for important signals
  • Maintain consistent color schemes across related indicators
  • Consider color blindness in palette selection
  • Use transparency to avoid visual clutter

Element Hierarchy

  • Establish visual hierarchy with size, color, and placement
  • Use z-ordering to control element stacking
  • Group related elements visually
  • Maintain consistent spacing and alignment

Labeling and Annotation

  • Include clear, concise labels for all elements
  • Use price labels for reference lines
  • Add context through tooltips or hover text
  • Ensure text is readable at all zoom levels

Responsiveness

  • Design for different screen sizes and resolutions
  • Implement adaptive rendering for various timeframes
  • Test visualizations at multiple zoom levels
  • Ensure touch-friendly sizing for mobile devices

User Experience

  • Provide configuration options for visual preferences
  • Allow toggling of less important elements
  • Implement smooth transitions for dynamic updates
  • Ensure accessibility compliance

Following these best practices ensures that visualizations are not only technically correct but also effective communication tools for traders.

graph TD
Clarity[Visual Clarity Best Practices] --> Color["Color & Contrast"]
Clarity --> Hierarchy["Element Hierarchy"]
Clarity --> Labeling["Labeling & Annotation"]
Clarity --> Responsiveness["Responsiveness"]
Clarity --> UX["User Experience"]
Color --> HighContrast
Color --> ConsistentPalette
Color --> Accessibility
Hierarchy --> VisualWeight
Hierarchy --> ZOrdering
Hierarchy --> Grouping
Labeling --> ClearLabels
Labeling --> PriceLabels
Labeling --> Tooltips
Responsiveness --> MultipleResolutions
Responsiveness --> AdaptiveRendering
Responsiveness --> ZoomLevels
UX --> ConfigurationOptions
UX --> ToggleElements
UX --> SmoothTransitions
Loading

Conclusion

The PyTradingView framework provides a comprehensive system for creating sophisticated trading visualizations through its TVDrawable data structure, extensive shapes library, and flexible drawing mechanisms. By understanding the core components—particularly the points array for positioning and shape objects for styling—developers can create effective indicators that enhance trading analysis.

The default drawing system offers a powerful, automated approach for rendering signals and elements, while the custom draw() method override provides complete control for specialized visualizations. The false breakout indicator example demonstrates practical implementation of these concepts, showing how to create horizontal trend lines and customize arrow styling.

Effective entity management ensures drawing persistence and prevents memory issues, while performance optimization techniques help maintain responsiveness even with complex visualizations. Following best practices for visual clarity ensures that indicators are not only functional but also user-friendly and informative.

By leveraging these features and principles, developers can create professional-quality trading indicators that provide valuable insights to traders.

Clone this wiki locally