Calculating Drawdown Duration in Pine Script

This lesson demonstrates how to calculate the drawdown duration (in time) for your TradingView strategy scripts.

Check out my other free lessons!

Source Code

Note: The code below is slightly different to the code shown in the video as I made some improvements.


// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © ZenAndTheArtOfTrading
// @version=5

//////////////////////////////////////////////////////////////////////////
// EXAMPLE STRATEGY (REPLACE THIS CODE WITH YOUR OWN)                   //
//////////////////////////////////////////////////////////////////////////

strategy("Drawdown Time", 
     overlay=true, 
     initial_capital=100000,
     default_qty_type=strategy.percent_of_equity, 
     default_qty_value=100,
     commission_type=strategy.commission.cash_per_contract, 
     commission_value=0.005)

// Get user input
i_ma1           = input.int(title="MA 1 Length", defval=200, step=10, group="Strategy Parameters", tooltip="Long-term MA", display=display.none)
i_ma2           = input.int(title="MA 2 Length", defval=10, step=10, group="Strategy Parameters", tooltip="Short-term MA", display=display.none)
i_stopPercent   = input.float(title="Stop Loss Percent", defval=0.10, step=0.1, group="Strategy Parameters", tooltip="Failsafe Stop Loss Percent Decline", display=display.none)
i_lowerClose    = input.bool(title="Exit On Lower Close", defval=false, group="Strategy Parameters", tooltip="Wait for a lower-close before exiting above MA2", display=display.none)
i_startTime     = input.time(title="Start Filter", defval=timestamp("01 Jan 1995 13:30 +0000"), group="Time Filter", tooltip="Start date & time to begin searching for setups", display=display.none)
i_endTime       = input.time(title="End Filter", defval=timestamp("1 Jan 2099 19:30 +0000"), group="Time Filter", tooltip="End date & time to stop searching for setups", display=display.none)

// Get indicator values
ma1 = ta.sma(close, i_ma1)
ma2 = ta.sma(close, i_ma2)

// Check filter(s)
f_dateFilter = time >= i_startTime and time <= i_endTime

// Check buy/sell conditions
var float buyPrice = 0
buyCondition    = close > ma1 and close < ma2 and strategy.position_size == 0 and f_dateFilter
sellCondition   = close > ma2 and strategy.position_size > 0 and (not i_lowerClose or close < low[1])
stopDistance    = strategy.position_size > 0 ? ((buyPrice - close) / close) : na
stopPrice       = strategy.position_size > 0 ? buyPrice - (buyPrice * i_stopPercent) : na
stopCondition   = strategy.position_size > 0 and stopDistance > i_stopPercent

// Enter positions
if buyCondition
    strategy.entry(id="Long", direction=strategy.long, comment="Equity = $" + str.tostring(strategy.equity, "#.##"))

if buyCondition[1]
    buyPrice := strategy.position_avg_price

// Exit positions
if sellCondition or stopCondition
    strategy.close(id="Long", comment="Exit" + (stopCondition ? "SL=true" : ""))
    buyPrice := na

// Draw pretty colors
plot(buyPrice, color=color.lime, style=plot.style_linebr)
plot(stopPrice, color=color.red, style=plot.style_linebr, offset=-1)
plot(ma1, color=color.blue)
plot(ma2, color=color.orange)

////////////////////////////////////////////////////////////
// END EXAMPLE STRATEGY (REPLACE THIS CODE WITH YOUR OWN) //
////////////////////////////////////////////////////////////

// Drawdown time code
var float equityPeak = strategy.initial_capital

var int currentDrawdownBars = 0
var array<int> drawdownBarsArray = array.new<int>(0)

if (barstate.isconfirmed) 
    // If bar is closed, check the following:
    if (strategy.closedtrades != strategy.closedtrades[1]) // Check if a trade just closed
        if (strategy.equity > equityPeak) // If so, update REALIZED equity peak
            equityPeak := strategy.equity
    if (strategy.equity < equityPeak) // If current equity is lower than the peak, we're in drawdown
        currentDrawdownBars := currentDrawdownBars + 1
    else // If current equity is not lower than the peak, we've cleared the drawdown
        if (currentDrawdownBars > 0) // Save this last drawdown so we can average all DD's later
            drawdownBarsArray.push(currentDrawdownBars)
        // Reset counter
        currentDrawdownBars := 0 

// Calculate average drawdown
int averageBarsInDD = drawdownBarsArray.avg()

// Draw table information
var table displayTable = na

if (barstate.isconfirmed)
    displayTable := table.new(position.top_right, columns = 1, rows = 2, bgcolor = color.black, border_width = 1)
    // Get timeframe period 
    string timeInDrawdown = "Timeframe Unsupported"
    string avgDrawdown = "Timeframe Unsupported"
    if (timeframe.isdwm)
        if (timeframe.isdaily)
            timeInDrawdown := str.tostring(drawdownBarsArray.max() * timeframe.multiplier) + " days"
            avgDrawdown := str.tostring(averageBarsInDD * timeframe.multiplier, "#.##") + " days"
        else
            displayTable.cell(0, 0, "Timeframe Unsupported")
    else
        int minutesInDrawdown = drawdownBarsArray.max() * int(str.tonumber(timeframe.period))
        if (minutesInDrawdown <= 60) // Less than 1 hour
            timeInDrawdown := str.tostring(minutesInDrawdown) + " minutes"
            avgDrawdown := str.tostring(averageBarsInDD * str.tonumber(timeframe.period), "#.##") + " minutes"
        else if (minutesInDrawdown <= 1440) // Less than 1 day
            timeInDrawdown := str.tostring(minutesInDrawdown / 60, "#.##") + " hours"
            avgDrawdown := str.tostring((averageBarsInDD * str.tonumber(timeframe.period)) / 60, "#.##") + " hours"
        else // More than 1 day
            timeInDrawdown := str.tostring(minutesInDrawdown / 1440, "#.##") + " days"
            avgDrawdown := str.tostring((averageBarsInDD * str.tonumber(timeframe.period)) / 1440, "#.##") + " days"

    displayTable.cell(0, 0, "Worst Drawdown: " + timeInDrawdown, text_color=color.white)
    displayTable.cell(0, 1, "Avg Drawdown: " + avgDrawdown, tooltip="Total Drawdown Count: " + str.tostring(drawdownBarsArray.size()), text_color=color.white)