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):
- Environment variables
.envfile (loaded at startup if present)- Code defaults
Core settings:
PORTFOLIOHUB_ALLOWED_ORIGINS: comma-separated CORS originsPORTFOLIOHUB_READ_TOKENS: comma-separated read tokensPORTFOLIOHUB_TRADE_WRITE_TOKENS: comma-separated write tokensPORTFOLIOHUB_ADMIN_TOKENS: comma-separated admin tokensPORTFOLIOHUB_DB_ENABLED: enables DB repository selection whentrue
Database settings (active source):
TIMESCALEDB_HOSTTIMESCALEDB_PORTTIMESCALEDB_NAMETIMESCALEDB_USERTIMESCALEDB_PASSWORDTIMESCALEDB_SSLMODETIMESCALEDB_CONNECT_TIMEOUT_SECTIMESCALEDB_APPLICATION_NAME
Derived setting:
active_database_urluses 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:
readtrade-writeadmin
Authorization hierarchy:
admincan access all endpointstrade-writecan accesstrade-write+readreadcan 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 ofPortfolioListItem
Get Portfolio Status
GET /portfolios/{portfolio_id}- Auth:
read - Query params:
recent_trades(int, default5)
- 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, defaultfalse)
- Response
204: no content
7.3 Example Requests and Responses
- See
docs/api-examples.mdfor maintained request/response examples.
8. Schema Specification
8.1 Request Models
CreatePortfolioRequest
portfolio_id: strbroker: str = "kis"status: str = "enabled"symbol: strstrategy_name: str = "target_value"strategy_params: object = {}cash_balance: float >= 0asset_quantity: float >= 0asset_price: float >= 0profit_buffer: float >= 0
UpdatePortfolioRequest (all fields optional)
broker: str | nullstatus: str | nullsymbol: str | nullstrategy_name: str | nullstrategy_params: object | nullasset_price: float >= 0 | null
BuyRequest / SellRequest
quantity: float > 0price: float > 0fee: float >= 0tax: float >= 0note: str = ""
InvestRequest
movement_type: "deposit" | "profit" | "withdraw"amount: float > 0note: str = ""
TransferRequest
direction: "to-buffer" | "from-buffer"amount: float > 0note: str = ""
ResetPortfolioRequest
infer_symbol: str | null
8.2 Response Models
PortfolioListItem
- Includes portfolio summary fields and computed
total_value
PortfolioDetailResponse
portfolio: PortfolioSummaryrecent_entries: LedgerEntryResponse[]strategy_set: StrategySetResponse | nulllive_quantity: float | null
StrategySetResponse
id: strname: strconfig: strnick: strcreated_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-dbportfoliohub 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.
--forcebypasses confirmation prompts.
10. Persistence and Migration Specification
Repository selection:
- If
PORTFOLIOHUB_DB_ENABLED=false: an unimplemented placeholder repository raisesFeatureNotReadyErroron access. - If
PORTFOLIOHUB_DB_ENABLED=true:PostgresPortfolioRepositoryis used.
Migration commands:
init-db: create target DB if missing, then apply Alembicheadmigrate-db: apply Alembicheadto 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_originsfrom configured origins listallow_credentials = trueallow_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