fix corporate action ledger replay invariants

This commit is contained in:
2026-09-13 14:04:30 +09:00
parent 48735bbb4a
commit 14c118f503
4 changed files with 59 additions and 15 deletions
+6 -3
View File
@@ -1313,6 +1313,8 @@ def get_holdings_cost_basis(
@router.post("/{portfolio_id}/corporate-actions", response_model=CorporateActionResponse, status_code=status.HTTP_201_CREATED)
def create_corporate_action(portfolio_id: str, payload: CorporateActionRequest, response: Response, auth: AuthContext = Depends(require_trade_write), idempotency_key: str = Header(..., alias="Idempotency-Key")):
if not idempotency_key or not idempotency_key.strip():
raise HTTPException(status_code=422, detail="Idempotency-Key must not be empty")
repo = _get_repo()
try:
portfolio = repo.get(portfolio_id)
@@ -1322,9 +1324,10 @@ def create_corporate_action(portfolio_id: str, payload: CorporateActionRequest,
raise HTTPException(status_code=403, detail="Portfolio ownership mismatch")
if portfolio.managed_by_daemon_id and not payload.confirm_daemon_managed:
raise HTTPException(status_code=409, detail="DAEMON_MANAGED_CONFIRMATION_REQUIRED")
from app.domain.corporate_actions import calculate_stock_consolidation
calc = calculate_stock_consolidation(payload.quantity_before, payload.quantity_after, Decimal(str(getattr(portfolio, "price", 0))))
digest = hashlib.sha256(payload.model_dump_json().encode()).hexdigest()
from app.domain.corporate_actions import calculate_stock_consolidation, canonical_payload_hash
# The repository validates and derives WAC from the locked ledger replay.
calc = calculate_stock_consolidation(payload.quantity_before, payload.quantity_after, Decimal("0"))
digest = canonical_payload_hash(payload)
action = CorporateAction(str(uuid.uuid4()), portfolio_id, portfolio.asset_id, payload.action_type, payload.effective_date, calc.quantity_before, calc.quantity_after, calc.average_price_before, calc.average_price_after, calc.total_cost_basis, Decimal(str(portfolio.cash_balance)), Decimal(str(portfolio.profit_buffer)), payload.evidence_ref, idempotency_key, digest, 0, auth.user_id)
saved = repo.record_corporate_action(portfolio_id, action=action, idempotency_key=idempotency_key, payload_hash=digest)
return CorporateActionResponse(**saved.__dict__)