Files
sure/test/models/provider
Scott HughesandClaude Opus 5 5bba880e66 Fix SnapTrade holdings by using the /positions/all endpoint (#3043)
* Fix SnapTrade holdings by using the /positions/all endpoint

SnapTrade returns HTTP 410 Gone on /positions, /holdings and /options for
apps registered after their 2026 cutoff, so newly connected accounts import
their balance but never any holdings. The importer rescues the error and the
sync still reports success, which makes it look like the brokerage simply
holds nothing.

Switch get_positions to the documented replacement, /positions/all, and teach
the shared payload helpers to read its flat `instrument` object alongside the
legacy nested `symbol.symbol` shape.

Fixes #3029

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Filter unsupported instrument kinds before they reach the payload

Derivatives skipped by HoldingsProcessor still landed in
raw_holdings_payload, where calculate_holdings_value sums units * price
over every entry. Any non-zero sum then displaces SnapTrade's own figure
in calculate_total_balance, so an options-only account could report a
balance derived from per-contract units against a per-share price.

Move the denylist to Provider::Snaptrade#get_positions so unsupported
kinds never enter the payload at all, keeping holdings, balance, currency
detection and cash-equivalent handling consistent with one filter.

Also raise when the positions response carries no results array, so a
partial or schema-changed response leaves the previous snapshot in place
instead of overwriting it with nothing. An empty results array remains a
legitimately empty account.

Note that tax_lots[].cost_basis is documented as a whole-lot total,
unlike the per-share field this reads.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 06:00:43 +02:00
..