Rebalancer¶
Stateless allocation math library for MultiStrategyVault. Computes per-strategy target debt, deviation, and deposit caps. Not imported at deploy — logic inlined in MSV (titanoboa 0.2.8 + Vyper 0.4.3 constructor issue).
Source: khomdev-keep/src/Rebalancer.vy (Vyper 0.4.x)
Consumer: MSV rebalance · allocateCapital
Implementation overview¶
- Fixed mode — targets from manager
target_weight_bps, minus liquid buffer, capped bymax_debt - Smart mode — APR-proportional targets; falls back to fixed weights when all APRs zero
- Lazy rebalance — skip moves within
REBALANCE_THRESHOLD_BPS(5%) deviation - CoilMaker —
compute_coilmaker_allocationbounds idle → maker deploy
flowchart TD
TVL[TVL idle + debt] --> RT{smart routing?}
RT -->|no| FIX[compute_fixed_targets]
RT -->|yes| SMART[compute_smart_targets]
FIX --> DEV[needs_rebalance]
SMART --> DEV
Constants¶
| Name | Value | Role |
|---|---|---|
REBALANCE_THRESHOLD_BPS |
500 (5%) | Min deviation to trigger rebalance |
MAX_BPS |
10_000 | Weight / buffer math |
Internal functions¶
compute_fixed_targets¶
Rebalancer.compute_fixed_targets(tvl, buffer_bps, target_weights, max_debts) -> DynArray[uint256, 10]
Per-strategy target debt from manager-set weight BPS (fixed allocation mode).
| Param | Type | Description |
|---|---|---|
tvl |
uint256 |
total_idle + total_debt |
buffer_bps |
uint256 |
Liquid buffer kept idle (BPS of TVL) |
target_weights |
uint256[≤10] |
Each strategy's target_weight_bps |
max_debts |
uint256[≤10] |
Per-strategy max_debt ceiling (0 = uncapped) |
Returns: DynArray[uint256, 10] — target debt per strategy in underlying units. Empty if no strategies.
Math: deployable = tvl - tvl * buffer_bps / MAX_BPS; each target = deployable * weight / MAX_BPS, capped by max_debt.
Access: @internal @pure — inlined in MSV when smart routing off.
Source (khomdev-keep/src/Rebalancer.vy:25-58):
@internal
@pure
def compute_fixed_targets(tvl, buffer_bps, target_weights, max_debts):
deployable = tvl - tvl * buffer_bps // MAX_BPS
target = deployable * weight // MAX_BPS
if max_debt != 0 and target > max_debt: target = max_debt
return targets
Example:
Cross-links: compute_smart_targets · MultiStrategyVault rebalance
compute_smart_targets¶
Rebalancer.compute_smart_targets(tvl, buffer_bps, aprs, max_debts, fallback_weights) -> DynArray[uint256, 10]
APR-proportional target debt (smart routing mode). Zero-APR strategies get 0. All APRs zero → compute_fixed_targets.
| Param | Type | Description |
|---|---|---|
tvl |
uint256 |
Total assets under management |
buffer_bps |
uint256 |
Liquid buffer BPS |
aprs |
uint256[≤10] |
Each strategy's getApr() |
max_debts |
uint256[≤10] |
Per-strategy debt ceilings |
fallback_weights |
uint256[≤10] |
Fixed weights when sum(aprs) == 0 |
Returns: DynArray[uint256, 10] — target debt per strategy, max_debt-capped.
Access: @internal @pure — inlined when MSV smart routing on.
Source (khomdev-keep/src/Rebalancer.vy:63-108):
@internal
@pure
def compute_smart_targets(tvl, buffer_bps, aprs, max_debts, fallback_weights):
if sum_apr == 0:
return self.compute_fixed_targets(tvl, buffer_bps, fallback_weights, max_debts)
target = deployable * aprs[i] // sum_apr
return targets
Example:
Cross-links: compute_fixed_targets · MultiStrategyVault smart routing
compute_deviation¶
Rebalancer.compute_deviation(current, target) -> uint256
Absolute deviation between current and target debt in BPS.
| Param | Type | Description |
|---|---|---|
current |
uint256 |
Strategy's current_debt |
target |
uint256 |
Target debt from fixed/smart targets |
Returns: uint256 — deviation bps. 0 when equal (including both zero). Divisor = smaller of the two (max_uint if smaller is 0).
Access: @internal @pure — lazy-rebalance gate input.
Source (khomdev-keep/src/Rebalancer.vy:113-127):
@internal
@pure
def compute_deviation(current, target) -> uint256:
if current == target:
return 0
if target > current:
divisor = current if current > 0 else max_value(uint256)
return (target - current) * 10_000 // divisor
# symmetric when current > target
Example:
Cross-links: needs_rebalance · REBALANCE_THRESHOLD_BPS
needs_rebalance¶
Rebalancer.needs_rebalance(current, target) -> bool
Whether deviation exceeds the 5% lazy-rebalance threshold.
| Param | Type | Description |
|---|---|---|
current |
uint256 |
Strategy current_debt |
target |
uint256 |
Computed target debt |
Returns: bool — True if compute_deviation > REBALANCE_THRESHOLD_BPS (500).
Access: @internal @pure — MSV skips micro-moves when False.
Source (khomdev-keep/src/Rebalancer.vy:132-138):
@internal
@pure
def needs_rebalance(current, target) -> bool:
return self.compute_deviation(current, target) > REBALANCE_THRESHOLD_BPS
Example:
Cross-links: compute_deviation · MultiStrategyVault rebalance
cap_deposit¶
Rebalancer.cap_deposit(want, current_debt, max_debt, total_idle) -> uint256
Bound a strategy deposit by max_debt headroom and vault idle.
| Param | Type | Description |
|---|---|---|
want |
uint256 |
Desired deposit amount |
current_debt |
uint256 |
Strategy's booked debt |
max_debt |
uint256 |
Strategy ceiling (0 = uncapped) |
total_idle |
uint256 |
Vault idle USDC |
Returns: uint256 — capped deposit (may be 0).
Access: @internal @pure — used during allocate/rebalance pushes.
Source (khomdev-keep/src/Rebalancer.vy:143-158):
@internal
@pure
def cap_deposit(want, current_debt, max_debt, total_idle) -> uint256:
if max_debt != 0 and current_debt + want > max_debt:
want = max_debt - current_debt if max_debt > current_debt else 0
if want > total_idle:
want = total_idle
return want
Example:
Cross-links: compute_fixed_targets · MultiStrategyVault allocateCapital
compute_coilmaker_allocation¶
Rebalancer.compute_coilmaker_allocation(idle, deploy_available, current_debt, max_debt) -> uint256
How much idle USDC to route into CoilMakerStrategy (FLYWHEEL 2.0).
| Param | Type | Description |
|---|---|---|
idle |
uint256 |
Vault idle balance |
deploy_available |
uint256 |
Maker's remaining deploy capacity |
current_debt |
uint256 |
CoilMaker booked debt |
max_debt |
uint256 |
CoilMaker max_debt (0 = uncapped) |
Returns: uint256 — min(idle, deploy_available), further capped by max_debt - current_debt. 0 at capacity.
Access: @internal @pure — MSV CoilMaker allocation path.
Source (khomdev-keep/src/Rebalancer.vy:163-177):
@internal
@pure
def compute_coilmaker_allocation(idle, deploy_available, current_debt, max_debt):
to_coil = min(idle, deploy_available)
if max_debt != 0 and current_debt + to_coil > max_debt:
to_coil = max_debt - current_debt if max_debt > current_debt else 0
return to_coil
Example:
Cross-links: CoilMakerStrategy · cap_deposit