// This Pine Script® code is licensed under Apache-2.0
// SPDX-License-Identifier: Apache-2.0
// © PineForge contributors 2026
//
// PF probe kanuck-probe-02-calc-on-every-tick — calc_on_every_tick semantics
// Clean-room: written from validation goal, not from source.
//
// Purpose: isolates `calc_on_every_tick=true` evaluation. The engine surface
// under test is the per-tick re-evaluation pass: a `var int` counter
// incremented every script run, plus a modulo-based entry trigger. On
// historical (replay) data both engine and TV evaluate once per bar; under
// `calc_on_every_tick` semantics the engine should still produce a trade
// list that matches TV's broker-emulator output for historical bars.
//
// Validation goal: bit-exact entries when `tick_counter % step == 0` after
// a momentum trigger fires. Note: engine vs TV may legitimately diverge on
// intra-bar order if `calc_on_every_tick` semantics differ on real-time
// bars; the historical replay used by the corpus should still match.
//
// 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 kanuck-probe-02-calc-on-every-tick", shorttitle="PF_kan02_TICK",
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,
calc_on_every_tick=true)
// ---- inputs --------------------------------------------------------
int i_step = input.int(3, "Tick-counter modulo step", minval=1)
int i_fast = input.int(8, "EMA fast length", minval=2)
int i_slow = input.int(21, "EMA slow length", minval=3)
// ---- per-evaluation tick counter ----------------------------------
var int tick_counter = 0
tick_counter += 1
// ---- momentum trigger ---------------------------------------------
float fast = ta.ema(close, i_fast)
float slow = ta.ema(close, i_slow)
bool cross_up = ta.crossover(fast, slow)
bool cross_down = ta.crossunder(fast, slow)
// ---- modulo gate on the counter -----------------------------------
bool gate = (tick_counter % i_step) == 0
// ---- order routing -------------------------------------------------
if cross_up and gate and strategy.position_size <= 0
strategy.entry("L", strategy.long, qty=1, comment="tick gated long")
if cross_down and gate and strategy.position_size >= 0
strategy.entry("S", strategy.short, qty=1, comment="tick gated short")