feat: v1 portfolio mutation, sub-resources, ledger path migration (BAT-008~011)
dcy-003/004 기반 v1 API surface 완성 및 BAT-008~011 구현·검증 완료:
- BAT-008 (REQ-019~021,024): 토큰 surface baseline 문서 갱신
- BAT-009 (REQ-025~028): POST/PATCH/DELETE/GET /v1/portfolios CRUD 신설
- _require_write_token, _get_portfolio_or_404, soft delete, daemon-scoped list
- BAT-010 (REQ-030~032): portfolio tag/comment 서브 라우트 신설 + baseline 갱신
- GET/POST/DELETE /v1/portfolios/{id}/tags
- POST/GET/PATCH/DELETE /v1/portfolios/{id}/comments
- BAT-011 (REQ-029): ledger path migration
- POST /v1/assets/{asset_id}/ledger/trades → /v1/portfolios/{portfolio_id}/ledger/trades
- v1_ledger.py 제거, disabled 차단(409) 추가
- test_v1.py / test_daemon_assignment.py 구 path 갱신
- 신규 tests: test_v1_portfolios_crud.py, test_v1_portfolios_list.py,
test_v1_portfolios_tags_comments.py, test_v1_portfolios_ledger.py
- 전체 suite: 340 passed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -134,6 +134,86 @@ class LedgerTradeResponse(BaseModel):
|
||||
status: Literal["new", "duplicate"] = "new"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# POST/PATCH/DELETE/GET /v1/portfolios (REQ-025, REQ-026, REQ-027, REQ-028)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class PortfolioCreateRequest(BaseModel):
|
||||
asset_id: str = Field(..., min_length=1)
|
||||
broker: str = Field(..., min_length=1)
|
||||
name: str | None = None
|
||||
cash_balance: float = Field(default=0.0, ge=0.0)
|
||||
asset_quantity: float = Field(default=0.0, ge=0.0)
|
||||
asset_price: float = Field(default=0.0, ge=0.0)
|
||||
profit_buffer: float = Field(default=0.0, ge=0.0)
|
||||
strategy_name: str | None = None
|
||||
strategy_params: dict[str, Any] = Field(default_factory=dict)
|
||||
managed_by_daemon_id: str | None = None
|
||||
user_id: str | None = None # admin-only: override owner
|
||||
|
||||
|
||||
class PortfolioUpdateRequest(BaseModel):
|
||||
broker: str | None = None
|
||||
status: str | None = None
|
||||
name: str | None = None
|
||||
strategy_name: str | None = None
|
||||
strategy_params: dict[str, Any] | None = None
|
||||
asset_price: float | None = Field(default=None, ge=0.0)
|
||||
managed_by_daemon_id: str | None = None
|
||||
|
||||
|
||||
class PortfolioItem(BaseModel):
|
||||
portfolio_id: str
|
||||
asset_id: str | None = None
|
||||
owner_user_id: str | None = None
|
||||
broker: str
|
||||
status: str
|
||||
name: str
|
||||
cash_balance: float = 0.0
|
||||
asset_quantity: float = 0.0
|
||||
price: float = 0.0
|
||||
profit_buffer: float = 0.0
|
||||
tag_ids: list[str] = Field(default_factory=list)
|
||||
managed_by_daemon_id: str | None = None
|
||||
created_at: datetime | None = None
|
||||
updated_at: datetime | None = None
|
||||
|
||||
|
||||
class PortfolioListResponse(BaseModel):
|
||||
portfolios: list[PortfolioItem]
|
||||
total: int
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /v1/portfolios/{id}/tags, CRUD /v1/portfolios/{id}/comments (REQ-030, REQ-031)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class PortfolioTagListResponse(BaseModel):
|
||||
tags: list[TagItem]
|
||||
total: int
|
||||
|
||||
|
||||
class PortfolioCommentItem(BaseModel):
|
||||
comment_id: str
|
||||
portfolio_id: str
|
||||
content: str
|
||||
created_at: datetime | None = None
|
||||
updated_at: datetime | None = None
|
||||
|
||||
|
||||
class PortfolioCommentRequest(BaseModel):
|
||||
content: str = Field(..., min_length=1)
|
||||
|
||||
|
||||
class PortfolioCommentUpdateRequest(BaseModel):
|
||||
content: str = Field(..., min_length=1)
|
||||
|
||||
|
||||
class PortfolioCommentListResponse(BaseModel):
|
||||
comments: list[PortfolioCommentItem]
|
||||
total: int
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# POST/PATCH /v1/assets (admin catalog management — REQ-010)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user