Solver Deep Dive
The current optimizer is implemented in solver.ts asDeterministicSolver.
It is intentionally simple, deterministic, and auditable.
Design choice
The solver is not using:- machine learning
- reinforcement learning
- black-box optimization
- probabilistic routing
Inputs
The solver constructor receives:strategiespolicymanageradapters
snapshotsnavUsdgeneratedAt
Output
The solver returns aRebalancePlan with:
reserveBpsexpectedBlendedApyBpsriskBudgetUsageBpstargetWeightsstrategyScoresplannedStepsplanHashtargetWeightsHashtxBundleHashnoOp
Objective function
For each strategy, the solver computes:Mathematical notation
For documentation purposes, the current V0 solver can be written as:S_iis the final score of strategyiE_iis expected net APYR_iis risk haircutL_iis liquidity haircutF_iis fee haircutC_iis concentration haircutO_iis operational haircut
- this is not solved with linear programming or quadratic optimization
- the current implementation approximates this objective with deterministic score-ranked proportional allocation under caps
Exact components
1. Expected net APY
The solver delegates this to the adapter:2. Risk haircut
3. Liquidity haircut
Liquidity is penalized by profile plus withdrawal delay:| Liquidity profile | Base penalty |
|---|---|
instant | 25 bps |
same_day | 60 bps |
batched | 135 bps |
term | 220 bps |
4. Fee haircut
5. Concentration haircut
6. Operational haircut
Starts from:| Condition | Extra penalty |
|---|---|
| canary sleeve | +120 bps |
| oracle unhealthy | +400 bps |
| protocol unhealthy | +600 bps |
| withdrawals unhealthy | +300 bps |
Reserve logic
The reserve is dynamic but simple. The solver counts how many strategies have:100 bps, capped at 30%.
In notation:
Candidate filter
A strategy is considered allocatable only if all of these are true:- snapshot exists
- score exists
- strategy id is in
manager.allowed_strategies - strategy status is
active oracleHealthy === trueprotocolHealthy === truescoreBps > 0
withdrawalsHealthyis currently a penalty, not a hard filter
Allocation algorithm
After reserve is set, the solver allocates the remaining basis points in rounds.Step 1. Initialize remaining capacity
- per-protocol usage
- exotic usage
- MarginFi usage
Step 2. Sort candidates by score
Candidates are sorted descending byscoreBps.
Step 3. Allocate proportionally by score
For each round:100 bps when a candidate is still eligible.
Step 4. Respect caps
Actual allocation is clipped by:- strategy cap
- protocol cap
- exotic cap
- MarginFi cap
- remaining unallocated bps
Step 5. Repeat until no progress
The loop stops when:remainingBps == 0, or- no candidate can receive additional weight in the round
Important behavior: residual cash can remain unallocated
The solver does not force all non-reserve capital into strategies. If all strategy caps are reached before allremainingBps are consumed, the difference stays implicitly in cash.
That matters in the MVP because:
- reserve is explicit
- strategy weights are explicit
- residual idle capital is implicit
Planned steps generation
After weights are computed, the solver asks each adapter to build execution steps:- removes zero-notional steps except
hold - chunks oversized static steps with
chunkExecutionSteps
Blended metrics
After target weights are computed, the solver calculates portfolio-level summary metrics.Expected blended APY
Risk budget usage
Rebalance trigger
The plan is markednoOp when the total intended change is too small:
Hashes and execution integrity
The solver also emits deterministic hashes:- preserves SDK or manifest bundles as single units when envelopes are already present
- live protocol bundles are not arbitrarily chunked if
instructionEnvelopesalready exist
Blended metrics
The plan includes:Expected blended APY
A weighted average ofexpectedNetApyBps using targetWeightBps.
Risk budget usage
A weighted average ofriskScoreBps using targetWeightBps.
Rebalance gating
The solver computes:noOp if the net change is too small.
Hashes and proofs
The solver creates:targetWeightsHashtxBundleHashplanHash
Current limitations
This V0 solver does not yet include:- covariance between strategies
- explicit drawdown modeling
- dynamic transaction cost estimation from chain state
- probabilistic slippage distributions
- volatility targeting
- regime detection
Why this is still a good V0
It is:- explainable
- deterministic
- easy to audit
- easy to backtest
- easy to constrain by policy