top of page

QG-Dynamic Channel Breakout Strategy

KAUSTUBH KESKAR

This strategy is based on G channel indicator.


The trades are entered on a combination of a long and short periods(100 and 300) of the G channel.


The SL and TP based on a multiple of ATR period.


Alternatively a trade can be closed after X no of bars in market.


This is tested on 1 hour chart of MES futures contract.


// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © QuantG

//@version=5
strategy("QG-Dynamic Channel Breakout Strategy", overlay=true,default_qty_type=strategy.percent_of_equity , default_qty_value=50, margin_long=100, margin_short=100)

//Input
lengthLong = input(300)
lengthShort = input(100)
src = input.source(close)
CloseTradeAfterXBars = input.int(defval=20, title='Close Trades after X no of bars')

//----------------------------------------------------------------------|
// channel calculation                                                  |
//----------------------------------------------------------------------|

a = 0.,b = 0.
a := math.max(src,nz(a[1])) - nz(a[1] - b[1])/lengthLong
b := math.min(src,nz(b[1])) + nz(a[1] - b[1])/lengthLong
avg = math.avg(a,b)
//----
crossup = b[1] < src[1] and b > src
crossdn = a[1] < src[1] and a > src
bullish = ta.barssince(crossdn) <= ta.barssince(crossup)
c = bullish ? color.new(color.lime,50) : color.new(color.red,50)
p1=plot(avg,"Average",color=c,linewidth=2)
p2=plot(close,"Close price",color=c,linewidth=1)
//fill(p1,p2,color=c)

showcross = input(true)
plotshape(showcross and not bullish and bullish[1] ? avg : na, location=location.absolute, style=shape.labeldown, color=color.red, size=size.tiny, text="Sell", textcolor=#ffffff, offset=-1)
plotshape(showcross and bullish and not bullish[1] ? avg : na, location=location.absolute, style=shape.labelup, color=color.lime, size=size.tiny, text="Buy", textcolor=#ffffff, offset=-1)

// short length channel
x = 0.,y = 0.
x := math.max(src,nz(x[1])) - nz(x[1] - y[1])/lengthShort
y := math.min(src,nz(y[1])) + nz(x[1] - y[1])/lengthShort
avgS = math.avg(x,y)
p3=plot(avgS,"Average1",color=color.new(color.blue, 50),linewidth=2)
crossUP = y[1] < close[1] and y > close
crossDN = x[1] < close[1] and x > close
bullishS = ta.barssince(crossDN) <= ta.barssince(crossUP)


//Custom functions

//Exit after X no of bars
BarsSinceFirstEntry() =>
    bar_index - strategy.opentrades.entry_bar_index(0)
    
    
//Time delay between trades    
i_qtyTimeUnits  = input.int(defval=1, title="Delay between orders:", inline = "1", minval = 0, tooltip = "Use 0 for no delay")
i_timeUnits     = input.string(defval="days", title="Enter time units", inline = "1", options = ["seconds", "minutes", "hours", "days", "months", "years"])

// ————— Converts current chart timeframe into a float minutes value.
f_tfInMinutes() => 
    _tfInMinutes = timeframe.multiplier * (
      timeframe.isseconds ? 1. / 60             :
      timeframe.isminutes ? 1.                  :
      timeframe.isdaily   ? 60. * 24            :
      timeframe.isweekly  ? 60. * 24 * 7        :
      timeframe.ismonthly ? 60. * 24 * 30.4375  : na)


f_timeFrom(_from, _qty, _units) =>
    int _timeFrom = na
    _unit = str.replace_all(_units, "s", "")
    _t = _from == "bar" ? time : _from == "close" ? time_close : timenow
    if _units == "chart"
        _timeFrom := int(_t + (f_tfInMinutes() * 60 * 1000 * _qty))
    else
        _year   = year(_t)       + (_unit == "year"   ? int(_qty) : 0)
        _month  = month(_t)      + (_unit == "month"  ? int(_qty) : 0)
        _day    = dayofmonth(_t) + (_unit == "day"    ? int(_qty) : 0)
        _hour   = hour(_t)       + (_unit == "hour"   ? int(_qty) : 0)
        _minute = minute(_t)     + (_unit == "minute" ? int(_qty) : 0)
        _second = second(_t)     + (_unit == "econd"  ? int(_qty) : 0)
        _timeFrom := timestamp(_year, _month, _day, _hour, _minute, _second)
    
var float lastTradeTime = na
if nz(ta.change(strategy.position_size), time) != 0
    lastTradeTime := time

delayElapsed = f_timeFrom("bar", i_qtyTimeUnits, i_timeUnits) >= lastTradeTime



//Calculate ATR based SL and TP

ATRlength = input.int(title="ATR Length", defval=14, minval=1)
smoothing = input.string(title="ATR Smoothing", defval="EMA", options=["SMA", "EMA", "WMA"])
m = input.float(2, "ATR Multiplier")
rrr = input.float(defval= 1.5,title='Risk Reward Ratio',minval=1)
src1 = input.source(high)
src2 = input.source(low)

ma_function(source, length) =>
	if smoothing == "EMA"
		ta.ema(source, length)
	else
		if smoothing == "SMA"
			ta.sma(source, length)
		else
			if smoothing == "WMA"
				ta.wma(source, length)
			
				
//a = ma_function(ta.atr(true), length) * m
x1 = ma_function(ta.tr(true), ATRlength) * m + src1
x2 = src2 - ma_function(ta.tr(true), ATRlength) * m

//Buy and Sell algos

BuySignal = barstate.isconfirmed and bullish and avgS>avgS[1] and strategy.position_size==0 
SellSignal = barstate.isconfirmed and not bullish and avgS<avgS[1] and strategy.position_size==0 

//Exit signals
ExL = not bullishS and bullishS[1]
ExS = bullishS and not bullishS[1]

// Enable Long Strategy
enable_long_strategy = input.bool(true, title='Enable Long Strategy', group='SL/TP For Long Strategy', inline='1')
long_stoploss_value = ta.valuewhen(BuySignal, x2, 0)
long_takeprofit_value = ta.valuewhen(BuySignal,src1,0) + (rrr*(ta.valuewhen(BuySignal, close, 0)-long_stoploss_value))

// Enable Short Strategy
enable_short_strategy = input.bool(true, title='Enable Short Strategy', group='SL/TP For Short Strategy', inline='3')
short_stoploss_value = ta.valuewhen(SellSignal, x1, 0)
short_takeprofit_value = ta.valuewhen(SellSignal,src2,0) -(rrr*(short_stoploss_value-ta.valuewhen(SellSignal, close, 0)))

// Plot Stoploss & Take Profit Levels

LSL=plot(strategy.position_size >0?long_stoploss_value:na, color=color.new(#ff0000, 0), style=plot.style_linebr, linewidth=2, title='Long SL Level')
LTP=plot(strategy.position_size >0?long_takeprofit_value:na, color=color.new(#008000, 0), style=plot.style_linebr, linewidth=2, title='Long TP Level')
SSL=plot(strategy.position_size <0?short_stoploss_value:na, color=color.new(#ff0000, 0), style=plot.style_linebr, linewidth=2, title='Short SL Level')
STP=plot(strategy.position_size <0?short_takeprofit_value:na, color=color.new(#008000, 0), style=plot.style_linebr, linewidth=2, title='Short TP Level')
EP = plot(strategy.opentrades>0 ?strategy.position_avg_price:na,color=color.new(color.white, 50), style=plot.style_linebr, linewidth=2, title='Entry Price level')
fill(EP,LSL,color=color.new(color.red,80), title='loss area')
fill(EP,LTP,color=color.new(color.green,80), title='profit area')
fill(EP,SSL,color=color.new(color.red,80), title='loss area')
fill(EP,STP,color=color.new(color.green,80), title='profit area')


// Date Range
start_date = input.int(title='Start Date', defval=1, minval=1, maxval=31, group='Date Range')
start_month = input.int(title='Start Month', defval=1, minval=1, maxval=12, group='Date Range')
start_year = input.int(title='Start Year', defval=2020, minval=1800, maxval=3000, group='Date Range')
end_date = input.int(title='End Date', defval=1, minval=1, maxval=3, group='Date Range')
end_month = input.int(title='End Month', defval=1, minval=1, maxval=12, group='Date Range')
end_year = input.int(title='End Year', defval=2077, minval=2022, maxval=3000, group='Date Range')
in_date_range = time >= timestamp(syminfo.timezone, start_year, start_month, start_date, 0, 0) and time < timestamp(syminfo.timezone, end_year, end_month, end_date, 0, 0)
  
// Long Strategy
if BuySignal and in_date_range and enable_long_strategy == true and strategy.position_size==0
    strategy.entry('Long', strategy.long, alert_message='Open Long Position')
    strategy.exit('Long  SL/TP', from_entry='Long',qty_percent=100,stop=long_stoploss_value, limit=long_takeprofit_value,alert_message='Your Long SL/TP Limit As Been Triggered.')
   
// Short Strategy
if SellSignal and in_date_range and enable_short_strategy == true and strategy.position_size==0
    strategy.entry('Short', strategy.short, alert_message='Open Short Position')
    strategy.exit('Short SL/TP', from_entry='Short',qty_percent=100, stop=short_stoploss_value, limit=short_takeprofit_value,alert_message='Your Short SL/TP Limit As Been Triggered.')

strategy.close('Long', when=ExL, alert_message='Close Long Position') 
strategy.close('Short', when=ExS, alert_message='Close Short Position')    

if(BarsSinceFirstEntry()>CloseTradeAfterXBars and strategy.position_size!=0 )  
    strategy.close_all(comment = "close Max Bars")
    
///////////////////////////// --- BEGIN TESTER CODE --- ////////////////////////
// COPY below into your strategy to enable display
////////////////////////////////////////////////////////////////////////////////


// Declare performance tracking variables
drawTester = input.bool(true, "Draw Tester")
var balance = strategy.initial_capital
var drawdown = 0.0
var maxDrawdown = 0.0
var maxBalance = 0.0
var totalWins = 0
var totalLoss = 0    
    
// Prepare stats table
var table testTable = table.new(position.top_right, 5, 2, border_width=1)
f_fillCell(_table, _column, _row, _title, _value, _bgcolor, _txtcolor) =>
    _cellText = _title + "\n" + _value
    table.cell(_table, _column, _row, _cellText, bgcolor=_bgcolor, text_color=_txtcolor)
    
// Custom function to truncate (cut) excess decimal places
truncate(_number, _decimalPlaces) =>
    _factor = math.pow(10, _decimalPlaces)
    int(_number * _factor) / _factor
    
// Draw stats table
var bgcolor = color.new(color.black,0)
if drawTester
    if barstate.islastconfirmedhistory
        // Update table
        dollarReturn = strategy.netprofit
        f_fillCell(testTable, 0, 0, "Total Trades:", str.tostring(strategy.closedtrades), bgcolor, color.white)
        f_fillCell(testTable, 0, 1, "Win Rate:", str.tostring(truncate((strategy.wintrades/strategy.closedtrades)*100,2)) + "%", bgcolor, color.white)
        f_fillCell(testTable, 1, 0, "Starting:", "$" + str.tostring(strategy.initial_capital), bgcolor, color.white)
        f_fillCell(testTable, 1, 1, "Ending:", "$" + str.tostring(truncate(strategy.initial_capital + strategy.netprofit,2)), bgcolor, color.white)
        f_fillCell(testTable, 2, 0, "Avg Win:", "$"+ str.tostring(truncate(strategy.grossprofit / strategy.wintrades, 2)), bgcolor, color.white)
        f_fillCell(testTable, 2, 1, "Avg Loss:", "$"+ str.tostring(truncate(strategy.grossloss / strategy.losstrades, 2)), bgcolor, color.white)
        f_fillCell(testTable, 3, 0, "Profit Factor:", str.tostring(truncate(strategy.grossprofit / strategy.grossloss,2)), strategy.grossprofit > strategy.grossloss ? color.green : color.red, color.white)
        f_fillCell(testTable, 3, 1, "Max Runup:",  str.tostring(truncate(strategy.max_runup, 2 )), bgcolor, color.white)
        f_fillCell(testTable, 4, 0, "Return:", (dollarReturn > 0 ? "+" : "") + str.tostring(truncate((dollarReturn / strategy.initial_capital)*100,2)) + "%", dollarReturn > 0 ? color.green : color.red, color.white)
        f_fillCell(testTable, 4, 1, "Max DD:", str.tostring(truncate((strategy.max_drawdown / strategy.equity) * 100 ,2)) + "%", color.red, color.white)
// --- END TESTER CODE --- ///////////////

// Monthly Table Performance Dashboard By @QuantNomad
// 
// Dashboard Table Text Size
i_tableTextSize = input.string(title="Dashboard Size", defval="Normal", options=["Auto",  "Huge",  "Large", "Normal", "Small", "Tiny"], group="Dashboards")
table_text_size(s) =>
    switch s
        "Auto"   => size.auto   
        "Huge"   => size.huge   
        "Large"  => size.large  
        "Normal" => size.normal 
        "Small"  => size.small
        => size.tiny
tableTextSize = table_text_size(i_tableTextSize)

i_showMonthlyPerformance = input.bool(true, 'Monthly Performance', group='Dashboards', inline="Show Dashboards")
i_monthlyReturnPercision = 2

if i_showMonthlyPerformance
    new_month = month(time) != month(time[1])
    new_year  = year(time)  != year(time[1])
    
    eq = strategy.equity
    
    bar_pnl = eq / eq[1] - 1
    
    cur_month_pnl = 0.0
    cur_year_pnl  = 0.0
    
    // Current Monthly P&L
    cur_month_pnl := new_month ? 0.0 : 
                     (1 + cur_month_pnl[1]) * (1 + bar_pnl) - 1 
    
    // Current Yearly P&L
    cur_year_pnl := new_year ? 0.0 : 
                     (1 + cur_year_pnl[1]) * (1 + bar_pnl) - 1  
    
    // Arrays to store Yearly and Monthly P&Ls
    var month_pnl  = array.new_float(0)
    var month_time = array.new_int(0)
    
    var year_pnl  = array.new_float(0)
    var year_time = array.new_int(0)
    
    last_computed = false
    
    if (not na(cur_month_pnl[1]) and (new_month or barstate.islastconfirmedhistory))
        if (last_computed[1])
            array.pop(month_pnl)
            array.pop(month_time)
            
        array.push(month_pnl , cur_month_pnl[1])
        array.push(month_time, time[1])
    
    if (not na(cur_year_pnl[1]) and (new_year or barstate.islastconfirmedhistory))
        if (last_computed[1])
            array.pop(year_pnl)
            array.pop(year_time)
            
        array.push(year_pnl , cur_year_pnl[1])
        array.push(year_time, time[1])
    
    last_computed := barstate.islastconfirmedhistory ? true : nz(last_computed[1])
    
    // Monthly P&L Table    
    var monthly_table = table(na)
    
    if (barstate.islastconfirmedhistory)
        monthly_table := table.new(position.bottom_right, columns = 14, rows = array.size(year_pnl) + 1, border_width = 1)
    
        table.cell(monthly_table, 0,  0, "",     bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 1,  0, "Jan",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 2,  0, "Feb",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 3,  0, "Mar",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 4,  0, "Apr",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 5,  0, "May",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 6,  0, "Jun",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 7,  0, "Jul",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 8,  0, "Aug",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 9,  0, "Sep",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 10, 0, "Oct",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 11, 0, "Nov",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 12, 0, "Dec",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 13, 0, "Year", bgcolor = #999999, text_size=tableTextSize)
    
        for yi = 0 to array.size(year_pnl) - 1
            table.cell(monthly_table, 0,  yi + 1, str.tostring(year(array.get(year_time, yi))), bgcolor = #cccccc, text_size=tableTextSize)
            
            y_color = array.get(year_pnl, yi) > 0 ? color.new(color.teal, transp = 40) : color.new(color.gray, transp = 40)
            table.cell(monthly_table, 13, yi + 1, str.tostring(math.round(array.get(year_pnl, yi) * 100, i_monthlyReturnPercision)), bgcolor = y_color, text_color=color.new(color.white, 0),text_size=tableTextSize)
            
        for mi = 0 to array.size(month_time) - 1
            m_row   = year(array.get(month_time, mi))  - year(array.get(year_time, 0)) + 1
            m_col   = month(array.get(month_time, mi)) 
            m_color = array.get(month_pnl, mi) > 0 ? color.new(color.teal, transp = 40) : color.new(color.maroon, transp = 40)
            
            table.cell(monthly_table, m_col, m_row, str.tostring(math.round(array.get(month_pnl, mi) * 100, i_monthlyReturnPercision)), bgcolor = m_color, text_color=color.new(color.white, 0), text_size=tableTextSize)

7 views0 comments

Recent Posts

See All

Comentários


bottom of page