Swiss Equity Backtesting: A Point-in-Time Audit Before You Trust the Curve

The short answer

Do not approve a Swiss-equity strategy because its historical return curve is attractive. Approve the research process only after you can show that every input existed when the simulated decision was made, the security universe reflects what could actually have been traded then, the fill model respects SIX trading phases and plausible costs, and the final strategy was not selected by repeatedly looking at the same test period. If any one of those proofs is missing, label the result exploratory and do not present it as evidence of expected live performance.

This is a technical due-diligence guide for people building, reviewing, or buying quantitative research—not an investment recommendation. Its reusable asset is a point-in-time review matrix and a small, executable Python gate for a trade ledger. The code can catch timestamp, membership, price, spread, and cost inconsistencies in a CSV. It cannot prove that the strategy is profitable, that a data vendor is complete, or that the historical universe has no missing delisted securities.

The distinction matters. A backtest is a counterfactual simulation, not a time machine. The SEC’s US investment-adviser marketing rule treats hypothetical performance as a category that needs sufficient assumptions, criteria, risks, and limitations for its intended audience; the rule is US-specific and is not Swiss legal advice. In a 2022 enforcement matter, the SEC described a backtest/live mismatch involving different securities and insufficiently correlated substitutes. That case does not establish a universal error rate. It is a concrete reminder to test whether the simulated instruments and the instruments actually available to the operator match. (SEC final rule, 2020; SEC administrative proceeding, 2022)

What the result already visible on the chart leaves unanswered

A cumulative-equity chart compresses a long chain of choices into one line: which securities entered the universe, when a signal became knowable, what price could be executed, how fees and spread were charged, how corporate actions were handled, how many versions were tried, and which period was reserved for final evaluation. A chart can be calculated correctly from a flawed chain. It can also be a helpful diagnostic when the chain is transparent. The question is not “Are backtests useless?” It is “What claims can this specific backtest support, given its data and procedure?”

The CFA Institute’s backtesting and simulation reading identifies look-ahead and survivorship bias as issues and discusses rolling-window methods, historical simulation, fat tails, and structural breaks. Its point is not that one validation recipe removes uncertainty; it is that the design of the test controls what the result means. A rolling or walk-forward design is closer to the sequence of real decisions than repeatedly optimizing on the whole period, but it still inherits errors in timestamps, universe construction, execution assumptions, and data quality.

One real user question makes the gap tangible. In an October 2026 r/algotrading discussion, a poster compared a rotation strategy on today’s Dow constituents with one using historical membership and asked whether researchers use point-in-time constituents or accept survivorship bias. Other comments raised the more specific issue that a dataset may drop a security’s price history after delisting. This is useful evidence of a practical question, not evidence that a particular backtest result is typical: it is one public thread, self-reported, with no independently audited data. (Discussion and question)

The same question applies to Swiss equities, but the answer cannot be copied from a US index example. A SIX-listed security may be a blue chip, a mid- or small-cap, a secondary listing, a right, or a separate trading line. The operational universe and liquidity assumptions need to match the instrument and period under study. A SIX liquidity and order-book guide explains why a “listed on SIX” label alone does not tell a reviewer whether an assumed order size, spread, market phase, or venue is plausible. It is a practical guide, not a source for historical tick data or a claim about any specific instrument today.

A four-proof rule for deciding whether a backtest is ready to review

Treat these as independent gates. A strong answer in one column never compensates for a missing answer in another. For example, a careful walk-forward split does not repair an index universe made from constituents that survived until today. A point-in-time constituent file does not fix a signal calculated from a revised financial statement that was unavailable at the decision timestamp. A plausible spread model does not remove parameter selection after the “holdout” results were inspected.

Proof Review question Minimum evidence A fail-closed answer
Information timing Could this exact feature or event have been observed before the decision? Original publication/availability timestamp, timezone, revision policy, and a lag rule “We use the fiscal-period date” is not enough when publication was later
Tradable universe Was this security in the eligible set on that date, with identity and delisting history intact? Effective-dated membership, stable security identifier, listing/delisting and corporate-action records Today’s members reused across the past = exploratory only
Execution Could the stated fill and size plausibly have occurred in that phase and market? Order type, next eligible event/bar, spread, fees, slippage, size/depth and sensitivity Same-bar close fills with no execution evidence = reject headline performance
Research selection Was the final rule chosen without repeatedly using the final test period? Trial log, frozen design choices, untouched evaluation window, walk-forward or other justified procedure Unknown number of trials or reused holdout = selection risk remains

The decision rule is intentionally conservative: “unknown” is not “pass.” Unknown means the result is still a research artifact. That does not mean the idea is false; it means the evidence is not yet fit to carry a claim about live behavior.

Proof one: reconstruct when information became usable

For each input, distinguish at least four times: the economic period it describes, the time an issuer or source published it, the time your data vendor received or revised it, and the first time your simulated process could have used it. These are not interchangeable. A company’s annual result may describe a year ending in December and be published months later. If a backtest joins that value to December rows without a publication lag, it gives the historical strategy information earlier than a real operator had.

Store timestamps with explicit timezone offsets. Define a decision time rather than relying on a date label. If a daily strategy makes a decision using the official close, state whether the order is assumed to be submitted after that close and filled at the next eligible market event. If a feature depends on a filing, use the filing’s public availability time—not the fiscal year end. If the vendor backfills a corrected value, preserve the vintage that was available at the time or disclose the lag and its rationale. If historical vintages do not exist, mark that feature as non-point-in-time and exclude it from a strict historical claim.

An effective rule can be expressed as:

feature_is_eligible = source_available_at <= strategy_decision_at fill_is_eligible = fill_event_at > strategy_decision_at 

These comparisons are necessary, not sufficient. A timestamp can be syntactically valid but semantically wrong because it is an ingestion date rather than a public release date, because a local time was parsed as UTC, or because the exchange calendar was ignored. Keep the raw vendor timestamp, normalized UTC timestamp, exchange-local display time, and transformation rule in the audit trail. Never silently replace a missing timestamp with midnight or the period-end date.

The CFA Institute discussion of backtesting describes look-ahead bias as using information that would not have been available at the time. The operational implication is to make availability part of the data model, not a footnote in the report. Add it to feature rows, join keys, and test assertions so that a future data refresh cannot quietly alter the past.

A practical timestamp audit

Pick a handful of signals from the most profitable and least profitable parts of the curve. For each, trace the complete lineage backwards: output trade, signal row, feature values, upstream record, source publication, vendor capture, revisions, and timezone conversion. Then run the same pipeline with all inputs cut off at a historical timestamp. The earlier output should not change when information published after that timestamp is removed. If it does change, the pipeline has a leak or a hidden dependency. Record the exact cutoff and data snapshot hash so someone else can repeat the check.

Do not confuse a “lagged feature” with proof of availability. Shifting a series by one bar is useful for detecting same-bar mistakes, but it does not answer whether the source was available at that bar. For monthly or irregular data, the correct lag may depend on publication schedules. A rule such as “always lag one day” can still leak when the actual release arrives later, and can be unnecessarily conservative when a timestamped release was genuinely available sooner. The documented availability event should decide.

Proof two: build the universe that existed then

Survivorship bias is not just forgetting companies that failed. It can enter through the list of current constituents, the list of securities that still have complete price data, ticker reuse, renamed issuers, mergers, secondary lines, rights, suspended instruments, and vendor symbol mappings. If the database contains only securities that remain active today, a historical strategy may never see the failures, acquisitions, delistings, or exits it would have encountered. If the security identifier changes but the code joins on ticker alone, the test may splice two different instruments into one apparent history.

For a changing index or screening universe, define membership as an interval with an effective start and end. Preserve both the announcement date and effective date where available. A future announced index change must not enter the eligible universe before the decision system could reasonably know it; the actual effective date matters for tradability. For a broad screen that is not an index, define the screen inputs and their timestamps too. If the research uses a fixed universe, state why that universe was knowable at the beginning of the test and what it excludes. Swiss event data also needs its own scope: a SIX Exchange Regulation communiqué describes specific reporting processes for certain own-share buyback and price-stabilisation transactions. It does not give a backtest dataset or execution history; it is a reminder to identify which market-event record a vendor has captured before interpreting a price series.

Delisted-security treatment needs an explicit event policy. A missing quote after delisting is not a neutral return. Depending on the instrument and event, the position may receive cash, a successor security, a final tradable price, a cancelled instrument, or another corporate-action outcome. The data vendor may provide a delisting return, may stop the series, or may fold an event into adjusted prices. Do not infer which behavior occurred from a smooth chart. Read the vendor’s data dictionary, compare selected event records with primary exchange or issuer notices, and document any unresolved gap.

Ticker is a label; it is not a durable identity key. Maintain a mapping table with stable instrument ID, issuer ID where appropriate, listing venue, currency, share class, ticker validity interval, and corporate-action links. On a ticker change, use the mapping valid for the historical date. For a merger or separate trading line, decide whether the strategy could have held both securities and how positions are translated. If your data source cannot support that distinction, narrow the claim instead of assuming the mapping is harmless.

The community thread above illustrates a real dispute about current versus historical membership; the CFA Institute’s model-validation monograph explains that testing only surviving companies can omit failed firms from the evaluation. Neither source provides a Swiss-security membership database. For that, obtain a point-in-time file from a data provider or the exchange, and test its completeness against records for additions, removals, suspensions, delistings, and corporate actions.

Proof three: model Swiss execution, not just prices

Daily OHLC data can be enough for a broad research question, but it often cannot establish a precise fill. A candle’s high and low do not tell you the order-book depth, queue position, auction imbalance, bid-ask spread at the decision, or whether an order could have participated in the stated phase. Closing auction, continuous trading, and post-close trading are different mechanisms. A close printed during an auction is not automatically an executable price for an order submitted after the auction has ended.

SIX describes its order-book trading models, including the Central Limit Order Book, and explains that market models determine how orders are matched and how trading periods are structured. Its trading-hours materials also distinguish opening, continuous, closing-auction, and Trading-at-Last phases for applicable segments. These official sources support a market-mechanics check; they do not prove that a particular historical order would have filled. (SIX on-order-book trading; SIX trading hours)

For a daily close-based signal, a defensible baseline is to compute the signal using information available at the close and then model an order at the next eligible event. For a bar-only simulation, that might mean next-session open plus a disclosed slippage rule. But “next open” is still an approximation: opening auctions, limit constraints, market gaps, order size, and the data vendor’s adjusted/unadjusted price convention all matter. Treat it as a scenario, not as a verified fill.

At minimum, report a cost stack separately: commission and exchange fees; bid-ask spread; slippage or market impact; financing or borrow where applicable; currency conversion where the strategy crosses currencies; and cash or corporate-action effects. State whether the spread is charged one-way or round-trip. If you use basis points, define the denominator and side. Use multiple plausible assumptions and show whether the qualitative conclusion survives. Do not select a single cost rate because it preserves a desired result.

The liquidity guide recommends checking the actual bid, ask, visible size, recent volume, trading phase, and news context. That is a useful operational checklist. SIX itself publishes market-data information and describes its order books; actual historic order book data may require access beyond public pages. (SIX market data) If you only have bars, write “bar-based proxy” rather than “realistic execution.”

Use sensitivity as a diagnostic, not as a rescue

Re-run the same frozen strategy under a small, predeclared grid of execution assumptions: baseline spread/slippage, a wider-cost scenario, and a stressed liquidity case. The exact values should come from the instrument, order size, venue, period, and available data—not from a universal percentage. If the result flips sign under a modest plausible change, that is a material finding. It does not prove the strategy is bad; it tells the reviewer that the historical edge is execution-sensitive and the current data is insufficient to make a stronger claim.

Avoid a common mistake: calibrating transaction costs on the same period and then presenting the most favorable estimate as a neutral assumption. Document where each cost came from, whether it is observed or modeled, how it changes by turnover and liquidity, and whether cost assumptions were chosen before the final test. If you cannot observe historical spreads, say that. A transparent imperfect model is more useful than a precise-looking number with no provenance.

Proof four: count the experiments that selected the winner

If you tried many rules, thresholds, lookback windows, universes, cost assumptions, and feature sets, the best historical curve is also the result of a selection process. Reporting only the winning variant hides the search. The exact number of trials may be hard to reconstruct in old research, but “we tried a few” is not an audit trail. Record each meaningful variant, the data period used to choose it, and whether the final period influenced the choice.

Separate the design period from a final evaluation period that is not used to revise the strategy. Once you inspect the holdout result and modify the rule in response, that period has become part of development; it is no longer untouched. A walk-forward process can mimic repeated decision-making by fitting on past windows and evaluating on subsequent windows. It can reduce the temptation to pick parameters from the whole history, but it still needs point-in-time data and explicit rules for when models are refit. Rolling windows can also produce unstable estimates when markets experience structural breaks or fat-tailed returns; the CFA reading explicitly notes these limitations.

The peer-reviewed work by Bailey, Borwein, López de Prado, and Zhu on the probability of backtest overfitting proposes combinatorially symmetric cross-validation as a framework for analyzing how strategy selection may overfit. This is not a magic score that turns a backtest into a forecast. The method answers a particular question under its design and assumptions. It does not repair missing delisted prices, wrong timestamps, unmodeled spreads, or an out-of-scope market. A result should say what procedure was used, how many variants were compared, and which parts of the sample were exposed during development.

The SEC’s discussion of hypothetical performance is useful here as an audience and disclosure lens: the Commission notes that backtests can be optimized with hindsight. Its marketing rule applies to covered US investment advisers, not automatically to a Swiss publisher or investor. The methodological point travels more broadly: readers need assumptions, selection criteria, risk, limitations, and a clear label distinguishing hypothetical from realized performance.

An original reusable asset: the point-in-time backtest review matrix

Use the matrix below at the beginning of a review, before debating the headline return. The statuses are PASS, FAIL, and UNKNOWN; unknown never silently becomes pass. A reviewer should be able to attach a file, query, version, or screenshot to every pass. The matrix is original synthesis, not a statistically validated scoring model. It has no weighted total because a high score in one category should not cancel a fatal leak in another.

flowchart TD A[Define the claim and audience] --> B{Point-in-time inputs proven?} B -- no / unknown --> X[Restrict scope or label exploratory] B -- yes --> C{Historical universe and identities complete?} C -- no / unknown --> X C -- yes --> D{Execution phase and cost model defensible?} D -- no / unknown --> X D -- yes --> E{Selection process and holdout documented?} E -- no / unknown --> X E -- yes --> F[Independent review and reproducible run] F --> G[State remaining uncertainty beside the claim]
Gate Evidence to attach Automated check Manual challenge Stop condition
Decision timeline Feature source, publication time, vendor capture, timezone conversion available_at <= decision_at Was available_at a public release or just ingestion? Any material feature arrives after decision
Security identity Stable ID and dated ticker/listing map No ambiguous ticker join Could a symbol represent a different issuer or line? Unresolved identity splice
Universe membership Effective-dated inclusion and removal events Decision time inside membership interval Does the file contain securities that later failed or left? Today-only constituent list reused historically
Corporate actions Split, dividend, merger, delisting treatment Price/quantity continuity assertions Does the vendor already adjust this series? Double adjustment or missing event outcome
Order timing Signal and fill timestamps, order type, phase Fill strictly after decision Could the order participate in this auction/session? Same-event fill without evidence
Cost realism Commission, spread, slippage and sensitivity source Nonnegative costs and valid quotes Does the cost scale with size and liquidity? Costs omitted or selected after looking at outcome
Selection discipline Variant log, frozen code/config, split dates Test-set access recorded Was this holdout reused after inspection? Final evaluation was used to tune the winner
Reproducibility Data version/hash, code commit, run config Repeat run emits same trades Can an independent implementation reproduce key rows? Missing input or run cannot be recreated

Do not convert this into “seven out of eight means good.” The matrix is a gate map, not a risk probability. One failed stop condition can invalidate the headline claim even when every other row passes. Conversely, an unknown cost source might be a fixable data gap rather than evidence that the strategy itself has no merit. The output should name the exact uncertainty and the least costly way to resolve it.

A compact Python CSV gate you can adapt

The following standard-library script checks each trade row for a few common point-in-time and quote inconsistencies. Save it as audit_trades.py and run python audit_trades.py trades.csv. The CSV requires the named columns. Date-times must be ISO 8601 strings with a timezone offset; the script rejects naive timestamps rather than guessing. member_to is inclusive in the data convention used here; adapt that rule if your source defines effective end dates differently.

#!/usr/bin/env python3 """Fail-closed checks for an illustrative point-in-time trade CSV. Required columns: trade_id,decision_at,signal_available_at,fill_at,data_available_at, member_from,member_to,bid,ask,fill_price,cost_bps This is a data gate, not a backtest engine, execution simulator, or proof of profit. """ import csv import sys from datetime import datetime from decimal import Decimal, InvalidOperation from pathlib import Path def instant(value, field, row_number, issues): try: parsed = datetime.fromisoformat(value.replace("Z", "+00:00")) if parsed.tzinfo is None or parsed.utcoffset() is None: raise ValueError("timezone offset missing") return parsed except (ValueError, AttributeError): issues.append(f"row {row_number}: invalid timezone-aware {field}") return None def decimal(value, field, row_number, issues): try: number = Decimal(value) if not number.is_finite(): raise InvalidOperation return number except (InvalidOperation, TypeError): issues.append(f"row {row_number}: invalid numeric {field}") return None def audit(path): required = { "trade_id", "decision_at", "signal_available_at", "fill_at", "data_available_at", "member_from", "member_to", "bid", "ask", "fill_price", "cost_bps", } findings = [] with Path(path).open(newline="", encoding="utf-8-sig") as handle: reader = csv.DictReader(handle) missing = required - set(reader.fieldnames or []) if missing: raise SystemExit("missing columns: " + ", ".join(sorted(missing))) rows = list(reader) for number, row in enumerate(rows, start=2): issues = [] decision = instant(row["decision_at"], "decision_at", number, issues) signal = instant(row["signal_available_at"], "signal_available_at", number, issues) fill = instant(row["fill_at"], "fill_at", number, issues) data = instant(row["data_available_at"], "data_available_at", number, issues) start = instant(row["member_from"], "member_from", number, issues) end = instant(row["member_to"], "member_to", number, issues) if row["member_to"].strip() else None if decision and signal and signal > decision: issues.append("signal became available after decision") if decision and data and data > decision: issues.append("input data became available after decision") if decision and fill and fill <= decision: issues.append("fill is not strictly after decision") if decision and start and decision < start: issues.append("security not yet in eligible universe") if decision and end and decision > end: issues.append("security already outside eligible universe") bid = decimal(row["bid"], "bid", number, issues) ask = decimal(row["ask"], "ask", number, issues) price = decimal(row["fill_price"], "fill_price", number, issues) cost = decimal(row["cost_bps"], "cost_bps", number, issues) if bid is not None and bid <= 0: issues.append("bid must be positive") if ask is not None and ask <= 0: issues.append("ask must be positive") if bid is not None and ask is not None and ask < bid: issues.append("ask is below bid; inspect crossed/incorrect quote") if price is not None and price <= 0: issues.append("fill_price must be positive") if cost is not None and cost < 0: issues.append("cost_bps must not be negative") if issues: findings.append((row.get("trade_id", f"line-{number}"), issues)) print(f"rows_checked={len(rows)} rows_flagged={len(findings)}") for trade_id, issues in findings: print(f"{trade_id}: " + "; ".join(issues)) return 1 if findings else 0 if __name__ == "__main__": if len(sys.argv) != 2: raise SystemExit("usage: python audit_trades.py trades.csv") raise SystemExit(audit(sys.argv[1])) 

The script deliberately refuses to invent a timezone, membership end, or execution price. It flags rows where source data or decision timing is contradictory, and returns a nonzero exit code if any row is flagged. It does not decide whether the member_from and member_to values themselves are complete or historically correct. It does not confirm a spread was available at the fill time, estimate queue priority, handle partial fills, model corporate actions, calculate returns, or detect that a researcher tried dozens of strategies and only saved one. Those require other files and review steps.

Example fixture and method note

To explain expected behavior, create four synthetic rows: one clean row; one where the signal timestamp is after the decision; one where the security’s membership interval starts after the decision; and one with a fill timestamp equal to the decision. Run the script and confirm that the first row passes and the other three are flagged. This is a deterministic fixture, not market data, a performance study, or evidence about Swiss shares. The method is a four-row hand-authored unit test on 25 October 2026; all market observations are excluded; the test only checks the stated validation conditions. It cannot estimate how frequently real datasets contain these errors. The limitation should stay beside the example whenever someone reuses it.

That fixture is useful because it tests the review tool, not a trading idea. A team can add a row for a crossed quote, negative costs, missing offset, or fill before decision and assert the expected flag. Keep the fixture in version control with the code and make it part of continuous integration. Then a future refactor that accidentally permits a naive timestamp or same-event fill fails early. A green unit test means the gate behaves as designed on those examples; it does not mean a production feed has been independently verified.

What this script cannot see—and what to add next

The checker sees only rows that are already in the trade CSV. If a bankrupt company has disappeared from the source universe entirely, no row can tell you that it should have been there. Add a second, effective-dated universe file containing every eligible instrument at each rebalance, plus its provenance and membership dates. Compare the point-in-time universe with the actual index or screening methodology for selected dates. Sample both additions and removals, and deliberately include names that later delisted. If you do not have a delisted-security source, say so and narrow the result to the covered universe; do not describe it as a full-market backtest.

Likewise, the script cannot establish historical spread or order-book availability. To make that claim, connect the trade to a quote or order-book...