Metadata-Version: 2.4
Name: trading-platform-core
Version: 1.0.0rc2
Summary: Engine-agnostic Python library providing shared infrastructure for algorithmic trading engines (Swinger, Creeper, future engines).
Project-URL: Homepage, https://github.com/michaeleasterly-cpu/trading-platform-core
Project-URL: Repository, https://github.com/michaeleasterly-cpu/trading-platform-core
Author: Michael Easterly
License-Expression: LicenseRef-Proprietary
License-File: LICENSE
Keywords: alpaca,backtest,broker,risk-governor,trading
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Financial :: Investment
Requires-Python: >=3.11
Requires-Dist: alembic>=1.13
Requires-Dist: alpaca-py>=0.30.0
Requires-Dist: psycopg2-binary>=2.9
Requires-Dist: pydantic<3.0,>=2.0
Requires-Dist: requests>=2.31
Requires-Dist: sqlalchemy<3.0,>=2.0
Provides-Extra: dev
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=4.1; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.3; extra == 'dev'
Requires-Dist: types-requests>=2.31; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
Requires-Dist: mkdocs>=1.5; extra == 'docs'
Requires-Dist: mkdocstrings[python]>=0.24; extra == 'docs'
Description-Content-Type: text/markdown

> **PROPRIETARY — PERSONAL USE ONLY.** This software is licensed under a proprietary, personal-use-only license. See [LICENSE](LICENSE) for full terms. Commercial deployment is BLOCKED without explicit Legal-Hat ratification per upstream creeper D-096 Sub-decision 6.

# trading-platform-core

Engine-agnostic Python library providing shared infrastructure for algorithmic trading engines (Swinger, Creeper, future engines).

---

## What it is

`trading-platform-core` is a **library**, not a service. Engines import it as a pinned dependency; it does not run as a standalone process, has no webhook listeners, no cron deployments, and no service runtime. There is no Railway / Vercel / Fly footprint.

It provides:

- Provider-abstraction ABCs (`DataProviderInterface`, `BrokerExecutionInterface`)
- Concrete Alpaca adapters (data, broker, paper-account readiness gate)
- Quality-scoring Pydantic models + DB writers (`DataQualityScore`, `ExecutionQualityScore`)
- `RiskGovernor` state machine (per-engine instance, hard-limit enforcement)
- AAR (After-Action Report) Pydantic models + writer to `platform.aar_events`
- Live-vs-paper parity harness (drift detection)
- Generic Discord alerting (engine-agnostic)
- Callback-driven backtest harness with 14-dimension `BacktestCredibilityScore` rubric

It does **not** contain engine-specific trading logic, hardcoded strategy parameters, or any signal-generation code. Engines inject those via the public API (e.g., `SignalGenerator` callback into the backtest harness).

See [`MASTER_PLAN.md`](MASTER_PLAN.md) for the full architecture, decision log, and component contracts.

## Mission and scope

> **PERSONAL USE ONLY.** This library is licensed Proprietary per the [`LICENSE`](LICENSE) file (declared in [`pyproject.toml`](pyproject.toml) `project.license`). Commercial deployment is BLOCKED without explicit Legal-Hat ratification. See [`CLAUDE.md`](CLAUDE.md) "Project Identity" for the upstream scope guard (creeper D-096 Sub-decision 6).

The library exists to ensure that every engine in the family shares a single source of truth for provider abstraction, point-in-time (PIT) data discipline, risk governance, AAR auditability, and backtest credibility — so engine-side code stays focused on signal generation, not infrastructure plumbing.

## Component overview

| Component | Module | Purpose |
|-----------|--------|---------|
| ABCs | `tpcore.interfaces.data_provider`, `tpcore.interfaces.broker_execution` | `DataProviderInterface` + `BrokerExecutionInterface` — every engine talks to providers exclusively through these. No direct vendor SDK calls in engine code. |
| Alpaca adapters | `tpcore.alpaca.data_adapter`, `tpcore.alpaca.broker_adapter`, `tpcore.alpaca.readiness_gate` | `AlpacaDataAdapter` (Market Data API v2), `AlpacaBrokerAdapter` (Trading API; paper/live via `ALPACA_PAPER`; stop→stop-limit conversion; `client_order_id` idempotency; `emergency_flatten` with limit-widening), `AlpacaReadinessGate` (pytest suite, must pass before any engine goes live). |
| Quality models | `tpcore.quality.data_quality`, `tpcore.quality.execution_quality` | `DataQualityScore` + `ExecutionQualityScore` Pydantic models + DB writers to `platform.data_quality_log` / `platform.execution_quality_log`. Bitemporal (`valid_at` + `recorded_at`) for PIT safety. |
| AAR pipeline | `tpcore.aar.models`, `tpcore.aar.writer` | Engine-agnostic AAR Pydantic models + `AARWriter` to `platform.aar_events` (canonical schema per D-139 — adopts upstream creeper D-140 framework-portable schema; 18 typed columns + UNIQUE on `(engine_id, trade_table, trade_id)` + 7 CHECK constraints + 4 indexes + RLS). Auto-normalises `setup_confidence` to 0-100; embeds `_schema_version` in JSONB; writes `engine_version`. |
| RiskGovernor | `tpcore.risk.governor` | Per-engine state machine; tracks daily/weekly P&L + open positions; `check_trade()` gate; `record_loss()` / `record_win()` accumulators; hard limits (`max_daily_loss_percent` 2%, `max_weekly_loss_percent` 4%, `max_open_positions` 8 — defaults; configurable per engine); `emergency_kill()` flag. No engine ships live without governor ratification of every trade. |
| ParityHarness | `tpcore.parity.live_paper_harness` | `LivePaperParityHarness` — submits identical orders to paper + live (when both enabled), compares fill prices / timestamps / slippage, logs to `platform.parity_drift_log`, alerts on drift threshold breach (default 0.2% per order, 1% cumulative weekly). |
| Discord alerts | `tpcore.alerts.discord` | Generic `send_discord_alert(webhook_url, title, fields, color)`. Engine-supplied webhook URL via env. No engine-specific formatting in the library. HTTP via `requests` (declared dep; `httpx` swap is one-line forward change — see [`MASTER_PLAN.md`](MASTER_PLAN.md) §3.7). |
| Backtest harness | `tpcore.backtest.harness` | Callback-driven, provider-agnostic. Accepts `DataProviderInterface` instance + `SignalGenerator: Callable[[date, DataSnapshot], List[Signal]]` callback (signature FROZEN at v1.0.0 per MP §3.8 design decision A) + slippage/spread/commission `Protocol`s. Returns full report with PIT verification, survivorship check, configurable walk-forward (calendar days), and 14-dimension `BacktestCredibilityScore` rubric (graduation threshold ≥70 default, configurable per engine). |

Full per-component specifications: [`MASTER_PLAN.md`](MASTER_PLAN.md) §3.1-§3.8.

## Installation

### From PyPI (engines, production)

Engines pin to the current major version, allowing additive minor / patch upgrades:

```bash
pip install "trading-platform-core>=1.0,<2.0"
```

> **Note:** The PyPI distribution name is `trading-platform-core`; the Python import name is `tpcore`. This split is intentional — see [`MASTER_PLAN.md`](MASTER_PLAN.md) §2 Naming note. Importing as `tpcore` avoids shadowing the Python stdlib `platform` module (which `setuptools`, `pip`, and many CLI tools depend on via `platform.system()`).

### Editable install (development)

For library development, including the `dev` optional-dependency block (pytest, mypy, ruff, etc., per [`pyproject.toml`](pyproject.toml) `project.optional-dependencies.dev`):

```bash
git clone git@github.com:michaeleasterly-cpu/trading-platform-core.git
cd trading-platform-core
pip install -e ".[dev]"
```

Minimum Python version: **3.11** (declared in `pyproject.toml` `requires-python`). Tested on 3.11 and 3.12.

## Quick start

```python
import os
from tpcore.alpaca.data_adapter import AlpacaDataAdapter
from tpcore.alpaca.broker_adapter import AlpacaBrokerAdapter

# Engines parse ALPACA_PAPER themselves and pass it to the adapter constructors.
# `ALPACA_PAPER=true` (default) → paper endpoint; `ALPACA_PAPER=false` → live endpoint.
paper = os.environ.get("ALPACA_PAPER", "true").lower() == "true"

data = AlpacaDataAdapter(
    api_key=os.environ["ALPACA_API_KEY"],
    api_secret=os.environ["ALPACA_API_SECRET"],
    paper=paper,
)

broker = AlpacaBrokerAdapter(
    api_key=os.environ["ALPACA_API_KEY"],
    api_secret=os.environ["ALPACA_API_SECRET"],
    paper=paper,
)

# Both adapters implement the engine-agnostic ABCs:
#   tpcore.interfaces.data_provider.DataProviderInterface
#   tpcore.interfaces.broker_execution.BrokerExecutionInterface
# Engine code should type-annotate against the ABCs, never the concrete class.

bars = data.get_daily_bars(symbol="SPY", start="2025-01-01", end="2025-12-31")
account = broker.get_account()
assert account.is_paper is True  # paper account confirmed
```

A typical engine wires the adapters into a `RiskGovernor`, `AARWriter`, and (for backtests) the harness with an engine-specific `SignalGenerator` callback. See [`MASTER_PLAN.md`](MASTER_PLAN.md) §6 for the full integration pattern.

### Required environment variables

| Variable | Purpose |
|----------|---------|
| `ALPACA_API_KEY` | Alpaca API key (paper or live, matching `ALPACA_PAPER`) |
| `ALPACA_API_SECRET` | Alpaca API secret (paper or live, matching `ALPACA_PAPER`) |
| `ALPACA_PAPER` | `true` (default) routes to the paper endpoint; `false` routes to the live endpoint. Engines parse this and pass `paper=` to the adapter constructors. |
| `TPCORE_DATABASE_URL` | Postgres connection string for `platform.*` tables (engine-supplied; consumed by `migrations/env.py`) |

## Postgres setup

The library ships Alembic migrations under [`migrations/versions/`](migrations/versions/). Engines provide the connection (the library does not own credentials or pool config). Today there are five migrations:

| Migration | Table |
|-----------|-------|
| `001_data_quality_log.py` | `platform.data_quality_log` |
| `002_execution_quality_log.py` | `platform.execution_quality_log` |
| `003_aar_events.py` | `platform.aar_events` (per D-139 / upstream D-140) |
| `004_parity_drift_log.py` | `platform.parity_drift_log` |
| `005_add_dq_provenance_columns.py` | additive: `data_quality_log` provenance columns |

To apply migrations against an engine-provided database:

```bash
export TPCORE_DATABASE_URL="postgresql+psycopg2://user:pass@host:5432/db"
alembic upgrade head
```

Every `platform.*` table includes:

- `engine` column (`'creeper'`, `'swinger'`, etc.) — for engine-scoped writes / RLS
- `valid_at` + `recorded_at` — bitemporal columns for point-in-time (PIT) historical queries

PIT-safe historical reads use the canonical pattern:

```sql
WHERE valid_at <= :date AND recorded_at <= :date
```

For Alembic conventions (revision ordering, downgrade discipline, schema-version embedding in JSONB columns), see [`MIGRATION.md`](MIGRATION.md).

## Engine integration

Swinger and Creeper are the two reference consumers; both follow the integration pattern documented in [`MASTER_PLAN.md`](MASTER_PLAN.md) §6:

1. Pin `trading-platform-core>=1.0,<2.0` in the engine's dependency manifest.
2. Import providers + governor + writer through the ABC-typed entrypoints (`from tpcore.interfaces.data_provider import DataProviderInterface`, etc.).
3. Instantiate **one `RiskGovernor` per engine** (separate state files; never share an instance across engines).
4. Provide an engine-specific `SignalGenerator` callback to the backtest harness (the harness has zero awareness of squeeze / swing / any other strategy semantics).
5. Write AARs through `AARWriter` (the writer auto-attaches `engine` + `engine_version`).

Canonical engine wiring snippet:

```python
from tpcore.interfaces.data_provider import DataProviderInterface
from tpcore.interfaces.broker_execution import BrokerExecutionInterface
from tpcore.alpaca.data_adapter import AlpacaDataAdapter
from tpcore.alpaca.broker_adapter import AlpacaBrokerAdapter
from tpcore.risk.governor import RiskGovernor
from tpcore.aar.writer import AARWriter

# Engine code annotates against the ABCs, not the concrete adapter classes.
data: DataProviderInterface = AlpacaDataAdapter(...)
broker: BrokerExecutionInterface = AlpacaBrokerAdapter(...)

# Per-engine governor instance — never share across engines.
governor = RiskGovernor(
    engine="swinger",
    max_daily_loss_percent=2.0,
    max_weekly_loss_percent=4.0,
    max_open_positions=8,
)

aar_writer = AARWriter(engine=os.environ["TPCORE_DATABASE_URL"])

# Pre-trade gate
allowed, reason = governor.check_trade(symbol="AAPL", proposed_size_usd=2500)
if not allowed:
    logger.info("trade rejected: %s", reason)
```

Engine repositories must NOT contain:

- Direct Alpaca SDK calls (use the ABCs)
- Hardcoded provider credentials (env vars only)
- Duplicate quality / risk / AAR logic (import from `tpcore`)

See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the contributor workflow (adding new adapters, modifying ABCs, writing tests, opening PRs).

## Design discipline

Three non-negotiables enforced by the library and verified in CI:

1. **Provider abstraction is mandatory.** Every Alpaca SDK call lives behind an ABC method. Engines depending on `alpaca-py` directly is a process violation. The ABCs are versioned with the library and frozen at v1.0.0 (additive-only after that).
2. **Point-in-time (PIT) safety.** Historical reads always carry `valid_at` + `recorded_at` predicates. Bitemporal columns are required on every `platform.*` table; backtest harness verification fails the run if a snapshot includes data with `recorded_at > backtest_date`.
3. **Risk governor before live capital.** No engine ships live without `RiskGovernor.check_trade()` ratifying every order. The governor's `emergency_kill()` flag is engine-callable and blocks all new orders until explicitly reset.

Component-level design decisions (e.g., `SignalGenerator` signature freeze, `DataSnapshot` representation, slippage/spread/commission as `Protocol` rather than ABC, walk-forward window semantics, `BacktestCredibilityScore` graduation threshold) are locked in [`MASTER_PLAN.md`](MASTER_PLAN.md) §3.8 design-decisions A–E. The MASTER_PLAN is canonical for design — never re-litigate decisions in code comments.

## Versioning

Semantic versioning per [`MASTER_PLAN.md`](MASTER_PLAN.md) §7:

| Bump | Trigger |
|------|---------|
| **MAJOR** | Breaking changes to public ABCs or `platform.*` database schemas |
| **MINOR** | New optional ABC methods, new adapters, additive schema columns, non-breaking enhancements |
| **PATCH** | Bug fixes, documentation, internal refactors with zero API surface impact |

Pre-release tags follow PEP 440 (`v1.0.0-rc.1`, etc.). Engines pin to the current major (`>=1.0,<2.0`); new majors require a coordinated migration across consuming engines.

**Backward-compatibility commitment after v1.0.0:** zero breaking changes to public ABCs or schemas without a major bump.

## Testing

```bash
# All unit tests (mocked external APIs)
pytest tests/unit/

# Integration tests (requires Alpaca paper credentials in env + Postgres connection)
pytest -m integration tests/integration/

# Coverage report
pytest --cov=tpcore --cov-report=term-missing
```

Coverage target: **≥80%** for `tpcore.*` per `MASTER_PLAN.md` §8 / §10. Integration runtime target: **<2 minutes** per the success-metrics table in `MASTER_PLAN.md` §10.

## Repository layout

```
trading-platform-core/
├── tpcore/                # Importable library code
│   ├── interfaces/        # DataProviderInterface, BrokerExecutionInterface ABCs
│   ├── alpaca/            # AlpacaDataAdapter, AlpacaBrokerAdapter, AlpacaReadinessGate
│   ├── quality/           # DataQualityScore, ExecutionQualityScore
│   ├── risk/              # RiskGovernor
│   ├── aar/               # AAR models + AARWriter
│   ├── parity/            # LivePaperParityHarness
│   ├── alerts/            # Discord webhook sender
│   └── backtest/          # Callback-driven backtest harness
├── migrations/            # Alembic migrations for platform.* schema
├── tests/
│   ├── unit/              # Mocked-API unit tests
│   └── integration/       # Alpaca paper + Postgres integration tests
├── pyproject.toml         # PyPI metadata, deps, dev extras
├── alembic.ini
├── README.md              # This file
├── CONTRIBUTING.md        # Contributor workflow
├── MIGRATION.md           # Schema-migration guide
├── MASTER_PLAN.md         # Architecture + decision log
└── LICENSE                # Proprietary — personal use only
```

Full structural reference: [`MASTER_PLAN.md`](MASTER_PLAN.md) §2.

## License

Proprietary — personal use only. See [`LICENSE`](LICENSE) for the full text and [`pyproject.toml`](pyproject.toml) `project.license` for the declarative metadata. Commercial deployment is BLOCKED without explicit Legal-Hat ratification per the upstream scope guard (creeper D-096 Sub-decision 6, mirrored in [`CLAUDE.md`](CLAUDE.md) "Project Identity").
