Skip to content

fix(log): reconcile FinancialDataLog snapshot validity against operating result#3840

Draft
TaprootFreak wants to merge 1 commit into
developfrom
fix/log-reconcile-total-balance-valid
Draft

fix(log): reconcile FinancialDataLog snapshot validity against operating result#3840
TaprootFreak wants to merge 1 commit into
developfrom
fix/log-reconcile-total-balance-valid

Conversation

@TaprootFreak

@TaprootFreak TaprootFreak commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

The FinancialDataLog total-balance metric is assembled by live-polling many balance sources (exchange APIs, chain nodes, OTC custody, bank/in-transit matching). A failed/partial read can be persisted as 0 and a glitchy source can report an implausibly large value, so the logged total can diverge substantially from reality in both directions for the same underlying book.

The legacy valid guard compared the total only to a flat limit and accepted any value after 15 minutes (|| minutesDiff > 15), so during sustained read-flakiness those glitches passed through into the valid-filtered chart series.

Why this is a metric bug (not a real balance change)

The total only legitimately moves by operating result + FX. getChangeLog() computes the operating result purely from transaction fees (buyCrypto/buyFiat/trading minus bank/kraken/binance/ref/blockchain fees) — independent of balance reads. When a snapshot's total-balance delta does not match operating delta + FX, the difference is a read/aggregation error rather than a real movement (customer flow is balance-neutral: plus and minus move together).

What changed

  • Persist the month-to-date operating result (changeLog.total) in each snapshot as changesTotal (additive JSON field on the FinanceLog message; no schema/migration — message is a text column).
  • New isReconciledSnapshot(...): a snapshot is valid only if |ΔtotalBalance − Δoperating| <= financeLogTotalBalanceChangeLimit (FX/rounding absorbed by the existing limit). Glitches that break reconciliation are flagged invalid and excluded from the valid-filtered series.
  • Month-to-date reset handled: changeLog.total drops to ~0 at month start, so when changesTotal < lastFinanceLog.changesTotal there is no same-month delta and it falls back to the legacy escape (which accepts the near-unchanged month-start total and re-establishes the baseline).
  • The legacy time-based escape now applies only while no same-month delta is available (legacy snapshots / month reset), closing the unconditional 15-minute hole.

This change only sets the valid flag — no balance value is recomputed or altered.

Test plan

  • npm run type-check
  • npm run lint (changed files, 0 warnings/errors)
  • npm run format:check
  • npm test for log-job.service.spec.ts — 22 passed (5 new: reconciled-large-change, FX-noise-in-limit, unreconciled-jump-no-escape, legacy fallback both directions, month-to-date reset accept+reject)

Known limitation (pre-existing)

Reconciliation is always measured against the last valid snapshot, so a genuine multi-day invalid gap accumulates FX into a single delta that can exceed the limit and self-perpetuate. This is inherent to the valid-filtered baseline and not introduced here.

Out of scope (separate follow-ups)

This PR makes the valid-filtered series trustworthy. It does not change the unfiltered numeric latest balance (getLatestFinancialLog, a deliberate product choice) and does not fix the underlying read sources (distinguishing "read failed" from "genuinely zero" in the exchange/blockchain adapters, node sync checks, exchange-stream sanity bounds). Those require the dev environment with the real integrations to verify safely.

…ing result

The total-balance metric is built by live-polling many balance sources
(exchanges, chain nodes, OTC custody). A failed or partial read can be
persisted as 0 and a glitchy source can report an implausibly large value,
so the logged total drifts far from reality in both directions (observed
swings from ~0 to >1,000,000 CHF) and the legacy `valid` guard let these
through via its unconditional 15-minute escape.

A snapshot's total only legitimately moves by operating result + FX. Capture
the month-to-date operating result (changeLog.total) in each snapshot and mark
a snapshot valid only when its total-balance delta reconciles with the
operating delta within financeLogTotalBalanceChangeLimit (FX/rounding
absorbed). Glitches that break this reconciliation are flagged invalid and
excluded from the valid-filtered series. The month-to-date reset is handled by
falling back to the legacy escape when no same-month delta is available.

This only sets the `valid` flag; no balance value is altered.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant