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.
- Skill ID
- traderspost/pinescript-agents/pine-backtester
- Publisher
- traderspost
- Repository
- pinescript-agents
- Installs
- 0
- Files
- 1
- Synced
- Sep 23, 2026
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 analysisUse 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 processingConditional 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 = naTrade 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) : 0Trade 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 distributionVisualize 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)More from traderspost
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.
