//@version=5 strategy(title='S&R integration CLEAN', overlay=true, default_qty_type=strategy.percent_of_equity, initial_capital = 10000, default_qty_value=100, currency=currency.USD, commission_type=strategy.commission.cash_per_order,commission_value=1, process_orders_on_close=false, pyramiding=20, max_labels_count = 500 ) // ######################################################## // Bar Coloring // ######################################################## var upColor = #B1E693 var downColor = #FF7878 isUp = close>open barColor = isUp ? upColor : downColor barcolor(barColor) // ######################################################## // Backtesting // ######################################################## // Range // --------------------- // isTimeCond = input.bool(defval=true, title="Turn On/Off time range") fromDate = input.time(defval=timestamp("01 Jan 2019 00:00 -0400") ,title="From Date", group="Backtesting Range", confirm=true) toDate = input.time(defval=timestamp("01 Jan 2100 23:59 -0400") ,title="To Date", group="Backtesting Range", confirm=true) time_cond = time >= fromDate and time <= toDate // labelText = str.tostring(currentFunds) // newLabal = label.new(x=bar_index, y=na, text=labelText, yloc=yloc.abovebar, color=color.green, textcolor=color.white, style=label.style_label_down, size=size.normal) // label.delete(newLabal[1]) /////////////////////////////////////////////////////////// // ######################################################## // MA // ######################################################## // SMA --> Simple // EMA --> Exponential // WMA --> Weighted // VWMA --> Volume Weighted // SMMA --> Smoothed // DEMA --> Double Exponential // TEMA --> Triple Exponential // HMA --> Hull // TMA --> Triangular // SSMA --> SuperSmoother filter // ZEMA --> Zero Lag Exponential // JMA --> Jurik // turn on/off the MA strategy isMA = input.bool(defval=true, title="Turn On/Off MA", group="MA") displayMA = isMA ? 0 : 100 // select the MA and return according to selection (default is SMA) type = input.string(defval='EMA', title='MA Type: ', options=['SMA', 'EMA', 'WMA', 'VWMA', 'SMMA', 'DEMA', 'TEMA', 'HullMA', 'ZEMA', 'TMA', 'SSMA', 'JMA'], group="MA") len1 = input.int(defval=50, title='Fast MA Length', minval=1, group="MA") len2 = input.int(defval=200, title='Slow MA Length', minval=1, group="MA") pow = input(3, 'Pow for Jurik\'s MA', group="MA") variant(type, src, len, p) => v1 = ta.sma(src, len) // Simple v2 = ta.ema(src, len) // Exponential v3 = ta.wma(src, len) // Weighted v4 = ta.vwma(src, len) // Volume Weighted v5 = 0.0 sma_1 = ta.sma(src, len) // Smoothed v5 := na(v5[1]) ? sma_1 : (v5[1] * (len - 1) + src) / len v6 = 2 * v2 - ta.ema(v2, len) // Double Exponential v7 = 3 * (v2 - ta.ema(v2, len)) + ta.ema(ta.ema(v2, len), len) // Triple Exponential v8 = ta.wma(2 * ta.wma(src, len / 2) - ta.wma(src, len), math.round(math.sqrt(len))) // Hull v11 = ta.sma(ta.sma(src, len), len) // Triangular // SuperSmoother filter // © 2013 John F. Ehlers a1 = math.exp(-1.414 * 3.14159 / len) b1 = 2 * a1 * math.cos(1.414 * 3.14159 / len) c2 = b1 c3 = -a1 * a1 c1 = 1 - c2 - c3 v9 = 0.0 v9 := c1 * (src + nz(src[1])) / 2 + c2 * nz(v9[1]) + c3 * nz(v9[2]) // Zero Lag Exponential xLag = (len - 1) / 2 xEMAData = src + src - src[xLag] v10 = ta.ema(xEMAData, len) // Jurik's MA beta = 0.45 * (len - 1) / (0.45 * (len - 1) + 2) alpha = math.pow(beta, p) L0 = 0.0 L1 = 0.0 L2 = 0.0 L3 = 0.0 v12 = 0.0 L0 := (1 - alpha) * src + alpha * nz(L0[1]) L1 := (src - L0[0]) * (1 - beta) + beta * nz(L1[1]) L2 := L0[0] + L1[0] L3 := (L2[0] - nz(v12[1])) * (1 - alpha) * (1 - alpha) + alpha * alpha * nz(L3[1]) v12 := nz(v12[1]) + L3[0] v12 // return variant, defaults to SMA if input invalid. type == 'EMA' ? v2 : type == 'WMA' ? v3 : type == 'VWMA' ? v4 : type == 'SMMA' ? v5 : type == 'DEMA' ? v6 : type == 'TEMA' ? v7 : type == 'HullMA' ? v8 : type == 'SSMA' ? v9 : type == 'ZEMA' ? v10 : type == 'TMA' ? v11 : type == 'JMA' ? v12 : v1 ma_1 = variant(type, close, len1, pow) ma_2 = variant(type, close, len2, pow) // plot lines (transparent if MA is disabled) plot(ma_1, title='Fast MA', color=color.new(#1EAE98, displayMA), linewidth=2) plot(ma_2, title='Slow MA', color=color.new(#A9F1DF, displayMA), linewidth=2) // check for MA trigger MAlongCondition = ta.crossover(ma_1, ma_2) and isMA MAshortCondition = ta.crossunder(ma_1, ma_2) and isMA // plot MA trigger label plotshape(MAlongCondition and time_cond, title='Buy Signal', text='Buy MA', style=shape.labelup, location=location.belowbar, color=color.new(upColor, 0), textcolor=color.new(color.white, 0)) plotshape(MAshortCondition and time_cond, title='Sell Signal', text='Sell MA', style=shape.labeldown, location=location.abovebar, color=color.new(downColor, 0), textcolor=color.new(color.white, 0)) /////////////////////////////////////////////////////////// // ######################################################## // Candle Swing (CS) // ######################################################## // inputs // -------------------------- // turn on/off the Candle Swing strategy isCS = input.bool(defval=true, title="Turn On/Off Candle Swing", group="Candle Swings") // get inputs to Candle Swing strategy backwardLength = input.int(5, minval=2, title='Check # of candles BEFORE Hammer', group="Candle Swings") forwardLength = input.int(2, minval=1, title='Check # of candles AFTER Hammer', group="Candle Swings") fibLevel = input.int(title="Body should be above/below % of the shadow", minval=1, maxval=100, defval=33, group="Candle Swings", tooltip= "The shadow % which the candle should be above or below it") / 100 // inputs for minimum hammer requirements isMinimumCnadle = input.bool(defval=true, title="", group="Candle Swings", inline="candleToShadow") minimumCandlePerc = input.int(defval=15, minval=1, maxval=100, title='Minimum requirement % for body/shadow', group="Candle Swings", inline="candleToShadow") / 100 // hammer detetction // -------------------------- // calculate fibonacci level for latest candle bullFib = high[forwardLength] - ((high[forwardLength] - low[forwardLength]) * fibLevel) bearFib = low[forwardLength] + ((high[forwardLength] - low[forwardLength]) * fibLevel) // determine which price source close or open highest/lowest lowestBody = close[forwardLength] < open[forwardLength] ? close[forwardLength] : open[forwardLength] highestBody = close[forwardLength] > open[forwardLength] ? close[forwardLength] : open[forwardLength] // determine if we have a valid hammer hammerCandle = bool(na) if isMinimumCnadle == false hammerCandle := (lowestBody >= bullFib) or (highestBody <= bearFib) else candleToShadowRatio = math.abs((close[forwardLength] - open[forwardLength])) / math.abs((high[forwardLength] - low[forwardLength])) isCandleToShadowRatio = candleToShadowRatio >= minimumCandlePerc if isCandleToShadowRatio hammerCandle := (lowestBody >= bullFib) or (highestBody <= bearFib) // candle swing // -------------------------- counterForwardHammerLong = 0 counterBackwardHammerLong = 0 counterForwardHangingManShort = 0 counterBackwardHangingManShort = 0 CSpatternLong = bool(na) CSpatternShort = bool(na) // check backwards and forwards if it is a hammer to buy long if hammerCandle for i = 1 to forwardLength by 1 if close[i - 1] > close[i] counterForwardHammerLong := counterForwardHammerLong + 1 if counterForwardHammerLong == forwardLength for x = forwardLength to backwardLength by 1 if close[x] < close[x + 1] counterBackwardHammerLong := counterBackwardHammerLong + 1 if counterForwardHammerLong == forwardLength and counterBackwardHammerLong == backwardLength CSpatternLong := true // check backwards and forwards if it is hanging man to sell long if hammerCandle for i = 1 to forwardLength by 1 if close[i - 1] < close[i] counterForwardHangingManShort := counterForwardHangingManShort + 1 if counterForwardHangingManShort == forwardLength for x = forwardLength to backwardLength by 1 if close[x] > close[x + 1] counterBackwardHangingManShort := counterBackwardHangingManShort + 1 if counterForwardHangingManShort == forwardLength and counterBackwardHangingManShort == backwardLength CSpatternShort := true // check if Candle Swing strategy is turned on and if any pattern existed, and plot results CSlongCondition = CSpatternLong and isCS CSshortCondition = CSpatternShort and isCS plotshape(CSlongCondition and time_cond and not MAlongCondition, offset= -1, title = "Hammer", text = 'Hammer', style=shape.labelup, location=location.belowbar, color=color.new(upColor, 25), textcolor=color.new(color.white, 0)) plotshape(CSshortCondition and time_cond and not MAshortCondition, offset= -1, title = "Hanging Man", text= 'Hanging Man', style=shape.labeldown, location=location.abovebar, color=color.new(downColor, 25), textcolor=color.new(color.white, 0)) // plotshape(hammerCandle and time_cond, style=shape.labeldown, offset=-1, location=location.abovebar, color=color.new(upColor, 25), textcolor=color.new(color.white, 0)) // if hammerCandle // labelText = str.tostring(counterBackwardHammerLong) // newLabal = label.new(x=bar_index, y=na, text=labelText, yloc=yloc.abovebar, color=color.green, textcolor=color.white, style=label.style_label_down, size=size.normal) /////////////////////////////////////////////////////////// // ######################################################## // BB // ######################################################## // turn on/off the Boilinger Bands strategy isBB = input.bool(defval=true, title="Turn On/Off Boilinger Bands", group="Bollinger Bands") displayBB = isBB ? 30 : 100 displayBBFill = isBB ? 90 : 100 // calculate BB bb_length = input.int(defval=20, minval=1, title= 'BB length', group="Bollinger Bands") bb_mult = input.int(defval = 2, minval = 1, title = 'BB mult', group="Bollinger Bands") [middle, upper, lower] = ta.bb(close, bb_length, bb_mult) // plot lines and fill (transparent if BB is disabled) // plot(middle, title = "Middle BB Line", color=color.new(color.yellow, displayBB)) p1 = plot(upper, title = "Upper BB Line", color=color.new(#E0C097, displayBB)) p2 = plot(lower, title = "Lower BB Line", color=color.new(#E0C097, displayBB)) fill(plot1=p1, plot2=p2, title = "Fill BB Lines", color=color.new(#E0C097, displayBBFill), editable=true) // check what percentage of the candle is outside of the Boilinger Band outsideCandlePercLong = input.int(defval=50, title="% of Candle outside of LOWER band", minval=0, maxval=100, step=1, group="Bollinger Bands", tooltip="What percentage of the candle is outside of the lower Boilinger Band") /100 outsideCandlePercShort = input.int(defval=50, title="% of Candle outside of UPPER band", minval=0, maxval=100, step=1, group="Bollinger Bands", tooltip="What percentage of the candle is outside of the upper Boilinger Band") /100 BBlongCondition=bool(na) BBshortCondition=bool(na) BBoutsideLowerBand = close < lower and isBB BBlongPercentege = (lower-math.min(low, high))/math.abs(high-low) if BBoutsideLowerBand and BBlongPercentege >= outsideCandlePercLong BBlongCondition:=true BBoutsideUpperBand = close > upper and isBB BBshortPercentege = (math.max(low, high)-upper)/math.abs(high-low) if BBoutsideUpperBand and BBshortPercentege >= outsideCandlePercShort BBshortCondition:=true plotshape(BBlongCondition and time_cond and not MAlongCondition, title = "Buy BB", text='Buy BB', style=shape.labelup, location=location.belowbar, color=color.new(upColor, 25), textcolor=color.new(color.white, 0)) plotshape(BBshortCondition and time_cond and not MAshortCondition, title = "Sell BB", text='Sell BB', style=shape.labeldown, location=location.abovebar, color=color.new(downColor, 25), textcolor=color.new(color.white, 0)) /////////////////////////////////////////////////////////// // ######################################################## // Entry & Exit strategy // ######################################################## buyStrategyIfWeakerIndicator = input.int(title="% to BUY LONG if weaker indcator", minval=0, maxval=100, step=1, defval=75, group="Entry Parameters") / 100 isLimitEntry = input.bool(title="", defval=true, group="Entry Parameters", inline="limit") limitEntrySource = input.source(title="Limit BUY order by", defval=close, group="Entry Parameters", inline="limit") limitEntryPerc = input.float(title="+ %", minval=0, maxval=100, step=0.1, defval=3.0, group="Entry Parameters", inline="limit") / 100 isMinEntryQty = input.bool(title="", defval=true, group="Entry Parameters", inline="qty") minEntryQty = input.int(title="Minimum order quantity", defval=5, minval = 0, group="Entry Parameters", inline="qty") closeStrategyIfWeakerPerc = input.int(title="% to SELL LONG if weaker indcator", minval=0, maxval=100, step=1, defval=50, group="Entry Parameters") isInMA = ma_1 > ma_2 // check for how much funds left to invest and if it's enough to make a transaction currentFunds = strategy.initial_capital + strategy.netprofit - math.abs(nz((strategy.position_avg_price * strategy.position_size))) isStrongFundsLeft = currentFunds > close isWeakFundsLeft = currentFunds * buyStrategyIfWeakerIndicator > close weakPositionSize = math.floor(currentFunds * buyStrategyIfWeakerIndicator / close) strongPositionSize = math.floor(currentFunds / close) // Entry // -------------------------- limitPrice = isLimitEntry ? limitEntrySource * (1 + limitEntryPerc) : na weakMinEntryQtyCondition = isMinEntryQty ? weakPositionSize > minEntryQty : true strongMinEntryQtyCondition = isMinEntryQty ? strongPositionSize > minEntryQty : true shouldPlaceBBlongOrder = isWeakFundsLeft and BBlongCondition and time_cond shouldPlaceCSlongOrder = isWeakFundsLeft and CSlongCondition and time_cond shouldPlaceMAlongOrder = isStrongFundsLeft and MAlongCondition and time_cond if shouldPlaceBBlongOrder and weakMinEntryQtyCondition strategy.entry(id="Long BB", direction=strategy.long, qty=weakPositionSize, comment = "Buy long due to BB (" + str.tostring(buyStrategyIfWeakerIndicator * 100) + "%)", limit=limitPrice) alert("BUY BUY BUY (Long BB)\nNumber of Shares: " + str.tostring(weakPositionSize) + "\nLimit Price: " + str.tostring(limitPrice) + "\n\nNew strategy position is {{strategy.position_size}}") if shouldPlaceCSlongOrder and weakMinEntryQtyCondition strategy.entry(id="Long Hammer", direction=strategy.long, qty=weakPositionSize, comment = "Buy long due to Hammer (" + str.tostring(buyStrategyIfWeakerIndicator * 100) + "%)", limit=limitPrice) alert("BUY BUY BUY (Hammer)\nNumber of Shares: " + str.tostring(weakPositionSize) + "\nLimit Price: " + str.tostring(limitPrice) + "\n\nNew strategy position is {{strategy.position_size}}") if shouldPlaceMAlongOrder and strongMinEntryQtyCondition strategy.entry(id="Long MA", direction=strategy.long, qty=strongPositionSize, comment = "Buy long due to MA (100%)", limit=limitPrice) alert("BUY BUY BUY (MA)\nNumber of Shares: " + str.tostring(strongPositionSize) + "\nLimit Price: " + str.tostring(limitPrice) + "\n\nNew strategy position is {{strategy.position_size}}") // Stop Loss & Take Profit // -------------------------- var isTakeProfit = input.bool(defval=true, title="", inline="tp", group="Take Profit & Stop Loss") takeProfitPerc = input.float(title="Take Profit %", minval=0.01, maxval=100, step=0.01, defval=7, group="Take Profit & Stop Loss", inline="tp") / 100 isStopLoss = input.bool(defval=true, title="", inline="sl", group="Take Profit & Stop Loss") stopLossPerc = input.float(title="Stop Loss %", minval=0.01, maxval=100, step=0.01, defval=1.0, group="Take Profit & Stop Loss", inline="sl") / 100 // check for Stop Loss and Take Profit prices longTakeProfitExit = strategy.position_avg_price * (1 + takeProfitPerc) longStopLossExit = strategy.position_avg_price * (1 - stopLossPerc) longTakeProfitCondition = isTakeProfit and (close >= longTakeProfitExit) longStopLossCondition = isStopLoss and (close <= longStopLossExit) plot(longStopLossExit, linewidth=3, style=plot.style_linebr, color=color.new(color.aqua, 0), title="Stop Loss Price") // Exit // -------------------------- // close all positions if MA short condition or if SL/TP occurs closeAllTxt = "" if MAshortCondition closeAllTxt := "Close all due to MA" else if longTakeProfitCondition closeAllTxt := "Close all due to Take Profit" else if longStopLossCondition closeAllTxt := "Close all due to Stop Loss" closeAllTrigger = MAshortCondition or longTakeProfitCondition or longStopLossCondition if closeAllTrigger and time_cond alert("SELL ALL SELL ALL SELL ALL (" + closeAllTxt + ")\nNumber of Shares: " + str.tostring(strategy.position_size) + "\nLimit Price: " + str.tostring(close)) strategy.close_all(comment = closeAllTxt) // close positions in any other situation if closeAllTrigger == false if isInMA == false if (BBshortCondition or CSshortCondition) and time_cond alert("SELL ALL SELL ALL SELL ALL (weak in/weak out)\nNumber of Shares: " + str.tostring(strategy.position_size) + "\nLimit Price: " + str.tostring(close)) strategy.close("Long BB", comment = "Full sell long (bought due to BB/fully sold)") strategy.close("Long Hammer", comment = "Full sell long (bought to due Hammer/fully sold)") if isInMA == true if (BBshortCondition or CSshortCondition) and time_cond alert("SELL SELL SELL (MA in/BB or CS out)\nNumber of Shares: " + str.tostring(closeStrategyIfWeakerPerc * strategy.position_size) + "\nLimit Price: " + str.tostring(close)) strategy.close("Long BB", qty_percent=closeStrategyIfWeakerPerc, comment = "Partial sold (bought due to MA)") strategy.close("Long Hammer", qty_percent=closeStrategyIfWeakerPerc, comment = "Partial sold (bought due to MA)") strategy.close("Long MA", qty_percent=closeStrategyIfWeakerPerc, comment = "Partial sold (bought due to MA)") // ######################################################## // Table // ######################################################## // Buy & Hold // --------------------- var totalCandleCount = 0 var startClosePrice = float(na) var endClosePrice = float(na) var timeOfStartClosePriceTxt = "" var timeOfEndClosePriceTxt = "" if time_cond and (close > open or open > close or close==open) totalCandleCount += 1 // memory managment safeTotalCandleCount = totalCandleCount >= 4999 ? 4999 : totalCandleCount checkIfMaxBarsTxt = safeTotalCandleCount == 4999 ? " (Max bars)" : "" close_() => close time_() => time v = close_() t = time_() max_bars_back(v, 5000) max_bars_back(t, 5000) // calculation of B&H if time_cond startClosePrice := v[safeTotalCandleCount] endClosePrice := close[0] timeOfStartClosePriceTxt := str.tostring(dayofmonth(t[safeTotalCandleCount])) + "." + str.tostring(month(t[safeTotalCandleCount])) + "." + str.tostring(year(t[safeTotalCandleCount])) timeOfEndClosePriceTxt := str.tostring(dayofmonth(time[0])) + "." + str.tostring(month(time[0])) + "." + str.tostring(year(time[0])) bnhReturn = ((endClosePrice / startClosePrice) - 1) * 100 // Gross Profit // --------------------- grossProfitClosedtrades = 0.0 grossProfitOpentrades = 0.0 for i = 0 to strategy.closedtrades-1 grossProfitClosedtrades += strategy.closedtrades.profit(i) for i = 0 to strategy.opentrades-1 grossProfitOpentrades += strategy.opentrades.profit(i) grossProfitResult = (grossProfitClosedtrades / strategy.initial_capital) * 100 opentradesGrossProfitResult = (grossProfitOpentrades / strategy.initial_capital) * 100 // Open Trade Profit // --------------------- openTradeProfit = strategy.openprofit positionSize = strategy.position_size * strategy.position_avg_price openTradeProfitPerc = openTradeProfit / positionSize * 100 // Add Table // --------------------- bnhCellColor = bnhReturn > 0 ? upColor : downColor gpCellColor = grossProfitResult > 0 ? upColor : downColor var table bnhReturnTable = table.new(position.top_right, 1, 6, border_color = color.white, border_width = 2) if barstate.islastconfirmedhistory table.cell(bnhReturnTable, 0, 0, text= "Net Profit return: " + str.tostring(grossProfitResult, format=format.percent) + " (" + str.tostring(opentradesGrossProfitResult, format=format.percent) + ")", bgcolor = gpCellColor, text_color=color.black) table.cell(bnhReturnTable, 0, 1, text= "Buy & Hold return: " + str.tostring(bnhReturn, format=format.percent), bgcolor = bnhCellColor, text_color=color.black) table.cell(bnhReturnTable, 0, 2, text= "Number of bars calculated: " + str.tostring(safeTotalCandleCount) + checkIfMaxBarsTxt, bgcolor = color.new(color.white, 0), text_color=color.black, text_size = size.small) table.cell(bnhReturnTable, 0, 3, text= "Opening price for Buy & Hold: " + str.tostring(startClosePrice) + " (" + timeOfStartClosePriceTxt + ")", bgcolor = color.new(color.white, 0), text_color=color.black, text_size = size.small) table.cell(bnhReturnTable, 0, 4, text= "Closing price for Buy & Hold: " + str.tostring(endClosePrice) + " (" + timeOfEndClosePriceTxt + ")", bgcolor = color.new(color.white, 0), text_color=color.black, text_size = size.small) table.cell(bnhReturnTable, 0, 5, text= "Open Profit relative to position size: " + str.tostring(openTradeProfitPerc, format=format.percent), bgcolor = color.new(color.white, 0), text_color=color.black, text_size = size.small)