Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,6 @@ jobs:
run: |
python scripts/run_synthetic_demo.py --out "$RUNNER_TEMP/synthetic-demo"
grep -q "SYNTHETIC DEMONSTRATION" "$RUNNER_TEMP/synthetic-demo/metrics.json"

- name: Continuous capital and immutable revision CLI acceptance
run: python scripts/run_continuous_demo.py --out "$RUNNER_TEMP/continuous-demo"
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Quant Research Portfolio

[Continuous shared capital and paired revisions](docs/CONTINUOUS_CAPITAL.md) add
a runnable frozen-policy study on QExec's shared account and QDK's immutable lineage.

A small, reviewable portfolio of quantitative-research engineering patterns:

- deterministic synthetic data generation;
Expand Down
72 changes: 72 additions & 0 deletions docs/CONTINUOUS_CAPITAL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Continuous shared capital and paired revisions

Install this repository with its pinned QDK/QExec dependencies. The new commands
are exposed by the existing `quant-portfolio` CLI. The deterministic example is
entirely synthetic; it establishes accounting and revision plumbing only.

```bash
python scripts/run_continuous_demo.py --out /new/empty/path/capital-demo
```

This executes the real freeze, continuous-study and revision-study CLIs, pins both
market versions and result derivations in QDK's `ArtifactStore`, and writes
`acceptance.json`, `README.md`, both account reports, and a revision report. The
independent scalar expectation is +34 USD old PnL, +26 new PnL, and -8 difference.
No data, accounts, models, credentials or real orders are acquired or modified.

Run individual steps with your own explicitly declared research inputs:

```bash
quant-portfolio freeze-capital-plan --plan examples/continuous/plan.json --out plan-frozen.json
quant-portfolio continuous-study --plan plan-frozen.json --market examples/continuous/market-old.json --out old.json
quant-portfolio revision-study --request request.json --out revision.json
```

The draft `quant.capital-plan/v1` carries full QDK `InstrumentSpec` payloads,
instrument-to-sleeve ownership, opening currency allocations and FX, a fee-per-unit
cost policy, valuation policy, decisions, and optional explicit cash events.
Each decision fixes a notional target, signal observation, later execution
observation and settlement instant. It rounds the prior-observation notional to
quantity steps, then submits the target delta through QExec at the explicit later
price. It does not adapt or optimize the policy to evaluation outcomes. Missing
prices, unavailable observations, ambiguous order, insufficient funds, unsupported
assets or silently changed controls fail the run. Executions are declared research
fills, with explicit fees; this adapter does not claim order-book matching.

Market input `quant.capital-market/v1` has observations with unique IDs, UTC `at`,
`available_at <= at`, price mapping and FX mapping. Cash-event sequences occupy
0–99, observation marks 100–999, decisions 1000 upward. The QExec account retains
all ordered events, settlements, sleeves, journal postings and cash/margin state
continuously. See QExec `docs/SHARED_CAPITAL.md` for the exact event contract and
limitations. B's forecast replay may call that additive API with its own fills.

Freezing copies the controls and binds actual installed package Python source
hashes and runtime versions. Editing the plan, source or runtime requires a new
explicit freeze and cannot be reported as a data-only comparison. Freezing is
performed now: historical fixture dates do not confer historical/OOS provenance.
The adapter supports `synthetic/deterministic_fixture` or explicitly declared
retrospective studies, and exposes features/forecasts as unavailable.

For revision requests, publish old/new market JSON using `ArtifactStore.put_bytes`
and put their complete `ArtifactRef.to_dict()` objects in `old_input` and
`new_input`. `request.json` also contains absolute `store`, frozen `plan` path and
durable `owner`. `paired_study` checks the same frozen executable and controls,
recalculates both outputs, and registers exact dependencies. The report includes
transitive affected objects, signals/orders/history/NAV/return changes, unchanged
stage results, original references and explicit `intervention=data_revision`.
It never replaces earlier decisions. Dependency reachability is not causal proof.

Reports emit the common `quant.measurement/v1`. Return observations group all
same-instant events after respecting their explicit sequence; their return is the
product of each flow-adjusted event return. The timestamp grid includes opening
and final boundaries, is UTC and strictly increasing. Event returns are never
annualized. Cash is in base-currency major units, returns are decimal fractions,
and exact source, market, instrument, FX and economic-policy hashes are retained.
Old/new revisions intentionally have different input identities and therefore
must fail ordinary same-input economic comparison. The revision report is the
explicit intervention analysis.

Every CLI output uses exclusive creation. Keep frozen plans, raw references and
artifact-store pins with the reports. Re-running into a different new directory
is safe; overwriting past research is rejected. Roll back through Git revert while
preserving output history. No deployment or default-branch merge is performed.
35 changes: 35 additions & 0 deletions examples/continuous/market-new.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"schema": "quant.capital-market/v1",
"observations": [
{
"observation_id": "o1",
"at": "2026-01-02T00:00:00Z",
"available_at": "2026-01-02T00:00:00Z",
"prices": {
"A": "125",
"B": "50"
},
"fx": {}
},
{
"observation_id": "o2",
"at": "2026-01-03T00:00:00Z",
"available_at": "2026-01-03T00:00:00Z",
"prices": {
"A": "101",
"B": "50"
},
"fx": {}
},
{
"observation_id": "o3",
"at": "2026-01-04T00:00:00Z",
"available_at": "2026-01-04T00:00:00Z",
"prices": {
"A": "110",
"B": "50"
},
"fx": {}
}
]
}
35 changes: 35 additions & 0 deletions examples/continuous/market-old.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"schema": "quant.capital-market/v1",
"observations": [
{
"observation_id": "o1",
"at": "2026-01-02T00:00:00Z",
"available_at": "2026-01-02T00:00:00Z",
"prices": {
"A": "100",
"B": "50"
},
"fx": {}
},
{
"observation_id": "o2",
"at": "2026-01-03T00:00:00Z",
"available_at": "2026-01-03T00:00:00Z",
"prices": {
"A": "101",
"B": "50"
},
"fx": {}
},
{
"observation_id": "o3",
"at": "2026-01-04T00:00:00Z",
"available_at": "2026-01-04T00:00:00Z",
"prices": {
"A": "110",
"B": "50"
},
"fx": {}
}
]
}
113 changes: 113 additions & 0 deletions examples/continuous/plan.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
{
"schema": "quant.capital-plan/v1",
"evidence_kind": "synthetic",
"sample_kind": "deterministic_fixture",
"instruments": [
{
"instrument_id": "A",
"asset_class": "etf",
"product_type": "cash_etf",
"venue": "SYNTHETIC",
"native_symbol": "A",
"base_currency": null,
"quote_currency": null,
"settlement_currency": "USD",
"price_tick": {
"units": 1,
"scale": 2
},
"quantity_step": {
"units": 1,
"scale": 0
},
"contract_multiplier": {
"units": 1,
"scale": 0
},
"calendar_id": "explicit_fixture",
"margin_mode": "none",
"inverse": false,
"effective_from": "2026-01-01T00:00:00Z",
"effective_to": null,
"available_at": "2026-01-01T00:00:00Z",
"superseded_at": null,
"underlying_id": null,
"expiry_date": null,
"metadata": {}
},
{
"instrument_id": "B",
"asset_class": "etf",
"product_type": "cash_etf",
"venue": "SYNTHETIC",
"native_symbol": "B",
"base_currency": null,
"quote_currency": null,
"settlement_currency": "USD",
"price_tick": {
"units": 1,
"scale": 2
},
"quantity_step": {
"units": 1,
"scale": 0
},
"contract_multiplier": {
"units": 1,
"scale": 0
},
"calendar_id": "explicit_fixture",
"margin_mode": "none",
"inverse": false,
"effective_from": "2026-01-01T00:00:00Z",
"effective_to": null,
"available_at": "2026-01-01T00:00:00Z",
"superseded_at": null,
"underlying_id": null,
"expiry_date": null,
"metadata": {}
}
],
"owners": {
"A": "alpha",
"B": "beta"
},
"allocations": {
"alpha": {
"USD": "600"
},
"beta": {
"USD": "400"
}
},
"base_currency": "USD",
"opened_at": "2026-01-01T00:00:00Z",
"opening_fx": {},
"cost_policy": {
"model": "explicit_fee_per_unit",
"fee_per_unit": "1"
},
"valuation_policy": {
"model": "last_available_mark_and_fx",
"positive_futures_pnl_spendable": false
},
"decisions": [
{
"decision_id": "buy-a",
"signal_observation_id": "o1",
"execution_observation_id": "o2",
"instrument_id": "A",
"target_notional": "500",
"settles_at": "2026-01-03T00:00:00Z"
},
{
"decision_id": "buy-b",
"signal_observation_id": "o1",
"execution_observation_id": "o2",
"instrument_id": "B",
"target_notional": "300",
"settles_at": "2026-01-03T00:00:00Z"
}
],
"cash_events": []
}
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ readme = "README.md"
license = { text = "MIT" }
requires-python = ">=3.10"
dependencies = [
"quant-data-kit @ git+https://github.com/PureSaber/quant-data-kit.git@ba136c2fa2eea121bfb2ad7887b536c3952586f7",
"quant-execution @ git+https://github.com/PureSaber/quant-execution.git@21aace45d2dde6458db8fcda4829f252f8c640f1",
"quant-data-kit @ git+https://github.com/PureSaber/quant-data-kit.git@f85128307dd0e3407d24350282fe7b679b591309",
"quant-execution @ git+https://github.com/PureSaber/quant-execution.git@49ef53d2c502ad6faad6818f853dadd1f8b8b182",
"pandas>=2.0",
"pyarrow>=14.0",
"pyyaml>=6.0",
Expand Down
4 changes: 2 additions & 2 deletions requirements.lock
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ pyyaml==6.0.3
# via
# quant-data-kit
# quant-portfolio (pyproject.toml)
quant-data-kit @ git+https://github.com/PureSaber/quant-data-kit.git@ba136c2fa2eea121bfb2ad7887b536c3952586f7
quant-data-kit @ git+https://github.com/PureSaber/quant-data-kit.git@f85128307dd0e3407d24350282fe7b679b591309
# via
# quant-execution
# quant-portfolio (pyproject.toml)
quant-execution @ git+https://github.com/PureSaber/quant-execution.git@21aace45d2dde6458db8fcda4829f252f8c640f1
quant-execution @ git+https://github.com/PureSaber/quant-execution.git@49ef53d2c502ad6faad6818f853dadd1f8b8b182
# via quant-portfolio (pyproject.toml)
referencing==0.37.0
# via
Expand Down
Loading
Loading