// This Pine Script® code is licensed under Apache-2.0
// SPDX-License-Identifier: Apache-2.0
// © PineForge contributors 2026
//
// PF probe kkb-probe-02-breakout-trigger — EMA mid + ATR breakout band
// Clean-room: written from validation goal, not from source.
//
// Purpose: isolates a smoothed-band breakout. The engine surfaces under
// test are `ta.ema(close, len)` for the central band, `ta.atr(len)` for
// volatility, and the upper/lower band arithmetic. Long entries fire on
// `ta.crossover(close, upper)`, short on `ta.crossunder(close, lower)`.
// Verifies that engine vs TV agree on the EMA/ATR numerics at the
// breakout boundary.
//
// Validation goal: bit-exact entries on close-vs-band breakouts. Pass =
// identical trade list vs TradingView's broker emulator.
//
// TV setup: 15m chart, ETH-USDT-USDT (Binance USDT-M Perpetual). Full window
// matching corpus/data/ohlcv_ETH-USDT-USDT_15m.csv. Strategy properties:
// leave defaults from script header. Export "List of trades" → tv_trades.csv.
//@version=6
strategy("PF probe kkb-probe-02-breakout-trigger", shorttitle="PF_kkb02_BRK",
overlay=true, initial_capital=1000000,
commission_type=strategy.commission.percent, commission_value=0,
slippage=0, default_qty_type=strategy.fixed, default_qty_value=1,
pyramiding=1, process_orders_on_close=false)
// ---- inputs --------------------------------------------------------
int i_band_len = input.int(20, "Band EMA length", minval=2)
int i_atr_len = input.int(14, "ATR length", minval=2)
float i_atr_mult = input.float(1.5, "ATR multiplier", minval=0.1, step=0.1)
// ---- band ---------------------------------------------------------
float band = ta.ema(close, i_band_len)
float vol = ta.atr(i_atr_len)
float upper = band + vol * i_atr_mult
float lower = band - vol * i_atr_mult
// ---- crosses ------------------------------------------------------
bool cross_up = ta.crossover(close, upper)
bool cross_down = ta.crossunder(close, lower)
// ---- order routing -------------------------------------------------
if cross_up and strategy.position_size <= 0
strategy.entry("L", strategy.long, qty=1, comment="band breakout long")
if cross_down and strategy.position_size >= 0
strategy.entry("S", strategy.short, qty=1, comment="band breakdown short")