fix(holdings): /holdings/cost-basis WAC row에 fee_amount 편입 (QC보정)
get_holdings_cost_basis 핸들러의 row dict에 fee_amount 누락으로 /holdings/cost-basis의 cost_basis_per_unit이 매수 수수료 미편입값을 반환하던 버그 수정. /pnl/realized 핸들러와 동일하게 e.fee_amount 포함. 테스트: buy fee>0 케이스(1000.5 단언) + fee=0 회귀 케이스 추가. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
This commit is contained in:
@@ -859,6 +859,7 @@ def get_holdings_cost_basis(
|
|||||||
"side": e.side,
|
"side": e.side,
|
||||||
"quantity": e.quantity,
|
"quantity": e.quantity,
|
||||||
"price": e.price,
|
"price": e.price,
|
||||||
|
"fee_amount": e.fee_amount,
|
||||||
"executed_at": e.executed_at,
|
"executed_at": e.executed_at,
|
||||||
}
|
}
|
||||||
for e in ledger_entries
|
for e in ledger_entries
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ def _make_entry(
|
|||||||
quantity: float,
|
quantity: float,
|
||||||
price: float,
|
price: float,
|
||||||
executed_at: datetime,
|
executed_at: datetime,
|
||||||
|
fee_amount: float = 0.0,
|
||||||
) -> LedgerEntry:
|
) -> LedgerEntry:
|
||||||
return LedgerEntry(
|
return LedgerEntry(
|
||||||
ledger_entry_id=ledger_entry_id,
|
ledger_entry_id=ledger_entry_id,
|
||||||
@@ -87,7 +88,7 @@ def _make_entry(
|
|||||||
side=side,
|
side=side,
|
||||||
quantity=quantity,
|
quantity=quantity,
|
||||||
price=price,
|
price=price,
|
||||||
fee_amount=0.0,
|
fee_amount=fee_amount,
|
||||||
tax_amount=0.0,
|
tax_amount=0.0,
|
||||||
executed_at=executed_at,
|
executed_at=executed_at,
|
||||||
recorded_at=executed_at,
|
recorded_at=executed_at,
|
||||||
@@ -535,3 +536,68 @@ def test_portfolio_id_in_response_matches_request() -> None:
|
|||||||
resp = client.get(_PATH, headers={"Authorization": "Bearer tok"})
|
resp = client.get(_PATH, headers={"Authorization": "Bearer tok"})
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert resp.json()["portfolio_id"] == _PORTFOLIO_ID
|
assert resp.json()["portfolio_id"] == _PORTFOLIO_ID
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# QC보정: buy fee>0 케이스 — cost_basis_per_unit이 fee 편입값인지 확인
|
||||||
|
# buy 10@1000 fee=5 → cost_basis_per_unit = (10*1000 + 5) / 10 = 1000.5
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_cost_basis_per_unit_includes_buy_fee() -> None:
|
||||||
|
"""buy fee>0 케이스: cost_basis_per_unit이 매수 fee 편입값을 반환한다.
|
||||||
|
|
||||||
|
buy 10주 @ 1,000원, fee=5원 → WAC = (10*1000 + 5) / 10 = 1000.5
|
||||||
|
REQ-043 Notes: 매수 fee를 cost basis에 포함.
|
||||||
|
"""
|
||||||
|
settings = _make_settings()
|
||||||
|
trades = [
|
||||||
|
_make_entry(
|
||||||
|
ledger_entry_id=1,
|
||||||
|
side="buy",
|
||||||
|
quantity=10,
|
||||||
|
price=1_000,
|
||||||
|
fee_amount=5.0,
|
||||||
|
executed_at=datetime(2026, 6, 1, 9, 0, tzinfo=timezone.utc),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
repo = _make_service_repo(
|
||||||
|
portfolio=_make_portfolio(asset_quantity=10.0),
|
||||||
|
trades=trades,
|
||||||
|
)
|
||||||
|
with _with_repo(repo):
|
||||||
|
client = _make_client(settings)
|
||||||
|
resp = client.get(_PATH, headers={"Authorization": "Bearer tok"})
|
||||||
|
assert resp.status_code == 200
|
||||||
|
body = resp.json()
|
||||||
|
# (10 * 1000 + 5) / 10 = 1000.5
|
||||||
|
assert body["cost_basis_per_unit"] == pytest.approx(1000.5)
|
||||||
|
assert body["total_cost_basis"] == pytest.approx(10_005.0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_cost_basis_per_unit_zero_fee_unchanged() -> None:
|
||||||
|
"""buy fee=0 케이스: fee 편입 추가 후에도 fee=0이면 기존 값 그대로.
|
||||||
|
|
||||||
|
buy 10주 @ 1,000원, fee=0 → WAC = 1000.0 (회귀 확인)
|
||||||
|
"""
|
||||||
|
settings = _make_settings()
|
||||||
|
trades = [
|
||||||
|
_make_entry(
|
||||||
|
ledger_entry_id=1,
|
||||||
|
side="buy",
|
||||||
|
quantity=10,
|
||||||
|
price=1_000,
|
||||||
|
fee_amount=0.0,
|
||||||
|
executed_at=datetime(2026, 6, 1, 9, 0, tzinfo=timezone.utc),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
repo = _make_service_repo(
|
||||||
|
portfolio=_make_portfolio(asset_quantity=10.0),
|
||||||
|
trades=trades,
|
||||||
|
)
|
||||||
|
with _with_repo(repo):
|
||||||
|
client = _make_client(settings)
|
||||||
|
resp = client.get(_PATH, headers={"Authorization": "Bearer tok"})
|
||||||
|
assert resp.status_code == 200
|
||||||
|
body = resp.json()
|
||||||
|
assert body["cost_basis_per_unit"] == pytest.approx(1000.0)
|
||||||
|
assert body["total_cost_basis"] == pytest.approx(10_000.0)
|
||||||
|
|||||||
Reference in New Issue
Block a user