//@version=5
strategy(shorttitle=${SHORT_TITLE:-'BBS'},
title=${TITLE:-${SHORT_TITLE:-'BBS'}},
overlay=true,
pyramiding=${PYRAMIDING:-1},
initial_capital=${INITIAL_CAPITAL:-1000},
commission_value=${COMMISSION_PERCENT:-.07},
default_qty_type=${QTY_TYPE:-strategy.cash},
default_qty_value=${QTY_SIZE:-100})
// hidden input.8 functions, which just return the default value
hidden_input_float(defval, title='', minval=0, maxval=0, step=0, tooltip='', inline='', group='', confirm='') => defval
hidden_input_int(defval, title='', minval=0, maxval=0, step=0, tooltip='', inline='', group='', confirm='') => defval
// Group names
lbgrp = 'Low Band - Buy'
ubgrp = 'Upper Band - Sell'
advgrp = 'Pyramiding'
scgrp = 'Short confirmation'
lcgrp = 'Long confirmation'
uigrp = 'UI configuration'
tslgrp = 'Trailing stop loss'
// General Inputs
longEnabled = input.bool(${LONG_TRADES:-true}, 'Long trades', inline='mode')
shortEnabled = input.bool(${SHORT_TRADES:-false}, 'Short trades', inline='mode')
devMode = input.bool(${DEV_MODE:-true}, 'Development mode', inline='mode')
if not devMode and timeframe.period != ${CHART_TIMEFRAME:-'60'}
runtime.error('expecting chart timeframe: ' + ${CHART_TIMEFRAME:-'60'})
if not devMode and syminfo.tickerid != ${CHART_TICKERID:-'FTX:BTCPERP'}
runtime.error('expecting tickerid: ' + ${CHART_TICKERID:-'FTX:BTCPERP'})
backtestEnd = input.time(timestamp(${BACKTEST_END:-'2022-04-01T00:00:00'}), 'Backtest end')
// BB Low configuration
lbMAType = input.string(${LB_MA_TYPE:-${MA_TYPE:-'EMA'}}, 'Moving average', inline='lbma1', group=lbgrp,
options=['ALMA', 'EMA', 'SMA', 'WMA', 'VWMA', 'RMA', 'HMA', 'VAMA', 'MGD'])
lbMALen = input.int(${LB_MA_LENGTH:-${MA_LENGTH:-9}}, 'MA length', step=1, minval=3, inline='lbma1', group=lbgrp)
lbMASource = input.source(${LB_MA_SOURCE:-${MA_SOURCE:-low}}, 'MA source', group=lbgrp)
lbAlmaOffset = ${LB_ALMA_INPUT_PREFIX:-hidden_input_}float(${LB_ALMA_OFFSET:-${ALMA_OFFSET:-.85}}, 'ALMA offset', step=.01, minval=.01, inline='lbalma', group=lbgrp)
lbAlmaSigma = ${LB_ALMA_INPUT_PREFIX:-hidden_input_}float(${LB_ALMA_SIGMA:-${ALMA_SIGMA:-6}}, 'ALMA sigma', step=.1, minval=.1, inline='lbalma', group=lbgrp)
lbVamaFastLen = ${LB_VAMA_INPUT_PREFIX:-hidden_input_}int(${LB_VAMA_FAST_LENGTH:-${VAMA_FAST_LENGTH:-3}}, 'VAMA fast length', step=1, minval=2, inline='lbvama', group=lbgrp)
lbVamaWeight = ${LB_VAMA_INPUT_PREFIX:-hidden_input_}float(${LB_VAMA_WEIGHT:-${VAMA_WEIGHT:-.2}}, 'VAMA weight', step=.01, minval=.01, inline='lbvama', group=lbgrp)
lbDevType = ${LB_DEV_TYPE:-${DEV_TYPE:-'STD'}}
lbDevLen = input.int(${LB_DEV_LENGTH:-${LB_MA_LENGTH:-${MA_LENGTH:-9}}}, 'Dev. length', step=1, minval=3, inline='lbdev1', group=lbgrp)
lbDevSource = input.source(${LB_DEV_SOURCE:-${LB_MA_SOURCE:-${MA_SOURCE:-low}}}, 'Dev. source', inline='lbdev2', group=lbgrp)
lbDevScale = input.float(${LB_DEV_SCALE:-${DEV_SCALE:-2}}, 'Dev. scale factor', minval=.01, maxval=50, step=.05, inline='lbdev2', group=lbgrp)
lbLongSource = input.source(${LB_LONG_SOURCE:-${SOURCE:-close}}, 'Buy source', inline='long1', group=lbgrp)
lbLongStrategy = input.string(${LB_LONG_STRATEGY:-'Price x-under MA'}, 'Strategy', inline='long1', group=lbgrp,
options=['Price x-over MA', 'Price x-under MA', 'Price < MA'])
// Long position confirmation configuration
longCfrm = input.string(${LONG_CONFIRMATION:-'Disabled'}, title='Oscillator', inline='lc1', group=lcgrp, options=['Disabled', 'RSI'])
longCfrmSource = input.source(${LONG_CONFIRMATION_SOURCE:-ohlc4}, 'Source', inline='lc1', group=lcgrp)
longCfrmLength = input.int(${LONG_CONFIRMATION_LENGTH:-10}, minval=1, title='Length', inline='lc2', group=lcgrp)
longCfrmLimit = input.int(${LONG_CONFIRMATION_LIMIT:-30}, minval=1, title='Limit', inline='lc2', group=lcgrp)
lcMAType = input.string(${LC_MA_TYPE:-'Disabled'}, 'Moving average', inline='lcma', group=lcgrp, options=['Disabled', 'ALMA', 'EMA', 'SMA', 'WMA', 'VWMA', 'RMA', 'HMA', 'VAMA', 'MGD'])
lcMALength = input.int(${LC_MA_LENGTH:-5}, 'MA length', step=1, minval=3, inline='lcma', group=lcgrp)
lcAlmaOffset = ${LC_ALMA_INPUT_PREFIX:-input.}float(${LC_ALMA_OFFSET:-0.85}, 'ALMA offset', step=.01, minval=.01, inline='lcalma', group=lcgrp)
lcAlmaSigma = ${LC_ALMA_INPUT_PREFIX:-input.}float(${LC_ALMA_SIGMA:-6.0}, 'ALMA sigma', step=.1, minval=.1, inline='lcalma', group=lcgrp)
lcVamaFastLength = ${LC_VAMA_INPUT_PREFIX:-hidden_input_}int(${LC_VAMA_FAST_LENGTH:-3}, 'VAMA fast length', step=1, minval=2, inline='lcvama', group=lcgrp)
lcVamaWeight = ${LC_VAMA_INPUT_PREFIX:-hidden_input_}float(${LC_VAMA_WEIGHT:-.2}, 'VAMA weight', step=.01, minval=.01, inline='lcvama', group=lcgrp)
// BB High configuration
ubMAType = input.string(${UB_MA_TYPE:-${MA_TYPE:-'EMA'}}, 'Moving average', inline='ubma1', group=ubgrp,
options=['ALMA', 'EMA', 'SMA', 'WMA', 'VWMA', 'RMA', 'HMA', 'VAMA', 'MGD'])
ubMALen = input.int(${UB_MA_LENGTH:-${MA_LENGTH:-9}}, 'MA length', step=1, minval=3, inline='ubma1', group=ubgrp)
ubMASource = input.source(${UB_MA_SOURCE:-${MA_SOURCE:-high}}, 'MA source', group=ubgrp)
ubAlmaOffset = ${UB_ALMA_INPUT_PREFIX:-hidden_input_}float(${UB_ALMA_OFFSET:-${ALMA_OFFSET:-.85}}, 'ALMA offset', step=.01, minval=.01, inline='ubalma', group=ubgrp)
ubAlmaSigma = ${UB_ALMA_INPUT_PREFIX:-hidden_input_}float(${UB_ALMA_SIGMA:-${ALMA_SIGMA:-6}}, 'ALMA sigma', step=.1, minval=.1, inline='ubalma', group=ubgrp)
ubVamaFastLen = ${UB_VAMA_INPUT_PREFIX:-hidden_input_}int(${UB_VAMA_FAST_LENGTH:-${VAMA_FAST_LENGTH:-3}}, 'VAMA fast length', step=1, minval=2, inline='ubvama', group=ubgrp)
ubVamaWeight = ${UB_VAMA_INPUT_PREFIX:-hidden_input_}float(${UB_VAMA_WEIGHT:-${VAMA_WEIGHT:-.2}}, 'VAMA weight', step=.01, minval=.01, inline='ubvama', group=ubgrp)
ubDevType = ${UB_DEV_TYPE:-${DEV_TYPE:-'STD'}}
ubDevLen = input.int(${UB_DEV_LENGTH:-${UB_MA_LENGTH:-${MA_LENGTH:-9}}}, 'Dev. length', step=1, minval=3, inline='ubdev1', group=ubgrp)
ubDevSource = input.source(${UB_DEV_SOURCE:-${UB_MA_SOURCE:-${MA_SOURCE:-high}}}, 'Dev. source', inline='ubdev2', group=ubgrp)
ubDevScale = input.float(${UB_DEV_SCALE:-${DEV_SCALE:-2}}, 'Dev. scale factor', minval=.01, maxval=50, step=.05, inline='ubdev2', group=ubgrp)
ubShortSource = input.source(${UB_SHORT_SOURCE:-${SOURCE:-close}}, 'Sell source', inline='short1', group=ubgrp)
ubShortStrategy = input.string(${UB_SHORT_STRATEGY:-'Price > MA'}, 'Strategy', inline='short1', group=ubgrp,
options=['Price x-over MA', 'Price x-under MA', 'Price > MA'])
// Short position confirmation configuration
shortCfrm = input.string(${SHORT_CONFIRMATION:-'Disabled'}, title='Oscillator', inline='sc1', group=scgrp, options=['Disabled', 'RSI'])
shortCfrmSource = input.source(${SHORT_CONFIRMATION_SOURCE:-ohlc4}, 'Source', inline='sc1', group=scgrp)
shortCfrmLength = input.int(${SHORT_CONFIRMATION_LENGTH:-10}, minval=1, title='Length', inline='sc2', group=scgrp)
shortCfrmLimit = input.int(${SHORT_CONFIRMATION_LIMIT:-70}, minval=1, title='Limit', inline='sc2', group=scgrp)
scMAType = input.string(${SC_MA_TYPE:-'Disabled'}, 'Moving average', inline='scma', group=scgrp, options=['Disabled', 'ALMA', 'EMA', 'SMA', 'WMA', 'VWMA', 'RMA', 'HMA', 'VAMA', 'MGD'])
scMALength = input.int(${SC_MA_LENGTH:-5}, 'MA length', step=1, minval=3, inline='scma', group=scgrp)
scAlmaOffset = ${SC_ALMA_INPUT_PREFIX:-input.}float(${SC_ALMA_OFFSET:-0.85}, 'ALMA offset', step=.01, minval=.01, inline='scalma', group=scgrp)
scAlmaSigma = ${SC_ALMA_INPUT_PREFIX:-input.}float(${SC_ALMA_SIGMA:-6.0}, 'ALMA sigma', step=.1, minval=.1, inline='scalma', group=scgrp)
scVamaFastLength = ${SC_VAMA_INPUT_PREFIX:-hidden_input_}int(${SC_VAMA_FAST_LENGTH:-3}, 'VAMA fast length', step=1, minval=2, inline='scvama', group=scgrp)
scVamaWeight = ${SC_VAMA_INPUT_PREFIX:-hidden_input_}float(${SC_VAMA_WEIGHT:-.2}, 'VAMA weight', step=.01, minval=.01, inline='scvama', group=scgrp)
// Advanced configuration options
rebuyResistance = input.float(${PYRAMIDING_REBUY_RESISTANCE:-.01}, 'Rebuy resistance', minval=.01, step=.01, group=advgrp, inline='rebuy')
rebuyUsePosAvgPrice = input.bool(${PYRAMIDING_REBUY_USE_POS_AVG_PRICE:-true}, 'Use pos. average price', group=advgrp, inline='rebuy')
irreduciblePositions = input.int(${PYRAMIDING_IRREDUCIBLE_POSITIONS:-100}, 'Irreducible positions', step=1, minval=1, group=advgrp)
takeProfitPercent = input.float(${TAKE_PROFIT_PERCENT:-100}, 'Take profit %', minval=.1, step=.2, group=advgrp, inline='tp')
takeProfitRevertsPosition = input.bool(${TAKE_PROFIT_REVERTS_POSITION:-false}, 'Take profit reverts position', group=advgrp, inline='tp')
barsLimit = input.int(${BARS_LIMIT:-10000}, 'Limit max bars in entry', step=1, minval=1, group=advgrp, inline='bars')
barsLimitBreakEven = input.bool(${BARS_LIMIT_BREAK_EVEN:-false}, 'break even', group=advgrp, inline='bars')
tslType = input.string(${TSL_TYPE:-'ATR'}, 'Stop loss type', group=tslgrp, inline='tsl1', options=['Disabled', 'Percent', 'ATR'])
tslPercent = input.float(${TSL_PERCENT:-18}, 'Percent', minval=.1, step=.2, group=tslgrp, inline='tsl1')
tslATRLength = input.int(${TSL_ATR_LENGTH:-100}, 'ATR length', minval=1, step=1, group=tslgrp, inline='tslatr')
tslATRMult = input.int(${TSL_ATR_MULTIPLIER:-16}, 'ATR multiplier', minval=1, step=1, group=tslgrp, inline='tslatr')
tslRevertsPosition = input.bool(${TSL_REVERTS_POSITION:-false}, 'Trailing stop loss reverts position', group=tslgrp)
// UI specific configuration options
showInfoBox = input.bool(${SHOW_INFO_BOX:-false}, 'Show info box', group=uigrp)
showSignalMarkers = input.bool(${SHOW_SIGNAL_MARKERS:-false}, 'Show signal markers', group=uigrp)
// https://medium.com/the-investors-handbook/a-new-variation-on-the-bollinger-bands-in-python-7f6d91f02c5e
vama(src, dev1Len, dev2Len, scale) =>
longStdDev = ta.stdev(src, dev2Len >= dev1Len ? dev2Len : dev1Len)
shortStdDev = ta.stdev(src, dev2Len >= dev1Len ? dev1Len : dev2Len)
alpha = shortStdDev / longStdDev * scale
ma = float(na)
ma := alpha * src + (1 - alpha) * nz(ma[1])
// https://blog.earn2trade.com/mcginley-dynamic-indicator/
mcginley_dynamic(src, length) =>
ma = 0.0
ma := barstate.isfirst ? src : ma[1] + (src - ma[1]) / (0.6 * length * math.pow(src / ma[1], 4))
movingAverage(type, src, length) =>
switch type
'EMA' => ta.ema(src, length)
'HMA' => ta.ema(src, length)
'RMA' => ta.rma(src, length)
'SMA' => ta.sma(src, length)
'VWMA' => ta.vwma(src, length)
'WMA' => ta.wma(src, length)
'MGD' => mcginley_dynamic(src, length)
=> float(na)
deviation(type, src, length) =>
switch type
'STD' => ta.stdev(src, length)
'MEAN' => ta.dev(src, length)
=> float(na)
compareSources(type, sigsrc, masrc) =>
switch type
'Price > MA' => sigsrc > masrc
'Price < MA' => sigsrc < masrc
'Price x-over MA' => ta.crossover(sigsrc, masrc)
'Price x-under MA' => ta.crossunder(sigsrc, masrc)
=> bool(na)
// Lower Band Calculation
lbMovingAverage = switch lbMAType
'ALMA' => ta.alma(lbMASource, lbMALen, lbAlmaOffset, lbAlmaSigma)
'VAMA' => vama(lbMASource, lbMALen, lbVamaFastLen, lbVamaWeight)
=> movingAverage(lbMAType, lbMASource, lbMALen)
lowerBand = lbMovingAverage - deviation(lbDevType, lbDevSource, lbDevLen) * lbDevScale
// Upper Band Calculation
ubMovingAverage = switch ubMAType
'ALMA' => ta.alma(ubMASource, ubMALen, ubAlmaOffset, ubAlmaSigma)
'VAMA' => vama(ubMASource, ubMALen, ubVamaFastLen, ubVamaWeight)
=> movingAverage(ubMAType, ubMASource, ubMALen)
upperBand = ubMovingAverage + deviation(ubDevType, ubDevSource, ubDevLen) * ubDevScale
// Short Confirmation Calculation - Oscillator step
shortCfrmEnabled = shortCfrm == 'Disabled' ? false : true
shortCfrmSignal = switch shortCfrm
'RSI' => ta.rsi(shortCfrmSource, shortCfrmLength)
=> na
//overBought
shortConfirmed = shortCfrmEnabled and (shortCfrmSignal > shortCfrmLimit)
// Short confirmation calculation - MA step
scMAEnabled = scMAType == 'Disabled' ? false : shortCfrmEnabled ? true : false
scMASource = shortCfrmEnabled ? shortCfrmSignal : na
scMASignal = switch scMAType
'ALMA' => ta.alma(scMASource, scMALength, scAlmaOffset, scAlmaSigma)
'VAMA' => vama(scMASource, scMALength, scVamaFastLength, scVamaWeight)
=> movingAverage(scMAType, scMASource, scMALength)
shortConfirmed := scMAEnabled ? (scMASignal > shortCfrmLimit) : shortConfirmed
// Long Confirmation Calculation - Oscillator step
longCfrmEnabled = longCfrm == 'Disabled' ? false : true
longCfrmSignal = switch longCfrm
'RSI' => ta.rsi(longCfrmSource, longCfrmLength)
=> na
//overSold
longConfirmed = longCfrmEnabled and (longCfrmSignal < longCfrmLimit)
// Long confirmation calculation - MA step
lcMAEnabled = lcMAType == 'Disabled' ? false : longCfrmEnabled ? true : false
lcMASource = longCfrmEnabled ? longCfrmSignal : na
lcMASignal = switch lcMAType
'ALMA' => ta.alma(lcMASource, lcMALength, lcAlmaOffset, lcAlmaSigma)
'VAMA' => vama(lcMASource, lcMALength, lcVamaFastLength, lcVamaWeight)
=> movingAverage(lcMAType, lcMASource, lcMALength)
longConfirmed := lcMAEnabled ? (lcMASignal < longCfrmLimit) : longConfirmed
// Color Palette
colorBandLine = color.new(color.gray, 0)
colorTradeZone = color.new(color.teal, 50)
colorConfirmation = color.new(color.green, 0)
p1 = plot(upperBand, 'BB Upper', color = shortConfirmed ? colorConfirmation : colorBandLine)
p2 = plot(lowerBand, 'BB Lower', color = longConfirmed ? colorConfirmation : colorBandLine)
fill(p1, p2, color=color.new(color.gray, 80))
p3 = plot(lbLongSource, 'Buy', color=colorTradeZone)
p4 = plot(ubShortSource, 'Sell', color=colorTradeZone)
fill(p3, p4, color=colorTradeZone)
lbSignal = showSignalMarkers and compareSources(lbLongStrategy, lbLongSource, lowerBand) ? lbLongSource*0.99 : na
ubSignal = showSignalMarkers and compareSources(ubShortStrategy, ubShortSource, upperBand) ? ubShortSource*1.01 : na
plot(lbSignal, color=color.yellow, linewidth=2, style=plot.style_circles)
plot(ubSignal, color=color.yellow, linewidth=2, style=plot.style_circles)
// Information box about the strategy performance
AverageBarsInTrades() =>
result = 0
for i = 0 to strategy.closedtrades-1
result += strategy.closedtrades.exit_bar_index(i) - strategy.closedtrades.entry_bar_index(i) + 1
math.round(strategy.closedtrades > 0 ? result / strategy.closedtrades : na)
avgBarsInTrades = AverageBarsInTrades()
barsSinceLastEntry = bar_index - strategy.opentrades.entry_bar_index(strategy.opentrades - 1)
if barstate.islastconfirmedhistory
t0 = strategy.closedtrades.entry_time(0)
t1 = strategy.closedtrades.exit_time(strategy.closedtrades - 1)
prettyMonth = (month(t0) > 9 ? '' : '0') + str.tostring(month(t0))
prettyDay = (dayofmonth(t0) > 9 ? '' : '0') + str.tostring(dayofmonth(t0))
profitPerDay = nz(strategy.netprofit) / ((t1 - t0) / 86400000)
risk = math.min(tslPercent / 100, 1.0) * strategy.initial_capital
summary = str.format('First trade date: {0}-{1}-{2}\nDaily profit: \${3}\nPosition average price: \${4}\nUnrealized profit/loss: \${5}\nRisk/Reward ratio: {6}\nAvarage bars in trades: {7}\nBars since last entry: {8}',
str.tostring(year(t0)), prettyMonth, prettyDay,
math.round(profitPerDay, 2),
math.round(nz(strategy.position_avg_price), 2),
math.round(nz(strategy.openprofit), 2),
math.round(risk / strategy.netprofit, 2),
avgBarsInTrades,
barsSinceLastEntry)
if showInfoBox
tbl = table.new(position.top_right, 1, 1, bgcolor=color.black, frame_color=color.silver, frame_width=1, border_color=color.gray, border_width=1)
table.cell(tbl, 0, 0, summary, text_color = color.silver, text_halign=text.align_left, text_size=size.small)
else
tbl = table.new(position.top_right, 1, 1)
table.cell(tbl, 0, 0, 'ⓘ', text_color = color.blue, tooltip=summary)
// calculate the profit as it stands right now
profitPercent = strategy.opentrades > 0 ? 100 * (close / strategy.position_avg_price - 1) : .0
// Trailing Stop Loss calculation
tslSource = close
getTrailingStopLoss() =>
// Use Percent when the number of available bars is too low, giving ATR = 0
switch tslType
'Percent' => (tslSource * tslPercent) / 100
'ATR' => (bar_index - barsSinceLastEntry) < tslATRLength ? (tslSource * tslPercent) / 100 : ta.atr(tslATRLength) * tslATRMult
=> na
// Initialize trailing stop loss
var float trailingSL = na
tslTriggered = false
tslValue = getTrailingStopLoss()
// Determine position direction
positionIsLong = strategy.position_size > 0 ? true : false
positionIsShort = strategy.position_size < 0 ? true : false
if (tslType != 'Disabled')
trailingSL := positionIsLong ? ( barsSinceLastEntry == 0 ? tslSource - tslValue : math.max(tslSource - tslValue, nz(trailingSL[1])) )
: positionIsShort ? ( barsSinceLastEntry == 0 ? tslSource + tslValue : math.min(tslSource + tslValue, nz(trailingSL[1])) )
: na
tslTriggered := (positionIsLong and ta.crossunder(tslSource, nz(trailingSL)))
or (positionIsShort and ta.crossover(tslSource, nz(trailingSL)))
plot(trailingSL, color=color.gray, linewidth=1, style=plot.style_circles)
// Determine if short/long should be reverted when the Trailing Stop Loss or Take Profit is triggered
revertLong = positionIsLong and ( (tslTriggered and tslRevertsPosition) or (takeProfitRevertsPosition and profitPercent >= takeProfitPercent) ) ? true : false
revertShort = positionIsShort and ( (tslTriggered and tslRevertsPosition) or (takeProfitRevertsPosition and profitPercent <= -takeProfitPercent) ) ? true : false
// determine whether it is a good idea to enter long/short positions
shouldBuy = compareSources(lbLongStrategy, lbLongSource, lowerBand) and (longCfrmEnabled ? longConfirmed : true)
shouldSell = compareSources(ubShortStrategy, ubShortSource, upperBand) and (shortCfrmEnabled ? shortConfirmed : true)
// determine if bars limit is reached in current trade
barsLimitReached = barsSinceLastEntry > barsLimit and (not barsLimitBreakEven or strategy.openprofit >= 0)
// -- must close positions here --
// reduce position - only effective if there are any positions with the corresponding id
strategy.close('average down', comment='reduce long', when=(close >= strategy.position_avg_price))
strategy.close('average up', comment='reduce short', when=(close <= strategy.position_avg_price))
// take profit
strategy.close('buy', when=(shouldSell or profitPercent >= takeProfitPercent or barsLimitReached or tslTriggered),
comment=str.format('{0}: {1}%', profitPercent >= 0 ? 'profit' : 'loss', math.round(math.abs(profitPercent), 2)))
strategy.close('sell', when=(shouldBuy or profitPercent <= -takeProfitPercent or barsLimitReached or tslTriggered),
comment=str.format('{0}: {1}%', profitPercent <= 0 ? 'profit' : 'loss', math.round(math.abs(profitPercent), 2)))
// -- open positions here --
// GOTCHA: strategy.opentrades* is NOT updated by strategy.close
// Make sure consecutive buys/sells improve the average position price
posPrice = rebuyUsePosAvgPrice ? strategy.position_avg_price : strategy.opentrades.entry_price(strategy.opentrades - 1)
okOpenLong = longEnabled and (na(posPrice) or (posPrice / close) >= rebuyResistance) and
(devMode ? time <= backtestEnd : time > backtestEnd)
okOpenShort = shortEnabled and (na(posPrice) or (close / posPrice) >= rebuyResistance) and
(devMode ? time <= backtestEnd : time > backtestEnd)
// increase position
longId = strategy.opentrades.entry_id(0) == 'buy' and strategy.opentrades >= irreduciblePositions ? 'average down' : 'buy'
strategy.entry(longId, strategy.long, when=(okOpenLong and (shouldBuy or revertShort)))
shortId = strategy.opentrades.entry_id(0) == 'sell' and strategy.opentrades >= irreduciblePositions ? 'average up' : 'sell'
strategy.entry(shortId, strategy.short, when=(okOpenShort and (shouldSell or revertLong)))
EOF
Show all 328 lines