chore: add docs sync guardrails and spec documents
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
# 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 |
|
||||
Reference in New Issue
Block a user