Skip to content

How to get the profit factor of TradingView Pine Script strategies?

How to get the profit factor of TradingView Pine Script strategies?

Section titled “How to get the profit factor of TradingView Pine Script strategies?”

TL;DR
Profit factor is gross profit ÷ gross loss. Guard against zero loss, then use the ratio to plot performance, throttle sizing, or trigger alerts when results slip below one.

DifficultyIntermediate
Time to implement10-15 min
CategoryPerformance Metrics
//@version=5
strategy("Profit factor monitor", overlay=false)
profitFactor() =>
grossLoss = strategy.grossloss
grossLoss == 0 ? na : strategy.grossprofit / grossLoss
plot(profitFactor(), "PF", color=color.new(color.blue, 0))
Heads-up. Until the strategy books its first losing trade `strategy.grossloss` equals zero, so return `na` (or a large placeholder) to avoid divide-by-zero errors.

Profit factor summarises edge at a glance: values above 1.0 mean winners outweigh losers, below 1.0 flags a system giving back gains. Tracking it inside Pine lets you throttle order size, pause trading after drawdowns, or surface the ratio in dashboards without exporting results manually.

  • Build a reusable helper that guards against zero loss.
  • Detect profit-factor shifts with comparisons and crossovers.
  • Plot the ratio, annotate results, and conditionally size trades.
  • Wrap up with a sample strategy that reports the metric on chart.
CallPurpose
strategy.grossprofitTotal gains from closed trades.
strategy.grosslossTotal losses (positive number).
strategy.closedtradesUseful for confirming the strategy has traded before evaluating PF.
plot(series, ...)Visualises the profit factor history.
label.new()Displays a summary at the end of the backtest.
  1. Create a profit factor helper
    Hide the divide-by-zero guard inside a function so the rest of the script stays tidy.

    profitFactor() =>
    grossLoss = strategy.grossloss
    grossLoss == 0 ? na : strategy.grossprofit / grossLoss
  2. React to thresholds
    Compare the helper output against critical levels to control risk.

    pf = profitFactor()
    stall = not na(pf) and pf < 1.0
    if stall
    strategy.risk.max_intraday_filled_orders(0)
  3. Report the metric clearly
    Plot the ratio and summarise it in a label or table on the last bar.

    plot(pf, "Profit factor", color=color.new(color.teal, 0))
    if barstate.islastconfirmedhistory and not na(pf)
    label.new(bar_index + 2, close,
    "PF: " + str.tostring(pf, "0.000"),
    style=label.style_label_left,
    textcolor=color.white,
    bgcolor=color.new(color.teal, 65))
//@version=5
strategy("Bollinger band swings with PF monitor", overlay=true, default_qty_type=strategy.fixed, default_qty_value=1)
length = input.int(20, "BB length", minval=1)
mult = input.float(2.0, "Std dev multiplier", step=0.1)
minPF = input.float(1.2, "Pause trading below PF", step=0.1)
basis = ta.sma(close, length)
upper = basis + mult * ta.stdev(close, length)
lower = basis - mult * ta.stdev(close, length)
plot(basis, "Basis", color=color.new(color.gray, 0))
plot(upper, "Upper", color=color.new(color.green, 0))
plot(lower, "Lower", color=color.new(color.red, 0))
pf() =>
loss = strategy.grossloss
loss == 0 ? na : strategy.grossprofit / loss
currentPF = pf()
longSignal = ta.crossover(close, upper)
shortSignal = ta.crossunder(close, lower)
if longSignal and (na(currentPF) or currentPF >= minPF)
strategy.entry("Long", strategy.long)
if shortSignal and (na(currentPF) or currentPF >= minPF)
strategy.entry("Short", strategy.short)
if not na(currentPF)
plot(currentPF, "Profit factor", color=color.new(color.orange, 0), linewidth=2, display=display.bottom)
if barstate.islastconfirmedhistory and not na(currentPF)
summary = "Gross profit: " + str.tostring(strategy.grossprofit, "##,###.00") +
"\nGross loss: " + str.tostring(strategy.grossloss, "##,###.00") +
"\nProfit factor: " + str.tostring(currentPF, "0.000")
shade = currentPF >= 1.0 ? color.new(color.green, 60) : color.new(color.red, 60)
label.new(bar_index + 2, basis,
summary,
style=label.style_label_left,
textcolor=color.white,
bgcolor=shade)
Why this works.
  • The helper centralises error handling, so all comparisons read cleanly.
  • Guarding entries when PF drops below `minPF` pauses trading during drawdowns.
  • The summary label makes it easy to screenshot results or share backtests without opening the Strategy Tester panel.
  • Profit factor only reflects closed trades. During open drawdowns it may still look healthy—combine it with equity curve checks for a fuller picture.
  • Because strategy.grossloss is positive, dividing by it is safe once a losing trade exists; you do not need math.abs.
  • Compare PF against different thresholds to gate pyramiding or tighten stops when edge weakens.
  • Resetting the strategy (removing/re-adding) clears the history—handy for segmenting sessions.

The strategy has not booked any losing trades yet. Leave chart running or seed a losing trade so strategy.grossloss becomes non-zero.

Large ratios occur when losses are tiny relative to wins. Consider minimum-loss guards or pair PF with additional stats (e.g., win rate) before trusting it in isolation.

Can I calculate PF per symbol or timeframe?

Section titled “Can I calculate PF per symbol or timeframe?”

Yes—store cumulative profits/losses in arrays keyed by symbol/time-frame, or run the strategy separately per chart. Pine’s built-in metrics are global to the script instance.

  • Profit factor equals total wins divided by total losses; guard against zero loss before dividing.
  • Wrap the math in a helper so charts, alerts, and position sizing can reuse the value cleanly.
  • Visualise and label the metric to understand when the system is thriving or failing.
  • Combine PF with other controls to automate drawdown responses without babysitting the Strategy Tester.

Adapted from tradingcode.net, optimised for Algo Trade Analytics users.