Appearance
MllRiskManager
Centralized Maximum Loss Limit (MLL) risk calculations, intraday extreme tracking, and immutable breach recording for challenges.
High-Level Design
MllRiskManager is a stateless calculation engine with one piece of owned state: the intraday_tracking table. It does not initiate any actions itself -- it is always called by other managers who supply the account value and react to the results.
Core Concept: MLL Thresholds
Each challenge plan defines a capital amount and an MLL threshold. Two derived balances drive all status logic:
minBalance = capital - mllThreshold (breach line)
alertBalance = capital - mllThreshold * 0.9 (warning buffer, ~10% above breach)Both are computed from static CHALLENGE_CONFIGURATIONS -- no stored state involved.
MLL Status Classification
Given an account value, the manager classifies it into one of three states:
accountValue
───────────────────┼──────────────────────
breached │ at-risk │ safe
──────────────────►│◄─────────────►│◄──────
minBalance alertBalance- safe: Above
alertBalance - at-risk: Between
alertBalanceandminBalance(warning zone) - breached: At or below
minBalance(challenge fails)
Non-ACTIVE challenges always return "safe".
Data Flow: Who Calls What
MllRiskManager is invoked from four breach detection sites, each covering a different timing window:
┌──────────────────────────────┐
│ 1. BalanceManager.applyPnL │──► recordMllBreach()
│ (on realized PnL) │ Balance-only check, no unrealized PnL.
└──────────────────────────────┘ Catches breaches at trade close time.
┌──────────────────────────────────────────┐
│ 2. OrderExecutionManager.closePosition │──► recordMllBreach()
│ (after position close) │ Checks full account value
└──────────────────────────────────────────┘ (balance + unrealized PnL).
┌──────────────────────────────────────────────┐
│ 3. AccountHealthManager │──► recordMllBreach()
│ (periodic + on-demand checks) │ Hierarchical search drills
│ │ from 1h → 1m → per-second
└──────────────────────────────────────────────┘ to find exact breach moment.
┌──────────────────────────────────────────┐
│ 4. ChallengeManager.passChallenge │──► recordMllBreach()
│ (final guard before passing) │ Prevents passing if account
└──────────────────────────────────────────┘ value is at or below MLL.For display purposes, BalanceManager calls getMllRiskInfo() when building portfolio state, providing the risk dashboard data (status, distances, percentages).
Intraday Extreme Tracking
MllRiskManager owns the intraday_tracking SQLite table, which stores per-day peak and bottom account values for the current challenge account.
Write path: AccountValueTrackerManager receives hourly extremes from AccountValueAggregationTracker DO callbacks and forwards them here via updateIntradayExtremes().
Read path: ChallengeManager reads intraday low via getIntradayLow() for challenge status display.
Lifecycle:
- Hourly: UPSERT with conditional update -- only overwrites if the new value is more extreme (lower bottom or higher peak). Uses
CAST(... AS REAL)for numeric comparison of string-stored BigNumbers. - Daily (UTC midnight):
clearIntradayTracking()deletes records older than today, called from the daily extremes callback.
Immutable Breach Recording
When any detection site finds accountValue <= minBalance, it calls recordMllBreach(accountValue, timestamp). This records the breach in the challenge table:
- First-write-wins: If
mll_breached_atis already set, subsequent calls are no-ops (returnsfalse). - Dual update: Writes to SQLite and updates the in-memory challenge object for immediate consistency.
- Forensic accuracy: The first breach timestamp is preserved regardless of how many detection sites fire. AccountHealthManager's hierarchical search provides per-second precision by drilling through 1h → 1m → per-second prices.
Multiple detection sites may race to record the same breach -- the immutability guarantee ensures exactly one timestamp is recorded.
See Also
- ChallengeManager -- challenge lifecycle, pass/fail logic, and the final MLL guard
- AccountHealthManager -- periodic breach detection via account value callbacks