Compare stock lookbacks after a common warm-up period
Changing a lookback can change both the signal and the date on which a backtest begins. If a short-window candidate earns or loses money before a long-window candidate becomes eligible, their final balances do not isolate the parameter effect. Give every candidate enough historical input, then rerun them from the same executable start with the same initial account state.
The output is a first-valid-date table, one common scoring schedule and aligned reruns. The standard-library example uses ten invented prices, one-share target positions and zero costs. It is a timing demonstration, not historical stock performance or verification of a CoTrading feature. Keep each research run within one market and currency.
Separate history, valid signals and executable fills
An indicator can require historical observations before it produces a meaningful value. A valid signal still needs an executable price after its information becomes available. The backtesting.py API documentation warns that indicator warm-up affects the start and describes its default next-bar market execution. We use that documentation as a reference behavior, without asserting that another engine follows it.
Write three dates per candidate: earliest complete input window, first permissible signal and first permissible fill. State whether the current observation enters the indicator and when it becomes known. A strategy using today’s close cannot automatically treat that close as an available fill. Inspect the actual engine’s scheduling rather than adding a guessed extra day everywhere.
Distinguish indicator validity from a positive signal. A fully computed indicator that says remain in cash is valid. Waiting until each candidate’s first buy would select different market conditions and create another comparability problem. The common start should depend on information eligibility and execution rules, not on when a favorable event happens.
Obtain enough input without scoring the warm-up
Request the agreed scored period plus the longest required lookback history and any additional lag. Preserve the source version and market calendar. A five-observation window needs five actual eligible observations, not five calendar days when holidays or missing quotes intervene. Do not forward-fill missing securities into fictitious tradable observations merely to satisfy a count.
Provide the same earlier history to all candidates, even if some need less. Compute indicators on that input, but exclude warm-up returns and trades from the scored account. If the chosen initialization policy is flat, warm-up signals must not create inherited holdings. If the policy instead carries holdings, define the common predecessor account explicitly and explain why it serves the comparison.
Nested indicators may need more than the largest visible parameter. For example, a moving average of an already warmed indicator can extend the effective requirement. Record first actual valid outputs, not just a formula guessed from the parameter label. An engine may also delay its first callback, so verify the produced signal and fill events.
Work through two lookbacks
Our invented price sequence is [10,20,30,40,30,25,20,15,18,22]. The signal at index t is one if the current price exceeds the mean of the last n prices including itself; otherwise it is zero. Target holdings are either zero or one share. A signal fills at index t+1, and the same synthetic price serves as that session’s execution and marking price.
| Lookback | First complete signal index | Earliest fill index | Common fill start |
|---|---|---|---|
| 3 | 2 | 3 | 5 |
| 5 | 4 | 5 | 5 |
Indices start at zero. The longest signal window is complete at index four, so the earliest common executable start is five. Both accounts start there with cash 1000 and no shares. Scored observations are indices five through nine, giving five observations for both candidates. They can use earlier prices for indicators without inheriting earlier gains or losses.
Rebuild the account rather than crop the old curve
For every scored fill index, evaluate the preceding signal, compute target shares, trade the difference from current shares and update cash. Then mark the resulting holdings at that index’s price. This explicitly preserves the initial-state policy. Simply clipping a previously generated equity curve at index five would retain whatever position and cash the earlier run had accumulated.
for fill_t in range(common_start, len(prices)):
signal_t = fill_t - 1
assert signal_t >= lookback - 1
signal = int(prices[signal_t] >
sum(prices[signal_t-lookback+1:signal_t+1]) / lookback)
quantity = signal - shares
cash -= quantity * prices[fill_t]
shares = signal
The complete sample records each scored index, target and marked equity. Real execution would additionally need costs, lot sizes, rejected orders and an explicit cash constraint. Those restrictions must be shared across candidates; this simple calculation does not demonstrate that a real market permits the displayed trades.
Read the aligned and unaligned results
The aligned ending values are 1000 for lookback three and 995 for lookback five. The long-window candidate buys at index five for 25 and later sells at index six for 20, losing five. The short-window account does not inherit the earlier trade and finishes at its opening equity under the example’s final mark.
When lookback three instead begins at its own first fill, index three, it buys at 40 and later exits at 25. Its ending value becomes 985. That fifteen-unit loss occurred because the experiment allowed earlier participation, not because the formula changed. Comparing 985 with 995 would rank candidates over different opportunities; comparing 1000 with 995 answers the declared common-start question.
An open terminal holding is marked consistently, not silently discarded. The short-window candidate can hold a share at the last mark without generating subsequent profit in this dataset. Keep ending cash and holdings in a real report so identical equity does not hide different remaining exposures. Decide beforehand whether terminal liquidation and its costs belong to the scoring policy.
Reject insufficient history and inherited state
The counterexample asks the five-observation candidate to fill at index three. Its preceding signal index is two, so a complete five-price window does not exist. The assertion rejects that run. Letting a short Python slice return fewer values and dividing by five could otherwise create a plausible-looking but invalid mean.
Another mechanism is carrying the short candidate’s earlier shares into the common start. That may be a valid separate experiment, but it violates our flat-account comparison. Compare initial cash, share count and pending orders, not only start-date labels. Two curves beginning on the same date can still have different starting risks or capital.
Treat missing quotes as a separate state from warm-up. If a candidate becomes invalid again after the nominal start because an input is missing, record that event and apply the frozen missing-data policy. Moving the whole scoring interval after every unfavorable gap would introduce outcome-dependent selection rather than solve input quality.
Validate the schedule and archive the rerun
Download the complete example. With Python 3.12 or later, run python R608.py; it uses only the standard library and synthetic prices. Actual assertions verify first signal indices two and four, first fill indices three and five, common start five, five shared scored observations, ending values [1000,995] and the short candidate’s own-start value 985.
For a production handoff, export the input coverage, actual validity dates, shared scored dates, initial account state and each run reference. Confirm that all candidates have the same scored session keys and that warm-up events contribute no account profit. A common nominal date is insufficient if one stock was suspended or a source supplied a different calendar.
Recalculate the common start when the candidate set, indicator dependencies, execution lag or available history changes. Preserve earlier comparisons instead of mixing old and newly aligned scores in one ranking. Warm-up alignment removes one source of unfair comparison; it does not establish statistical independence, eliminate look-ahead elsewhere or prove that a chosen parameter will work out of sample.
FAQ
Should the common start be the first buy date?
No. Eligibility and execution determine the start. A valid no-buy signal is still a valid observation.
Can I align by cropping existing equity curves?
Only if the account-state policy genuinely matches. This guide resets cash and holdings and reruns, so cropping does not implement it.