# Synthetic Equity Dossier v1

Created 2026-10-16 for Systematic Equity Research (site 46).
This is original educational synthetic data, not a stock model, market dataset,
backtest, recommendation, broker execution, or evidence of investment skill.
All six security identifiers and scores are invented. No market data was used.

## Browser / Node API

Import `fixture`, `dossier`, and `costAnalysis` from `../assets/engine.mjs`.
The module imports `synthetic-equities.json` with a JSON import attribute;
serve the `data` directory alongside `assets` with an application/json MIME type.
Node 22+ and current browsers support this module syntax. There are no packages,
network requests by the calculation functions, clocks, random seeds, or writes.

```js
const result = dossier({
  selectionCount: 3, // integer 1..6; chosen before inspecting returns
  maxWeight: 0.4,    // fraction, 0..1; per-security cap at formation
  costBps: 10,       // 0..200 basis points per unit of equity traded
  returnType: 'total', // 'total' or split-adjusted 'price'
  fixture,          // optional; defaults to the exported deeply frozen fixture
});
const cost = costAnalysis({
  priorWeights: { 'SYN-01': 0, CASH: 1 },
  targetWeights: { 'SYN-01': 1, CASH: 0 },
  costBps: 10,
});
```

`dossier` returns `{version, synthetic, label, options, policy, sources,
securities, periods, summary, warnings}`. Rates, weights, turnover and notional
are fractions, not percentages; only `costBps` uses basis points. No rounding is
performed internally. `periods` have:

- `id`, `split`, `formation`, `return_start`, `return_end`, `selected` (IDs).
- `rows`: all six records in rank order, including `id`, `name`, `sector`,
  `signal_available`, `factor_score`, `rank`, `selected`, `weight`,
  `priorWeight`, `realizedReturn`, `contribution`, `price_return`, `total_return`.
- `priorWeights`, `weights`, `endingWeights` (maps including explicit `CASH`),
  `sectorWeights` (equities only), `cashWeight`, `maxSecurityWeight`.
- `turnover`, `preCostTradedNotional`, `tradedNotional`, `buyNotional`,
  `sellNotional`, `costFraction`, `grossReturn`, `netReturn`, `costDrag`.
- `grossWealth`, `netWealth` (both start at 1), `cumulativeGrossReturn`,
  `cumulativeNetReturn`.

`summary` has `periodCount`, `grossReturn`, `netReturn`, `costDrag`,
`grossWealth`, `netWealth`, `averageTurnover`, `totalTurnover`,
`totalTradedNotional`, `maxSecurityWeight` and `maxSectorWeight`.
Totals of turnover/notional sum period ratios, not currency amounts or annual
statistics. Summary returns are compounded, not sums of period returns.

`costAnalysis` requires both weight maps with identical keys and explicit CASH.
Allowed keys are synthetic IDs `SYN-01`..`SYN-06` plus CASH. Missing securities
must appear explicitly with zero in both maps, or be absent from both maps.
Each map sums to 1 and has only finite nonnegative numbers. Returns
`{costBps, turnover, preCostTradedNotional, tradedNotional, buyNotional,
sellNotional, costFraction, investableFraction, postCostHoldings, trades}`.
Holdings and trades here are fractions of pre-trade NAV, unlike normalized
target weights. CASH has no trade commission. Unknown options throw, as do
invalid numbers, dates, missing observations, and future information. Errors
have a stable `code` and an explanatory message; no values are silently imputed.

## Frozen educational policy

The six-member universe is fixed before the first formation. Rank descending
factor score, break exact ties by ascending synthetic ID, and select the first
`selectionCount` names. Each gets `min(1 / selectionCount, maxWeight)`; residual
weight is zero-return CASH. There is no score fitting, sector neutralization,
optimization or return-based selection. The security cap is applied only at
formation; return drift may subsequently exceed it. Sector weights are reported,
not constrained. Returns never decide ranks. Controls are sensitivity scenarios,
not a search for the best historical setting.

Signal availability and universe availability must be strictly before formation;
the return interval starts at formation and ends at the next stated boundary.
Equal signal/formation timestamps are conservatively rejected: event ordering
within that timestamp is not established. No real exchange session/calendar or
execution-price claim is implied by the invented UTC boundaries. All periods
are contiguous. Initial weights are explicit; subsequent prior weights are
carried from the preceding period after return drift, never reset to targets.

The three consecutive periods are labelled development, validation and holdout
to illustrate a frozen split. All numbers were authored together. One period
per split cannot measure predictive validity, independent strategy performance,
or an out-of-sample premium. There is no training or parameter selection here.

## Exact cost and compounding convention

Let p be pre-trade weights, w normalized target weights, and k = costBps/10000.
One-way policy turnover is `0.5 * sum(abs(w - p))`, **including CASH**.
The pre-cost equity trade reference is `sum_equities(abs(w - p))`. These are
different measures; deploying cash is not a purchase and a fictitious cash sale.

Costs are paid at formation. To fund them without borrowing, solve
`c = k * sum_equities(abs((1-c)*w - p))`. The actual equity buy-plus-sell
notional is the sum on the right, as a fraction of beginning NAV. Post-cost
holdings, including cash, are `(1-c)*w`. Buy and sell legs are each charged once;
the quoted rate is not a round-trip rate, and no extra factor of two is applied.
The bounded JS root solve and independent rational Python sign enumeration
verify this same equation by different numerical methods.

Gross period return is `g = sum(w_i * r_i)` including zero-return cash.
Net period return is `(1-c)*(1+g)-1`, not `g-c`: paying the fee before returns
also changes the capital earning the period return. `costDrag = g-netReturn`
therefore differs from `costFraction`. End weights are
`w_i*(1+r_i)/(1+g)`; the common cost scale cancels. Gross and net wealth multiply
their respective `(1+return)` each period. Costs for entering the initial
allocation are included; there is no terminal liquidation, account fee, tax,
spread/impact model, interest on cash, or external flow. The flat rate is an
assumption, not an execution-cost estimate. The price-only switch deliberately
discards distributions and is a measurement comparison, not a funded portfolio.

## Corporate actions and missing data

Synthetic Security 01 in period 1 starts at 100, has a two-for-one split,
finishes at 54 per new share, and pays 2 per original share at the period end.
Split-adjusted price return is `2*54/100-1 = 8%`; total return is
`(2*54+2)/100-1 = 10%`. A raw `54/100-1 = -46%` is not a loss calculation.
Distributions in the fixture are treated as reinvested at the period endpoint;
no intra-period reinvestment, taxes or withholding are modelled. The action is
mechanically reconciled to the stored returns. Other rows have explicitly
authored price/total returns; their difference is an invented distribution.
The fixed universe keeps losing names, and makes no survivor or delisting claim.
Missing rows or either return field fail even for unselected names. A changed
universe requires a different study, not a silent row drop.

## Provenance and rights

`synthetic-equities.json` is the sole canonical input. Source links in the JSON
and cases are method references, not the origin of any fixture value. The three
primary sources were read on 2026-10-16: Kenneth French's portfolio construction
description (formation and lagged accounting inputs), S&P DJI Index Mathematics
(September 2026; total return, corporate actions, turnover), and AQR's
Transactions Costs: Practical Application (2018; cost-model limitations).
No third-party data, article prose or source PDFs are redistributed. Source
copyright remains with its owners; no endorsement or replicated index is claimed.
CRSP search results were considered but its PDF fetch failed; no successful CRSP
retrieval is claimed or used as the retained evidence source.

Original synthetic fixture numbers and original explanatory data documentation
were authored for this publication. They are offered under CC0 1.0 for reuse:
https://creativecommons.org/publicdomain/zero/1.0/ . This grant covers these
original fixture materials only, not linked sources or unrelated repository code.

## Verification

From this site's folder run `node --test tests/engine.test.mjs` and
`python -B tests/reference.py`. The Python reference uses exact Fraction
arithmetic and a separate fee-solving algorithm, calls Node only for comparison,
and also asserts independent hand-calculated first-period and compound results.
Neither command starts a server, writes production state or requires network.
