//HOZAN TAHER //@version=5 INITIAL_CAPITAL = 1000 DEFAULT_COMMISSION = 0.02 MAX_DRAWINGS = 500 IS_OVERLAY = true indicator("JOKER Scalping", "JOKER Scalping", overlay = IS_OVERLAY, max_labels_count = MAX_DRAWINGS, max_boxes_count = MAX_DRAWINGS, max_lines_count = MAX_DRAWINGS) // ============================================================================= // INPUTS // ============================================================================= trendFilterTf = input.timeframe ("60", "Trend Timeframe        ", group = "Strategy: Higher Timeframe Trend Filter", inline = "TF1", tooltip = "Higher timeframe to use for finding main trand direction") trendFastEmaLen = input.int (8, "Fast EMA Length        ", group = "Strategy: Higher Timeframe Trend Filter", inline = "TF2", minval = 1, tooltip = "Length of Fast EMA of higher timeframe") trendSlowEmaLen = input.int (21, "Slow EMA Length          ", group = "Strategy: Higher Timeframe Trend Filter", inline = "TF3", minval = 1, tooltip = "Length of Slow EMA of higher timeframe") // ---------------------- // Trade Entry Conditions // ---------------------- entryFastEmaLen = input.int (8, "Fast EMA Length          ", group = "Strategy: Trade Entry Conditions", inline = "TE1", minval = 1, tooltip = "Fast EMA used to find entry candles when price pulls back into this line") entryMedEmaLen = input.int (13, "Medium EMA Length      ", group = "Strategy: Trade Entry Conditions", inline = "TE2", minval = 1, tooltip = "Medium EMA used to filter out entries when lines are not fanned out or not trending in the desired direction") entrySlowEmaLen = input.int (21, "Slow EMA Length         ", group = "Strategy: Trade Entry Conditions", inline = "TE3", minval = 1, tooltip = "Slow EMA used to filter out entries when lines are not fanned out or not trending in the desired direction.\n\nAlso used to cancel open orders which close below this EMA before entry has been triggered") fanoutAtrMult = input.float (0.5, "EMA Fanout ATR Multiplier    ", group = "Strategy: Trade Entry Conditions", inline = "TE4", step = 0.1, tooltip = "ATR multiplier to determine how but the fanout gap must be between the Fast EMA and Slow EMA over the specified number of candles") fanoutLookback = input.int (3, "EMA Fanout Lookback      ", group = "Strategy: Trade Entry Conditions", inline = "TE5", minval = 1, tooltip = "Number of candles over which the entry EMA lines must be fanned out by the specified amount") priceBufferAtrMult = input.float (0.5, "Price Buffer ATR Multiplier    ", group = "Strategy: Trade Entry Conditions", inline = "TE6", step = 0.1, tooltip = "ATR multiplier to determine how big of a buffer should be set above/below trade entry. Used to calculate the stop order for trade entry") localHighLookback = input.int (5, "Entry Price High/Low Lookback ", group = "Strategy: Trade Entry Conditions", inline = "TE7", minval = 1, tooltip = "Number of candles to lookback on to find the local high/low which is used to establish the stop order entry price") slPips = input.int (300, "Stop Loss Size in Pips      ", group = "Strategy: Trade Entry Conditions", inline = "TE8", tooltip = "Stop Loss size defined as a number of pips") // --------------- // Risk Management // --------------- riskReward = input.float(2, "Risk : Reward        1 :", group = "Strategy: Risk Management", inline = "RM1", minval = 0, step = 0.1, tooltip = "Previous high or low (long/short dependant) is used to determine TP level. 'Risk : Reward' ratio is then used to calculate SL based of previous high/low level.\n\nIn short, the higher the R:R ratio, the smaller the SL since TP target is fixed by previous high/low price data.") accountRiskPercent = input.float(2, "Portfolio Risk %         ", group = "Strategy: Risk Management", inline = "RM2", minval = 0, step = 0.1, tooltip = "Percentage of portfolio you lose if trade hits SL.\n\nYou then stand to gain\n Portfolio Risk % * Risk : Reward\nif trade hits TP.") // ---------- // Date Range // ---------- startYear = input.int (2023, "Start Date       ", group = 'Strategy: Date Range', inline = 'DR1', minval = 1900, maxval = 2100) startMonth = input.int (10, "", group = 'Strategy: Date Range', inline = 'DR1', options = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) startDate = input.int (1, "", group = 'Strategy: Date Range', inline = 'DR1', options = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]) endYear = input.int (2100, "End Date      ", group = 'Strategy: Date Range', inline = 'DR2', minval = 1900, maxval = 2100) endMonth = input.int (1, "", group = 'Strategy: Date Range', inline = 'DR2', options = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) endDate = input.int (1, "", group = 'Strategy: Date Range', inline = 'DR2', options = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]) // ---------------- // Drawing Settings // ---------------- plotEmas = input.bool(false, "Plot EMAs", group = "Strategy: Drawings", inline = "D3") dashOn = input(true, 'Dashboard On / Off') dashDist = input(13, 'Dashboard Distance') dashColor = input.color(color.new(#696969, 80), 'Dashboard Color', inline='Dash Line') dashTextColor = input.color(color.new(#ffffff, 0), 'Text Color', inline='Dash Line') equity = input.float (INITIAL_CAPITAL, "Equity", group = 'Strategy: Equity', inline = 'ST1') var initialEquity = equity var currentEquity = equity // ============================================================================= // INDICATORS // ============================================================================= // -------------------------- // Trend Higher Timeframe EMA // -------------------------- var float currTrendFastEma = na var float currTrendSlowEma = na var color emaFillColor = na trendFastEma = ta.ema(close, trendFastEmaLen) trendSlowEma = ta.ema(close, trendSlowEmaLen) fastSec = request.security(syminfo.tickerid, trendFilterTf, trendFastEma, barmerge.gaps_off, barmerge.lookahead_off) slowSec = request.security(syminfo.tickerid, trendFilterTf, trendSlowEma, barmerge.gaps_off, barmerge.lookahead_off) currTrendFastEma := na(fastSec) ? currTrendFastEma[1] : fastSec currTrendSlowEma := na(slowSec) ? currTrendSlowEma[1] : slowSec fastEmaPlot = plot(fastSec, color=color.green, display = plotEmas ? display.all : display.none) slowEmaPlot = plot(slowSec, color=color.red, display = plotEmas ? display.all : display.none) emaFillColor := fastSec > slowSec ? color.new(color.green, 80) : fastSec < slowSec ? color.new(color.red, 80) : emaFillColor[1] fill(fastEmaPlot, slowEmaPlot, color = emaFillColor, fillgaps = true, display = plotEmas ? display.all : display.none) // --------------- // Trade Entry EMA // --------------- entryFastEma = ta.ema(close, entryFastEmaLen) entryMedEma = ta.ema(close, entryMedEmaLen) entrySlowEma = ta.ema(close, entrySlowEmaLen) plot(entryFastEma, color = color.orange, display = plotEmas ? display.all : display.none) plot(entryMedEma, color = color.yellow, display = plotEmas ? display.all : display.none) plot(entrySlowEma, color = color.purple, display = plotEmas ? display.all : display.none) // ============================================================================= // STRATEGY LOGIC // ============================================================================= // --------- // FUNCTIONS // --------- percentAsPoints(pcnt) => math.round(pcnt / 100 * close / syminfo.mintick) calcStopLossPrice(pointsOffset, entryPrice, isLong) => priceOffset = pointsOffset * syminfo.mintick if isLong entryPrice - priceOffset else entryPrice + priceOffset calcProfitTrgtPrice(pointsOffset, entryPrice, isLong) => calcStopLossPrice(-pointsOffset, entryPrice, isLong) pipsToPrice(pips) => priceChange = pips * syminfo.mintick priceChange // Function to pad single-digit numbers with a leading zero padNumber(num) => num < 10 ? "0" + str.tostring(num) : str.tostring(num) percentDifference(A, B) => math.abs((A - B) / ((A + B) / 2)) * 100 // ---------- // CONDITIONS // ---------- inDateRange = time >= timestamp(syminfo.timezone, startYear, startMonth, startDate, 0, 0) and time < timestamp(syminfo.timezone, endYear, endMonth, endDate, 0, 0) // Condition 1: Higher timeframe trend EMAs must be in correct order bullTrendDir = currTrendFastEma > currTrendSlowEma bearTrendDir = currTrendFastEma < currTrendSlowEma // Condition 2: Price must be outside the EMA trend band bullTrendPrice = close > currTrendFastEma bearTrendPrice = close < currTrendFastEma // Condition 3: EMA lines must be in correct order bullEmaOrder = entryFastEma > entryMedEma and entryMedEma > entrySlowEma bearEmaOrder = entryFastEma < entryMedEma and entryMedEma < entrySlowEma // Condition 4: EMA lines must fanned out for previous X candles fanOutGap = ta.atr(14) * fanoutAtrMult var bullFanOutGap = false var bearFanOutGap = false bullFanOutGap := false bearFanOutGap := false for lookback = 0 to fanoutLookback - 1 bullFanOutGap := (entryFastEma[lookback] - fanOutGap) > entrySlowEma[lookback] if bullFanOutGap == false break for lookback = 0 to fanoutLookback - 1 bearFanOutGap := (entryFastEma[lookback] + fanOutGap) < entrySlowEma[lookback] if bearFanOutGap == false break // Condition 5: Previous candle must not have touched any entry EMA lines bullBreakout = low[1] > entryFastEma[1] bearBreakout = high[1] < entryFastEma[1] // Condition 6: Current candle must have touched the Fast EMA bullPullback = low < entryFastEma bearPullback = high > entryFastEma // Combine all entry conditions goLong = inDateRange and bullTrendDir and bullTrendPrice and bullEmaOrder and bullFanOutGap and bullBreakout and bullPullback goShort = inDateRange and bearTrendDir and bearTrendPrice and bearEmaOrder and bearFanOutGap and bearBreakout and bearPullback // Trade entry and exit variables var tradeEntryBar = bar_index var profitPoints = 0. var lossPoints = 0. var slPrice = 0. var slAmount = 0. var slPercent = 0. var tpPrice = 0. var float tpPercent = 0. var inLong = false var inShort = false var float entryPrice= na var tradeCancelled = false var tradeActive = false // Entry decisions openLong = (goLong and not inLong) openShort = (goShort and not inShort) flippingSides = (goLong and inShort) or (goShort and inLong) enteringTrade = openLong or openShort inTrade = inLong or inShort // Exit calculations // Condition 7: Price which must be broken to enter trade priceBuffer = ta.atr(14) * priceBufferAtrMult localHigh = ta.highest(high, localHighLookback) localLow = ta.lowest(low, localHighLookback) // Risk calculations riskAmt = currentEquity * accountRiskPercent / 100 entryQty = math.abs(riskAmt / slPercent * 100) / close // Position var float positionSize = 0 var float longTriggerPrice = 0 var float shortTriggerPrice = 0 var bool longFoundEntry = false var bool shortFoundEntry = false var int longTradeCount = 0 var int shortTradeCount = 0 var int winningTradeCount = 0 var int losingTradeCount = 0 var float profitAmt = 0 var float lossAmt = 0 // Conditions are met to set stop order. Trade will not actually open until stop order price is met (calculated further down) if openLong // If we were already in a position of opposite side which did not hit TP/SL we need to close it out now if flippingSides and positionSize < 0 if entryPrice[1] < close[1] // loss y = high + pipsToPrice(10) label.new(x = bar_index, y = y, text = "Close at Loss", style=label.style_label_down, color = color.gray) losingTradeCount := losingTradeCount + 1 pd = percentDifference(entryPrice[1], close[1]) lossAmt := lossAmt + currentEquity * (pd / 100) currentEquity := currentEquity + (currentEquity * (pd / 100)) else y = high + pipsToPrice(10) label.new(x = bar_index, y = y, text = "Close in Profit", style=label.style_label_down, color = color.gray) winningTradeCount := winningTradeCount + 1 pd = percentDifference(entryPrice[1], close[1]) profitAmt := profitAmt + currentEquity * (pd / 100) currentEquity := currentEquity + (currentEquity * (pd / 100)) enteringTrade := true inLong := true inShort := false longTriggerPrice := entryPrice longFoundEntry := false shortFoundEntry := false tradeCancelled := false // Conditions are met to set stop order. Trade will not actually open until stop order price is met (calculated further down) if openShort // If we were already in a position of opposite side which did not hit TP/SL we need to close it out now if flippingSides and positionSize > 0 if entryPrice[1] > close[1] // loss y = low - pipsToPrice(10) label.new(x = bar_index, y = y, text = "Close at Loss", style=label.style_label_up, color = color.gray) losingTradeCount := losingTradeCount + 1 pd = percentDifference(entryPrice[1], close[1]) lossAmt := lossAmt + currentEquity * (pd / 100) currentEquity := currentEquity + (currentEquity * (pd / 100)) else y = low - pipsToPrice(10) label.new(x = bar_index, y = y, text = "Close in Profit", style=label.style_label_up, color = color.gray) winningTradeCount := winningTradeCount + 1 pd = percentDifference(entryPrice[1], close[1]) profitAmt := profitAmt + currentEquity * (pd / 100) currentEquity := currentEquity + (currentEquity * (pd / 100)) enteringTrade := true inShort := true inLong := false shortTriggerPrice := entryPrice longFoundEntry := false shortFoundEntry := false tradeCancelled := false slAmt = pipsToPrice(slPips) // Calculate entry and target prices if enteringTrade // Calculate entry price for stop order entry entryPrice := goLong ? localHigh + priceBuffer : goShort ? localLow - priceBuffer : na slPrice := openLong ? entryPrice - slAmt : openShort ? entryPrice + slAmt : na slAmount := slAmt slPercent := math.abs((1 - (entryPrice - slAmount) / entryPrice) * 100) tpPercent := slPercent * riskReward // Check if trade is active or not longActive = positionSize[1] > 0 shortActive = positionSize[1] < 0 activeTrade = longActive or shortActive // If price closes beyond the Slow EMA, then cancel any pending orders if not activeTrade and ((inLong and close < entrySlowEma) or (inShort and close > entrySlowEma)) // printVerticalLine(color.new(color.silver, 60)) inLong := false inShort := false tradeCancelled := true longFoundEntry := false shortFoundEntry := false longEntryAlert = false shortEntryAlert = false // Enter the trade via stop order if inLong and not longFoundEntry and high > entryPrice and not tradeCancelled y = low - pipsToPrice(10) label.new(x = bar_index, y = y, text = "BUY", style=label.style_label_up, color = color.green) alert("BUY", alert.freq_once_per_bar) longTradeCount := longTradeCount + 1 positionSize := entryQty longFoundEntry := true shortFoundEntry := false longEntryAlert := true else if inShort and not shortFoundEntry and low < entryPrice and not tradeCancelled y = high + pipsToPrice(10) label.new(x = bar_index, y = y, text = "SELL", style=label.style_label_down, color = color.red) alert("SELL", alert.freq_once_per_bar) shortTradeCount := shortTradeCount + 1 positionSize := -entryQty shortFoundEntry := true longFoundEntry := false shortEntryAlert := true if enteringTrade profitPoints := percentAsPoints(tpPercent) lossPoints := percentAsPoints(slPercent) tpPrice := calcProfitTrgtPrice(profitPoints, entryPrice, openLong) tradeEntryBar := bar_index // Exit trade via take profit or stop loss longTpHit = (longFoundEntry and high >= tpPrice) shortTpHit = (shortFoundEntry and low <= tpPrice) longSlHit = (longFoundEntry and low <= slPrice) shortSlHit = (shortFoundEntry and high >= slPrice) if longTpHit or shortTpHit y = longTpHit ? high + pipsToPrice(10) : low - pipsToPrice(10) label.new(x = bar_index, y = y, text = "TP", style = longTpHit ? label.style_label_down : label.style_label_up, color = longTpHit ? color.green : color.red) inLong := false inShort:= false longFoundEntry := false shortFoundEntry := false positionSize := 0 winningTradeCount := winningTradeCount + 1 profitAmt := profitAmt + (riskAmt * riskReward) currentEquity := currentEquity + (riskAmt * riskReward) if longTpHit alert("Long TP", alert.freq_once_per_bar) if shortTpHit alert("Short TP", alert.freq_once_per_bar) else if longSlHit or shortSlHit y = longSlHit ? low - pipsToPrice(10) : high + pipsToPrice(10) label.new(x = bar_index, y = longSlHit ? low : high, text = "SL", style = longSlHit ? label.style_label_up : label.style_label_down, color = longSlHit ? color.green : color.red) inLong := false inShort:= false longFoundEntry := false shortFoundEntry := false positionSize := 0 losingTradeCount := losingTradeCount + 1 lossAmt := lossAmt + riskAmt currentEquity := currentEquity - riskAmt if longSlHit alert("Long SL", alert.freq_once_per_bar) if shortSlHit alert("Short SL", alert.freq_once_per_bar) // ALERTS alertcondition(longEntryAlert, title = "BUY" ,message = 'BUY Signall') alertcondition(shortEntryAlert, title = "SELL" , message = 'SELL Signall') alertcondition(longSlHit, title = "Long SL" , message = 'Long SL') alertcondition(longTpHit, title = "Long TP" , message = 'LongTP') alertcondition(shortSlHit, title = "short SL" , message = 'short SL') alertcondition(shortTpHit, title = "short TP" , message = 'short TP') // Code for performance dashboard totalTradeCount = longTradeCount + shortTradeCount winRate = (winningTradeCount / (losingTradeCount + winningTradeCount)) * 100 profitFactor = profitAmt / lossAmt var string startDateStr = na if dashOn if (bar_index == 0) startDateStr := str.tostring(startYear) + "-" + padNumber(startMonth) + "-" + padNumber(startDate) label lemonLabel = label.new(time, close, text=' Hozan_taher_j (JOKER)/ᴶᴼᴷᴱᴿ ˁᶜᴬᴸᴾᴵᴺᴳ ' + '\n━━━━━━━━━━━━━━━━━' + '\n   🤹 Strategy Performance 🤹' + '\n━━━━━━━━━━━━━━━━━' + '\n🎈   Total Trades | ' + str.tostring(totalTradeCount) + '\n🎈 Win/Loss | ' + str.tostring(winningTradeCount) + " / " + str.tostring(losingTradeCount) + '\n🎈 Win Rate  | ' + str.tostring(winRate, '##.##') + '%' + '\n🎈 Profit Factor | ' + str.tostring(profitFactor, "##.##") + '\n🎈 PNL | ' + str.tostring(currentEquity - equity, "##.##") + '\n🎈 Closing Balance | ' + str.tostring(currentEquity, "##.##") + '\n🎈 Start Date | ' + startDateStr + '\n━━━━━━━━━━━━━━━━━' + '\n Instagram: 𝗴𝗮𝗺𝗲𝗺𝗮𝗸𝗲𝗿𝘀.𝗼𝗳𝗳𝗶𝗰𝗶𝗮𝗹 ', color=dashColor, xloc=xloc.bar_time, style=label.style_label_left, textcolor=dashTextColor, textalign=text.align_left) label.set_x(lemonLabel, label.get_x(lemonLabel) + math.round(ta.change(time) * dashDist)) label.delete(lemonLabel[1])