quantcore 0.3.0
Pure trading math and domain models: orders, positions, portfolio, PnL, sizing, risk.
License: MIT
Requires Python: >=3.11

# Quantcore

**Pure Python trading math and domain models for quantitative finance.**

Quantcore is a lightweight, dependency-free library providing core primitives for trading systems: orders, positions, portfolio accounting, PnL calculation, risk metrics, sizing, health/monitoring, security utilities, and JSON serialization. All math uses `Decimal` to avoid floating-point drift.

[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

> Disclaimer: This library is for informational and educational use only. It is not financial, investment, or trading advice. Use at your own risk. The authors and contributors accept no responsibility for any losses or damages. Ensure your usage complies with applicable laws and regulations.

How this fits into a trading stack:

```
Signals/Orders -> Sizing -> Fill events -> Quantcore (positions | portfolio | PnL | risk) -> Logs/metrics/equity -> Backtester/execution layer
```

---

## Features

- **Decimal-first math** - High-precision arithmetic for PnL, risk, sizing, and funding
- **Immutable positions** - Lot-level cost basis tracking (FIFO/LIFO/Average)
- **Multi-currency portfolio** - FX conversion, ledger, and cash flow accounting
- **Comprehensive PnL** - Realized, unrealized, funding, fees, slippage breakdowns
- **Risk & margin** - Sharpe/Sortino, drawdown, VaR, liquidation prices
- **Position sizing** - Fixed, percent-based, risk-based, Kelly criterion
- **Order types** - Market, limit, stop, trailing stop, iceberg
- **Observability** - Health checks plus metrics collection for timings/counters/gauges
- **Security utilities** - Audit trail, rate limiting, and input sanitization helpers
- **Serialization** - JSON codecs for positions, portfolios, ledger, and events
- **Zero dependencies** - Pure Python stdlib + `decimal`

---

## Installation

```bash
pip install quantcore
```

For local development:

```bash
pip install -e ".[dev]"
```

**Requirements:** Python >= 3.11

---

## Quick Start

### 1. Initialise Quantcore (recommended)

```python
from quantcore import init_quantcore, QuantcoreConfig

config = init_quantcore(
    env="dev",              # "dev"/"development" | "test"/"testing" | "prod"/"production" | None
    run_health_checks=True, # run basic health checks on startup
    # config=QuantcoreConfig.development(),  # optional: pass an explicit config
)
```

QUANTCORE_ENV accepts the same values and defaults to "production" when unset.
Passing an explicit `config` bypasses env parsing and overrides.
Invalid configs raise `ConfigError`; failed startup health checks raise `HealthCheckError`.

This:

- Sets the global `Decimal` context (precision, rounding).
- Configures Quantcore logging.
- Optionally runs health checks and raises if unhealthy.

Most of the public surface is re-exported from `quantcore` for convenience (`init_quantcore`, `QuantcoreConfig`, `ConfigError`, `HealthCheckError`, `check_health`, validators, and core exceptions).

You can still access the active decimal context if needed:

```python
from quantcore import decimal_context

print(decimal_context.prec)
```

Basic validation helpers:
- `validate_fill_sequence(fills)` to catch malformed/duplicate fills early.
- `validate_portfolio_state(portfolio)` to spot negative balances or mismatched keys.
- `normalize_symbol(raw, market=spot|perp|future|option)` to coerce exchange symbols to the canonical `BASE-QUOTE[...]` shape (e.g. `BTC/USDT` -> `BTC-USDT`, `BTCUSDT` perp -> `BTC-USDT-PERP`); used automatically by `Fill.from_exchange_fill(..., market=...)` and `sanitize_all_fields(symbol=..., market=...)` so you rarely need to call it directly.

### Environment profiles

- Precedence: `env` argument (if provided) overrides `QUANTCORE_ENV`; fallback is `"production"`.
- Accepted values: `dev`/`development`, `test`/`testing`, `prod`/`production`.

| Env          | Decimal precision | Log level | Audit logging | Performance logging | Health checks on init |
|--------------|-------------------|-----------|---------------|---------------------|-----------------------|
| development  | 28                | DEBUG     | On            | On                  | Yes                   |
| testing      | 28                | ERROR     | Off           | Off                 | Yes (skips operations)|
| production   | 34                | INFO      | On            | Off                 | Yes                   |

### 2. Position & PnL Calculation

```python
from decimal import Decimal
from quantcore import OrderSide, Fill, calculate_pnl, PnLMode
from quantcore import BasePositionImpl

# Open position
pos = BasePositionImpl.flat("BTC-USD")
pos = pos.apply_fill(
    Fill("buy1", "BTC-USD", OrderSide.BUY, Decimal("1"), Decimal("10000"), Decimal("10"), 0)
)

# Partial close
pos = pos.apply_fill(
    Fill("sell1", "BTC-USD", OrderSide.SELL, Decimal("0.4"), Decimal("11000"), Decimal("4"), 1)
)

# Calculate PnL
pnl = calculate_pnl(position=pos, mark_price=Decimal("10500"), mode=PnLMode.BOTH)
print(f"Realized: {pnl.realized}, Unrealized: {pnl.unrealized}, Total: {pnl.total}")
```

### 3. Portfolio, Funding, and Metrics

```python
from decimal import Decimal
from quantcore import Portfolio
from quantcore.events.funding import FundingEvent
from quantcore import timed_operation, get_metrics

portfolio = Portfolio()
fills = [...]  # list[Fill]

with timed_operation("apply_fill_batch"):
    for fill in fills:
        portfolio.apply_fill(fill)

funding = FundingEvent(
    instrument_id="BTC-PERP",
    rate=Decimal("0.0001"),
    period_start_ms=0,
    period_end_ms=28800000,  # 8 hours
    index_price=Decimal("10500"),
)
portfolio.apply_funding(funding, fills=fills)

metrics = get_metrics().get_stats()
print(metrics["timings"]["apply_fill_batch"])
```

### 4. Security & Serialization

```python
from decimal import Decimal
from quantcore import AuditTrail, rate_limit, sanitize_all_fields, save_to_file, load_from_file, Portfolio

audit = AuditTrail()
audit.record_fill(
    user_id="user1",
    order_id="o1",
    instrument_id="BTC-USD",
    side="BUY",
    quantity=Decimal("1"),
    price=Decimal("10000"),
    fee=Decimal("10"),
)

@rate_limit(max_calls=10, time_window=1.0, identifier="order_submit")
def submit_order(payload: dict):
    safe = sanitize_all_fields(payload)
    # ... send to exchange
    return safe

portfolio = Portfolio()
save_to_file(portfolio, "portfolio.json")
restored = load_from_file("portfolio.json", Portfolio)
```

### 5. Health Checks

```python
from quantcore import check_health

health = check_health()
if not health.is_healthy:
    raise SystemExit(f"Unhealthy: {health.errors}")
```

### Minimal backtester loop

- See `examples/backtester_minimal.py` for a runnable backtest-style loop (bars -> fills -> portfolio -> equity).
- Canonical fill identifier: use `order_id` for your fill/trade ID (exchange fill ID, composite order+seq, etc.).
- Helpers: `Fill.from_exchange_fill(...)` parses common exchange payloads; `apply_fills_to_portfolio(portfolio, fills, timed_metric="fill_batch")` applies batches safely.

```python
from decimal import Decimal
from quantcore import OrderSide, Fill, apply_fills_to_portfolio, Portfolio

portfolio = Portfolio()
fills = [
    Fill.from_dict({
        "order_id": "trade-1",
        "instrument_id": "BTC/USD",  # raw symbols auto-normalize to BTC-USD
        "side": "BUY",
        "quantity": "0.1",
        "price": "10000",
        "fee": "1",
        "timestamp_ms": 0,
    })
]
apply_fills_to_portfolio(portfolio, fills, timed_metric="apply_fill_batch")
```

### Using Quantcore in a backtester

- Map signals → target size → fills: build `Fill` objects (use `order_id` as the canonical fill/trade ID) and apply via `Portfolio.apply_fill` or `apply_fill_batch`.
- Mark to market with `calculate_portfolio_pnl(...)` and set `portfolio.account.update_unrealized_pnl(pnl.total.unrealized)` to get equity (`portfolio.account.equity`).
- Include fees (`fee` on fills or `FeeEvent`), funding (`FundingEvent`), and margin requirements from the `margin` module when sizing positions.
- Multi-asset sketch:

```python
marks = {"BTC-USD": Decimal("31000"), "ETH-USD": Decimal("1800")}
pnl = calculate_portfolio_pnl(portfolio=portfolio, marks=marks, mode=PnLMode.BOTH)
portfolio.account.update_unrealized_pnl(pnl.total.unrealized)
equity_usd = portfolio.account.equity(base_currency="USD", rates={"USDT": Decimal("1")})
```

---

## Architecture

| Module | Purpose |
|--------|---------|
| `quantcore.core` | Base types (`Money`, `Price`, `Quantity`), enums, exceptions |
| `quantcore.positions` | Position models with lot-level cost basis |
| `quantcore.portfolio` | Portfolio container, account balances, ledger |
| `quantcore.pnl` | PnL calculation engine with funding segmentation |
| `quantcore.orders` | Order types and state machine |
| `quantcore.risk` | Risk metrics, liquidation prices, exposure |
| `quantcore.margin` | Initial/maintenance margin, utilization |
| `quantcore.sizing` | Position sizing strategies |
| `quantcore.fees` | Fee calculation and VIP tiers |
| `quantcore.pricing` | Mark price, VWAP, slippage estimation |
| `quantcore.instruments` | Spot, perpetual, futures, options specs |
| `quantcore.data` | Market data structures (candles, orderbook, trades) |
| `quantcore.math` | Decimal rounding, returns, statistics, volatility |
| `quantcore.time` | Timestamp conversion, intervals |
| `quantcore.monitoring` | Metrics collection (timings, counters, gauges) |
| `quantcore.security` | Audit trail, rate limiting, sanitization helpers |
| `quantcore.serialization` | JSON codecs for portfolio, positions, ledger, events |
| `quantcore.health` | System health checks for config/imports/operations |
| `quantcore.utils` | Validation, formatting, logging, serialization helpers |

---

## Documentation

- **[Concepts](docs/concepts.md)** - Core ideas and data flow
- **[Quick Reference](docs/quickref.md)** - One-page cheatsheet
- **[API Reference](docs/api.md)** - Module documentation
- **[Production Guide](docs/PRODUCTION_GUIDE.md)** - Hardening, monitoring, and operations
- **[Changelog](CHANGELOG.md)** - Release notes
- **[Minimal backtester loop](examples/backtester_minimal.py)** - Bars -> fills -> portfolio -> equity

---

## Testing

```bash
pip install -e ".[dev]"
pytest --cov=quantcore --cov-report=term-missing
ruff check src tests
black --check src tests
mypy --config-file mypy.ini src
```

---

## Design Principles

1. **Pure functions** - Immutable data structures, no side effects
2. **Type hints** - Full type coverage for IDE support
3. **Decimal precision** - No floating-point drift in financial calculations
4. **Zero dependencies** - Easy to audit, vendor, or embed
5. **Engine-agnostic** - Plug into any backtest or execution system

---

## Limitations

- **Not a trading engine** - No order matching, execution, or market connectivity
- **Not a backtester** - No historical simulation framework
- **Not a broker adapter** - No exchange API integrations
- **In-memory only** - No persistence layer (use your own DB)

Quantcore provides the **math and domain models**. You build the engine around it.

---

## Roadmap / Non-goals

- Pre-1.0 and experimental: minor breaking changes can occur between releases; tags will note breaking changes.
- No plan to build a full backtester or exchange adapters; thin glue/examples may be added to ease integration with existing engines.
- Focus remains on correctness and auditability of trading math, not on orchestration or networking.

---

## Stability & Releases

- Tagged releases live in `CHANGELOG.md`; use tags (e.g., `v0.1.0`) for pinning.
- Targeting semantic versioning as the API stabilises; pre-1.0 may ship small breaking changes with clear notes.

---

## Performance (example)

- **Portfolio updates**: ~1.6ms for 1000 fills
- **PnL calculation**: ~0.8ms for 1000-position portfolio
- **Cost basis matching**: ~0.12ms per FIFO match on 100 lots
- **Funding segmentation**: ~0.03ms per event with fill segments

See `benchmarks/` for reproducible tests.

---

## Benchmarks

```bash
pip install -e ".[dev]"
python benchmarks/portfolio.py
python benchmarks/pnl.py
```

Expect results in the same ballpark as the numbers above on a recent laptop CPU; large deviations usually indicate missing `Decimal` context setup or a hot loop regression.

---

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on code style, testing, and PR workflow.

---

## License

MIT License - see [LICENSE](LICENSE) for details.

---

## Support

- **Issues**: GitHub Issues
- **Discussions**: GitHub Discussions

Built for quantitative traders who need reliable, auditable trading math without heavyweight frameworks.