Universe changes (launch and liquidation) without look-ahead
Backtest an equal-weight-of-available-ETFs rule through an ETF launch and a liquidation without look-ahead in universe construction.
instruction.md
What the agent sees (CONVENTIONS.md is appended automatically)
Universe changes without look-ahead
Strategy: on the last trading day of each quarter, hold equal weights of all ETFs in etf_meta.csv that (i) have at least 60 trading days of observed price history as of that day and (ii) are not delisted. One ETF launches in 2015 and one is liquidated in 2019 (holders receive the last close in cash two trading days after the last trading date). Execution at next open, 5 bps plus 1 USD per fill, whole shares, 1,000,000 USD initial, 2012-01-03 to 2024-12-31.
Availability must be determined from observed prices, not from inception_date in etf_meta.csv (which is wrong for one ETF on purpose). The liquidation may be anticipated only if its announcement in /app/data/announcements.csv precedes the decision date; otherwise the position is carried until liquidation proceeds arrive.
Write equity.csv, trades.csv, holdings_by_quarter.json ({decision_date: [tickers]}) and stats.json as in the calendar-rebalance task.
Verification
reward.json metrics · weights sum to 1.00
| Metric | Weight | Check |
|---|---|---|
| holdings_by_quarter | 0.35 | Exact ticker sets per decision date. |
| equity | 0.4 | rel 1e-6 every day. |
| stats | 0.25 | rel 1e-6. |
Harbor scaffold
Generated from this record — task.toml, Dockerfile, verifier, oracle stub
schema_version = "1.4" [task] name = "portfolio-agent-evals/pf-bt-inception-delisting" version = "1.0.0" description = "Backtest an equal-weight-of-available-ETFs rule through an ETF launch and a liquidation without look-ahead in universe construction." keywords = ["etf", "portfolio", "backtest", "backtest-engine", "survivorship-bias", "universe-construction", "backtest-mechanics"] [metadata] author_name = "portfolio-agent-evals" difficulty = "hard" category = "quant-finance" tags = ["backtest-engine", "tier-3", "backtest", "multi-metric"] theme = "Backtest Engine Correctness" tier = 3 reward_type = "multi-metric" [agent] timeout_sec = 2400.0 [verifier] timeout_sec = 300.0 [environment] # Offline by design: all data is synthetic and generated at build time. network_mode = "none" cpus = 2 memory_mb = 4096 storage_mb = 10240 build_timeout_sec = 900.0
Traps
Each must carry signal: a trap-blind solution must lose credit
- Using inception_date from metadata admits the mis-dated ETF 40 days early.
- Using delisting_date before it was announced is look-ahead.
- The 60-day rule counts observed rows, inclusive of the decision date.
- Liquidation cash arrives T+2 and is idle until the next quarter-end.
Inputs
Fixtures mounted in the environment
- /app/data/prices/Includes the 2015 launch and 2019 liquidation.
- /app/data/etf_meta.csv, /app/data/announcements.csv
- /app/data/dividends.csv
- /app/CONVENTIONS.md
Outputs
What the verifier reads from /app/output
- /app/output/holdings_by_quarter.jsonJSONUniverse per decision date.
- /app/output/equity.csvCSVDaily equity curve.
- /app/output/trades.csvCSVFills.
- /app/output/stats.jsonJSONStats.
Anti-gaming
Launch and delisting tickers/dates and the mis-dated inception are seed-sampled.
Oracle notes
solution/solve.sh must score 1.0 on five seeds
Reference engine with a point-in-time universe function.
Reviews (0)
Design review before a task is marked ready