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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ jobs:
- name: Enforce core branch coverage
run: >-
python tools/check_branch_coverage.py coverage.json --threshold 90
artifacts broker contracts schemas engine matching state_machine ledger rules
artifacts broker contracts schemas engine matching state_machine ledger rules dividends
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,24 @@ cash-per-share amount or receivable total, while a registered zero-holding entit
valid zero-effect lifecycle event. Missing payment dates must be rejected by the upstream
corporate-action bridge.

The independent evidence-bound dividend lifecycle uses explicit entitlement, issuer-conversion,
and payment phases under `EVIDENCED_PIT` valuation. Issuer conversion posts the issuer's published
payment amount; the published rate and amount are retained with an exact rational difference and
an `unverified_no_rounding_contract` relationship status because the source contract does not
define how those two printed values were rounded. No tolerance or inferred rounding mode is used.
Scenario execution makes no external-certification claim. Production execution verifies the
entitlement basis, verifies a payment policy when it first affects an amount, and separately
verifies the actual payment. Manifest `trusted_verification_scope` lists only those checked facts;
`market_admission_certified` remains false because this module does not assess complete market
admission.

Manifest `1.1.0` dividend exports seal one ledger snapshot before writing. Opening positions,
external cash, marks, ordinary ledger events, PIT FX observations, valuations, and dividend phases
share one ordered operation sequence. Replay applies those business facts in order and uses final
marks, lots, balances, lifecycle state, transactions, NAV, and journal only as comparison targets.
All public dividend records recursively own immutable nested values, while `to_dict()` returns a
fresh mutable copy.

## Deterministic replay

`DeterministicRunEngine.replay` sorts by availability/event time and stable stream
Expand Down Expand Up @@ -182,7 +200,7 @@ python -m pytest --cov=quant_execution --cov-branch --cov-report=term-missing \
--cov-report=json:coverage.json -q
python -m coverage report --fail-under=80
python tools/check_branch_coverage.py coverage.json --threshold 90 \
broker contracts schemas engine matching state_machine ledger rules artifacts
broker contracts schemas engine matching state_machine ledger rules artifacts dividends
python benchmarks/benchmark_replay.py --workload matching --matching-events 10000000 \
--repeat 3 --require-rate 50000 --memory-limit-gib 16 --artifact-mode arrow \
--artifact-root /dedicated/m7-artifacts --artifact-retention keep \
Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "quant-execution"
version = "0.5.1"
version = "0.6.0"
description = "Deterministic execution, matching, risk and ledger for PureSaber quant research"
readme = "README.md"
license = {text = "MIT"}
Expand Down Expand Up @@ -40,6 +40,8 @@ schemas = [
{ id = "puresaber.execution.account-snapshot", version = "1.1.0" },
{ id = "puresaber.execution.run-result", version = "1.1.0" },
{ id = "puresaber.execution.replay-artifact-manifest", version = "1.0.0" },
{ id = "puresaber.execution.replay-artifact-manifest", version = "1.1.0" },
{ id = "puresaber.execution.dividend-record", version = "1" },
]
lock-files = ["requirements.lock"]

Expand Down
36 changes: 35 additions & 1 deletion src/quant_execution/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

from quant_execution.artifacts import (
ArrowReplayArtifactSink,
DividendReplayResult,
StoredRunArtifacts,
export_dividend_run,
load_stored_artifacts,
replay_dividend_run,
)
from quant_execution.broker import DeterministicBroker, remaining_quantity
from quant_execution.contracts import (
Expand All @@ -30,6 +33,21 @@
Side,
TimeInForce,
)
from quant_execution.dividends import (
DIVIDEND_RECORD_SCHEMA_ID,
DividendEntitlementBasis,
DividendExecutionMode,
DividendExecutionPhase,
DividendExecutionRecord,
DividendExecutionRequest,
DividendExposureItem,
DividendExposureSnapshot,
DividendLifecycleState,
DividendValuationRecord,
EntitlementEvidenceVerifier,
FxValuationMode,
PitFxObservationRecord,
)
from quant_execution.engine import (
DeterministicRunEngine,
ReplayError,
Expand Down Expand Up @@ -77,11 +95,12 @@
)
from quant_execution.state_machine import ALLOWED_TRANSITIONS, transition_order

__version__ = "0.5.1"
__version__ = "0.6.0"

__all__ = [
"ACCOUNT_SNAPSHOT_SCHEMA_ID",
"ALLOWED_TRANSITIONS",
"DIVIDEND_RECORD_SCHEMA_ID",
"FEE_SCHEMA_ID",
"FILL_SCHEMA_ID",
"FUNDING_SCHEMA_ID",
Expand All @@ -102,11 +121,23 @@
"CryptoSpotRule",
"DeterministicBroker",
"DeterministicRunEngine",
"DividendEntitlementBasis",
"DividendExecutionMode",
"DividendExecutionPhase",
"DividendExecutionRecord",
"DividendExecutionRequest",
"DividendExposureItem",
"DividendExposureSnapshot",
"DividendLifecycleState",
"DividendReplayResult",
"DividendValuationRecord",
"EntitlementEvidenceVerifier",
"ExactAccountLedger",
"Fee",
"Fill",
"Funding",
"FuturesRule",
"FxValuationMode",
"L2MatchingModel",
"LedgerEvent",
"LedgerEventType",
Expand All @@ -120,6 +151,7 @@
"OrderIntent",
"OrderStatus",
"OrderType",
"PitFxObservationRecord",
"PortfolioRiskPolicy",
"PortfolioRiskSnapshot",
"PositionRiskSnapshot",
Expand All @@ -140,10 +172,12 @@
"TimeInForce",
"TradeBBOModel",
"execution_payload",
"export_dividend_run",
"get_arrow_schema",
"get_json_schema",
"load_stored_artifacts",
"remaining_quantity",
"replay_dividend_run",
"resolve_a_share_replay_status",
"transition_order",
"validate_arrow_table",
Expand Down
66 changes: 66 additions & 0 deletions src/quant_execution/_fixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

from __future__ import annotations

from collections.abc import Iterable
from decimal import ROUND_DOWN, ROUND_HALF_EVEN, Decimal
from fractions import Fraction

from quant_data_kit import FixedPoint
from quant_data_kit.exceptions import ValidationError
Expand Down Expand Up @@ -48,6 +50,70 @@ def aligned(parts) -> int:
return Decimal((int(total < 0), digits, exponent))


def sum_decimal_exact(values: Iterable[Decimal], start: Decimal = Decimal(0)) -> Decimal:
"""Sum finite decimals exactly without consulting the ambient context."""

total = start
for value in values:
total = add_decimal_exact(total, value)
return total


def decimal_fraction(value: Decimal | FixedPoint | int) -> Fraction:
"""Return the exact rational value of a finite decimal-like value."""

if isinstance(value, FixedPoint):
return Fraction(value.units, 10**value.scale)
if isinstance(value, int) and not isinstance(value, bool):
return Fraction(value)
if not isinstance(value, Decimal) or not value.is_finite():
raise ValidationError("exact fraction conversion requires a finite decimal value")
parts = value.as_tuple()
coefficient = 0
for digit in parts.digits:
coefficient = coefficient * 10 + digit
if parts.sign:
coefficient = -coefficient
exponent = int(parts.exponent)
return (
Fraction(coefficient * 10**exponent)
if exponent >= 0
else Fraction(coefficient, 10 ** (-exponent))
)


def fraction_decimal_exact(value: Fraction) -> Decimal:
"""Convert a finite base-10 fraction to Decimal without rounding."""

if not isinstance(value, Fraction):
raise ValidationError("value must be a Fraction")
numerator = value.numerator
denominator = value.denominator
twos = 0
fives = 0
while denominator % 2 == 0:
denominator //= 2
twos += 1
while denominator % 5 == 0:
denominator //= 5
fives += 1
if denominator != 1:
raise ValidationError("exact decimal result is non-terminating")
scale = max(twos, fives)
coefficient = abs(numerator) * 2 ** (scale - twos) * 5 ** (scale - fives)
digits = tuple(int(digit) for digit in str(coefficient)) if coefficient else (0,)
return Decimal((int(numerator < 0), digits, -scale))


def multiply_decimal_exact(*values: Decimal | FixedPoint | int) -> Decimal:
"""Multiply finite decimal-like values exactly."""

result = Fraction(1)
for value in values:
result *= decimal_fraction(value)
return fraction_decimal_exact(result)


def floor_to_scale(value: Decimal, scale: int) -> FixedPoint:
return fixed(value, scale, rounding=ROUND_DOWN)

Expand Down
Loading
Loading