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

10 KiB

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_0007
  • Baseline files:
    • migrations/versions/20260403_0001_initial_portfolio_schema.py
    • migrations/versions/20260403_0002_strategy_set_columns.py
    • migrations/versions/20260403_0003_portfolio_strategy_refs.py
    • migrations/versions/20260403_0004_users_table.py
    • migrations/versions/20260403_0005_auth_user_fields_and_tokens.py
    • migrations/versions/20260403_0006_bot_principals_and_bot_tokens.py
    • migrations/versions/20260403_0007_bot_principal_permission_only.py

2. Scope

Schema objects covered by baseline revision:

  • strategy_sets
  • users
  • auth_tokens
  • bot_principals
  • 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 users

Purpose:

  • Stores admin-managed user records for operational ownership and future identity mapping.

Columns:

Column Type Null Default Constraints / Notes
id Text NO - Primary Key
username Text NO - Unique username
email Text YES - Unique when present
permission Text NO "read" User permission level
is_active Boolean NO TRUE Whether login is allowed
password_hash Text YES - Scrypt password hash
created_at DateTime(timezone=True) NO NOW() Creation timestamp
updated_at DateTime(timezone=True) NO NOW() Last update timestamp
last_login_at DateTime(timezone=True) YES - Last successful login timestamp

Constraints:

  • Primary key: id
  • Unique: uq_users_username on (username)
  • Unique: uq_users_email on (email)

4.2 auth_tokens

Purpose:

  • Stores DB-issued session tokens and client tokens.

Columns:

Column Type Null Default Constraints / Notes
id Text NO - Primary Key
user_id Text NO - Logical user reference
name Text NO - Human-readable token name
token_prefix Text NO - Display prefix for issued token
token_hash Text NO - Unique SHA-256 token hash
permission Text NO - Effective token permission
token_type Text NO - session, client, or bot
bot_principal_id Text YES - Logical reference to bot_principals.id
expires_at DateTime(timezone=True) YES - Expiration time
last_used_at DateTime(timezone=True) YES - Last successful use
revoked_at DateTime(timezone=True) YES - Revocation time
created_at DateTime(timezone=True) NO NOW() Creation timestamp

Constraints:

  • Primary key: id
  • Unique: uq_auth_tokens_token_hash on (token_hash)

4.3 bot_principals

Purpose:

  • Stores external bot identities for admin-managed API key issuance and lifecycle control.

Columns:

Column Type Null Default Constraints / Notes
id Text NO - Primary Key
name Text NO - Unique bot name
permission Text NO - Bot permission ceiling
description Text YES - Optional operator description
is_active Boolean NO TRUE When false, bot tokens are rejected
created_at DateTime(timezone=True) NO NOW() Creation timestamp
updated_at DateTime(timezone=True) NO NOW() Last update timestamp
deactivated_at DateTime(timezone=True) YES - Deactivation time

Constraints:

  • Primary key: id
  • Unique: uq_bot_principals_name on (name)

4.3 strategy_sets

Purpose:

  • Stores normalized strategy configuration snapshots.

Columns:

Column Type Null Default Constraints / Notes
id Text NO - Primary Key
name Text NO - Strategy name, part of unique pair
config Text NO "" Strategy config payload, part of unique pair
nick Text NO - Unique strategy set nickname
created_at DateTime(timezone=True) NO NOW() Creation timestamp

Constraints:

  • Primary key: id
  • Unique: uq_strategy_sets_name_config on (name, config)
  • Unique: uq_strategy_sets_nick on (nick)

4.4 portfolios

Purpose:

  • Stores current portfolio state and strategy linkage.

Current baseline note:

  • The columns below describe the currently migrated schema through revision 20260403_0005.

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
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
active_strategy_id Text YES - Logical reference to strategy_sets.id
avabile_strategies Text[] YES - Nullable array of strategy_sets.id values
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

Constraint notes:

  • active_strategy_id is a nullable logical reference to strategy_sets.id
  • avabile_strategies stores zero or more selectable strategy set IDs when present

4.5 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 ix_users_created_at

  • Table: users
  • Columns/expressions: (created_at)
  • Unique: no
  • Intent: speed operational listing by recency.

5.2 idx_auth_tokens_user_created

  • Table: auth_tokens
  • Columns/expressions: (user_id, created_at)
  • Unique: no
  • Intent: fetch current user's issued tokens efficiently.

5.3 idx_auth_tokens_hash_active

  • Table: auth_tokens
  • Columns/expressions: (token_hash)
  • Unique: no
  • Intent: resolve bearer tokens efficiently.

5.4 idx_auth_tokens_bot_created

  • Table: auth_tokens
  • Columns/expressions: (bot_principal_id, created_at)
  • Unique: no
  • Intent: fetch bot-issued token lists efficiently.

5.5 idx_bot_principals_active_created

  • Table: bot_principals
  • Columns/expressions: (is_active, created_at)
  • Unique: no
  • Intent: speed operational filtering of active/inactive bots.

5.6 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.7 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
20260403_0002 migrations/versions/20260403_0002_strategy_set_columns.py Rename strategy_name->name, strategy_config->config, add unique nick, handle legacy unnamed unique constraint
20260403_0003 migrations/versions/20260403_0003_portfolio_strategy_refs.py Remove embedded portfolio strategy fields, add active_strategy_id and avabile_strategies
20260403_0004 migrations/versions/20260403_0004_users_table.py Add users table with unique username/email and created_at index
20260403_0005 migrations/versions/20260403_0005_auth_user_fields_and_tokens.py Add password/permission/login fields to users and create auth_tokens table
20260403_0006 migrations/versions/20260403_0006_bot_principals_and_bot_tokens.py Add bot_principals table and auth_tokens.bot_principal_id for bot API key management
20260403_0007 migrations/versions/20260403_0007_bot_principal_permission_only.py Remove bot_principals.owner_user_id and enforce bot-local permission model