1
votes

Thanks for checking this and hope you can help me out.

I have created a code that takes into consideration both the Bollinger Bands and RSI indicators and created a strategy when the close is underneath the lower band of the BB and RSI is showing oversold, the signal is to buy, and exit should be when the close is back above the basis line of the Bollinger Bands.

Also when the close is above the upper band of the Bollinger Bands and the RSI is showing overbought, the signal is to sell, and exit should be when the close is back below the basis line of the Bollinger Bands.

However, there is something wrong with the exit as it sometimes triggers correctly and sometimes not. Note that I have added that should there be a 200 pip move in the opposite direction, it should close out the trade.

Below you'll find the code. Please let me know what I've done wrong. Thanks!

// © hkanaan0

//@version=3
// 1. Define strategy settings
strategy(title="Bollinger Breakout", overlay=true,
     pyramiding=0, initial_capital=100000,
     commission_type=strategy.commission.cash_per_order,
     commission_value=4, slippage=2)

smaLength = input(title="SMA Length", type=integer, defval=50)
stdLength = input(title="StdDev Length", type=integer, defval=50)

ubOffset = input(title="Upper Band Offset", type=float, defval=2, step=0.5)
lbOffset = input(title="Lower Band Offset", type=float, defval=2, step=0.5)

usePosSize = input(title="Use Position Sizing?", type=bool, defval=true)
riskPerc   = input(title="Risk %", type=float, defval=0.5, step=0.25)

rsiSource = input(title = "RSI Source", type = source, defval = hlc3)
rsiLength = input(title = "RSI Length", type = integer, defval = 14)
rsiOverbought = input(title = "RSI Overbought Level", type = integer, defval = 70)
rsiOversold = input(title = "RSI Oversold Level", type = integer, defval = 30)

// 2. Calculate strategy values
rsiValue = rsi(rsiSource, rsiLength)
isRSIOB = rsiValue >= rsiOverbought
isRSIOS = rsiValue <= rsiOversold
isBullish = crossover(rsiValue, 55)
isBearish = crossunder(rsiValue, 45)

smaValue = sma(hlc3, smaLength)
stdDev   = stdev(hlc3, stdLength)

upperBand = smaValue + (stdDev * ubOffset)
lowerBand = smaValue - (stdDev * lbOffset)

riskEquity  = (riskPerc / 100) * strategy.equity
atrCurrency = (atr(20) * syminfo.pointvalue)
posSize     = usePosSize ? floor(riskEquity / atrCurrency) : 1

// 3. Output strategy data
plot(series=smaValue, title="SMA", color=blue, linewidth=2)

plot(series=upperBand, title="UB", color=green,
     linewidth=4)
plot(series=lowerBand, title="LB", color=red,
     linewidth=4)
     
//plotshape(isRSIOB, title="Overbought" , location=location.abovebar , color=red , transp=0 , style=shape.triangledown , text="Sell")
//plotshape(isRSIOS, title = "Oversold", location = location.belowbar, color =lime, transp = 0, style = shape.triangleup, text = "Buy")
plotshape(isBullish, title = "Bullish Momentum", location=location.abovebar, color=lime, transp = 0, style = shape.arrowup, text = "Bullish")
plotshape(isBearish, title = "Bearish Momentum", location=location.belowbar, color=red, transp = 0, style = shape.arrowdown, text = "Bearish")


// 4. Determine long trading conditions
enterLong = crossunder(close, lowerBand) and isRSIOS
exitLong  = crossover(close, smaValue)

// 5. Code short trading conditions
enterShort = crossover(close, upperBand) and isRSIOB
exitShort  = crossunder(close, smaValue)

// 6. Submit entry orders
if (enterLong)
    strategy.order(id="Enter Long", long=true, qty=posSize)

if (enterShort)
    strategy.order(id="Enter Short", long=false, qty=posSize)

// 7. Submit exit orders
strategy.exit(id="Exit Long", when=exitLong, loss = 200)
strategy.exit(id="Exit Short", when=exitShort, loss = 200)```
1

1 Answers

0
votes

What is happening is that the 200 pip is calculated when the exit_short or exit_long triggers. So if an exit_short trigger at 12000, the price order will only take place in 11800, even if you shorted at 13000.

To fix this I calculated what the stop price should be when you entered the market:

opened_order = strategy.position_size[0] != 0 and strategy.position_size[1] == 0

If you weren't in a position then, but is now, the strategy.position_size changes. So I use valuewhen(opened_order, close, 0) to get the close price when you opened an order. I take this price and +- 200 ticks from it, depending on which direction the order was opened.

In the end, if the current low/high has touched the stop price, the position closes. I've also made different comments, so you can see if the trade has stopped, or if you took profit. To do this I had to update the PineScript to v4, tho, so if you don't want the comments you can just delete them.

This is the code:

// Strategy settings
strategy(title="Bollinger Breakout", overlay=true,
     pyramiding=0, initial_capital=100000,
     calc_on_order_fills = true,
     commission_type=strategy.commission.cash_per_order,
     commission_value=4, slippage=2)

smaLength = input(title="SMA Length", defval=50)
stdLength = input(title="StdDev Length", defval=50)

ubOffset = input(title="Upper Band Offset", defval=2, step=0.5)
lbOffset = input(title="Lower Band Offset", defval=2, step=0.5)

usePosSize = input(title="Use Position Sizing?", defval=false)
riskPerc   = input(title="Risk %", defval=2.5, step=0.25)

rsiSource = input(title = "RSI Source", defval = hlc3)
rsiLength = input(title = "RSI Length", defval = 14)
rsiOverbought = input(title = "RSI Overbought Level", defval = 70)
rsiOversold = input(title = "RSI Oversold Level", defval = 30)

// Calculate strategy values
rsiValue = rsi(rsiSource, rsiLength)
isRSIOB = rsiValue >= rsiOverbought
isRSIOS = rsiValue <= rsiOversold
isBullish = crossover(rsiValue, 55)
isBearish = crossunder(rsiValue, 45)

smaValue = sma(hlc3, smaLength)
stdDev   = stdev(hlc3, stdLength)

upperBand = smaValue + (stdDev * ubOffset)
lowerBand = smaValue - (stdDev * lbOffset)

riskEquity  = (riskPerc / 100) * strategy.equity
atrCurrency = (atr(20) * syminfo.pointvalue)
posSize     = usePosSize ? floor(riskEquity / atrCurrency) : 1

in_market = strategy.opentrades != 0
opened_order = strategy.position_size[0] != 0 and strategy.position_size[1] == 0
is_long = strategy.position_size > 0
is_short = strategy.position_size < 0
bought = is_long[0] and not is_long[1]
sold = is_short[0] and not is_short[1]

// Output strategy data

plot(series=smaValue, title="SMA", color=color.blue, linewidth=2)
plot(series=upperBand, title="UB", color=color.green, linewidth=4)
plot(series=lowerBand, title="LB", color=color.red, linewidth=4)

// Determine long trading conditions

enterLong = crossunder(close, lowerBand) and isRSIOS
plotshape(enterLong, title="Entry long signal", location=location.abovebar, color=color.lime, style=shape.triangledown)
exitLong  = crossover(close, smaValue)

// Determine short trading conditions

enterShort = crossover(close, upperBand) and isRSIOB
plotshape(enterShort, title="Entry short signal", location=location.belowbar, color=color.red, style = shape.triangleup)
exitShort  = crossunder(close, smaValue)

// Stop loss calculations

moving_stop_price_long = not in_market ? close - (200 * syminfo.mintick) : na
moving_stop_price_short = not in_market ? close + (200 * syminfo.mintick) : na
stop_price = is_long ? valuewhen(bought, close[1], 0) - (200 * syminfo.mintick): is_short ? valuewhen(sold, close[1], 0) + (200 * syminfo.mintick) : na
plot(stop_price ? stop_price : stop_price[1], "Stop price", color.red, 2, plot.style_linebr)

// Submit entry orders

if enterLong and not in_market
    strategy.order(id="Long", long=true, qty=posSize)

if enterShort and not in_market
    strategy.order(id="Short", long=false, qty=posSize)

// Submit exit orders
if is_long
    strategy.exit("Long", limit=exitLong ? close : na, stop=stop_price)
if is_short
    strategy.exit("Short", limit=exitShort ? close : na, stop=stop_price)