Data contract
Holdout Data Contract v1
The file format for sending us data: returns only (Tier 1), or positions and trades for a deeper audit (Tier 2). You get the spec, sample files, a JSON Schema per file and a free validator, so you can check your folder before anything is sent.
Version 1.0, 2026-09-26. Questions: [email protected].
The short version
- Tier 1, returns only: one file,
returns.csv. The minimum for every service, and anonymised is fine. - Tier 2, positions and trades: add
instruments.csvandpositions.csv(plustrades.csvandbaskets.csvwhere relevant) for holdings-based checks. It takes us more time, so it costs more: see pricing. - Our format, checked by you first: download the samples and run the validator on your folder. PASS means it is ready to send.
- Can’t produce this format? We can convert your files as a paid add-on, quoted separately, from $500.
- Same confidentiality either way. Position and trade data is handled exactly like returns, under the same terms.
Two tiers
What you send, and what it unlocks
| Tier 1: returns only | Tier 2: positions, trades, exposures | |
|---|---|---|
| Files | returns.csv, plus the number of variants you tried (and the variant grid, if you have one) | Tier 1, plus instruments.csv, positions.csv; trades.csv and baskets.csv where relevant |
| Anonymised? | Yes: “Strategy A”, no names, optionally rescaled | Possible, with trade-offs (see below) |
| What we can test | Everything built on the return series: deflated Sharpe, PBO, haircuts, Reality Check and SPA, holdout decay, stability, return-based factor alpha, smoothing flags, drawdown ranges | Tier 1, plus holdings-based factor exposure, cost and capacity against volume, look-ahead and survivorship checks, positions-to-returns reconciliation, concentration, leverage, liquidity and drift |
| Price | The listed price | About 1.8× the Tier 1 price (pricing) |
Downloads
Samples, schemas and the validator
- All sample files (zip): a small, fictitious multi-asset book that passes the validator. Or one at a time: returns.csv, instruments.csv, positions.csv, trades.csv, baskets.csv.
- JSON Schemas (2020-12), one per file: returns, instruments, positions, trades, baskets.
- holdout_validate.py: the free validator. One file, Python 3.10 or later, standard library only. It runs on your machine and sends nothing anywhere.
The sample instruments are invented (SAMPLE-*); the SPY and TLT prices in them are simulated, not market data.
Check your folder
python holdout_validate.py my-folder/
PASS returns.csv 63 rows ok
PASS instruments.csv 12 rows ok
PASS positions.csv 90 rows ok
PASS trades.csv 2 rows ok
PASS baskets.csv 6 rows ok
RESULT: PASS (0 error(s), 0 warning(s))
Exit code 0 means PASS, 1 means FAIL. Every problem is listed with its file and line number. Add --json for a machine-readable report. The same check is holdout-audit validate in our toolkit. A PASS means the folder is in the right format, not that the numbers are right; we still review them.
General rules
How every file is laid out
- One folder, fixed file names:
returns.csv,instruments.csv,positions.csv,trades.csv,baskets.csv. - CSV (UTF-8, comma-separated, one header row, the column names below in lower case) or Parquet with the same names (
returns.parquet). - One row per record. No merged cells, subtotals, spacer rows, notes under the table or multi-row headers.
- Dates in ISO-8601:
2026-03-18(or a full timestamp for intraday data). Not03/18/2026, not Excel serial numbers. - Plain numbers: a
.decimal point, no%, thousands separators, currency symbols or brackets. Returns are decimals:0.012= +1.2%. - One base currency, declared in
returns.csv. NAV, flows, market values and fees are in it. - Consistent IDs: every
instrument_idin positions, trades or baskets appears exactly once ininstruments.csv. - Signed quantities: negative means short (or a sale). Leave optional cells empty rather than writing
0orN/A.
Files and fields
What goes in each file
returns.csv · Tier 1, always required
| Column | Required | Meaning |
|---|---|---|
date | yes | Period end (daily, weekly or monthly); unique, in date order |
nav | nav or return | Net asset value at the period end, base currency, above zero |
return | nav or return | Simple decimal return for the period, time-weighted, net of costs and fees unless agreed. With nav, the first row may be blank |
flow | no | External cash flow at the period end: + subscription, − redemption. Used with nav for time-weighted returns |
currency | yes | Base currency (ISO 4217, e.g. USD); the same on every row |
A flow on day t is included in that day’s NAV, so return = (NAVt − flowt) / NAVt−1 − 1. If you send both NAV and returns they must agree within 1 basis point. An audit needs at least 60 periods; two years or more is better.
instruments.csv · required with any Tier 2 file
| Column | Required | Meaning |
|---|---|---|
instrument_id | yes | Your identifier, unique in the file |
id_type | yes | ISIN, CUSIP, FIGI, TICKER_MIC (e.g. SPY.ARCX), OCC (option symbol), FUTURES (contract code), CUSTOM (internal or masked) |
description | no | Free text; keep it generic if anonymising |
asset_class | yes | equity, etf, index, future, option, fx, bond, commodity, swap, basket, cash, other |
currency | yes | Currency the instrument is priced in |
multiplier | no (blank = 1) | Contract multiplier: 100 for US equity options, the point value for futures |
underlying_id | derivatives, swaps | The underlying’s instrument_id (listed in this file too) |
expiry | futures, options | Expiry or maturity date |
strike | options | Strike, in the instrument currency |
put_call | options | P or C |
exchange | no | Listing venue, ideally an ISO 10383 MIC (XNYS, XCME) |
positions.csv · Tier 2
| Column | Required | Meaning |
|---|---|---|
date | yes | End-of-day holdings date, ideally on the returns.csv calendar |
instrument_id | yes | From instruments.csv |
quantity | yes | Signed units or contracts; negative = short. For cash rows, the amount |
price | price or market_value | Price per unit, in the instrument currency |
market_value | price or market_value | Value in the base currency, negative for shorts. Futures and swaps: the notional (their P&L sits in cash) |
currency | yes | Currency of price; the instrument’s currency |
sleeve | no | Sub-strategy or book label |
delta | options | Per-unit option delta, between −1 and 1 (puts are negative) |
One row per instrument, date and sleeve. When price and market value are both given, market value must equal quantity × price × multiplier within 0.5%. Include cash rows if you want positions reconciled to NAV.
trades.csv · optional
| Column | Required | Meaning |
|---|---|---|
trade_date | yes | Trade date |
instrument_id | yes | From instruments.csv |
quantity | yes | Signed: + buy, − sell; never zero |
price | yes | Execution price, instrument currency |
fees | no | Commissions and fees, base currency, as a positive number |
sleeve | no | Sub-strategy or book label |
trade_id | no | Your unique reference; add it when two fills are otherwise identical |
baskets.csv · only for custom baskets or swap look-through
| Column | Required | Meaning |
|---|---|---|
basket_id | yes | The basket’s instrument_id (asset class basket, swap or index) |
date | yes | Composition date; applies until the next date for that basket |
constituent_id | yes | The constituent’s instrument_id |
weight | weight or units | Decimal weight; a basket’s weights on a date sum to 1 |
units | weight or units | Units per one unit of the basket; use for long/short or leveraged baskets |
Special cases
Indices, baskets, derivatives, anonymisation, market data
Index products
An index future, option, ETF or swap is one instrument by default: send its price and we treat it as a single exposure. We look through to constituents only if you supply them in baskets.csv. We do not source licensed index constituent data, so look-through of a proprietary index depends on your own right to share it.
Custom baskets and swaps
Either send the basket as a single price series (simplest), or also send baskets.csv with its composition on each rebalance date, which lets us analyse factors, concentration and liquidity on the constituents. A total return swap is asset_class = swap with underlying_id pointing at its reference basket.
Derivatives
Futures: market value is the notional, and P&L runs through cash. Options: send multiplier, strike, expiry, put/call, underlying and a per-position delta, and we report both notional and delta exposure. Without delta, delta exposure needs a pricing model, and the report says so.
The anonymisation trade-off
Masked CUSTOM identifiers are fine, but then we can’t look anything up. You supply the prices (and volumes, for capacity), and some checks are lost or weaker: survivorship and look-ahead checks against public records, holdings-based factor exposures, and capacity against real traded volume. Masking only the sensitive names keeps most of the value. Anonymised Tier 1 returns remain the option with the least disclosure.
Market data
We prefer your prices: they are what your book was actually marked at. Any public or licensed market data we use to fill gaps or run checks stays inside the analysis. We do not redistribute market data; reports show derived statistics, not vendor price series.
Tier 2
What positions and trades unlock
- Holdings-based factor exposure: loadings from what was actually held, compared with the return-based regression, to reveal style drift that returns alone hide.
- Cost and capacity: turnover and trade sizes against average daily volume, estimated market impact, and the size at which costs erase the edge.
- Look-ahead and survivorship checks: positions taken before information was public, and instruments in a backtest universe that did not yet exist or later disappeared.
- Positions-to-returns reconciliation: daily returns rebuilt from holdings and prices, and the size of the unexplained gap.
- Concentration, leverage, liquidity and drift: top holdings, gross and net exposure, days to liquidate, and how they moved over time.
Validator
What the validator checks
- The right files are present, UTF-8, comma-separated, with every row as wide as the header.
- Required columns, ISO dates, plain numbers, allowed codes and identifier formats.
- Returns: unique ordered dates, one base currency, positive NAV, percent-scaled returns caught, returns matching NAV and flows within 1 bp.
- Every instrument used in positions, trades and baskets exists in
instruments.csv; options have strike, expiry and put/call. - No duplicate positions, trades or basket rows.
- Market value matches quantity × price × multiplier within 0.5%; its sign follows the quantity.
- Basket weights sum to 1 (±0.01).
- Warnings for position dates missing from the returns calendar, and for positions that don’t add up to NAV when cash rows are supplied.
A folder with problems
FAIL returns.csv 4 rows 3 error(s)
FAIL positions.csv 3 rows 3 error(s)
ERROR returns.csv returns look like percentages
(median |return| = 0.35);
send decimals: 1.2% -> 0.012
ERROR returns.csv:5 'date' = '01/07/2026' is not
an ISO-8601 date (YYYY-MM-DD)
ERROR positions.csv:2 market_value 5,000.00 !=
-100 x 50 x 1 = -5,000.00
ERROR positions.csv:3 instrument_id 'SAMPLE-EQ-999'
is not in instruments.csv
RESULT: FAIL
Output shortened for this page.
Data in another shape? We can prepare it
If you can’t produce these files (a broker or administrator export, several spreadsheets, PDFs), we convert them for you as a paid add-on, quoted separately, from $500, depending on the number of sources and the cleaning needed. The converted folder goes through the same validator and back to you for sign-off before the audit starts, so you confirm that the data we audit is your data. Send data only after scoping, by the agreed secure method; never through the intake form.