fix(portfolio): match a holding listed under a broker alias to the analyzed instrument (#1556)

- a holding matches when its symbol resolves to the one the run prices (BTCUSDT -> BTC-USD, 700.HK -> 0700.HK); holdings keep the spelling they were given
- every matching holding is shown, labelled with its listed spelling
This commit is contained in:
Yijia-Xiao
2026-10-27 07:58:23 +00:00
parent 28d32653d3
commit 62a06edb38
2 changed files with 52 additions and 9 deletions
+30
View File
@@ -45,6 +45,20 @@ def test_a_ticker_held_under_another_spelling_is_matched():
assert "No current position" not in text and "5" in text
@pytest.mark.unit
@pytest.mark.parametrize("held, analyzed, canonical", [
("BTCUSDT", "BTC-USD", "BTC-USD"),
("XAUUSD+", "GC=F", "GC=F"),
("700.HK", "0700.HK", "0700.HK"),
("0700.HK", "700.HK", "0700.HK"),
])
def test_a_holding_under_a_broker_alias_is_the_analyzed_instrument(held, analyzed, canonical):
book = PortfolioContext.model_validate({"positions": [{"ticker": held, "quantity": 2}, {"ticker": "ethusdt", "quantity": 1}]})
text = book.render(analyzed)
assert f"Current position in {canonical}: 2 units" in text
assert "Other positions: ETHUSDT 1" in text # as listed
@pytest.mark.unit
def test_absent_context_is_reported_as_not_provided():
notice = get_portfolio_context_from_state({"company_of_interest": "AAPL"})
@@ -66,6 +80,22 @@ def test_load_rejects_a_malformed_file_with_a_clear_error(tmp_path):
load_portfolio(bad)
@pytest.mark.unit
def test_one_instrument_held_under_two_spellings_shows_both_holdings():
book = PortfolioContext.model_validate({"positions": [
{"ticker": "BTCUSDT", "quantity": 1}, {"ticker": "BTC-USD", "quantity": 2}]})
text = book.render("BTC-USD")
assert "Current position in BTC-USD: 1 units (listed as BTCUSDT)" in text
assert "Current position in BTC-USD: 2 units" in text and "Other positions" not in text
@pytest.mark.unit
def test_a_holding_keeps_the_instrument_it_was_listed_as():
# WTI is also an alias of crude oil futures; listed beside AAPL it is the stock as written.
text = PortfolioContext.model_validate({"positions": [{"ticker": "WTI", "quantity": 100}]}).render("AAPL")
assert "Other positions: WTI 100" in text and "CL=F" not in text
@pytest.mark.unit
def test_load_reads_a_valid_file(tmp_path):
good = tmp_path / "p.json"