Skip to content

Commit 7edb7fa

Browse files
authored
Fix managed portfolio equity
1 parent 38bac79 commit 7edb7fa

2 files changed

Lines changed: 39 additions & 5 deletions

File tree

application/runtime_broker_adapters.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,23 @@ def _resolve_total_equity(
8989
cash_balance: float | None,
9090
buying_power: float | None,
9191
position_market_value: float,
92+
prefer_cash_plus_positions: bool = False,
9293
) -> tuple[float, str]:
94+
resolved_cash = _positive_or_none(cash_balance)
95+
if resolved_cash is not None:
96+
combined_value = resolved_cash + max(0.0, float(position_market_value))
97+
if prefer_cash_plus_positions and combined_value > 0.0:
98+
return combined_value, "cash_plus_positions"
99+
else:
100+
combined_value = None
101+
93102
balance_total = _positive_or_none(
94103
_first_numeric_by_keyword_groups(balances, _TOTAL_EQUITY_KEYWORD_GROUPS)
95104
)
96105
if balance_total is not None:
97106
return balance_total, "balance_total"
98107

99-
resolved_cash = _positive_or_none(cash_balance)
100-
if resolved_cash is not None:
101-
combined_value = resolved_cash + max(0.0, float(position_market_value))
108+
if combined_value is not None:
102109
if combined_value > 0.0:
103110
return combined_value, "cash_plus_positions"
104111

@@ -257,6 +264,7 @@ def build_portfolio_snapshot(self) -> PortfolioSnapshot:
257264
cash_balance=cash_balance,
258265
buying_power=buying_power,
259266
position_market_value=position_market_value,
267+
prefer_cash_plus_positions=bool(managed),
260268
)
261269
return PortfolioSnapshot(
262270
as_of=self.clock(),

tests/test_runtime_broker_adapters.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ def test_runtime_adapters_build_quote_and_portfolio_ports():
4343
portfolio = adapters.build_portfolio_port().get_portfolio_snapshot()
4444

4545
assert quote.last_price == 10.5
46-
assert portfolio.total_equity == 120.0
46+
assert portfolio.total_equity == 41.0
4747
assert portfolio.cash_balance == 20.0
4848
assert portfolio.positions[0].symbol == "SPY"
49+
assert portfolio.metadata["total_equity_source"] == "cash_plus_positions"
4950

5051

5152
def test_portfolio_snapshot_uses_account_value_balance_key():
@@ -56,7 +57,6 @@ def get_balances(self, _account):
5657
adapters = build_runtime_broker_adapters(
5758
client=AccountValueClient(),
5859
account="12345678",
59-
strategy_symbols=("SPY",),
6060
)
6161

6262
portfolio = adapters.build_portfolio_port().get_portfolio_snapshot()
@@ -66,6 +66,32 @@ def get_balances(self, _account):
6666
assert portfolio.metadata["total_equity_source"] == "balance_total"
6767

6868

69+
def test_managed_portfolio_snapshot_ignores_full_account_value_balance_key():
70+
class AccountValueClient(FakeClient):
71+
def get_balances(self, _account):
72+
return {"account_value": "$1,234.56", "cash_balance": "$200.00"}
73+
74+
def get_positions(self, _account):
75+
return {
76+
"items": [
77+
{"symbol": "SPY", "quantity": "2", "market_value": "21.00"},
78+
{"symbol": "AAPL", "quantity": "3", "market_value": "300.00"},
79+
]
80+
}
81+
82+
adapters = build_runtime_broker_adapters(
83+
client=AccountValueClient(),
84+
account="12345678",
85+
strategy_symbols=("SPY",),
86+
)
87+
88+
portfolio = adapters.build_portfolio_port().get_portfolio_snapshot()
89+
90+
assert portfolio.total_equity == 221.0
91+
assert [position.symbol for position in portfolio.positions] == ["SPY"]
92+
assert portfolio.metadata["total_equity_source"] == "cash_plus_positions"
93+
94+
6995
def test_portfolio_snapshot_falls_back_to_cash_when_total_value_missing():
7096
class CashOnlyClient(FakeClient):
7197
def get_balances(self, _account):

0 commit comments

Comments
 (0)