Breakout Trend Trading Strategy - V1
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © HeWhoMustNotBeNamed

//@version=4

strategy("Breakout Trend Trading - V1", overlay=true, initial_capital = 2000, default_qty_type = strategy.percent_of_equity, default_qty_value = 100, commission_type = strategy.commission.cash_per_order, pyramiding = 1, commission_value = 2)

///////////////////////////////////////// Input ////////////////////////////////////////////////////////////////////
//supertrendMult = input(6, minval=1, maxval=10, step=0.5)
//supertrendLength = input(22, minval=1)

//volumelength = input(title="Volume MA Length", defval=20, minval=10, step=10)
//volumeMaType = input(title="Volume MA Type", defval="sma", options=["ema", "sma", "hma", "rma", "vwma", "wma"])
//trendVolumeMarkup = input(15, minval=0, maxval=100, step=5)
//reverseVolumeMarkup = input(15, minval=0, maxval=100, step=5)
//highVolumeMultiplyer = input(5, minval=2, maxval=10, step=1)

//atrlength = input(22, minval=1)
//trendTargetMult = input(1, minval=1, maxval=10, step=0.5)
//reverseTargetMult = input(1, minval=1, maxval=10, step=0.5)
//highBreakoutMult = input(2, minval=2, maxval=10, step=1)

//trendValueMarkup = input(15, minval=0, maxval=100, step=5)
//reverseValueMarkup = input(30, minval=0, maxval=100, step=5)
//stopMultFactor = input(1, minval=0.5, maxval=10, step=0.5)

//considerNewLongTermHighLows = input(true,"Filter Highs/Lows for entry")
//shortHighLowPeriod = input(120, step=10)
//longHighLowPeriod = input(180, step=10)

//considerMaAlignment = input(true,"Check recent moving average alignment")
//MAType = input(title="Moving Average Type", defval="sma", options=["ema", "sma", "hma", "rma", "vwma", "wma"])
//LookbackPeriod = input(200, step=10)
//alignmentIndexThreshold = input(0, step=10)
//includePartiallyAligned = input(true)

//considerMaSupertrend = input(true)
//source = input(ohlc4)
//MaTypeMa = input(title="Volume MA Type", defval="sma", options=["ema", "sma", "hma", "rma", "vwma", "wma"])
//MaLengthMa = input(10, step=10)
//supertrendLengthMa = input(10, step=10)
//supertrendMultMa = input(1, step=1)


supertrendMult = 6
supertrendLength = 22

volumelength = 20
volumeMaType = "sma"
trendVolumeMarkup = 15
reverseVolumeMarkup = 25
highVolumeMultiplyer = 5

atrlength = 22
trendTargetMult = 1
reverseTargetMult = 1
highBreakoutMult = 2

trendValueMarkup = 15
reverseValueMarkup = 30
stopMultFactor = 1
considerYearlyHighLow = true
ignoreReverseBreakout = true
//allowImmediateCompound = true
considerOpenCloseBreakout = false
allowReduceCompound = true
considerNewLongTermHighLows = true
shortHighLowPeriod = 120
longHighLowPeriod = 180
considerMaAlignment = true
MAType = "sma"
LookbackPeriod = 280
alignmentIndexThreshold = 0
includePartiallyAligned = true

considerMaSupertrend = true
source = ohlc4
MaTypeMa = "sma"
MaLengthMa = 10
supertrendLengthMa = 10
supertrendMultMa = 1

exitOnSignal = input(false)
tradeDirection = input(title="Trade Direction", defval=strategy.direction.long, options=[strategy.direction.all, strategy.direction.long, strategy.direction.short])
allowImmediateCompound = input(false)
displayMode = input(title="Display Mode", defval="Targets", options=["Targets", "Target Channel", "Target with Stops", "Up Channel", "Down Channel"])
showTrailingStop = input(true)
showTargetLevels = input(true)
showPreviousLevels = input(false)
stopMultiplyer = input(6, minval=1, maxval=10, step=0.5)
compoundMultiplyer = input(1, minval=1, maxval=10, step=0.5)
backtestYears = input(10, minval=1, step=1)

///////////////////////////// Display mode calculation ////////////////////////////////////////////////
f_getDisplayModeVeriables(displayMode)=>
    bool showTargets = false
    bool showChannel = false
    bool showTargetStops = false
    bool showUpChannel = false
    bool showDownChannel = false
    if(displayMode == "Targets")
        showTargets := true
    if(displayMode == "Target Channel")
        showChannel := true
    if(displayMode == "Target with Stops")
        showTargetStops := true
    if(displayMode == "Up Channel")
        showUpChannel := true
    if(displayMode == "Down Channel")
        showDownChannel := true
    [showTargets,showChannel,showTargetStops,showUpChannel,showDownChannel]


///////////////////////////// Get Price breakout values ////////////////////////////////////////////////
f_getPriceBreakout(atr, trendTargetMult, reverseTargetMult, considerOpenCloseBreakout, highBreakoutMult)=>
    trendBreakout = false
    if(abs(close[1] - close) > atr * trendTargetMult) or (high-low > 2 * atr * trendTargetMult and considerOpenCloseBreakout)
        trendBreakout := true
    
    reverseBreakout = false
    if(abs(close[1] - close) > atr * reverseTargetMult) or (high-low > 2 * atr * reverseTargetMult and considerOpenCloseBreakout)
        reverseBreakout := true
        
    highBreakout = false
    if(abs(close[1] - close) > atr * highBreakoutMult) or (high-low > 2 * atr * highBreakoutMult and considerOpenCloseBreakout)
        highBreakout:=true
        
    [trendBreakout, reverseBreakout, highBreakout]

///////////////////////////// Get Volume breakout values ////////////////////////////////////////////////
f_getVolumeBreakout(vma, trendVolumeMarkup, reverseVolumeMarkup, highVolumeMarkup)=>
    vmaMarkup = vma * (1+(trendVolumeMarkup/100))
    vmaReverseMarkup = vma * (1+(reverseVolumeMarkup/100))
    vmaHighVolumeMarkup = vma * highVolumeMarkup
    trendVolumeBreakout = vmaMarkup <  volume or na(volume)
    stopVolumeBreakout = vmaReverseMarkup <  volume or na(volume)
    highVolumeBreakout = vmaHighVolumeMarkup < volume and not na(volume)
    [trendVolumeBreakout, stopVolumeBreakout, highVolumeBreakout]


//////////////////////////////////// Get Candle color //////////////////////////////////////////////////
f_getBarState(dir,trendBreakout, reverseBreakout, highBreakout, trendVolumeBreakout, reverseVolumeBreakout,  highVolumeBreakout)=>
    barState = 0
    if(close > close[1] and ( (dir == -1 and trendBreakout and trendVolumeBreakout) or (dir == 1 and reverseBreakout and reverseVolumeBreakout) or highVolumeBreakout or highBreakout))
        barState := 1
    if(close < close[1] and ( (dir == 1 and trendBreakout and trendVolumeBreakout) or (dir == -1 and reverseBreakout and reverseVolumeBreakout) or highVolumeBreakout or highBreakout))
        barState := -1
    barState

//////////////////////////////////// Calculate target values //////////////////////////////////////////////////
f_getTargets(barState, trendValueMarkup, reverseValueMarkup, stopMultFactor, dir, atr)=>
    diff = max(high-low, max(abs(high-close[1]), abs(low-close[1])))
    upTarget = (barState == 1) ? high + ((dir==-1?trendValueMarkup:reverseValueMarkup)/100)*diff : na
    downTarget = (barState == -1) ? low - ((dir==1?trendValueMarkup:reverseValueMarkup)/100)*diff : na
    stopUpTarget = (barState == 1) ? low - atr * stopMultFactor : na
    stopDownTarget = (barState == -1) ? high + atr * stopMultFactor : na
    lastBreakout = barState
    lastBreakout:= lastBreakout == 0? nz(lastBreakout[1], 0): lastBreakout
    [upTarget,stopUpTarget,downTarget,stopDownTarget,lastBreakout]

//////////////////////////////////// Calculate target values //////////////////////////////////////////////////
f_getChannels(upTarget, stopUpTarget, downTarget, stopDownTarget)=>
    upLine = upTarget
    downLine = downTarget
    stopUpLine = stopUpTarget
    stopDownLine = stopDownTarget
    upLine := na(upLine) ? nz(upLine[1],na) : upLine
    downLine := na(downLine) ? nz(downLine[1],na) : downLine
    stopUpLine := na(stopUpLine) ? nz(stopUpLine[1],na) : stopUpLine
    stopDownLine := na(stopDownLine) ? nz(stopDownLine[1],na) : stopDownLine
    [upLine,stopUpLine,downLine,stopDownLine]

//////////////////////////////////// Calculate Yearly High Low //////////////////////////////////////////////////
f_getYearlyHighLowCondition(considerYearlyHighLow)=>
    yhigh = security(syminfo.tickerid, '12M', high[1]) 
    ylow = security(syminfo.tickerid, '12M', low[1]) 
    yhighlast = yhigh[365]
    ylowlast = ylow[365]
    yhighllast = yhigh[2 * 365]
    ylowllast = ylow[2 * 365]
    
    yearlyTrendUp = na(yhigh)? true : na(yhighlast)? close > yhigh : na(yhighllast)? close > max(yhigh,yhighlast) : close > max(yhigh, min(yhighlast, yhighllast))
    yearlyHighCondition = (  (na(yhigh) or na(yhighlast) ? true : (yhigh > yhighlast) ) and ( na(yhigh) or na(yhighllast) ? true : (yhigh > yhighllast))) or yearlyTrendUp or not considerYearlyHighLow
    yearlyTrendDown = na(ylow)? true : na(ylowlast)? close < ylow : na(ylowllast)? close < min(ylow,ylowlast) : close < min(ylow, max(ylowlast, ylowllast))
    yearlyLowCondition = (  (na(ylow) or na(ylowlast) ? true : (ylow < ylowlast) ) and ( na(ylow) or na(ylowllast) ? true : (ylow < ylowllast))) or yearlyTrendDown or not considerYearlyHighLow
    
    [yearlyHighCondition,yearlyLowCondition]

//////////////////////////////////// Calculate Trade Flags /////////////////////////////////////////////////////
f_calculateTradeFlags(dir, barState, buySignal, stopBuySignal, sellSignal, stopSellSignal, yearlyHighCondition, yearlyLowCondition, inDateRange)=>
    buyPrerequisites = inDateRange and yearlyHighCondition
    sellPrerequisites = inDateRange and yearlyLowCondition  
    buyEntry = buySignal and yearlyHighCondition and barState != -1 and inDateRange
    sellEntry = sellSignal and yearlyLowCondition and barState != 1 and inDateRange
    stopBuyEntry = stopBuySignal and barState != 1 and inDateRange
    stopSellEntry = stopSellSignal and barState != -1 and inDateRange
    [buyEntry, sellEntry, stopBuyEntry, stopSellEntry]


//////////////////////////////////// Get Moving average ///////////////////////////////////
f_getMovingAverage(source, MAType, length)=>
    ma = sma(source, length)
    if(MAType == "ema")
        ma := ema(source,length)
    if(MAType == "hma")
        ma := hma(source,length)
    if(MAType == "rma")
        ma := rma(source,length)
    if(MAType == "vwma")
        ma := vwma(source,length)
    if(MAType == "wma")
        ma := wma(source,length)
    ma

f_supertrendOnMa(source, MaType, MaLength, supertrendLength, supertrendMult)=>
    supertrendDistance = supertrendMult * atr(supertrendLength)
    
    ma = f_getMovingAverage(source,MaType, MaLength)
    longStop = ma - supertrendDistance
    longStopPrev = nz(longStop[1], longStop)
    longStop := ma > longStopPrev ? max(longStop, longStopPrev) : longStop
    
    shortStop = ma + supertrendDistance
    shortStopPrev = nz(shortStop[1], shortStop)
    shortStop := ma < shortStopPrev ? min(shortStop, shortStopPrev) : shortStop
    
    dir = 1
    dir := nz(dir[1], dir)
    dir := dir == -1 and ma > shortStopPrev ? 1 : dir == 1 and ma < longStopPrev ? -1 : dir
    supertrend =  dir==1 ? longStop: shortStop
    [supertrend, ma, dir]

f_getMaAlignment(MAType, includePartiallyAligned)=>
    ma5 = f_getMovingAverage(close,MAType,5)
    ma10 = f_getMovingAverage(close,MAType,10)
    ma20 = f_getMovingAverage(close,MAType,20)
    ma30 = f_getMovingAverage(close,MAType,30)
    ma50 = f_getMovingAverage(close,MAType,50)
    ma100 = f_getMovingAverage(close,MAType,100)
    ma200 = f_getMovingAverage(close,MAType,200)

    upwardScore = 0
    upwardScore := close > ma5? upwardScore+1:upwardScore
    upwardScore := ma5 > ma10? upwardScore+1:upwardScore
    upwardScore := ma10 > ma20? upwardScore+1:upwardScore
    upwardScore := ma20 > ma30? upwardScore+1:upwardScore
    upwardScore := ma30 > ma50? upwardScore+1:upwardScore
    upwardScore := ma50 > ma100? upwardScore+1:upwardScore
    upwardScore := ma100 > ma200? upwardScore+1:upwardScore
    
    upwards = close > ma5 and ma5 > ma10 and ma10 > ma20 and ma20 > ma30 and ma30 > ma50 and ma50 > ma100 and ma100 > ma200
    downwards = close < ma5 and ma5 < ma10 and ma10 < ma20 and ma20 < ma30 and ma30 < ma50 and ma50 < ma100 and ma100 < ma200
    upwards?1:downwards?-1:includePartiallyAligned ? (upwardScore > 5? 0.5: upwardScore < 2?-0.5:upwardScore>3?0.25:-0.25) : 0

//////////////////////////////////// Calculate Buy Sell signals //////////////////////////////////////////////////
f_calculateBuySellFlags(dir,lastBreakout,ignoreReverseBreakout, barState, upLine, stopUpLine, downLine, stopDownLine)=>
    greenBreakout = (lastBreakout > 0) or ignoreReverseBreakout
    redBreakout = (lastBreakout < 0) or ignoreReverseBreakout
    buySignal = ((crossover(dir==-1?high:low, upLine[1]) and greenBreakout and close > downLine[1]) or (crossover(dir==-1?high:low, stopDownLine[1]) and redBreakout) or ((barState == 1) and (dir==-1?high:low) > upLine[1]))
    stopBuySignal = (crossunder(dir==-1?close:low, downLine[1]) or crossunder(dir==-1?close:low, stopUpLine[1]))
    sellSignal = ((crossunder(dir==1?low:high, downLine[1])  and redBreakout and close < upLine[1]) or (crossunder(dir==1?low:high, stopUpLine[1]) and greenBreakout) or (barState == -1) and (dir==1?low:high) < downLine[1])
    stopSellSignal = (crossover(dir==1?close:high, upLine[1]) or crossover(dir==1?close:high, stopDownLine[1]))
    [buySignal,stopBuySignal,sellSignal,stopSellSignal]

//////////////////////////////////// Calculate new high low condition //////////////////////////////////////////////////
f_calculateNewHighLows(shortHighLowPeriod, longHighLowPeriod, considerNewLongTermHighLows)=>
    newHigh = highest(shortHighLowPeriod) == highest(longHighLowPeriod) or not considerNewLongTermHighLows
    newLow = lowest(shortHighLowPeriod) == lowest(longHighLowPeriod) or not considerNewLongTermHighLows
    [newHigh,newLow]
    
//////////////////////////////////// Calculate trailing stop //////////////////////////////////////////////////
f_calculateTrailingStop(stop)=>
    strategy.position_size > 0? (stop > stop[1]? stop : stop[1]) : strategy.position_size < 0 ? (stop < stop[1]? stop : stop[1]) : stop

//////////////////////////////////// Calculate stop and compound //////////////////////////////////////////////////
f_calculateStopAndCompound(target, atr, stopMultiplyer, compoundMultiplyer, barState)=>
    buyStop = target - (stopMultiplyer * atr)
    sellStop = target + (stopMultiplyer * atr)
    if(barState > 0 or strategy.position_size > 0)
        buyStop := target - (stopMultiplyer * atr)
        sellStop := target + (compoundMultiplyer * atr)
        buyStop := (buyStop < buyStop[1] or close < sellStop[1])? buyStop[1] : buyStop
        sellStop := (sellStop < sellStop[1] or close < sellStop[1]) ? sellStop[1] : sellStop
    else if(barState < 0 or strategy.position_size < 0)
        buyStop := target - (compoundMultiplyer * atr)
        sellStop := target + (stopMultiplyer * atr)
        sellStop := (sellStop > sellStop[1] or close > buyStop[1])? sellStop[1] : sellStop
        buyStop := (buyStop > buyStop[1] or close > buyStop[1]) ? buyStop[1] : buyStop
    [buyStop, sellStop]
    
//////////////////////////////////// Draw Lines with labels //////////////////////////////////////////////////
f_drawLinesWithLabels(target, lbl, linecolor)=>
    line_x1 = time+(60*60*24*1000*1)
    line_x2 = time+(60*60*24*1000*7)
    label_x = time+(60*60*24*1000*4)
    targetLine = line.new(x1=line_x1, y1=target, x2=line_x2, y2=target, color=linecolor, xloc=xloc.bar_time)
    targetLabel = label.new(x=label_x, y=target, text=lbl + " : "+tostring(target), xloc=xloc.bar_time, style=label.style_none, textcolor=color.black, size=size.normal)
    [targetLine,targetLabel]

//////////////////////////////////// Delete Lines with labels //////////////////////////////////////////////////
f_deleteLinesWithLabels(targetLine, targetLabel, last)=>
    line.delete(id=last?targetLine[1]:targetLine)
    label.delete(id=last?targetLabel[1]:targetLabel)
/////////////////////////////////////////////////// Calculate Trend //////////////////////////////////////////////////////
inDateRange = time >= timestamp(syminfo.timezone, year(timenow) - backtestYears, 01, 01, 0, 0)

[showTargets,showChannel,showTargetStops,showUpChannel,showDownChannel] = f_getDisplayModeVeriables(displayMode)
vma = f_getMovingAverage(volume, volumeMaType, volumelength)

[superTrend, dir] = supertrend(supertrendMult, supertrendLength)

atr = atr(atrlength)
[trendBreakout, reverseBreakout, highBreakout] = f_getPriceBreakout(atr, trendTargetMult, reverseTargetMult, considerOpenCloseBreakout, highBreakoutMult)

[trendVolumeBreakout, reverseVolumeBreakout, highVolumeBreakout] = f_getVolumeBreakout(vma, trendVolumeMarkup, reverseVolumeMarkup, highVolumeMultiplyer)

barState = f_getBarState(dir,trendBreakout, reverseBreakout, highBreakout, trendVolumeBreakout, reverseVolumeBreakout, highVolumeBreakout)

[upTarget,stopUpTarget,downTarget,stopDownTarget, lastBreakout] = f_getTargets(barState, trendValueMarkup, reverseValueMarkup, stopMultFactor, dir, atr)

[upLine,stopUpLine,downLine,stopDownLine] = f_getChannels(upTarget, stopUpTarget, downTarget, stopDownTarget)

[yearlyHighCondition,yearlyLowCondition] = f_getYearlyHighLowCondition(considerYearlyHighLow)

[supertrendMa, ma, dirMa] =  f_supertrendOnMa(source, MaTypeMa, MaLengthMa, supertrendLengthMa, supertrendMultMa)

//////////////////////////////////////// Calculate Trade //////////////////////////////////////////////////////
percentRank = percentrank(source, LookbackPeriod)

[buySignal,stopBuySignal,sellSignal,stopSellSignal] = f_calculateBuySellFlags(dir,lastBreakout,ignoreReverseBreakout, barState, upLine, stopUpLine, downLine, stopDownLine)
[newHigh, newLow] = f_calculateNewHighLows(shortHighLowPeriod, longHighLowPeriod, considerNewLongTermHighLows)
[buyEntry, sellEntry, stopBuyEntry, stopSellEntry] = f_calculateTradeFlags(dir, barState, buySignal, stopBuySignal, sellSignal, stopSellSignal, yearlyHighCondition, yearlyLowCondition, inDateRange)

[buyStop, sellStop] = f_calculateStopAndCompound(close, atr, stopMultiplyer, compoundMultiplyer, barState)

crossedCompound = (strategy.position_size > 0 ? close > sellStop : strategy.position_size < 0 ? close < buyStop : true) or allowImmediateCompound

maAlignment = f_getMaAlignment(MAType,includePartiallyAligned)

alignedMaIndex = sum(maAlignment,LookbackPeriod)

maHigh = alignedMaIndex > alignmentIndexThreshold or not considerMaAlignment
maLow = alignedMaIndex < -alignmentIndexThreshold or not considerMaAlignment
//buyEntry := buyEntry and ((newHigh and maHigh and percentRank > 50) or strategy.netprofit >= 0)
//sellEntry := sellEntry and ((newLow and maLow and percentRank < 50) or strategy.netprofit >= 0)


buyEntry := buyEntry and ((newHigh and maHigh and (dirMa == 1 or not considerMaSupertrend) and percentRank > 70) or strategy.netprofit >= 0)
sellEntry := sellEntry and ((newLow and maLow and (dirMa == -1 or not considerMaSupertrend) and percentRank < 30) or strategy.netprofit >= 0)
//////////////////////////////////////// Plot //////////////////////////////////////////////////////
plot(showChannel?upLine:na, title="UpChannel", color=color.green, linewidth=2, style=plot.style_line)
plot(showChannel?downLine:na, title="downChannel", color=color.red, linewidth=2, style=plot.style_line)

plotshape(showTargets or showTargetStops?upTarget:na, title="UpTarget", color=color.green, style=shape.triangleup, location = location.absolute)
plotshape(showTargets or showTargetStops?downTarget:na, title="DownTarget", color=color.red, style=shape.triangledown, location = location.absolute)

plotshape(showTargetStops?stopUpTarget:na, title="UpStop", color=color.green, style=shape.triangleup, location = location.absolute)
plotshape(showTargetStops?stopDownTarget:na, title="DownStop", color=color.red, style=shape.triangledown, location = location.absolute)

plot(showUpChannel?upLine:na, title="UpChannel", color=color.green, linewidth=2, style=plot.style_line)
plot(showUpChannel?stopUpLine:na, title="StopUpChannel", color=color.red, linewidth=2, style=plot.style_line)

plot(showDownChannel?stopDownLine:na, title="StopDownChannel", color=color.green, linewidth=2, style=plot.style_line)
plot(showDownChannel?downLine:na, title="DownChannel", color=color.red, linewidth=2, style=plot.style_line)

channelview = showChannel or showUpChannel or showDownChannel
plot(showTrailingStop and  strategy.position_size != 0 and not channelview? buyStop : na, title="DownStop", color=(strategy.position_size > 0?color.green:strategy.position_size < 0 ?color.red:color.blue), linewidth=2, style=plot.style_stepline)
plot(showTrailingStop and  strategy.position_size != 0 and not channelview? sellStop : na, title="UpStop", color=(strategy.position_size > 0?color.green:strategy.position_size < 0 ?color.red:color.blue), linewidth=2, style=plot.style_stepline)

//plot(percentValue, title="percentRank", color=color.blue, linewidth=2, style=plot.style_stepline)

barcolor(barState == 1? color.lime : barState == -1? color.orange : color.silver)
//////////////////////////////////////// Plot Trade //////////////////////////////////////////////////////
if(buyEntry or sellEntry) and (showTargetLevels)
    [targetLine, targetLabel] = f_drawLinesWithLabels(close,buyEntry?"Long":"Short",color.blue)
    if(not showPreviousLevels)
        f_deleteLinesWithLabels(targetLine, targetLabel, true)

if(buyEntry or sellEntry or strategy.position_size != 0)
    [stopLine, stopLabel] = f_drawLinesWithLabels(buyEntry?buyStop:sellStop,"Stop",color.red)
    [compoundLine, compoundLabel] = f_drawLinesWithLabels(buyEntry?sellStop:buyStop,"Compound",color.green)
    f_deleteLinesWithLabels(stopLine, stopLabel, true)
    f_deleteLinesWithLabels(compoundLine, compoundLabel, true)
    
//////////////////////////////////////// Trade //////////////////////////////////////////////////////
strategy.risk.allow_entry_in(tradeDirection)
strategy.entry("Buy", strategy.long, when=buyEntry and crossedCompound)
strategy.close("Buy", when=stopBuyEntry and exitOnSignal)
strategy.exit("ExitBuy", "Buy", stop = buyStop)

strategy.entry("Sell", strategy.short, when=sellEntry and crossedCompound)
strategy.close("Sell", when=stopSellEntry and exitOnSignal)
strategy.exit("ExitSell", "Sell", stop = sellStop)

