TickRun convention: a signal calculated on session t changes end-of-session position state on row t; the new position first earns the close-to-close asset return recorded on row t+1.
Build an information clock
A daily bar contains Open, High, Low, Close, and Volume, but those fields do not become known simultaneously. Open is known near the start of the session. The final high, low, close, and full-session volume exist only when the session finishes. An indicator using today’s close cannot legitimately make a decision earlier that same day.
Write three timestamps for each rule: when source information becomes final, when the order can be submitted, and which price defines the simulated fill. “Buy when RSI crosses 30” is incomplete until these timestamps are stated. A vectorized calculation can align arrays perfectly while aligning economics incorrectly.
Understand row-labelled returns
TickRun calculates the asset return on row t as Close[t] / Close[t−1] − 1. That value represents the price movement from the previous close into the current close. It is already over by the time every field on row t is known.
If a closing indicator on row t creates a buy and the backtest multiplies that same row’s return by the new long position, the strategy earns a movement that helped create its own signal. This is classic look-ahead through array alignment. The code may contain no explicit reference to “tomorrow,” yet it uses future information relative to the simulated holding interval.
TickRun’s bar-by-bar state transition
The engine separates the end-of-row position from the return-bearing position. In conceptual pseudocode:
strategy_return[t] = position[t−1] × asset_return[t] − trading_cost[t]
position[t] = state_after_processing_signal[t]
Consider closes of 100, 105, and 103. A buy generated from the 105 close is too late to earn the 5% move from 100 to 105. The new long state earns the next return, from 105 to 103, which is approximately −1.90%. A pleasing but biased implementation might incorrectly book +5%.
On an exit signal at close t, the old long position still earns the return into that close because it was held during the interval. The sell changes state for the following interval. TickRun subtracts the position-change cost on the event row. The marker is a decision/state marker, not a claim that the strategy captured the return before it.
What signal delay actually does
The signal-delay control shifts raw buy and sell events forward by a number of trading observations before the state machine processes them. A delay of one does not mean one calendar day; a Friday event generally moves to the next available market row, often Monday.
Delay is useful as a robustness stress. If a result disappears after one observation, it may depend on an unrealistically precise boundary. But delay is not a complete next-open execution model. TickRun still earns close-to-close returns according to its position convention and charges a proportional cost; it does not fill against unadjusted next-session Open, simulate an auction, or model overnight gaps separately.
Minimum holding period and event priority
A signal stream must become a valid position sequence. TickRun is long-only, flat or fully invested. A buy while flat opens the position. Further buys while long are ignored. A sell while flat is ignored. The minimum-holding control suppresses an otherwise valid exit until the required number of observations has elapsed.
This means the displayed raw indicator concept and actual trades need not have one-to-one correspondence. Delayed events can collide, repeated crosses can occur while already positioned, and early exit events can be rejected. Auditing only chart markers without checking position state can therefore misdiagnose a trade.
The final open position contributes daily portfolio returns and drawdown but is not counted as a completed round trip. Closing it artificially at the last price would add an exit the strategy never requested and introduce endpoint knowledge.
Can a same-close fill ever be defensible?
An order may participate in a closing auction, but a rule based on the official closing price cannot know that final price before the auction completes. A practical model might use information available before a cutoff and model an auction fill, or calculate from today’s final close and trade at the next available price. It cannot simultaneously depend on the finalized close and assume guaranteed execution at that exact close without explaining the mechanism.
Likewise, a daily low touching a limit price does not prove a fill. The within-bar path, queue position, displayed size, and whether the signal existed before the touch are unknown. OHLC data report extrema, not event ordering.
Rolling windows and hidden future data
A trailing 20-session average at t may use observations t−19 through t. A centered 20-session average uses later data and is invalid for trading even though it may produce a smoother plot. Full-sample normalization, backward filling early missing indicators, and using a final sample mean to standardize every historical row are other leakage channels.
Warm-up values should remain unavailable until the required trailing history exists. Filling them with later valid values gives early decisions information from the future. When comparing strategies with different window lengths, align their scored intervals after a common warm-up if the objective is a fair ranking.
Corporate actions and timestamp semantics
Adjusted prices are typically restated using corporate actions known later. They are valuable for creating a continuous economic return series, but an adjusted historical number is not necessarily the literal price visible on that historical screen. This is normally acceptable for split/dividend-consistent technical research, provided every OHLC field and benchmark uses the same convention.
Fundamentals, constituents, analyst data, and macroeconomic series pose harder timestamp problems because values can be released after the period they describe and later revised. An observation labelled “Q1” is not available on March 31 merely because that is the period end. TickRun currently uses only saved daily OHLCV, avoiding those release-lag fields but not solving broader point-in-time research.
A timing audit that catches most errors
- Select several entry and exit events, including the first and last.
- List every input observation used to create each event.
- Identify the latest timestamp among those inputs.
- Verify the simulated position earns only returns beginning after that time.
- Recalculate row returns and state transitions manually.
- Check the cost appears exactly when position changes.
- Test weekend/holiday gaps and delayed events near the dataset end.
- Confirm warm-up missing values were not backfilled.
- Inspect an open final position separately from completed trades.