610 lines
11 KiB
Markdown
610 lines
11 KiB
Markdown
# PortfolioHub API Examples
|
|
|
|
## 1. Purpose
|
|
|
|
This document contains example requests and responses for PortfolioHub HTTP APIs.
|
|
|
|
- Sync rule: when files under `app/api/` or `app/schemas/` change, this document must be updated in the same commit/PR.
|
|
- Companion spec: `docs/portfoliohub-spec.md`
|
|
|
|
## 2. Conventions
|
|
|
|
- Base URL example: `http://localhost:8000`
|
|
- Auth header format: `Authorization: Bearer <TOKEN>`
|
|
- JSON examples are representative and may omit some optional fields.
|
|
|
|
## 3. Health
|
|
|
|
### 3.1 GET /health
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X GET "http://localhost:8000/health"
|
|
```
|
|
|
|
Response 200:
|
|
|
|
```json
|
|
{
|
|
"status": "ok"
|
|
}
|
|
```
|
|
|
|
## 4. Portfolios
|
|
|
|
### 4.1 GET /portfolios
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X GET "http://localhost:8000/portfolios?broker=upbit&status_filter=enabled" \
|
|
-H "Authorization: Bearer <READ_TOKEN>"
|
|
```
|
|
|
|
Response 200:
|
|
|
|
```json
|
|
[
|
|
{
|
|
"portfolio_id": "krw-btc",
|
|
"broker": "upbit",
|
|
"status": "enabled",
|
|
"symbol": "KRW-BTC",
|
|
"cash_balance": 1000000.0,
|
|
"asset_quantity": 0.1,
|
|
"price": 100000000.0,
|
|
"profit_buffer": 0.0,
|
|
"active_strategy_id": "stv-001",
|
|
"avabile_strategies": ["stv-001"],
|
|
"created_at": "2026-04-03T00:00:00Z",
|
|
"updated_at": "2026-04-03T00:00:00Z",
|
|
"last_trade_at": "2026-04-03T00:00:00Z",
|
|
"total_value": 11000000.0
|
|
}
|
|
]
|
|
```
|
|
|
|
### 4.2 GET /portfolios/{portfolio_id}
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X GET "http://localhost:8000/portfolios/krw-btc?recent_trades=5" \
|
|
-H "Authorization: Bearer <READ_TOKEN>"
|
|
```
|
|
|
|
Response 200:
|
|
|
|
```json
|
|
{
|
|
"portfolio": {
|
|
"portfolio_id": "krw-btc",
|
|
"broker": "upbit",
|
|
"status": "enabled",
|
|
"symbol": "KRW-BTC",
|
|
"cash_balance": 1000000.0,
|
|
"asset_quantity": 0.1,
|
|
"price": 100000000.0,
|
|
"profit_buffer": 0.0,
|
|
"active_strategy_id": "stv-001",
|
|
"avabile_strategies": ["stv-001"],
|
|
"created_at": "2026-04-03T00:00:00Z",
|
|
"updated_at": "2026-04-03T00:00:00Z",
|
|
"last_trade_at": "2026-04-03T00:00:00Z"
|
|
},
|
|
"recent_entries": [
|
|
{
|
|
"portfolio_id": "krw-btc",
|
|
"recorded_at": "2026-04-03T00:00:00Z",
|
|
"action": "buy",
|
|
"price": 100000000.0,
|
|
"quantity": 0.1,
|
|
"fee_amount": 0.0,
|
|
"tax_amount": 0.0,
|
|
"cash_balance_after": 1000000.0,
|
|
"asset_quantity_after": 0.1,
|
|
"total_value_after": 11000000.0,
|
|
"profit_buffer_after": 0.0,
|
|
"strategy_set_id": "stv-001",
|
|
"note": ""
|
|
}
|
|
],
|
|
"strategy_set": {
|
|
"id": "stv-001",
|
|
"name": "target_value",
|
|
"config": "{}",
|
|
"nick": "target_value:99914b932bd3",
|
|
"created_at": "2026-04-03T00:00:00Z"
|
|
},
|
|
"live_quantity": null
|
|
}
|
|
```
|
|
|
|
### 4.3 POST /portfolios
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X POST "http://localhost:8000/portfolios" \
|
|
-H "Authorization: Bearer <TRADE_WRITE_TOKEN>" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"portfolio_id": "krw-btc",
|
|
"broker": "upbit",
|
|
"status": "enabled",
|
|
"symbol": "KRW-BTC",
|
|
"strategy_name": "target_value",
|
|
"strategy_params": {},
|
|
"active_strategy_id": null,
|
|
"avabile_strategies": null,
|
|
"cash_balance": 1000000,
|
|
"asset_quantity": 0,
|
|
"asset_price": 0,
|
|
"profit_buffer": 0
|
|
}'
|
|
```
|
|
|
|
Response 201:
|
|
|
|
- Response body shape is `PortfolioDetailResponse` (same as 4.2).
|
|
|
|
### 4.4 PATCH /portfolios/{portfolio_id}
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X PATCH "http://localhost:8000/portfolios/krw-btc" \
|
|
-H "Authorization: Bearer <TRADE_WRITE_TOKEN>" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"broker": "upbit",
|
|
"symbol": "KRW-BTC"
|
|
}'
|
|
```
|
|
|
|
Response 200:
|
|
|
|
- Response body shape is `PortfolioDetailResponse`.
|
|
|
|
### 4.5 POST /portfolios/{portfolio_id}/buy
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X POST "http://localhost:8000/portfolios/krw-btc/buy" \
|
|
-H "Authorization: Bearer <TRADE_WRITE_TOKEN>" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"quantity": 0.01, "price": 100000000, "fee": 0, "tax": 0, "note": ""}'
|
|
```
|
|
|
|
Response 200:
|
|
|
|
- Response body shape is `PortfolioDetailResponse`.
|
|
|
|
### 4.6 POST /portfolios/{portfolio_id}/sell
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X POST "http://localhost:8000/portfolios/krw-btc/sell" \
|
|
-H "Authorization: Bearer <TRADE_WRITE_TOKEN>" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"quantity": 0.01, "price": 100000000, "fee": 0, "tax": 0, "note": ""}'
|
|
```
|
|
|
|
Response 200:
|
|
|
|
- Response body shape is `PortfolioDetailResponse`.
|
|
|
|
### 4.7 POST /portfolios/{portfolio_id}/invest
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X POST "http://localhost:8000/portfolios/krw-btc/invest" \
|
|
-H "Authorization: Bearer <TRADE_WRITE_TOKEN>" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"movement_type": "deposit", "amount": 50000, "note": ""}'
|
|
```
|
|
|
|
Response 200:
|
|
|
|
- Response body shape is `PortfolioDetailResponse`.
|
|
|
|
### 4.8 POST /portfolios/{portfolio_id}/transfer
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X POST "http://localhost:8000/portfolios/krw-btc/transfer" \
|
|
-H "Authorization: Bearer <TRADE_WRITE_TOKEN>" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"direction": "to-buffer", "amount": 10000, "note": ""}'
|
|
```
|
|
|
|
Response 200:
|
|
|
|
- Response body shape is `PortfolioDetailResponse`.
|
|
|
|
### 4.9 POST /portfolios/{portfolio_id}/reset
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X POST "http://localhost:8000/portfolios/krw-btc/reset" \
|
|
-H "Authorization: Bearer <TRADE_WRITE_TOKEN>" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"infer_symbol": "KRW-BTC"}'
|
|
```
|
|
|
|
Response 200:
|
|
|
|
- Response body shape is `PortfolioDetailResponse`.
|
|
|
|
### 4.10 DELETE /portfolios/{portfolio_id}
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X DELETE "http://localhost:8000/portfolios/krw-btc?keep_ledger=false" \
|
|
-H "Authorization: Bearer <ADMIN_TOKEN>"
|
|
```
|
|
|
|
Response 204:
|
|
|
|
- No content.
|
|
|
|
## 5. Strategy Sets
|
|
|
|
### 5.1 GET /strategy-sets
|
|
|
|
## 6. Auth
|
|
|
|
### 6.1 POST /auth/login
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X POST "http://localhost:8000/auth/login" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"username": "admin",
|
|
"password": "bootstrap123"
|
|
}'
|
|
```
|
|
|
|
Response 200:
|
|
|
|
```json
|
|
{
|
|
"access_token": "plain-token-value",
|
|
"token_type": "bearer",
|
|
"expires_at": "2026-04-03T12:00:00Z",
|
|
"permission": "admin",
|
|
"user": {
|
|
"id": "user-1",
|
|
"username": "admin",
|
|
"email": "admin@example.com",
|
|
"permission": "admin",
|
|
"is_active": true,
|
|
"created_at": "2026-04-03T00:00:00Z",
|
|
"updated_at": "2026-04-03T00:00:00Z",
|
|
"last_login_at": "2026-04-03T00:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 6.2 POST /auth/tokens
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X POST "http://localhost:8000/auth/tokens" \
|
|
-H "Authorization: Bearer <SESSION_OR_DB_TOKEN>" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"name": "mobile-app",
|
|
"permission": "read",
|
|
"expires_in_days": 30
|
|
}'
|
|
```
|
|
|
|
Response 201:
|
|
|
|
```json
|
|
{
|
|
"token": "plain-client-token",
|
|
"token_type": "bearer",
|
|
"auth_token": {
|
|
"id": "token-1",
|
|
"user_id": "user-1",
|
|
"name": "mobile-app",
|
|
"token_prefix": "abcd1234",
|
|
"permission": "read",
|
|
"token_type": "client",
|
|
"expires_at": "2026-05-03T00:00:00Z",
|
|
"last_used_at": null,
|
|
"revoked_at": null,
|
|
"created_at": "2026-04-03T00:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 6.3 GET /auth/tokens
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X GET "http://localhost:8000/auth/tokens" \
|
|
-H "Authorization: Bearer <SESSION_OR_DB_TOKEN>"
|
|
```
|
|
|
|
### 6.4 DELETE /auth/tokens/{token_id}
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X DELETE "http://localhost:8000/auth/tokens/token-1" \
|
|
-H "Authorization: Bearer <SESSION_OR_DB_TOKEN>"
|
|
```
|
|
|
|
Response 204:
|
|
|
|
- No content.
|
|
|
|
## 7. Users
|
|
|
|
All user management endpoints require an admin token.
|
|
|
|
### 7.1 GET /users
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X GET "http://localhost:8000/users" \
|
|
-H "Authorization: Bearer <ADMIN_TOKEN>"
|
|
```
|
|
|
|
Response 200:
|
|
|
|
```json
|
|
[
|
|
{
|
|
"id": "user-1",
|
|
"username": "alice",
|
|
"email": "alice@example.com",
|
|
"created_at": "2026-04-03T00:00:00Z",
|
|
"updated_at": "2026-04-03T00:00:00Z"
|
|
}
|
|
]
|
|
```
|
|
|
|
### 7.2 GET /users/{user_id}
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X GET "http://localhost:8000/users/user-1" \
|
|
-H "Authorization: Bearer <ADMIN_TOKEN>"
|
|
```
|
|
|
|
Response 200:
|
|
|
|
```json
|
|
{
|
|
"id": "user-1",
|
|
"username": "alice",
|
|
"email": "alice@example.com",
|
|
"created_at": "2026-04-03T00:00:00Z",
|
|
"updated_at": "2026-04-03T00:00:00Z"
|
|
}
|
|
```
|
|
|
|
### 7.3 POST /users
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X POST "http://localhost:8000/users" \
|
|
-H "Authorization: Bearer <ADMIN_TOKEN>" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"username": "alice",
|
|
"email": "alice@example.com",
|
|
"password": "password123",
|
|
"permission": "read",
|
|
"is_active": true
|
|
}'
|
|
```
|
|
|
|
Response 201:
|
|
|
|
```json
|
|
{
|
|
"id": "user-1",
|
|
"username": "alice",
|
|
"email": "alice@example.com",
|
|
"permission": "read",
|
|
"is_active": true,
|
|
"created_at": "2026-04-03T00:00:00Z",
|
|
"updated_at": "2026-04-03T00:00:00Z",
|
|
"last_login_at": null
|
|
}
|
|
```
|
|
|
|
### 7.4 PATCH /users/{user_id}
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X PATCH "http://localhost:8000/users/user-1" \
|
|
-H "Authorization: Bearer <ADMIN_TOKEN>" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"username": "alice-updated"
|
|
}'
|
|
```
|
|
|
|
Response 200:
|
|
|
|
```json
|
|
{
|
|
"id": "user-1",
|
|
"username": "alice-updated",
|
|
"email": "alice@example.com",
|
|
"permission": "read",
|
|
"is_active": true,
|
|
"created_at": "2026-04-03T00:00:00Z",
|
|
"updated_at": "2026-04-03T00:10:00Z",
|
|
"last_login_at": null
|
|
}
|
|
```
|
|
|
|
### 7.5 DELETE /users/{user_id}
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X DELETE "http://localhost:8000/users/user-1" \
|
|
-H "Authorization: Bearer <ADMIN_TOKEN>"
|
|
```
|
|
|
|
Response 204:
|
|
|
|
- No content.
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X GET "http://localhost:8000/strategy-sets" \
|
|
-H "Authorization: Bearer <READ_TOKEN>"
|
|
```
|
|
|
|
Response 200:
|
|
|
|
```json
|
|
[
|
|
{
|
|
"id": "set-1",
|
|
"name": "target_value",
|
|
"config": "{\"target\": 1000}",
|
|
"nick": "target_value:abc123def456",
|
|
"created_at": "2026-04-03T08:13:08.318Z"
|
|
},
|
|
{
|
|
"id": "set-2",
|
|
"name": "momentum",
|
|
"config": "{\"window\": 20}",
|
|
"nick": "momentum:xyz789uvw012",
|
|
"created_at": "2026-04-03T08:14:15.450Z"
|
|
}
|
|
]
|
|
```
|
|
|
|
### 5.2 POST /strategy-sets
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X POST "http://localhost:8000/strategy-sets" \
|
|
-H "Authorization: Bearer <TRADE_WRITE_TOKEN>" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"name": "target_value",
|
|
"config": "{\"target\": 1000}"
|
|
}'
|
|
```
|
|
|
|
Response 201:
|
|
|
|
```json
|
|
{
|
|
"id": "set-1",
|
|
"name": "target_value",
|
|
"config": "{\"target\": 1000}",
|
|
"nick": "target_value:abc123def456",
|
|
"created_at": "2026-04-03T08:13:08.318Z"
|
|
}
|
|
```
|
|
|
|
### 5.3 PATCH /strategy-sets/{strategy_set_id}
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X PATCH "http://localhost:8000/strategy-sets/set-1" \
|
|
-H "Authorization: Bearer <TRADE_WRITE_TOKEN>" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"config": "{\"target\": 2000}"
|
|
}'
|
|
```
|
|
|
|
Response 200:
|
|
|
|
```json
|
|
{
|
|
"id": "set-1-updated",
|
|
"name": "target_value",
|
|
"config": "{\"target\": 2000}",
|
|
"nick": "target_value:def789ghi012",
|
|
"created_at": "2026-04-03T08:13:08.318Z"
|
|
}
|
|
```
|
|
|
|
Note: Since strategy sets are identified by (name, config) uniqueness, updating may result in a new strategy set ID.
|
|
|
|
### 5.4 DELETE /strategy-sets/{strategy_set_id}
|
|
|
|
Request:
|
|
|
|
```bash
|
|
curl -X DELETE "http://localhost:8000/strategy-sets/set-1" \
|
|
-H "Authorization: Bearer <TRADE_WRITE_TOKEN>"
|
|
```
|
|
|
|
Response 204:
|
|
|
|
- No content on success.
|
|
|
|
Response 409 Conflict (if strategy set is in use):
|
|
|
|
```json
|
|
{
|
|
"detail": "Strategy set is in use by 1 portfolio(s) and cannot be deleted"
|
|
}
|
|
```
|
|
|
|
## 6. Common Error Examples
|
|
|
|
### 6.1 Missing token
|
|
|
|
Response 401:
|
|
|
|
```json
|
|
{
|
|
"detail": "Missing bearer token"
|
|
}
|
|
```
|
|
|
|
### 6.2 Invalid token
|
|
|
|
Response 401:
|
|
|
|
```json
|
|
{
|
|
"detail": "Invalid token"
|
|
}
|
|
```
|
|
|
|
### 6.3 Insufficient permission
|
|
|
|
Response 403:
|
|
|
|
```json
|
|
{
|
|
"detail": "Insufficient permission"
|
|
}
|
|
```
|