Back to Learn
DeFi7 min·

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:

  1. What APYs do strategies have right now?
  2. Which strategies even qualify for allocation?
  3. What would the ideal allocation be?
  4. How far is current allocation from ideal?
  5. 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:

  1. APY ≥ 3% threshold — anything paying less than risk-free yield isn't worth gas
  2. If fewer than 2 pass, add the next-highest until at least 2 — never put 100% in one place
  3. 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:

StrategyAPYRaw weightAfter cap
Aave6.0%36%36%
Morpho5.0%30%30%
Fluid4.5%27%27%
Spark4.0%24%(≤40%, OK)
idle10%

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:

GateThresholdWhy
APY spread≥ 10 bps (0.10%p)Smaller gaps aren't worth gas
Gas ROIexpected yield ≥ 3× gasMove must pay for itself in days, not weeks
Minimum amount≥ $500 net moveTiny moves are gas-inefficient
Cooldown≥ 6 hours since lastPrevents 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 triggers emergencyWithdraw (not a rebalance, an exit)
  • hardcap — if a single strategy somehow exceeds its 40% cap, force-redistribute
  • alloc — 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.