Appearance
CoefficientManager
Manages coefficient snapshots for computing account values using the formula accountValue = constantTerm + Σ(coefficient[token] × price[token]).
Purpose
CoefficientManager stores and retrieves coefficient snapshots that represent a user's position state at a point in time. Each snapshot contains a constantTerm and a map of per-token coefficients derived from position sizes and entry prices.
The manager is stateless — all operations are direct SQL queries against the coefficient_snapshots table with no in-memory state.
High-Level Design
The Coefficient Formula
Account value normally requires knowing every position's entry price, size, direction, and the current market price. The coefficient system rewrites this into a linear equation over current prices:
accountValue = balance + Σ(sizeAsset × direction × (currentPrice - entryPrice))
= [balance - Σ(entryPrice × sizeAsset × direction)] + Σ(sizeAsset × direction × currentPrice)
= constantTerm + Σ(coefficient[token] × price[token])- constantTerm =
balance - Σ(entryPrice × sizeAsset × direction)— a fixed scalar that changes only when positions or balance change - coefficient[token] =
sizeAsset × direction(LONG = +1, SHORT = −1) — one value per token, aggregated across positions
This decomposition is what makes the tracker viable: the tracker only needs coefficient snapshots and raw price data — it never needs to understand positions, trades, or balances directly.
Append-Only Snapshot History
Each coefficient update from the portfolio is a sequenced snapshot (seq, validFrom, constantTerm, coefficients). The history is append-only within a day:
- Sequence numbers provide ordering and consistency. Each position change in the portfolio atomically increments a
coefficient_seqcounter. - Gap detection: if incoming
seq > maxSeq + 1, the manager signals a gap so the portfolio can reconcile by bulk-sending missing snapshots. - Out-of-order acceptance: snapshots with
seq < maxSeqare inserted if that seq doesn't already exist (fills gaps from retried deliveries). - Duplicate rejection: same seq returns
{ inserted: false, reason: "duplicate" }.
Snapshot Lookup
findApplicableSnapshot(userId, timestamp) finds the correct snapshot for any price tick by ordering valid_from DESC, seq DESC. When two snapshots share the same validFrom, the higher seq wins — this handles rapid position changes within the same second.
Timestamps before account creation return null (no snapshot applies).
Day Boundary Reset
At UTC midnight, resetHistory() deletes all existing snapshots and inserts a caller-provided snapshot as the baseline for the new day. The caller is responsible for computing the correct starting snapshot. This bounds storage growth while preserving the starting state needed for next-day calculations. The reset is wrapped in a transaction by the caller for atomicity.
Cleanup Strategy
cleanupOldSnapshots(cutoffTimestamp) deletes old snapshots but always preserves the latest snapshot before the cutoff as a baseline. This ensures any future calculation starting from the cutoff timestamp still has a valid starting snapshot.
Edge Cases & Error Handling
UNIQUE constraint failederrors during insert are caught and treated as duplicates- Tokens with zero coefficients are skipped during calculation (no price lookup needed)
MissingPriceDataErrorthrown when a non-zero coefficient has no corresponding price — this halts extremes processing to avoid missing peaks/bottoms from incomplete data- All incoming
constantTermand coefficient values are validated viavalidateBigNumberString()in the caller to reject NaN, Infinity, or malformed values
See Also
- ExtremesTrackingManager — primary consumer of coefficient calculations
- AccountValueTrackingManager — manages snapshot lifecycle
- AccountValueAggregationTracker — parent DO