Lines & Fills
Drawing Dynamic Lines
You can draw programmatic lines connecting two specific bars/prices using the line.new API.
const myLine = line.new(x1, y1, x2, y2, color, style, width)
- x1, x2: Bar indices.
- y1, y2: Prices.
- style: 'solid', 'dashed', or 'dotted'.
Shading Between Lines
You can fill the background area between two generated lines using linefill.new.
linefill.new(line1, line2, color)
Note that line1 and line2 must be references returned by line.new.
Coordinate Mapping
Drawing on a canvas requires pixel coordinates. The system provides two primary helper conversion methods on the ctx context:
ctx.barToX(i): Converts a bar indexi(from0tobars.length - 1) to its respective X pixel coordinate on the canvas.ctx.priceToY(p): Converts a numeric price valuep(e.g.1.2345) to its respective Y pixel coordinate on the canvas.
State Preservation
Because scripts execute repeatedly on every UI draw call, understanding scope is critical:
- Persistent State (var): Declaring variables in the global block scope (which translates to outer declarations in the transpiler) preserves their values across render cycles. Use these to store rolling totals, win/loss stats, or active drawing references.
- Loop State: Variables defined inside the main bar execution loop are re-initialized on each bar iteration and do not persist across bars or render cycles.
Exposing Technical Indicators & Volume
Your custom indicator scripts have access to standard technical indicator values in two distinct ways:
- Built-in Math Helpers: Call standard math functions directly from
ctx(such asctx.sma(closes, 20),ctx.rsi(closes, 14), orctx.macd(closes)) to calculate indicators dynamically. - Active Indicators Array: Inspect
ctx.indicatorsto read pre-calculated arrays for active indicators running on the chart. Every indicator object contains a populated.valuesproperty. - Accessing Volume: Retrieve raw bar volume using
ctx.bars[i].volume, or get the entire history array viactx.vol(ctx.bars).
Rendering Optimization & 60 FPS Guidelines
To ensure high-performance scripting (60 FPS rendering without lag or stutter):
- Clamp rendering loops: Restrict canvas rendering iterations from
Math.max(0, Math.floor(bounds.startIndex) - 1)toMath.ceil(bounds.endIndex) + 1. This padding ensures indicators pan smoothly without abruptly disappearing. Avoid looping through the entire historical dataset. - Function Hoisting (CRITICAL): Never declare helper functions inside loops or main rendering callbacks. Hoist all user-defined functions to the global scope to prevent heavy memory allocation.
- Local Scoping: Scope loop-local variables with block-level
letorconstto prevent global namespace lookups and scope leaks. - Map Callback Dereferencing: Avoid allocating new array references or parsing objects inside map callbacks. Dereference precalculated indicator arrays directly using index keys.
- Execution Limits: Scripts taking longer than 12ms to execute will trigger a performance warning in the console.
Mouse Interactions
You can query the real-time mouse position and action states using the ctx.mouse object. This allows you to build interactive hover overlays, highlight target coordinates, or reveal tooltip stats under the cursor.