Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
- // © danielx888
- //@version=5
- strategy('MA mean reversion strategy', overlay=true, initial_capital = 5000, default_qty_value = 10000, slippage=20)
- // USER INPUTS
- i_src = input.source(close, 'MA source', group="MA Settings")
- i_maLen = input.int(200,'MA length',0, group="MA Settings")
- i_maType = input.string('SMA', 'MA type', options=['SMA', 'HMA', 'EMA', 'SMMA (RMA)', 'WMA', 'VWMA'], group="MA Settings")
- i_numStdev = input.float(2,'Number of standard deviations away from MA for trigger', 0.1, step=0.1,group='StDev Settings')
- i_lookbackMult = input.int(10,'Multiple of MA to lookback for StDev length',1, step=1,group='StDev Settings')
- i_excludeInitial = input.bool(true, 'Exclude initial bars until bands are fully developed', 'Recommended to leave this feature enabled. If disabled, strategy may "repaint" ' +
- 'the first trades as tradingview chops off the bars at the start of the chart. If disabled, this becomes an even larger issue with a higher "Multiple of MA" entered.')
- s_closeAll = input.bool(true, 'Close out position on reversal', group='Trade Management')
- i_minimumMove = input.string('StDev','Select criteria to filter out trades if price hasn\'t moved from last entry',['StDev','Flat','None'], group='Trade Management')
- i_moveStDev = input.float(0.5,'Number of St Deviations for price to move (select -StDev- above)',0,step=0.01, group='Trade Management')
- i_moveFlat = input.float(0.1, 'Flat % for price to move (select -Flat- above)',0,step=0.01, group='Trade Management')/100
- i_lag = input.int(1, 'Slope lag', 1,20,1, group='Slope Settings')
- i_slopeMult = input.int(1, 'Slope multiplier', 1,10,1, group='Slope Settings')
- i_slopeStDev = input.float(2, 'Number of standard deviations of slope change', 0.1, step=0.1,group='Slope Settings')
- i_shading = input.bool(false, 'Shade areas between bands where slope is at extremes', group='Slope Settings')
- // CALCULATE MOVING AVERAGE
- ma(source, length, type) =>
- switch type
- "SMA" => ta.sma(source, length)
- "HMA" => ta.hma(source, length)
- "EMA" => ta.ema(source, length)
- "SMMA (RMA)" => ta.rma(source, length)
- "WMA" => ta.wma(source, length)
- "VWMA" => ta.vwma(source, length)
- ma = ma(i_src, i_maLen, i_maType)
- lookback = i_maLen * i_lookbackMult
- color c = na
- // CALCULATE STANDARD DEVIATION FOR DISTANCE OF PRICE TO MOVING AVERAGE
- //****
- // need to fix arrays starting out as all 0s then slowly filling. during the first few hundred bars of strategy opening
- // the bands are too tight as the arrays are not fully filled yet. Possible fix to initiliaze array size as 0 and
- // use an if statement array length > lookback then array.shift. Implement the same new logic below at the section to calculate
- // standard deviation of slope
- //****
- var a_aboveMA = array.new_float()
- var a_belowMA = array.new_float()
- if i_src > ma
- array.push(a_aboveMA, i_src - ma)
- if array.size(a_aboveMA) > lookback
- array.shift(a_aboveMA)
- if i_src < ma
- array.push(a_belowMA, ma - i_src)
- if array.size(a_belowMA) > lookback
- array.shift(a_belowMA)
- stdevAbove = array.stdev(a_aboveMA) * i_numStdev
- stdevBelow = array.stdev(a_belowMA) * i_numStdev
- overbought = i_src > ma and i_src - ma > stdevAbove
- oversold = i_src < ma and ma - i_src > stdevBelow
- upCandle = close >= open
- downCandle = close <= open
- doji = open == close
- // VISUAL SETTINGS FOR DISTANCE TO MOVING AVERAGE
- var ob = 0
- var os = 0
- if overbought
- ob := 1
- c := color.red
- if oversold
- os := 1
- c := color.green
- dn = close > ma and overbought[1] and downCandle and upCandle[1]
- up = close < ma and oversold[1] and upCandle and downCandle[1]
- if dn
- ob := 0
- if up
- os := 0
- // DEFINE CANDLE REVERSAL PRICE ACTION
- var count = 0
- var float diff = 0
- dnMark = (dn and not dn[1] and not dn[2]) or (ob == 1 and upCandle[1] and downCandle and (ta.barssince(dn) <= ta.barssince(upCandle)))
- upMark = (up and not up[1] and not up[2]) or (os == 1 and downCandle[1] and upCandle and (ta.barssince(up) <= ta.barssince(downCandle)))
- if dnMark
- ob := 0
- count +=1
- diff += (i_src-ma)
- if upMark
- os := 0
- count +=1
- diff -= (ma-i_src)
- // CALCULATE STANDARD DEVIATION FOR SLOPE OF MOVING AVERAGE
- slopeLookback = i_maLen * i_slopeMult
- laggingMA = ma[i_lag]
- var a_slopeUp = array.new_float()
- var a_slopeDn = array.new_float()
- upSlope = ma > laggingMA
- upDiff = ma - laggingMA
- if upSlope
- array.push(a_slopeUp, upDiff)
- if array.size(a_slopeUp) > slopeLookback
- array.shift(a_slopeUp)
- dnSlope = ma < laggingMA
- dnDiff = laggingMA - ma
- if dnSlope
- array.push(a_slopeDn, dnDiff)
- if array.size(a_slopeDn) > slopeLookback
- array.shift(a_slopeDn)
- stdevUpSlope = array.stdev(a_slopeUp) * i_slopeStDev
- stdevDnSlope = array.stdev(a_slopeDn) * i_slopeStDev
- // DEFINE COLOR CHANGE FOR SLOPE
- colNeutral = color.gray
- colFastUp = color.green
- colFastDn = color.red
- colUpShading = color.rgb(76, 175, 79, 80)
- colDnShading = color.rgb(255, 82, 82, 80)
- color col = na
- if upSlope and upDiff > stdevUpSlope
- col := colFastUp
- else if dnSlope and dnDiff > stdevDnSlope
- col := colFastDn
- else
- col := colNeutral
- color fillCol = na
- if i_shading
- fillCol := col == colFastUp ? colUpShading : col == colFastDn ? colDnShading : na
- // DEFINE LIMITS FOR MINIMUM MOVES TO CONSIDER OPENING NEW POSITION
- priceMovedShorts =
- i_minimumMove == 'None' ? true :
- i_minimumMove == 'StDev' ? close > close[ta.barssince(strategy.position_size != strategy.position_size[1])+1] + i_moveStDev*stdevAbove :
- i_minimumMove == 'Flat' ? close > close[ta.barssince(strategy.position_size != strategy.position_size[1])+1] * (1+i_moveFlat) : na
- priceMovedLongs =
- i_minimumMove == 'None' ? true :
- i_minimumMove == 'StDev' ? close < close[ta.barssince(strategy.position_size != strategy.position_size[1])+1] - i_moveStDev*stdevBelow :
- i_minimumMove == 'Flat' ? close < close[ta.barssince(strategy.position_size != strategy.position_size[1])+1] * (1-i_moveFlat) : na
- excludeDn = i_excludeInitial ? array.size(a_aboveMA) == lookback : true
- excludeUp = i_excludeInitial ? array.size(a_belowMA) == lookback : true
- // OPEN AND CLOSE POSITIONS
- if dnMark and excludeDn
- if ta.barssince(dnMark[1]) > ta.barssince(upMark[1]) or priceMovedShorts or strategy.closedtrades + strategy.opentrades == 0
- if s_closeAll and strategy.position_size > 0
- strategy.close_all()
- strategy.order('Short',strategy.short)
- if upMark and excludeUp
- if ta.barssince(upMark[1]) > ta.barssince(dnMark[1]) or priceMovedLongs or strategy.closedtrades + strategy.opentrades == 0
- if s_closeAll and strategy.position_size < 0
- strategy.close_all()
- strategy.order('Long',strategy.long)
- // DEBUG
- // label.new(bar_index,high, "array length: " + str.tostring(array.size(a_aboveMA)), color=color.white, style=label.style_label_down, textcolor=color.black)
- // PLOT VISUALS
- plotchar(dnMark, 'dn char', '𝅏', location.abovebar)
- plotchar(upMark, 'up char', '▴', location.belowbar)
- barcolor(c)
- p1 = plot(ma, color=col)
- p2 = plot(ma+stdevAbove, color=col)
- p3 = plot(ma-stdevBelow, color=col)
- fill(p2, p3, color=fillCol)
Advertisement
Comments
-
- Just realized this repaints the first trades as time moves on and TradingView chops off the first bars. Edited the strategy to wait until bands are fully developed for more accurate backtesting results
Add Comment
Please, Sign In to add comment