Key Concepts for Drawing in Indie
Before diving into patterns, let’s recall these essential concepts that affect how drawings work in Indie.Two Execution Phases: History vs Realtime
Indie indicators run in two distinct phases:- History calculation: Each bar is processed once sequentially from left to right. Drawing objects are created and remain stable.
- Realtime updates: When new intrabar data arrives, the indicator re-executes for the current bar multiple times.
Code that works perfectly during history calculation can break during realtime updates. Always consider both phases when writing drawing code, and test your indicators on instruments with realtime updates on 1m timeframe for several minutes to ensure runtime stability.
Chart State Rollbacks
During realtime updates, when new intrabar data arrives:- The chart state rolls back to what it was at the beginning of the current bar
- Your indicator’s
calc()function executes again with updated data
If you created a drawing during an intrabar update, the rollback will remove it. If you erased a drawing during an intrabar update, the rollback will restore it.
When and Why to Use Var for Drawings
The mismatch between chart rollbacks and class field persistence introduces a state synchronization conflict: class fields retain references to drawings that may no longer exist after a rollback. This can lead to runtime errors when trying to erase non-existent objects, or duplicate drawings when modifying and redrawing “ghost” references. UseVar[T] when you need to:
- Update the same drawing object across multiple bars
- Maintain drawing references during realtime updates
- Erase drawings created in previous executions
- One-time drawings (labels on conditions): Just create and draw - no special handling
- Persistent drawings (info panels): Store in class fields or
Varand reuse the object - Erasable drawings (anything you’ll erase): Must use
Varfor rollback safety
Common Drawing Patterns
Pattern 1: Draw labels when conditions are met
Problem: You want to display labels on specific bars when certain conditions are satisfied, such as price patterns, indicator signals, or periodic intervals. Solution: Use conditional logic to check your criteria and create new label objects when conditions are met. No special object management is required.Label breakouts and periodic bars

Pattern 2: Display information in fixed screen position
Problem: You want to show live statistics, indicator values, or other information that stays in a fixed position on screen regardless of chart scrolling or zooming. Solution: Create aLabelRel object once and reuse it by updating its text property. Store the label in a class field (for class-based indicators) or use Var (for function-based indicators).
Always reuse the same
LabelRel object. Creating new relative-positioned labels on each bar will cause an error “count of relative positioned drawings exceeded the limit 100”.
Pattern 3: Label that follows the current bar
Problem: You want a label that appears only above the most recent bar and moves with it as new bars arrive, displaying current values or active signals. Solution: Create aLabelAbs object once and update both its position and text properties on each bar to follow the current price action.

Pattern 4: Draw polyline connecting specific points
Problem: You want to create connected lines that pass through specific price points (e.g., highs every 5 bars), forming a polyline that updates as new bars arrive. Solution: Maintain only the current active segment usingVar. Update the segment’s endpoint between anchor points, and create new segments at anchor points.
Var the polyline would break. The segment reference must survive rollbacks to maintain continuity. Note that even if you used a class field to store the segment, you would still need to wrap it in Var[Optional[LineSegment]].

self.current_segment: Optional[LineSegment] instead:
- On anchor bar (e.g., bar 10), you create a new segment starting from the previous segment’s endpoint and save it to
self.current_segment - An intrabar update arrives, chart rolls back to the start of bar 10
- During recalculation, the code tries to create a new LineSegment, but takes its start point from
self.current_segment, which was modified during the previous update and wasn’t rolled back - This creates a disconnected polyline - the new segment starts from an incorrect position
Pattern 5: Safely erase drawings created on previous executions
Problem: You need to manually erase drawings that were created during previous bar updates or previous intrabar executions. This is common for temporary overlays, alerts, or any drawings that should appear and disappear based on conditions. Solution: Let’s explore this through a concrete example - creating a blinking line indicator. We’ll work through the challenges step-by-step to understand why proper erasure handling is crucial. First attempt - using class fields:- On the last update of bar N, the line is erased (
is_visiblewas False) - Bar N+1 starts with no line on the chart
- First execution:
is_visiblebecomes True, line is drawn - Intrabar update arrives, chart rolls back to start of bar N+1 (no line)
- Second execution:
is_visiblebecomes False, tries to erase non-existent line - Runtime error!
This type of error often manifests as “Error: drawing object does not exist” and only appears during realtime, making it hard to catch during development.
- On the last update of bar N, the line is drawn (
is_visiblewas True) - Bar N+1 starts with the line on the chart
- First execution:
is_visiblebecomes False, line is erased - Intrabar update arrives, chart rolls back to start of bar N+1 (line is back!)
- Second execution:
is_visiblebecomes True, draws another line - Result: Two lines on the chart!
is_visible is true and disappearing when false. During realtime updates, each intrabar update toggles the visibility, creating a visual heartbeat of market activity.
Pattern 6: Skip redundant draws during history with is_last_bar
Problem: You have a drawing that gets updated every bar (e.g., an info panel, a “current price” label) but only the final state matters to the user. During history calculation the runtime tracks every chart.draw() call internally, so redrawing the same object on each of thousands of history bars wastes memory and slows down calculation — even though only the last bar’s state will ever be visible.
Solution: Guard the drawing call with self.is_last_bar so that the object is only drawn once — on the most recent bar. During realtime updates is_last_bar is always True, so the drawing updates normally.