feat(CORE-002): add GLD share quote conversion seam
This commit is contained in:
@@ -1,16 +1,22 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from app.domain.portfolio_math import resolve_portfolio_spot_from_quote
|
||||
from app.models.portfolio import PortfolioConfig
|
||||
from app.pages.overview import _resolve_overview_spot
|
||||
from app.services.alerts import build_portfolio_alert_context
|
||||
|
||||
|
||||
def test_overview_falls_back_to_configured_entry_price_when_live_quote_units_do_not_match() -> None:
|
||||
def test_overview_converts_gld_share_quote_to_ounce_equivalent_spot() -> None:
|
||||
config = PortfolioConfig(entry_price=4400.0, gold_ounces=220.0, entry_basis_mode="weight", loan_amount=145000.0)
|
||||
|
||||
spot_price, source, updated_at = _resolve_overview_spot(
|
||||
spot_price, source, updated_at = resolve_portfolio_spot_from_quote(
|
||||
config,
|
||||
{"price": 404.19, "source": "yfinance", "updated_at": "2026-03-24T00:00:00+00:00"},
|
||||
{
|
||||
"symbol": "GLD",
|
||||
"price": 404.19,
|
||||
"quote_unit": "share",
|
||||
"source": "yfinance",
|
||||
"updated_at": "2026-03-24T00:00:00+00:00",
|
||||
},
|
||||
)
|
||||
portfolio = build_portfolio_alert_context(
|
||||
config,
|
||||
@@ -19,8 +25,90 @@ def test_overview_falls_back_to_configured_entry_price_when_live_quote_units_do_
|
||||
updated_at=updated_at,
|
||||
)
|
||||
|
||||
assert spot_price == 4041.9
|
||||
assert source == "yfinance"
|
||||
assert updated_at == "2026-03-24T00:00:00+00:00"
|
||||
assert portfolio["gold_value"] == 889218.0
|
||||
assert portfolio["net_equity"] == 744218.0
|
||||
assert round(float(portfolio["margin_call_price"]), 2) == 878.79
|
||||
|
||||
|
||||
def test_overview_fails_closed_to_configured_entry_price_for_unsupported_quote_symbol() -> None:
|
||||
config = PortfolioConfig(entry_price=4400.0, gold_ounces=220.0, entry_basis_mode="weight", loan_amount=145000.0)
|
||||
|
||||
spot_price, source, updated_at = resolve_portfolio_spot_from_quote(
|
||||
config,
|
||||
{
|
||||
"symbol": "SLV",
|
||||
"price": 28.50,
|
||||
"quote_unit": "share",
|
||||
"source": "yfinance",
|
||||
"updated_at": "2026-03-24T00:00:00+00:00",
|
||||
},
|
||||
)
|
||||
|
||||
assert spot_price == 4400.0
|
||||
assert source == "configured_entry_price"
|
||||
assert portfolio["gold_value"] == 968000.0
|
||||
assert portfolio["net_equity"] == 823000.0
|
||||
assert round(float(portfolio["margin_call_price"]), 2) == 878.79
|
||||
assert updated_at == ""
|
||||
|
||||
|
||||
def test_overview_uses_fallback_symbol_when_quote_payload_omits_symbol() -> None:
|
||||
config = PortfolioConfig(entry_price=4400.0, gold_ounces=220.0, entry_basis_mode="weight", loan_amount=145000.0)
|
||||
|
||||
spot_price, source, updated_at = resolve_portfolio_spot_from_quote(
|
||||
config,
|
||||
{"price": 404.19, "quote_unit": "share", "source": "yfinance", "updated_at": "2026-03-24T00:00:00+00:00"},
|
||||
fallback_symbol="GLD",
|
||||
)
|
||||
|
||||
assert spot_price == 4041.9
|
||||
assert source == "yfinance"
|
||||
assert updated_at == "2026-03-24T00:00:00+00:00"
|
||||
|
||||
|
||||
def test_overview_falls_back_when_quote_price_is_missing_or_invalid() -> None:
|
||||
config = PortfolioConfig(entry_price=4400.0, gold_ounces=220.0, entry_basis_mode="weight", loan_amount=145000.0)
|
||||
|
||||
missing_price = resolve_portfolio_spot_from_quote(
|
||||
config,
|
||||
{"symbol": "GLD", "quote_unit": "share", "source": "yfinance", "updated_at": "2026-03-24T00:00:00+00:00"},
|
||||
fallback_symbol="GLD",
|
||||
)
|
||||
zero_price = resolve_portfolio_spot_from_quote(
|
||||
config,
|
||||
{
|
||||
"symbol": "GLD",
|
||||
"price": 0.0,
|
||||
"quote_unit": "share",
|
||||
"source": "yfinance",
|
||||
"updated_at": "2026-03-24T00:00:00+00:00",
|
||||
},
|
||||
fallback_symbol="GLD",
|
||||
)
|
||||
bad_string_price = resolve_portfolio_spot_from_quote(
|
||||
config,
|
||||
{
|
||||
"symbol": "GLD",
|
||||
"price": "not-a-number",
|
||||
"quote_unit": "share",
|
||||
"source": "yfinance",
|
||||
"updated_at": "2026-03-24T00:00:00+00:00",
|
||||
},
|
||||
fallback_symbol="GLD",
|
||||
)
|
||||
|
||||
assert missing_price == (4400.0, "configured_entry_price", "")
|
||||
assert zero_price == (4400.0, "configured_entry_price", "")
|
||||
assert bad_string_price == (4400.0, "configured_entry_price", "")
|
||||
|
||||
|
||||
def test_overview_falls_back_when_quote_unit_metadata_is_missing() -> None:
|
||||
config = PortfolioConfig(entry_price=4400.0, gold_ounces=220.0, entry_basis_mode="weight", loan_amount=145000.0)
|
||||
|
||||
result = resolve_portfolio_spot_from_quote(
|
||||
config,
|
||||
{"symbol": "GLD", "price": 404.19, "source": "yfinance", "updated_at": "2026-03-24T00:00:00+00:00"},
|
||||
fallback_symbol="GLD",
|
||||
)
|
||||
|
||||
assert result == (4400.0, "configured_entry_price", "")
|
||||
|
||||
Reference in New Issue
Block a user