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

Strategy Trading & Backtesting

Introduction to Backtesting

The BacktestX Script engine includes a built-in strategy object that allows you to simulate trades, calculate win rates, and draw entry/exit markers directly on the chart.

The Strategy API

Use the following methods to record simulated trades. When you pass boolean arrays as the condArray, the engine will automatically draw Execution Arrows on the corresponding candles.

strategy.entry(id, direction, condArray)
strategy.close(id, condArray)
  • id: A string identifier for the trade (e.g., "MyLong").
  • direction: "Long" or "Short".
  • condArray: A boolean array representing the trigger condition (e.g., from ta.crossover()).

Strategy Tester UI

Whenever a script containing strategy.entry or strategy.close is successfully evaluated, a Strategy Tester panel will automatically slide up at the bottom of your terminal. It displays detailed statistics such as Net Profit, Win Rate, and Max Drawdown, alongside a fully formatted chronological ledger of all executed trades!

Advanced Strategy API

0. Capital & Account Settings

  • ctx.strategy.initial_capital(amount, currency): Programmatically sets the starting capital for the strategy, which automatically updates the Strategy Tester UI dropdown.

1. Advanced Exits

  • ctx.strategy.exit(id, condArray, profit, loss, limit, stop, trail_points, trail_offset): Advanced exit logic with trailing stops and brackets. Allows you to set absolute profit/loss targets and dynamic trailing stops.

2. Risk Management

These functions enforce risk rules which the Strategy Tester will mathematically honor when generating your final Net Profit and Drawdown stats:

  • ctx.strategy.risk.max_drawdown(value, type): Stops trading if the strategy loses a certain amount. (type can be 'percent' or 'cash').
  • ctx.strategy.risk.max_intraday_loss(value, type): Caps the maximum loss allowed per day.

3. Real-Time Tracking

You can read these live values inside your script to make dynamic sizing decisions (e.g. risking 1% of live equity per trade):

  • ctx.strategy.equity: Returns the live, updating equity balance (Initial Capital + Realized PnL).
  • ctx.strategy.position_size: Returns the current open quantity (positive for Longs, negative for Shorts).
  • ctx.strategy.position_avg_price: Returns the exact entry price of the current active trade.
  • ctx.strategy.openprofit(): Returns the floating Unrealized PnL of the current trade.

4. Custom Strategy UI

You can customize the Strategy Tester UI directly from your scripts:

  • ctx.strategy.setMetric(label, value, color): Injects a custom stats block into the top Key Stats dashboard.
    • label: The title of the metric (e.g. "Sharpe Ratio").
    • value: The text/number to display (e.g. "1.85").
    • color: (Optional) Hex code for the value.
  • ctx.strategy.tagTrade(id, columnName, value, color): Appends a custom data column to the trade table for a specific trade.
    • id: The Trade ID (e.g. "MACD Long").
    • columnName: The title of your custom column (e.g. "Confidence").
    • value: The value for this specific trade's row (e.g. "High").
    • color: (Optional) Hex color code for the text.

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.