traderspost/pinescript-agents1 file

Pine Backtester

Implements comprehensive backtesting capabilities for Pine Script indicators and strategies. Use when adding performance metrics, trade analysis, equity curves, win rates, drawdown tracking, or statistical validation. Triggers on "backtest", "performance", "metrics", "win rate", "drawdown", or testing requests.

Specification
Skill ID
traderspost/pinescript-agents/pine-backtester
Publisher
traderspost
Repository
pinescript-agents
Installs
0
Files
1
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

traderspost/pinescript-agents/pine-backtesterInstalls these files
  • SKILL.md

What this skill tells the agent

Pine Script Backtester

Specialized in adding comprehensive testing and validation capabilities to Pine Script indicators and strategies.

Core Responsibilities

Strategy Performance Metrics

  • Win rate and profit factor
  • Maximum drawdown analysis
  • Sharpe and Sortino ratios
  • Risk-adjusted returns
  • Trade distribution analysis

Indicator Accuracy Testing

  • Signal accuracy measurements
  • False positive/negative rates
  • Lag analysis
  • Divergence detection accuracy
  • Multi-timeframe validation

Statistical Analysis

  • Monte Carlo simulations
  • Walk-forward analysis
  • Confidence intervals
  • Statistical significance tests
  • Correlation analysis

Pine Script v6 2025 Backtesting Features

Enhanced Reporting (August 2025: 40,960 char strings)

Now possible: comprehensive multi-section reports:

// Build detailed performance report (up to 40K chars!)
var string fullReport = ""
fullReport := "═══════════════════════════════════════════\n"
fullReport += "           COMPREHENSIVE BACKTEST REPORT    \n"
fullReport += "═══════════════════════════════════════════\n\n"
fullReport += "PERFORMANCE SUMMARY\n"
fullReport += "───────────────────\n"
fullReport += "Win Rate: " + str.tostring(winRate, "#.##") + "%\n"
fullReport += "Profit Factor: " + str.tostring(pf, "#.##") + "\n"
fullReport += "Sharpe Ratio: " + str.tostring(sharpe, "#.##") + "\n\n"
fullReport += "TRADE DISTRIBUTION\n"
fullReport += "───────────────────\n"
// ... Can include much more detailed analysis

Use for...in for Trade Arrays (March 2025)

The preferred way to iterate over trade data arrays:

// ✅ BEST: Use for...in for arrays (safe, clean)
for tradeReturn in tradeReturns
    // Process each return directly

// ✅ BEST: With index when needed
for [i, tradeReturn] in tradeReturns
    // Have both index and value

// ❌ AVOID: Traditional for with dynamic boundary
for i = 0 to array.size(tradeReturns) - 1
    array.push(newArray, processedValue)  // Can infinite loop!

// ✅ FALLBACK: If traditional for needed, cache boundary
arrSize = array.size(tradeReturns)
for i = 0 to arrSize - 1
    // Safe processing

Conditional Backtest Settings (July 2025)

Use active parameter to show relevant settings:

showAdvancedMetrics = input.bool(false, "Show Advanced Metrics", group="Backtest")
sharpeRiskFreeRate = input.float(0.02, "Risk-Free Rate",
    group="Backtest",
    active=showAdvancedMetrics,  // Only show when advanced enabled
    tooltip="Annual risk-free rate for Sharpe calculation")

Visual Line Styles (September 2025)

Differentiate backtest visualization with line styles:

// Primary metrics: solid
plot(strategy.equity, "Equity", color.blue, linestyle=plot.linestyle_solid)
// Benchmarks: dashed
plot(benchmarkEquity, "Benchmark", color.gray, linestyle=plot.linestyle_dashed)
// Projections: dotted
plot(projectedEquity, "Projected", color.yellow, linestyle=plot.linestyle_dotted)

⚠️ UDT-First Trade Tracking

For comprehensive trade analysis, use UDTs to track individual trades.

Trade UDT Pattern

// Define Trade UDT for detailed tracking
type Trade
    // Time coordinates (for historical analysis)
    int entryTime
    int exitTime
    int entryBar
    int exitBar

    // Trade data
    float entryPrice
    float exitPrice
    float positionSize
    bool isLong
    string exitReason

    // Calculated metrics
    float pnl = 0.0
    float pnlPercent = 0.0
    int duration = 0

    // Visualization (optional)
    line tradeLine = na
    label entryLabel = na
    label exitLabel = na

// Methods for trade analysis
method calculate(Trade this) =>
    this.duration := this.exitBar - this.entryBar
    if this.isLong
        this.pnl := (this.exitPrice - this.entryPrice) * this.positionSize
        this.pnlPercent := ((this.exitPrice - this.entryPrice) / this.entryPrice) * 100
    else
        this.pnl := (this.entryPrice - this.exitPrice) * this.positionSize
        this.pnlPercent := ((this.entryPrice - this.exitPrice) / this.entryPrice) * 100

method draw(Trade this, color winColor, color loseColor) =>
    tradeColor = this.pnl >= 0 ? winColor : loseColor
    // Use xloc.bar_time for unlimited lookback!
    this.tradeLine := line.new(this.entryTime, this.entryPrice,
         this.exitTime, this.exitPrice,
         xloc=xloc.bar_time, color=tradeColor, width=2)

// Storage
var array<Trade> completedTrades = array.new<Trade>()
var Trade currentTrade = na

Trade Analysis with for...in

// Analyze all trades using for...in
if barstate.islastconfirmedhistory
    totalPnl = 0.0
    wins = 0
    losses = 0
    totalDuration = 0

    for trade in completedTrades
        totalPnl += trade.pnl
        if trade.pnl >= 0
            wins += 1
        else
            losses += 1
        totalDuration += trade.duration

    winRate = array.size(completedTrades) > 0 ? (wins / array.size(completedTrades)) * 100 : 0
    avgDuration = array.size(completedTrades) > 0 ? totalDuration / array.size(completedTrades) : 0

Trade Distribution with UDT Arrays

// Categorize trades by exit reason
var array<Trade> stopLossTrades = array.new<Trade>()
var array<Trade> takeProfitTrades = array.new<Trade>()
var array<Trade> timeoutTrades = array.new<Trade>()

// When trade closes, categorize it
if tradeJustClosed
    if currentTrade.exitReason == "SL"
        array.push(stopLossTrades, currentTrade)
    else if currentTrade.exitReason == "TP"
        array.push(takeProfitTrades, currentTrade)
    else
        array.push(timeoutTrades, currentTrade)

// Analyze by category
if barstate.islast
    slCount = array.size(stopLossTrades)
    tpCount = array.size(takeProfitTrades)
    // ... display distribution

Visualize Trade History with xloc.bar_time

// Draw all historical trades (no 5000 bar limit!)
if barstate.islast and showTradeHistory
    for trade in completedTrades
        trade.draw(color.green, color.red)

Backtesting Components

1. Comprehensive Strategy Metrics Table

// Strategy Performance Metrics
var table metricsTable = table.new(position.bottom_right, 2, 15, bgcolor=color.new(color.black, 90))

if barstate.islastconfirmedhistory
    wins = strategy.wintrades
    losses = strategy.losstrades
    totalTrades = wins + losses
    winRate = totalTrades > 0 ? (wins / totalTrades) * 100 : 0

    avgWin = strategy.grossprofit / math.max(wins, 1)
    avgLoss = math.abs(strategy.grossloss) / math.max(losses, 1)
    profitFactor = avgLoss > 0 ? avgWin / avgLoss : 0

    // Drawdown calculation
    var float maxEquity = strategy.initial_capital
    var float maxDrawdown = 0.0
    currentEquity = strategy.equity
    if currentEquity > maxEquity
        maxEquity := currentEquity
    drawdown = ((maxEquity - currentEquity) / maxEquity) * 100
    maxDrawdown := math.max(maxDrawdown, drawdown)

    // Populate table
    table.cell(metricsTable, 0, 0, "METRIC", bgcolor=color.gray, text_color=color.white)
    table.cell(metricsTable, 1, 0, "VALUE", bgcolor=color.gray, text_color=color.white)

    table.cell(metricsTable, 0, 1, "Total Trades", text_color=color.white)
    table.cell(metricsTable, 1, 1, str.tostring(totalTrades), text_color=color.yellow)

    table.cell(metricsTable, 0, 2, "Win Rate", text_color=color.white)
    table.cell(metricsTable, 1, 2, str.tostring(winRate, "#.##") + "%", text_color=winRate > 50 ? color.green : color.red)

    table.cell(metricsTable, 0, 3, "Profit Factor", text_color=color.white)
    table.cell(metricsTable, 1, 3, str.tostring(profitFactor, "#.##"), text_color=profitFactor > 1 ? color.green : color.red)

    table.cell(metricsTable, 0, 4, "Max Drawdown", text_color=color.white)
    table.cell(metricsTable, 1, 4, str.tostring(maxDrawdown, "#.##") + "%", text_color=maxDrawdown < 20 ? color.green : color.red)

    table.cell(metricsTable, 0, 5, "Net Profit", text_color=color.white)

Use this skill in a project

Open any RiverX project, search the Skills panel for traderspost/pinescript-agents/pine-backtester, and install it in one click.

Use this skill