Files
portfolio-hub/docs/db-schema-spec.md
T

158 lines
4.8 KiB
Markdown

# PortfolioHub DB Schema Specification
## 1. Purpose
This document defines the database schema in an AI-agent-friendly format.
- Source of truth: Alembic revisions in `migrations/versions`
- Current baseline revision: `20260403_0001`
- Baseline file: `migrations/versions/20260403_0001_initial_portfolio_schema.py`
## 2. Scope
Schema objects covered by baseline revision:
- `strategy_sets`
- `portfolios`
- `portfolio_ledger`
- secondary indexes
- Timescale hypertable conversion rule for `portfolio_ledger`
## 3. Conventions
- Data type names follow SQLAlchemy/Alembic declaration.
- `NULL` means nullable.
- Default means DB-level `server_default` where defined.
- Relationship columns are documented even when no FK constraint exists.
## 4. Tables
### 4.1 strategy_sets
Purpose:
- Stores normalized strategy configuration snapshots.
Columns:
| Column | Type | Null | Default | Constraints / Notes |
|---|---|---|---|---|
| id | Text | NO | - | Primary Key |
| strategy_name | Text | NO | - | Part of unique pair |
| strategy_config | Text | NO | "" | Part of unique pair |
| created_at | DateTime(timezone=True) | NO | NOW() | Creation timestamp |
Constraints:
- Primary key: `id`
- Unique: `uq_strategy_sets_name_config` on (`strategy_name`, `strategy_config`)
### 4.2 portfolios
Purpose:
- Stores current portfolio state and strategy linkage.
Columns:
| Column | Type | Null | Default | Constraints / Notes |
|---|---|---|---|---|
| portfolio_id | Text | NO | - | Primary Key |
| broker | Text | NO | "kis" | Broker code |
| status | Text | NO | "enabled" | Portfolio status |
| symbol | Text | NO | - | Trading symbol |
| strategy_name | Text | NO | "" | Strategy label |
| strategy_params | JSONB | NO | '{}'::jsonb | Strategy parameters |
| cash_balance | Float | NO | - | Current cash balance |
| asset_quantity | Float | NO | - | Current asset quantity |
| asset_price | Float | NO | 0 | Current price snapshot |
| profit_buffer | Float | NO | 0 | Profit buffer amount |
| current_strategy_set_id | Text | YES | - | Logical reference to `strategy_sets.id` (no FK) |
| created_at | DateTime(timezone=True) | NO | NOW() | Creation timestamp |
| updated_at | DateTime(timezone=True) | NO | NOW() | Last update timestamp |
| last_trade_at | DateTime(timezone=True) | YES | - | Last trade time |
Constraints:
- Primary key: `portfolio_id`
- Foreign key constraints: none in baseline
### 4.3 portfolio_ledger
Purpose:
- Stores append-like ledger snapshots per portfolio action.
Columns:
| Column | Type | Null | Default | Constraints / Notes |
|---|---|---|---|---|
| portfolio_id | Text | NO | - | Logical reference to `portfolios.portfolio_id` (no FK) |
| recorded_at | DateTime(timezone=True) | NO | - | Event timestamp |
| action | Text | NO | - | Action type |
| price | Float | NO | - | Trade/valuation unit price |
| quantity | Float | NO | - | Quantity delta |
| fee_amount | Float | NO | 0 | Fees |
| tax_amount | Float | NO | 0 | Taxes |
| cash_balance_after | Float | NO | - | Post-action cash |
| asset_quantity_after | Float | NO | - | Post-action asset quantity |
| total_value_after | Float | NO | - | Post-action total value |
| profit_buffer_after | Float | NO | 0 | Post-action profit buffer |
| strategy_set_id | Text | NO | "legacy" | Logical strategy set reference |
| note | Text | NO | "" | Operator/system note |
Constraints:
- Primary key: none in baseline
- Foreign key constraints: none in baseline
## 5. Indexes
### 5.1 idx_portfolio_ledger_portfolio_time
- Table: `portfolio_ledger`
- Columns/expressions: (`portfolio_id`, `recorded_at DESC`)
- Unique: no
- Intent: fetch recent ledger entries per portfolio efficiently.
### 5.2 idx_portfolios_broker_status_updated
- Table: `portfolios`
- Columns/expressions: (`broker`, `status`, `updated_at DESC`)
- Unique: no
- Intent: speed list/filter queries by broker/status and recency.
## 6. TimescaleDB Behavior
Conditional behavior in migration upgrade:
- Checks whether extension `timescaledb` exists.
- If present, executes:
`create_hypertable('portfolio_ledger', 'recorded_at', if_not_exists => TRUE)`
Implication:
- `portfolio_ledger` acts as a hypertable in environments with TimescaleDB extension enabled.
## 7. Integrity Notes
Current baseline intentionally has no DB-level FK constraints.
- Logical relations are managed by application/repository logic.
- When adding FK constraints in future migrations, update this document and include migration IDs.
## 8. Change Management Rule
When schema changes:
1. Add/modify Alembic revision under `migrations/versions`.
2. Update this document in the same commit.
3. Keep revision mapping table below in sync.
## 9. Revision Mapping
| Revision ID | File | Summary |
|---|---|---|
| 20260403_0001 | migrations/versions/20260403_0001_initial_portfolio_schema.py | Initial schema for strategy sets, portfolios, ledger, indexes, optional hypertable |