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


//This strategy uses the well-known Bollinger Bands Indicator
//@version=5
strategy("BOLLINGER BANDS STRATEGY", shorttitle="BB STRATEGY", overlay=true, initial_capital=1000, default_qty_type=strategy.cash, default_qty_value=950, commission_type=strategy.commission.percent, commission_value=0.18, slippage=3)


//---------------------------------------TOOL TIPS-----------------------------------------//

t1 = "Bollinger Bands length. Corresponds to the SMA length to which we add/substract the standard deviation to create upper and lower bands."
t2 = "Corresponds to the number of standard deviations to be added/subtracted to obtain the two bands. This number can be decimal."
t3 = "The length of the SMA sending the output signals."
t4 = "The maximum percentage of the trade value we can lose. Default is 6%"
t5 = "Each gain or losse (relative to the previous reference) in an amount equal to this fixed ratio will change quantity of orders."
t6 = "The amount of money to be added to or subtracted from orders once the fixed ratio has been reached."


//----------------------------------------FUNCTIONS---------------------------------------//

//@function Displays text passed to `txt` when called.
debugLabel(txt, color) =>
    label.new(bar_index, high, text = txt, color=color, style = label.style_label_lower_right, textcolor = color.black, size = size.small)

//@function which looks if the close date of the current bar falls inside the date range
inBacktestPeriod(start, end) => (time >= start) and (time <= end)


//---------------------------------------USER INPUTS--------------------------------------//

//Technical parameters
bbLength = input.int(defval=120, minval=1, title="BB Length", group="Technical Parameters", tooltip=t1)
mult = input.float(defval=2, minval=0.1, title="Standard Deviation Multipler", group="Technical Parameters", tooltip=t2)
smaLength = input.int(defval=110, minval=1, title="SMA Exit Signal Length", group="Technical Parameters", tooltip=t3)
slMax = input.float(6, minval=0, title="Max Risk per trade (in %)", group="Risk Management", tooltip=t4)
//Money Management
fixedRatio = input.int(defval=400, minval=1, title="Fixed Ratio Value ($)", group="Money Management", tooltip=t5)
increasingOrderAmount = input.int(defval=200, minval=1, title="Increasing Order Amount ($)", group="Money Management", tooltip=t6)
//Backtesting period
startDate = input.time(title="Start Date", defval=timestamp("1 Jan 2017 00:00:00"), group="Backtesting Period")
endDate = input.time(title="End Date", defval=timestamp("1 July 2024 00:00:00"), group="Backtesting Period")


//----------------------------------VARIABLES INITIALISATION-----------------------------//

//Exit
smaExit = ta.sma(close, smaLength)
//BB Calculation
basis = ta.sma(close, bbLength)
dev = mult * ta.stdev(close, bbLength)
upperBB = basis + dev
lowerBB = basis - dev
//Money management
equity = math.abs(strategy.equity - strategy.openprofit)
var float capital_ref = strategy.initial_capital
var float cashOrder = strategy.initial_capital * 0.95
//Backtesting period
bool inRange = na


//------------------------------CHECKING SOME CONDITIONS ON EACH SCRIPT EXECUTION-------------------------------//

//Checking if the date belong to the range
inRange := inBacktestPeriod(startDate, endDate)

//Checking performances of the strategy
if equity > capital_ref + fixedRatio
    spread = (equity - capital_ref)/fixedRatio
    nb_level = int(spread)
    increasingOrder = nb_level * increasingOrderAmount
    cashOrder := cashOrder + increasingOrder
    capital_ref := capital_ref + nb_level*fixedRatio
if equity < capital_ref - fixedRatio
    spread = (capital_ref - equity)/fixedRatio
    nb_level = int(spread)
    decreasingOrder = nb_level * increasingOrderAmount
    cashOrder := cashOrder - decreasingOrder
    capital_ref := capital_ref - nb_level*fixedRatio

//Checking if we close all trades in case where we exit the backtesting period
if strategy.position_size!=0 and not inRange
    strategy.close_all()
    debugLabel("END OF BACKTESTING PERIOD : we close the trade", color=color.rgb(116, 116, 116))


//-----------------------------------EXIT SIGNAL------------------------------//

//SMA Exit Signal
if strategy.position_size > 0 and close < smaExit
    strategy.close("Long")
if strategy.position_size < 0 and close > smaExit
    strategy.close("Short")


//----------------------------------LONG/SHORT CONDITION---------------------------//

//Long Condition
if close > upperBB and strategy.position_size==0 and inRange
    qty = cashOrder/close
    strategy.entry("Long", strategy.long, qty)
    strategy.exit("Exit Long", "Long", stop=close*(1-slMax/100))

//Short Condition
if close < lowerBB and strategy.position_size==0 and inRange
    qty = cashOrder/close
    strategy.entry("Short", strategy.short, qty)
    strategy.exit("Exit Short", "Short", stop=close*(1+slMax/100))


//---------------------------------PLOTTING ELEMENT----------------------------------//

plot(smaExit, color=color.orange)
upperBBPlot = plot(upperBB, color=color.blue)
lowerBBPlot = plot(lowerBB, color=color.blue)
fill(upperBBPlot, lowerBBPlot, title = "Background", color=strategy.position_size>0 ? color.rgb(0, 255, 0, 90) : strategy.position_size<0 ? color.rgb(255, 0, 0, 90) : color.rgb(33, 150, 243, 95))
