How the Apyee Keeper Decides When to Rebalance
Not every APY shift triggers a move. Here's the exact logic — APY weighting, gas ROI gate, direction lock, and cooldown — that decides whether the Keeper acts or skips.
The Apyee Keeper runs every 5 minutes per chain. Most cycles end in skip, not rebalance. That's the design. Here's exactly what it checks, in order, and why each gate exists.
The cycle: five questions, asked in order
Every cron tick on every chain, the Keeper asks:
- What APYs do strategies have right now?
- Which strategies even qualify for allocation?
- What would the ideal allocation be?
- How far is current allocation from ideal?
- Is the gap large enough — and cheap enough — to rebalance?
If any gate fails, it logs the reason and waits 5 minutes.
1. Where APY data comes from
Two sources, in this order:
- DefiLlama 7-day average (primary) — smooths short-term noise (TVL spikes, single-day rate shocks)
- Onchain
currentAPY()(fallback) — direct from the protocol when DefiLlama is missing or stale
If both return 0 or null, the strategy gets APY = 0. It still appears in the list but lands at the bottom of priority.
The rationale: making allocation decisions on a single-block snapshot is noisy. A weekly average is the smallest window that filters out rate manipulation while staying responsive.
2. Which strategies qualify
A simple filter:
- APY ≥ 3% threshold — anything paying less than risk-free yield isn't worth gas
- If fewer than 2 pass, add the next-highest until at least 2 — never put 100% in one place
- If the entire chain is sub-3%, include all — better to allocate than sit idle
The 3% floor is generous on purpose. Treasury yields are around 5% in 2026; we want strategies to actually beat that to justify the operational complexity.
3. The ideal allocation: APY-weighted with caps
The Keeper builds an "ideal" allocation by weighting each qualifying strategy by its APY:
raw_ratio[i] = (apy[i] / sum(apy)) × (10000 - idle_bps)
idle_bps = 1000 // 10% kept in vault idle for instant withdrawals
max_per_cap = 4000 // 40% per strategy
If any strategy exceeds 40%, the overage redistributes to non-capped strategies proportionally to their APY. The loop converges in 1–2 iterations.
Example on Ethereum with 4 strategies:
| Strategy | APY | Raw weight | After cap |
|---|---|---|---|
| Aave | 6.0% | 36% | 36% |
| Morpho | 5.0% | 30% | 30% |
| Fluid | 4.5% | 27% | 27% |
| Spark | 4.0% | 24% | (≤40%, OK) |
| idle | — | — | 10% |
In this case nothing hits the cap, so raw weights pass through. The 40% cap kicks in when one strategy massively outperforms (e.g., a temporary 12% spike).
4. How much off is current allocation?
For each strategy: deviation[i] = current[i] - ideal[i].
The Keeper picks one pair to act on: the strategy most over-allocated (maxOver) → the strategy most under-allocated (maxUnder).
Importantly, the pair is direction-locked: from.apy < to.apy must hold. We only move from a lower-yielding strategy to a higher-yielding one. If no such pair exists (e.g., the over-allocated one is also the highest APY), the cycle skips with no alert.
This direction lock was added in v2 (2026-05-13). v1 would sometimes propose moves that decreased expected yield because it minimized deviation without checking APY direction. Skipping is fine — the next cycle will re-evaluate.
5. The four execution gates
A rebalance only fires if all four pass:
| Gate | Threshold | Why |
|---|---|---|
| APY spread | ≥ 10 bps (0.10%p) | Smaller gaps aren't worth gas |
| Gas ROI | expected yield ≥ 3× gas | Move must pay for itself in days, not weeks |
| Minimum amount | ≥ $500 net move | Tiny moves are gas-inefficient |
| Cooldown | ≥ 6 hours since last | Prevents ping-pong from short-term swings |
Each gate failure is logged with the reason. A typical log line:
[rebalance ETH] ideal=Aave 36% / Morpho 30% / Fluid 27% / idle 10%
current=Aave 32% / Morpho 28% / Fluid 30% / idle 10%
proposed=Fluid → Aave (4%)
gate=APY ✓ (200bps) / gasROI ✓ ($120 vs $40) / minAmount ✓ / cooldown ✗ (남은 4h)
action=skip (cooldown)
The skip line is intentional — operators can see why nothing moved.
What this means for you as a depositor
Two practical consequences:
Your funds don't shuffle constantly. Vault rebalances are rare — typical chains see 1–3 moves per day during normal markets. Each move is meaningful (≥10 bps, ≥$500, ≥3× gas ROI). Your USDC isn't bouncing between protocols for the sake of bouncing.
You don't have to time anything. When a strategy's APY drops (e.g., a lending market gets oversaturated), the Keeper notices on the next cycle. By the time you check your portfolio, the move is already done — or properly skipped because the gap was small.
Variants the Keeper also handles
Beyond pure APY rebalance, the Keeper has three other reasonCodes for the same machinery:
emergency— protocol pause, security alert, or composite risk signal triggersemergencyWithdraw(not a rebalance, an exit)hardcap— if a single strategy somehow exceeds its 40% cap, force-redistributealloc— after an emergency withdraw, redistribute the reclaimed idle back into healthy strategies
All four reasonCodes go through the same execution pipeline. They differ in what triggers them and what gates apply, but the on-chain transaction shape is the same.
The takeaway
Optimization that fires constantly isn't optimization — it's noise that burns gas. The Apyee Keeper is conservative on purpose: many cycles, few moves, each move with a clear "why" in the logs. That's what makes the 15% performance fee earn its keep: not by chasing every basis point, but by capturing the spreads that actually matter after gas.

