Exciting Update: Version 1.0.1 is now available, introducing the high-performance BacktestX Custom Script Editor. Read more

Multi-Timeframe (MTF)

Multi-Timeframe Analysis

The request.security() function allows you to request data from higher timeframes without lookahead bias.

request.security(symbol, timeframe, exprFn)
  • symbol: The trading pair symbol (e.g., "BTC-USD"). Note: Currently only the active chart symbol is supported.
  • timeframe: A string like "5", "15", "1H", "4H", "1D", "1W".
  • exprFn: A callback function returning the indicator or value you want evaluated on the higher timeframe.

Example: 1-Hour SMA on a 5-Minute Chart

// This evaluates an SMA 20 on 1-hour candles, and aligns it to your current chart
const htfSma = request.security(sym, '1H', () => ta.sma(close, 20));
plot(htfSma, { title: '1H SMA', color: '#ffeb3b', width: 2 });

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 index i (from 0 to bars.length - 1) to its respective X pixel coordinate on the canvas.
  • ctx.priceToY(p): Converts a numeric price value p (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 as ctx.sma(closes, 20), ctx.rsi(closes, 14), or ctx.macd(closes)) to calculate indicators dynamically.
  • Active Indicators Array: Inspect ctx.indicators to read pre-calculated arrays for active indicators running on the chart. Every indicator object contains a populated .values property.
  • Accessing Volume: Retrieve raw bar volume using ctx.bars[i].volume, or get the entire history array via ctx.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) to Math.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 let or const to 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.