Metadata-Version: 2.4
Name: pycustodian
Version: 0.0.2
Summary: Event-sourced double-entry book of record for trading and wealth platforms
Project-URL: Homepage, https://github.com/optophi/pycustodian
Project-URL: Documentation, https://optophi.github.io/pycustodian/
Project-URL: Repository, https://github.com/optophi/pycustodian
Project-URL: Issues, https://github.com/optophi/pycustodian/issues
Project-URL: Changelog, https://github.com/optophi/pycustodian/blob/main/CHANGELOG.md
Project-URL: Contributing, https://github.com/optophi/pycustodian/blob/main/CONTRIBUTING.md
Author-email: Optophi <contact@optophi.com>
License-Expression: MIT
License-File: LICENSE
Keywords: book-of-record,cost-basis,double-entry,finance,ledger,pnl,positions,trading,wealth
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: pydantic>=2.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: all
Requires-Dist: alembic>=1.10; extra == 'all'
Requires-Dist: confluent-kafka>=2.0; extra == 'all'
Requires-Dist: fastapi>=0.100; extra == 'all'
Requires-Dist: psycopg[binary]>=3.1; extra == 'all'
Requires-Dist: sqlalchemy>=2.0; extra == 'all'
Requires-Dist: uvicorn>=0.20; extra == 'all'
Provides-Extra: api
Requires-Dist: fastapi>=0.100; extra == 'api'
Requires-Dist: uvicorn>=0.20; extra == 'api'
Provides-Extra: db
Requires-Dist: alembic>=1.10; extra == 'db'
Requires-Dist: sqlalchemy>=2.0; extra == 'db'
Provides-Extra: dev
Requires-Dist: alembic>=1.10; extra == 'dev'
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: fastapi>=0.100; extra == 'dev'
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: hypothesis>=6.80; extra == 'dev'
Requires-Dist: import-linter>=2.0; extra == 'dev'
Requires-Dist: mypy>=1.5; extra == 'dev'
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest-timeout>=2.1.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: ruff>=0.9.0; extra == 'dev'
Requires-Dist: sqlalchemy>=2.0; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Requires-Dist: uvicorn>=0.20; extra == 'dev'
Provides-Extra: kafka
Requires-Dist: confluent-kafka>=2.0; extra == 'kafka'
Provides-Extra: postgres
Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
Provides-Extra: worker
Requires-Dist: alembic>=1.10; extra == 'worker'
Requires-Dist: sqlalchemy>=2.0; extra == 'worker'
Description-Content-Type: text/markdown

# pycustodian

> **The book of record for trading and wealth platforms.**

pycustodian is the single, durable source of truth for **positions, cost basis,
cash, and P&L**. It ingests fills, records them in an **event-sourced
double-entry** journal, and serves positions, exposure, current weights, and NAV
to the rest of a platform (risk, optimization, dashboards).

It is a **service built around a pure, extractable domain core** (hexagonal
architecture): the accounting logic in `pycustodian.domain` is I/O-free and
deterministic; persistence (SQL event store), transport (FastAPI), and live
ingestion (Kafka) are adapters around it.

## Why it exists

Decisioning, risk, and execution can all be stateless or transient. The book of
record cannot: it must be the one authoritative, always-consistent copy of the
truth, with transactional integrity and exactly-once fill ingestion. That makes
it a service — but its accounting math stays a clean, testable library at the core.

## Design rules

1. **The domain core is pure.** `pycustodian.domain` imports no infrastructure
   (enforced by an import-linter contract). It can be extracted into a standalone
   package the day a second consumer (e.g. a backtester) needs the math.
2. **The journal is append-only.** Positions, cash, P&L, and NAV are *projections*
   folded from the immutable transaction log. Corrections are reversing entries,
   never mutations — giving a full audit trail and point-in-time replay.
3. **Money is `Decimal`, never `float`.**

## Status

Early foundation. The pure domain core (double-entry journal, FIFO/LIFO/HIFO/
AVERAGE cost basis, projections, valuation, performance) and its test suite are
in place. Persistence, application use cases, the API, Kafka ingest, and family
integrations follow.

## Install

```bash
pip install pycustodian                 # core (pure domain + config + CLI)
pip install "pycustodian[db,api,kafka]" # + durable store, HTTP service, live ingest
```

## License

MIT
