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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

- Add conservative next-open and adverse-touch completed-bar execution models with strict fixed
spread and linear participation-impact configuration, explicit missing-volume policy,
tick-aligned prices, and separate price-component audit attribution.
- Publish scenario/journal contract v13 and external strategy protocol v11 while preserving v12
and protocol v10 as frozen compatibility contracts.

- Add exact stock-dividend, rights, and spin-off distributions with explicit basis allocation,
fractional rejection or cash-in-lieu policy, destination currency validation, target adjustment,
and complete journal attribution.
Expand Down
27 changes: 15 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ scenario slices and scheduled or external intents
fee-component attribution
- Deterministic event IDs, ordered causal references, and order-creation attribution
- Contract-selected compiled execution modules with versioned model-owned configuration and
capability descriptors; v12 currently exposes `completed_bar_v1` configuration v2
capability descriptors; v13 adds next-open and adverse-touch models while freezing
`completed_bar_v1`
- Tick-aligned fixed-spread and participation-impact execution costs with separate reference,
spread, impact, and final-price audit attribution
- Strict batch JSON and bounded-memory JSON Lines scenario parsing with JSON Schemas
- Versioned synchronous JSON Lines strategy processes with per-request timeouts and strict
lifecycle supervision
Expand Down Expand Up @@ -91,23 +94,23 @@ Validate the included scenario with an in-memory replay:

```sh
opam exec -- dune exec trading-engine -- \
--input contracts/v12/fixtures/demo.scenario.json \
--input contracts/v13/fixtures/demo.scenario.json \
--validate-only
```

Run it and create a journal:

```sh
opam exec -- dune exec trading-engine -- \
--input contracts/v12/fixtures/demo.scenario.json \
--input contracts/v13/fixtures/demo.scenario.json \
--journal demo.journal.jsonl
```

For larger histories, validate and replay the equivalent stream one slice at a time:

```sh
opam exec -- dune exec trading-engine -- \
--input contracts/v12/fixtures/demo.scenario.jsonl \
--input contracts/v13/fixtures/demo.scenario.jsonl \
--input-format jsonl \
--journal demo.journal.jsonl
```
Expand All @@ -116,7 +119,7 @@ Run an external strategy against an empty-schedule scenario:

```sh
opam exec -- dune exec trading-engine -- \
--input contracts/strategy/v10/fixtures/external.scenario.json \
--input contracts/strategy/v11/fixtures/external.scenario.json \
--journal external.journal.jsonl \
--strategy-executable ./my-strategy \
--strategy-arg=config.toml \
Expand Down Expand Up @@ -231,19 +234,19 @@ do not provide reducer snapshots or restart recovery.
- [Diagnostic contract](docs/diagnostics.md)
- [Scenario contract](docs/scenario.md)
- [Contract conformance corpus](contracts/conformance/README.md)
- [Current contract v12 and conformance fixtures](contracts/v12/README.md)
- [Current contract v13 and conformance fixtures](contracts/v13/README.md)
- [Frozen contract v2](contracts/v2/README.md)
- [Historical contract v1](contracts/v1/README.md)
- [Scenario JSON Schema](contracts/v12/scenario.schema.json)
- [Scenario stream record JSON Schema](contracts/v12/scenario-stream.schema.json)
- [Journal record JSON Schema](contracts/v12/journal.schema.json)
- [External strategy protocol v10](contracts/strategy/v10/README.md)
- [Scenario JSON Schema](contracts/v13/scenario.schema.json)
- [Scenario stream record JSON Schema](contracts/v13/scenario-stream.schema.json)
- [Journal record JSON Schema](contracts/v13/journal.schema.json)
- [External strategy protocol v11](contracts/strategy/v11/README.md)
- [Historical strategy protocol v3](contracts/strategy/v3/README.md)
- [Historical strategy protocol v2](contracts/strategy/v2/README.md)
- [Historical strategy protocol v1](contracts/strategy/v1/README.md)
- [Persistra compatibility](docs/persistra.md)
- [Strategy message JSON Schema](contracts/strategy/v10/message.schema.json)
- [Strategy transcript JSON Schema](contracts/strategy/v10/transcript.schema.json)
- [Strategy message JSON Schema](contracts/strategy/v11/message.schema.json)
- [Strategy transcript JSON Schema](contracts/strategy/v11/transcript.schema.json)
- [Execution model](docs/execution-model.md)
- [OCaml coverage](docs/coverage.md)
- [Continuous integration and portability matrix](docs/continuous-integration.md)
Expand Down
84 changes: 84 additions & 0 deletions contracts/conformance/cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,90 @@
},
"mutations": [],
"schema_expectation": "accept"
},
{
"name": "scenario-v13-valid",
"artifact": "scenario-v13",
"kind": "scenario",
"source": "v13/fixtures/demo.scenario.json",
"mutations": [],
"schema_expectation": "accept",
"parser_expectation": "accept"
},
{
"name": "scenario-stream-v13-valid",
"artifact": "scenario-stream-v13",
"kind": "scenario_stream",
"source": "v13/fixtures/demo.scenario.jsonl",
"mutations": [],
"schema_expectation": "accept",
"parser_expectation": "accept"
},
{
"name": "strategy-ready-valid-v11",
"artifact": "strategy-message-v11",
"instance": {
"strategy_protocol_version": "11",
"strategy_sequence": "1",
"message_type": "ready",
"payload": { "strategy_name": "conformance", "strategy_version": null }
},
"mutations": [],
"schema_expectation": "accept",
"parser_expectation": "accept",
"parser_expected": "ready"
},
{
"name": "strategy-intents-valid-v11",
"artifact": "strategy-message-v11",
"instance": {
"strategy_protocol_version": "11",
"strategy_sequence": "2",
"message_type": "intents",
"payload": { "intents": [] }
},
"mutations": [],
"schema_expectation": "accept",
"parser_expectation": "accept",
"parser_expected": "intents"
},
{
"name": "strategy-error-valid-v11",
"artifact": "strategy-message-v11",
"instance": {
"strategy_protocol_version": "11",
"strategy_sequence": "7",
"message_type": "error",
"payload": { "message": "fixture failure" }
},
"mutations": [],
"schema_expectation": "accept"
},
{
"name": "strategy-v11-rejected-response-branch",
"artifact": "strategy-transcript-v11",
"instance": {
"strategy_diagnostic_version": "1",
"transcript_sequence": "2",
"record_type": "rejected_strategy_response",
"expected_strategy_sequence": "1",
"diagnostic": {
"diagnostic_version": "1",
"code": "strategy.protocol",
"phase": "strategy",
"message": "strategy initialization: invalid strategy response JSON",
"context": { "json_path": "$", "sequence": "1" },
"cause": null
},
"evidence": {
"encoding": "hex",
"prefix": "7b",
"observed_bytes": 1,
"truncated": false
}
},
"mutations": [],
"schema_expectation": "accept"
}
]
}
49 changes: 49 additions & 0 deletions contracts/conformance/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,55 @@
"sources": [
{ "path": "strategy/v10/fixtures/external.strategy.jsonl", "format": "jsonl" }
]
},
{
"name": "scenario-v13",
"schema": "v13/scenario.schema.json",
"version_field": "contract_version",
"version": "13",
"sources": [
{ "path": "v13/fixtures/demo.scenario.json", "format": "json" },
{ "path": "v13/fixtures/fill-clipped.scenario.json", "format": "json" },
{ "path": "strategy/v11/fixtures/external.scenario.json", "format": "json" }
]
},
{
"name": "scenario-stream-v13",
"schema": "v13/scenario-stream.schema.json",
"version_field": "contract_version",
"version": "13",
"sources": [
{ "path": "v13/fixtures/demo.scenario.jsonl", "format": "jsonl" },
{ "path": "strategy/v11/fixtures/external.scenario.jsonl", "format": "jsonl" }
]
},
{
"name": "journal-v13",
"schema": "v13/journal.schema.json",
"version_field": "contract_version",
"version": "13",
"sources": [
{ "path": "v13/fixtures/demo.journal.jsonl", "format": "jsonl" },
{ "path": "v13/fixtures/fill-clipped.journal.jsonl", "format": "jsonl" }
]
},
{
"name": "strategy-message-v11",
"schema": "strategy/v11/message.schema.json",
"version_field": "strategy_protocol_version",
"version": "11",
"sources": [
{ "path": "strategy/v11/fixtures/external.strategy.jsonl", "format": "jsonl", "extract": ["message"] }
]
},
{
"name": "strategy-transcript-v11",
"schema": "strategy/v11/transcript.schema.json",
"version_field": "strategy_protocol_version",
"version": "11",
"sources": [
{ "path": "strategy/v11/fixtures/external.strategy.jsonl", "format": "jsonl" }
]
}
]
}
59 changes: 59 additions & 0 deletions contracts/strategy/v11/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# External strategy protocol v11

Version 11 is a synchronous JSON Lines protocol over child-process standard input and output.
Trading Engine sends `initialize`, ordered `event` requests, and `shutdown`. The strategy answers
with `ready`, `intents`, and `stopped`. It may answer any request with `error`.
Protocol v10 remains available for scenario contract v12; earlier versions retain their frozen
shapes.

Every message repeats `strategy_protocol_version: "11"` and a positive canonical
`strategy_sequence`. A response must repeat the sequence of its request. Only one request is
outstanding. Trading Engine rejects unknown or duplicate fields, invalid canonical values,
oversized lines, a wrong version or sequence, unexpected response types, EOF, timeout, and a
nonzero process exit.

The event context contains the replay clock, a marked base-currency portfolio, deterministic group
exposure snapshots, all working orders, and the latest available bar for each instrument. Every
callback emitted for a market slice uses
that slice's `received_at` as `now` and uses its complete bars and FX vector. The portfolio reports
cash, equity, net, long, short, and gross market value plus every attributed cash ledger and
configured position. Position quantities and weights reflect applied fills. Weights are truncated
toward zero to six decimal places. `weights_available` is false and all weights are null when
equity is zero or negative.

The `initialize` request identifies scenario contract v13 and includes the exact `initial_portfolio`
snapshot alongside the legacy cash projection. It also carries the complete versioned venue
calendars, nested execution configuration, financing policy, and settlement policy, so a strategy
can construct DAY orders and reject incompatible execution, financing, or settlement state before
replay.

Matching pauses after each strategy callback. The engine applies the response against the exact
account and OMS state exposed by that callback before delivering another callback or considering
the next eligible order. Later same-slice contexts include the effects of earlier responses. The
eligible-order sequence is fixed at the start of matching, so newly submitted orders wait for a
later slice. Cancelling an order before its turn leaves its unused slice capacity available to the
next eligible order.

Event payloads cover completed market slices with effective-time borrow and cash-rate observations
plus explicit settlement failures, fills, order updates, and rejected intents. Portfolio contexts
include cash-interest attribution and settled and unsettled cash and position quantities. Response
intents use the scenario v13 intent shapes. Market-slice events include lifecycle transitions and
the expanded corporate-action catalog.

External replay requires an empty batch schedule and empty streamed intent batches. The engine
records accepted messages in both directions in a deterministic transcript. A response rejected
for invalid JSON, fields, version, sequence, EOF, or size is never stored as an accepted exchange.
Instead, the partial transcript ends with a `rejected_strategy_response` diagnostic record. Version
1 rejection diagnostics use the shared
[`diagnostic/v1`](../../diagnostic/v1/README.md) contract. The transcript schema narrows that
contract to the `strategy.protocol` and `resource.limit` codes in the `strategy` phase. The record
includes the structured rejection diagnostic and at most the first 256 raw response bytes encoded
as lowercase hexadecimal. `observed_bytes` counts bytes available when the engine rejected the
response, and `truncated` reports whether the prefix omits observed bytes. The transcript and audit
journal retain partial files after failure and finalize only after their respective success checks.

- `message.schema.json` validates individual requests and responses.
- `transcript.schema.json` validates accepted exchanges and rejected-response diagnostics.
- `fixtures/external.scenario.json` is the batch replay fixture.
- `fixtures/external.scenario.jsonl` is its bounded-memory stream form.
- `fixtures/external.strategy.jsonl` is the canonical protocol transcript.
15 changes: 15 additions & 0 deletions contracts/strategy/v11/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
(install
(section share)
(package trading_engine)
(files
(message.schema.json as contracts/strategy/v11/message.schema.json)
(transcript.schema.json as contracts/strategy/v11/transcript.schema.json)
(fixtures/external.scenario.json
as
contracts/strategy/v11/fixtures/external.scenario.json)
(fixtures/external.scenario.jsonl
as
contracts/strategy/v11/fixtures/external.scenario.jsonl)
(fixtures/external.strategy.jsonl
as
contracts/strategy/v11/fixtures/external.strategy.jsonl)))
Loading
Loading