fidelity: replace snapshot-push with delta gains-offset DEPOSITs
Per-fund snapshot import landed quantities but dropped cost basis + needed a separate quote-push path we never identified. Snapshotting also collided with WF's own TOTAL aggregation and ZEROED the Fidelity cash balance. Simpler plan: each monthly scrape emits a single DEPOSIT (or WITHDRAWAL on a market drop) sized to the delta between the live PlanViewer pot value and Wealthfolio's running total. dav_corrected PG view continues to subtract these offsets from net_contribution so the dashboard Growth/ROI math stays right. - New gains_offset_delta_activity() — current_gain - prior_offset. - New WealthfolioSink.cumulative_amount_with_notes_prefix() — sums the existing fidelity-planviewer:unrealised-gains-offset DEPOSITs in WF so we know what's already been emitted. - CLI runs sync_provider_to_wealthfolio first (cash flows), then computes + emits the delta via import_activities. - 4 new provider tests for the delta logic; full suite (144 + 1 skipped) green; mypy + ruff clean. The old fidelity_holdings_to_snapshot helper + push_manual_snapshots sink method stay for future use but are no longer called.
This commit is contained in:
parent
c9c0310733
commit
98c4729622
4 changed files with 183 additions and 35 deletions
|
|
@ -7,13 +7,14 @@ from pathlib import Path
|
|||
|
||||
import pytest
|
||||
|
||||
from broker_sync.models import Account, AccountType
|
||||
from broker_sync.models import Account, AccountType, ActivityType
|
||||
from broker_sync.providers.fidelity_planviewer import (
|
||||
ACCOUNT_ID,
|
||||
FidelityCreds,
|
||||
FidelityPlanViewerProvider,
|
||||
FidelityProviderConfigError,
|
||||
fidelity_holdings_to_snapshot,
|
||||
gains_offset_delta_activity,
|
||||
)
|
||||
from broker_sync.providers.parsers.fidelity import (
|
||||
parse_transactions_html,
|
||||
|
|
@ -146,3 +147,63 @@ def test_provider_caches_holdings_for_cli_snapshot_push() -> None:
|
|||
# Pre-fetch state: empty
|
||||
assert prov.last_holdings == []
|
||||
assert prov.last_total_contribution == Decimal(0)
|
||||
|
||||
|
||||
# -- delta-shaped gains offset (the monthly accumulation mechanism) --
|
||||
|
||||
|
||||
def _holdings_summing_to(total: Decimal) -> list:
|
||||
from broker_sync.providers.parsers.fidelity import FidelityHolding
|
||||
return [FidelityHolding(
|
||||
fund_code="KDOA", fund_name="Test", units=Decimal("100"),
|
||||
unit_price=total / Decimal("100"), currency="GBP", total_value=total,
|
||||
units_by_source={},
|
||||
)]
|
||||
|
||||
|
||||
def test_gains_delta_emits_deposit_when_gain_exceeds_prior_offset() -> None:
|
||||
# pot £145k, real contrib £102k → current gain £43k; prior offset £35k
|
||||
# → delta = +£8k
|
||||
activity = gains_offset_delta_activity(
|
||||
holdings=_holdings_summing_to(Decimal("145000")),
|
||||
total_real_contribution=Decimal("102000"),
|
||||
prior_offset_cumulative=Decimal("35000"),
|
||||
as_of=datetime(2026, 5, 17, tzinfo=UTC),
|
||||
)
|
||||
assert activity is not None
|
||||
assert activity.activity_type == ActivityType.DEPOSIT
|
||||
assert activity.amount == Decimal("8000")
|
||||
assert activity.external_id == "fidelity:gains-delta:2026-05-17"
|
||||
assert "unrealised-gains-offset" in (activity.notes or "")
|
||||
|
||||
|
||||
def test_gains_delta_emits_withdrawal_on_market_drop() -> None:
|
||||
# pot dropped: current gain £30k, prior offset £35k → delta = -£5k
|
||||
activity = gains_offset_delta_activity(
|
||||
holdings=_holdings_summing_to(Decimal("132000")),
|
||||
total_real_contribution=Decimal("102000"),
|
||||
prior_offset_cumulative=Decimal("35000"),
|
||||
as_of=datetime(2026, 5, 17, tzinfo=UTC),
|
||||
)
|
||||
assert activity is not None
|
||||
assert activity.activity_type == ActivityType.WITHDRAWAL
|
||||
assert activity.amount == Decimal("5000")
|
||||
|
||||
|
||||
def test_gains_delta_suppressed_below_minimum() -> None:
|
||||
# delta ~£0.20, below the £0.50 min — skip emission to avoid noise.
|
||||
activity = gains_offset_delta_activity(
|
||||
holdings=_holdings_summing_to(Decimal("137000.20")),
|
||||
total_real_contribution=Decimal("102000"),
|
||||
prior_offset_cumulative=Decimal("35000"),
|
||||
as_of=datetime(2026, 5, 17, tzinfo=UTC),
|
||||
)
|
||||
assert activity is None
|
||||
|
||||
|
||||
def test_gains_delta_none_when_no_holdings() -> None:
|
||||
assert gains_offset_delta_activity(
|
||||
holdings=[], total_real_contribution=Decimal("0"),
|
||||
prior_offset_cumulative=Decimal("0"),
|
||||
as_of=datetime(2026, 5, 17, tzinfo=UTC),
|
||||
) is None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue