feat(portfolio): expose corporate actions in unified ledger

This commit is contained in:
2026-09-13 13:52:50 +09:00
parent 77468631b1
commit a4664f5991
7 changed files with 96 additions and 8 deletions
+40
View File
@@ -274,6 +274,46 @@ def sell_portfolio(
render_output(ctx.obj.console, ctx.obj.output_format, detail)
@portfolio_app.command("corporate-action")
def corporate_action_portfolio(
ctx: typer.Context,
name: str = typer.Argument(...),
user: str = typer.Option(..., "--user"),
action_type: str = typer.Option("stock_consolidation", "--type"),
effective_date: str = typer.Option(..., "--effective-date"),
quantity_before: float = typer.Option(..., "--quantity-before", min=0.0000001),
quantity_after: float = typer.Option(..., "--quantity-after", min=0.0000001),
evidence_ref: str = typer.Option(..., "--evidence-ref"),
idempotency_key: str = typer.Option(..., "--idempotency-key"),
force: bool = typer.Option(False, "--force"),
) -> None:
"""Record an auditable stock consolidation without a trade."""
from datetime import date
from decimal import Decimal
from app.domain.corporate_actions import calculate_stock_consolidation
from app.domain.models import CorporateAction
if action_type != "stock_consolidation":
ctx.obj.console.print("Error: INVALID_CORPORATE_ACTION_TYPE")
raise typer.Exit(code=1)
try:
day = date.fromisoformat(effective_date)
except ValueError:
ctx.obj.console.print("Error: INVALID_EFFECTIVE_DATE")
raise typer.Exit(code=1)
if quantity_after >= quantity_before:
ctx.obj.console.print("Error: INVALID_CORPORATE_ACTION_RATIO")
raise typer.Exit(code=1)
_confirm(force, f"Record stock consolidation {quantity_before:g} → {quantity_after:g}; cash/profit buffer unchanged?")
def _callback(service, repository, app_context):
portfolio_id = _resolve_portfolio_id(repository, name, user)
portfolio = repository.get(portfolio_id)
calc = calculate_stock_consolidation(Decimal(str(quantity_before)), Decimal(str(quantity_after)), Decimal(str(portfolio.price)))
action = CorporateAction("", portfolio_id, portfolio.asset_id, action_type, day, 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)), evidence_ref, idempotency_key, "", 0, user)
return repository.record_corporate_action(portfolio_id, action=action, idempotency_key=idempotency_key, payload_hash="")
detail = _with_service_and_repo(ctx, _callback)
render_output(ctx.obj.console, ctx.obj.output_format, detail)
@portfolio_app.command("invest")
def invest_portfolio(
ctx: typer.Context,