fix(portfolio): tolerate consolidation WAC residue
Publish verified PortfolioHub image / verify (push) Canceled after 0s
Publish verified PortfolioHub image / publish (push) Canceled after 0s

This commit is contained in:
2026-09-13 18:36:00 +09:00
parent 4520fbbfbd
commit 2b470c735f
14 changed files with 146 additions and 34 deletions
+61 -4
View File
@@ -1,4 +1,4 @@
from datetime import date
from datetime import UTC, date, datetime
from decimal import Decimal
from unittest.mock import MagicMock
from uuid import UUID
@@ -36,7 +36,7 @@ def test_create_corporate_action_maps_postgres_missing_portfolio_to_404(monkeypa
def _action(asset_id="asset-1"):
return CorporateAction("ca-1", "pf-1", asset_id, "stock_consolidation", date(2026, 1, 1), Decimal(0), Decimal(1), Decimal(0), Decimal(0), Decimal(0), Decimal(10), Decimal(0), "ev", "idem", "hash", 0)
return CorporateAction("ca-1", "pf-1", asset_id, "stock_consolidation", date(2026, 1, 1), Decimal(2), Decimal(1), Decimal(0), Decimal(0), Decimal(0), Decimal(10), Decimal(0), "ev", "idem", "hash", 0)
def _repo_for_row(asset_id):
@@ -47,8 +47,8 @@ def _repo_for_row(asset_id):
conn.__enter__.return_value = conn
conn.cursor.return_value.__enter__.return_value = cur
repo._connect = MagicMock(return_value=conn)
cur.fetchone.side_effect = [(Decimal(0), Decimal(10), Decimal(0), asset_id, "enabled"), None, None, None, (7, date(2026, 1, 1))]
cur.fetchall.return_value = []
cur.fetchone.side_effect = [(Decimal(2), Decimal(10), Decimal(0), asset_id, "enabled"), None, None, None, (7, date(2026, 1, 1))]
cur.fetchall.return_value = [("buy", Decimal(2), Decimal(0), Decimal(0), Decimal(0), datetime(2025, 12, 31, tzinfo=UTC))]
return repo
@@ -63,3 +63,60 @@ def test_record_corporate_action_rejects_different_asset_id():
repo = _repo_for_row(UUID("12345678-1234-5678-1234-567812345678"))
with pytest.raises(ValueError, match="CORPORATE_ACTION_ASSET_MISMATCH"):
repo.record_corporate_action("pf-1", action=_action("87654321-4321-8765-4321-876543218765"), idempotency_key="idem", payload_hash="hash")
def test_record_corporate_action_accepts_daekyo_rounding_residue():
repo = object.__new__(PostgresPortfolioRepository)
repo.ensure_schema = MagicMock()
conn = MagicMock()
cur = MagicMock()
conn.__enter__.return_value = conn
conn.cursor.return_value.__enter__.return_value = cur
repo._connect = MagicMock(return_value=conn)
cur.fetchone.side_effect = [
(Decimal(1452), Decimal(10), Decimal(0), "asset-1", "enabled"),
None,
None,
None,
(7, datetime(2026, 9, 13, tzinfo=UTC)),
]
cur.fetchall.return_value = [
(
"buy",
Decimal(1452),
Decimal("1086.234145936639256198347107438016528926"),
Decimal(0),
Decimal(0),
datetime(2026, 9, 9, tzinfo=UTC),
)
]
action = CorporateAction(
"ca-1",
"pf-1",
"asset-1",
"stock_consolidation",
date(2026, 9, 10),
Decimal(1452),
Decimal(726),
Decimal(0),
Decimal(0),
Decimal(0),
Decimal(10),
Decimal(0),
"KIS 019685 2026-09-10",
"idem-daekyo",
"hash-daekyo",
0,
)
saved = repo.record_corporate_action(
"pf-1",
action=action,
idempotency_key="idem-daekyo",
payload_hash="hash-daekyo",
)
assert saved.quantity_after == Decimal(726)
assert saved.average_price_after == Decimal("2172.46829187")
assert saved.total_cost_basis == Decimal("1577211.97990000")
assert saved.ledger_entry_id == 7