Files
portfolio-hub/docs/portfoliohub-spec.md
T

9.3 KiB

PortfolioHub Specification

1. Overview

PortfolioHub is a FastAPI-based portfolio lifecycle service with a Typer CLI.

  • Runtime: Python 3.11+
  • API framework: FastAPI
  • CLI framework: Typer
  • DB driver: Psycopg v3
  • Migration: Alembic

Primary goals:

  • Manage portfolio lifecycle (create/update/delete/reset)
  • Record trade and balance-affecting actions (buy/sell/invest/transfer)
  • Expose status and ledger snapshots through API and CLI
  • Keep API and CLI behavior aligned through a shared service/repository layer

2. Scope and Non-Goals

In scope:

  • Authenticated portfolio read and write operations
  • Role-based access control (read, trade-write, admin)
  • Optional PostgreSQL/Timescale-backed repository via feature flag
  • Operational commands for DB bootstrap and migration

Current non-goals / limitations:

  • In-memory fallback repository is not implemented for production usage
  • External broker execution is not handled here (service focuses on portfolio state)
  • Advanced observability wiring for Redis/Loki is not yet implemented in runtime paths

3. Architecture

Layered components:

  • API layer: HTTP routes and request validation
  • CLI layer: command parsing and output rendering
  • Service layer: orchestration and response shaping
  • Domain layer: business logic rules
  • Repository layer: persistence abstraction and concrete Postgres implementation

Key design principle:

  • API and CLI call the same PortfolioService, so behavior is consistent regardless of entrypoint.

4. Entrypoints

HTTP service:

  • App factory: app.main:create_app
  • ASGI app: app.main:app
  • OpenAPI docs: /docs
  • ReDoc: /redoc

CLI:

  • Console script: portfoliohub
  • Entry function: app.cli.main:main

5. Configuration Specification

Configuration precedence (high to low):

  1. Environment variables
  2. .env file (loaded at startup if present)
  3. Code defaults

Core settings:

  • PORTFOLIOHUB_ALLOWED_ORIGINS: comma-separated CORS origins
  • PORTFOLIOHUB_READ_TOKENS: comma-separated read tokens
  • PORTFOLIOHUB_TRADE_WRITE_TOKENS: comma-separated write tokens
  • PORTFOLIOHUB_ADMIN_TOKENS: comma-separated admin tokens
  • PORTFOLIOHUB_DB_ENABLED: enables DB repository selection when true

Database settings (active source):

  • TIMESCALEDB_HOST
  • TIMESCALEDB_PORT
  • TIMESCALEDB_NAME
  • TIMESCALEDB_USER
  • TIMESCALEDB_PASSWORD
  • TIMESCALEDB_SSLMODE
  • TIMESCALEDB_CONNECT_TIMEOUT_SEC
  • TIMESCALEDB_APPLICATION_NAME

Derived setting:

  • active_database_url uses PostgreSQL psycopg URL format with encoded credentials and query options.

6. Authentication and Authorization

Auth scheme:

  • Authorization: Bearer <token> required for protected API routes

Permission levels:

  • read
  • trade-write
  • admin

Authorization hierarchy:

  • admin can access all endpoints
  • trade-write can access trade-write + read
  • read can access only read endpoints

Failure responses:

  • Missing/invalid bearer header: 401
  • Unknown token: 401
  • Insufficient permission: 403

7. HTTP API Specification

Base routes:

  • Health: /health
  • Portfolio: /portfolios

7.1 Health Endpoint

  • GET /health
  • Auth: none
  • Response 200:
{"status": "ok"}

7.2 Portfolio Endpoints

List Portfolios

  • GET /portfolios
  • Auth: read
  • Query params:
    • broker (optional string)
    • status_filter (optional string)
  • Response 200: array of PortfolioListItem

Get Portfolio Status

  • GET /portfolios/{portfolio_id}
  • Auth: read
  • Query params:
    • recent_trades (int, default 5)
  • Response 200: PortfolioDetailResponse

Create Portfolio

  • POST /portfolios
  • Auth: trade-write
  • Body: CreatePortfolioRequest
  • Response 201: PortfolioDetailResponse

Update Portfolio

  • PATCH /portfolios/{portfolio_id}
  • Auth: trade-write
  • Body: UpdatePortfolioRequest
  • Response 200: PortfolioDetailResponse

Buy

  • POST /portfolios/{portfolio_id}/buy
  • Auth: trade-write
  • Body: BuyRequest
  • Response 200: PortfolioDetailResponse

Sell

  • POST /portfolios/{portfolio_id}/sell
  • Auth: trade-write
  • Body: SellRequest
  • Response 200: PortfolioDetailResponse

Invest

  • POST /portfolios/{portfolio_id}/invest
  • Auth: trade-write
  • Body: InvestRequest
  • Response 200: PortfolioDetailResponse

Transfer

  • POST /portfolios/{portfolio_id}/transfer
  • Auth: trade-write
  • Body: TransferRequest
  • Response 200: PortfolioDetailResponse

Reset

  • POST /portfolios/{portfolio_id}/reset
  • Auth: trade-write
  • Body: ResetPortfolioRequest
  • Response 200: PortfolioDetailResponse

Delete

  • DELETE /portfolios/{portfolio_id}
  • Auth: admin
  • Query params:
    • keep_ledger (bool, default false)
  • Response 204: no content

7.3 Example Requests and Responses

  • See docs/api-examples.md for maintained request/response examples.

8. Schema Specification

8.1 Request Models

CreatePortfolioRequest

  • portfolio_id: str
  • broker: str = "kis"
  • status: str = "enabled"
  • symbol: str
  • strategy_name: str = "target_value"
  • strategy_params: object = {}
  • cash_balance: float >= 0
  • asset_quantity: float >= 0
  • asset_price: float >= 0
  • profit_buffer: float >= 0

UpdatePortfolioRequest (all fields optional)

  • broker: str | null
  • status: str | null
  • symbol: str | null
  • strategy_name: str | null
  • strategy_params: object | null
  • asset_price: float >= 0 | null

BuyRequest / SellRequest

  • quantity: float > 0
  • price: float > 0
  • fee: float >= 0
  • tax: float >= 0
  • note: str = ""

InvestRequest

  • movement_type: "deposit" | "profit" | "withdraw"
  • amount: float > 0
  • note: str = ""

TransferRequest

  • direction: "to-buffer" | "from-buffer"
  • amount: float > 0
  • note: str = ""

ResetPortfolioRequest

  • infer_symbol: str | null

8.2 Response Models

PortfolioListItem

  • Includes portfolio summary fields and computed total_value

PortfolioDetailResponse

  • portfolio: PortfolioSummary
  • recent_entries: LedgerEntryResponse[]
  • strategy_set: StrategySetResponse | null
  • live_quantity: float | null

StrategySetResponse

  • id: str
  • name: str
  • config: str
  • nick: str
  • created_at: datetime | null

LedgerEntryResponse

  • Snapshot-oriented ledger response including balances after each action

ErrorResponse

  • detail: str

9. CLI Specification

Global option:

  • --format [table|json] (default: table)

Commands:

  • portfoliohub init-db [--maintenance-db postgres]
  • portfoliohub migrate-db
  • portfoliohub health-check

Portfolio subcommands:

  • portfoliohub portfolio list [--broker ...] [--status ...]
  • portfoliohub portfolio status <portfolio_id> [--recent 5]
  • portfoliohub portfolio create <portfolio_id> --symbol ... [options] [--force]
  • portfoliohub portfolio update <portfolio_id> [options] [--force]
  • portfoliohub portfolio buy <portfolio_id> --quantity ... --price ... [options] [--force]
  • portfoliohub portfolio sell <portfolio_id> --quantity ... --price ... [options] [--force]
  • portfoliohub portfolio invest <portfolio_id> --type <deposit|profit|withdraw> --amount ... [options] [--force]
  • portfoliohub portfolio transfer <portfolio_id> --direction <to-buffer|from-buffer> --amount ... [options] [--force]
  • portfoliohub portfolio reset <portfolio_id> [--infer-symbol ...] [--force]
  • portfoliohub portfolio delete <portfolio_id> [--keep-ledger] [--force]

Confirmation behavior:

  • Mutating portfolio commands request confirmation by default.
  • --force bypasses confirmation prompts.

10. Persistence and Migration Specification

Repository selection:

  • If PORTFOLIOHUB_DB_ENABLED=false: an unimplemented placeholder repository raises FeatureNotReadyError on access.
  • If PORTFOLIOHUB_DB_ENABLED=true: PostgresPortfolioRepository is used.

Migration commands:

  • init-db: create target DB if missing, then apply Alembic head
  • migrate-db: apply Alembic head to existing target DB

Migration source of truth:

  • migrations/versions

DB schema specification:

  • Detailed table/column/index constraints are documented in docs/db-schema-spec.md.
  • The documentation is generated from and must stay consistent with Alembic revisions under migrations/versions.

11. Error Handling

Application-level errors are translated through registered exception handlers.

Typical error classes include:

  • authorization failures (401, 403)
  • domain/repository-level failures surfaced by service operations
  • feature-not-ready errors when DB mode is disabled but persistence is required

12. CORS Policy

CORS middleware is enabled with:

  • allow_origins from configured origins list
  • allow_credentials = true
  • allow_methods = ["GET", "POST", "PATCH", "DELETE", "OPTIONS"]
  • allow_headers = ["Authorization", "Content-Type"]

13. Testing Specification

Test framework:

  • pytest

Configured behavior:

  • test paths: tests
  • recursion excluded: temp, .git, .venv

Current test modules include API, auth, CLI, and domain service coverage.

14. Versioning and Compatibility

  • Current package version: 0.1.0
  • API version exposure follows application version setting (app_version)
  • Minimum Python version: 3.11

15. Future Extension Points

  • Redis-backed cache/session integration
  • Loki logging pipeline integration
  • Additional brokers and order-execution adapters
  • Enhanced operational/admin endpoints