//@version=5 strategy("BTC Trend Breakout — Risk Managed", overlay = true, initial_capital = 10000, currency = currency.USD, pyramiding = 0, commission_type = strategy.commission.percent, commission_value = 0.10, slippage = 10, margin_long = 100, calc_on_every_tick = false, calc_on_order_fills = false, process_orders_on_close = false) // Designed for spot BTC pairs or linear instruments. // Position sizing assumes account and quote currencies are equivalent. // ── Inputs ─────────────────────────────────────────────────────────── trendTF = input.timeframe("D", "Trend timeframe") trendLength = input.int(200, "Trend EMA length", minval = 2) entryLength = input.int(20, "Breakout lookback", minval = 2) exitLength = input.int(10, "Channel exit lookback", minval = 2) atrLength = input.int(14, "ATR length", minval = 1) stopATR = input.float(2.5, "Stop distance: ATR multiple", minval = 0.1, step = 0.1) rewardRisk = input.float(3.0, "Profit target: risk multiple", minval = 0.1, step = 0.1) riskPercent = input.float(0.5, "Equity risk per trade (%)", minval = 0.01, maxval = 5, step = 0.05) maxExposure = input.float(95, "Maximum equity invested (%)", minval = 1, maxval = 100) quantityStep = input.float(0.00001, "BTC quantity increment", minval = 0.00000001) startDate = input.time(timestamp("01 Jan 2020 00:00 +0000"), "Start trading") endDate = input.time(timestamp("01 Jan 2035 00:00 +0000"), "Stop trading") if barstate.isfirst and timeframe.in_seconds(trendTF) <= timeframe.in_seconds() runtime.error("Trend timeframe must be higher than chart timeframe.") // ── Confirmed higher-timeframe trend ───────────────────────────────── // Every requested value is offset, so no unfinished daily candle is used. [trendClose, trendEMA, previousTrendEMA] = request.security( syminfo.tickerid, trendTF, [close[1], ta.ema(close, trendLength)[1], ta.ema(close, trendLength)[2]], gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on) bullTrend = trendClose > trendEMA and trendEMA > previousTrendEMA // ── Signals ───────────────────────────────────────────────────────── // Channels exclude the current candle. entryChannel = ta.highest(high, entryLength)[1] exitChannel = ta.lowest(low, exitLength)[1] atrValue = ta.atr(atrLength) breakout = ta.crossover(close, entryChannel) inWindow = time >= startDate and time_close < endDate ready = not na(trendEMA) and not na(atrValue) and not na(entryChannel) and not na(exitChannel) // ── Position sizing ───────────────────────────────────────────────── // Round stop distance upward to a valid number of price ticks. stopTicks = math.max(1.0, math.ceil(atrValue * stopATR / syminfo.mintick)) stopDistance = stopTicks * syminfo.mintick targetTicks = math.max(1.0, math.round(stopTicks * rewardRisk)) equityAvailable = math.max(strategy.equity, 0) riskCash = equityAvailable * riskPercent / 100.0 riskPerUnit = stopDistance * syminfo.pointvalue riskQuantity = riskPerUnit > 0 ? riskCash / riskPerUnit : 0.0 capQuantity = equityAvailable * maxExposure / 100.0 / (close * syminfo.pointvalue) rawQuantity = math.min(riskQuantity, capQuantity) orderQuantity = math.floor(rawQuantity / quantityStep) * quantityStep // ── Orders ─────────────────────────────────────────────────────────── // Relative tick exits attach to the entry's actual simulated fill. // Distances are fixed when the entry order is submitted. if barstate.isconfirmed if strategy.position_size > 0 if time_close >= endDate strategy.close("Long", comment = "End date") else if close < exitChannel or not bullTrend strategy.close("Long", comment = "Trend/channel exit") if strategy.position_size == 0 and inWindow and ready and bullTrend and breakout and orderQuantity > 0 strategy.entry("Long", strategy.long, qty = orderQuantity) strategy.exit("Bracket", from_entry = "Long", loss = stopTicks, profit = targetTicks) // ── Visuals ────────────────────────────────────────────────────────── plot(trendEMA, "Confirmed trend EMA", color = color.orange, linewidth = 2) plot(entryChannel, "Breakout channel", color = color.new(color.green, 35)) plot(exitChannel, "Exit channel", color = color.new(color.red, 35)) bgcolor(bullTrend ? color.new(color.green, 94) : na)