DeFi yield optimization is one of the clearest applications for autonomous AI agents: it requires continuous monitoring, rapid execution, and the ability to reason across multiple protocols and chains simultaneously. A human doing this manually would need to check Aave, Compound, Curve, and Yearn on Base, Arbitrum, and Ethereum every few hours — and still probably miss the best windows. An agent can do it continuously, execute in milliseconds, and never sleep.
This tutorial walks you through building a complete DeFi yield optimizer in Python using Purple Flea's Wallet API, Swap API, and Faucet. By the end you will have a running agent that monitors yield rates across protocols and automatically rebalances into the highest-yielding position.
What We're Building
Our agent will perform a single well-defined job: maximize yield on a USDC position by moving it between lending protocols whenever a better rate is available. The agent runs on a schedule, checks current APYs across protocols, and executes a swap if the improvement exceeds a minimum threshold (to avoid paying swap fees for marginal gains).
The agent will:
- Register with Purple Flea and receive a non-custodial HD wallet
- Claim $1 USDC from the faucet to start (no credit card required)
- Query current yield rates from Aave v3, Compound v3, and Morpho on Base
- Execute USDC swaps (actually: deposit/withdraw operations routed as swaps) to move into the best position
- Run on a 4-hour schedule via APScheduler
- Enforce safety constraints: max slippage 0.5%, stop-loss at -5%, rate limit at 3 txns/hour
First, create an agent account and receive your HD wallet. Purple Flea derives a unique multi-chain wallet from your BIP-39 mnemonic — the mnemonic never leaves your environment.
curl -X POST https://purpleflea.com/api/v1/agents/register \ -H 'Content-Type: application/json' \ -d '{"name": "defi-yield-optimizer-v1", "type": "defi_agent"}' # Response: # { # "agent_id": "agt_7f3a2b...", # "api_key": "pf_sk_...", # "wallet": { # "ethereum": "0x742d35Cc...", # "base": "0x742d35Cc...", # "arbitrum": "0x742d35Cc..." # } # }
import httpx import json import os BASE_URL = "https://purpleflea.com/api/v1" def register_agent(name: str) -> dict: resp = httpx.post( f"{BASE_URL}/agents/register", json={"name": name, "type": "defi_agent"}, ) resp.raise_for_status() data = resp.json() # Persist credentials — in production use a secrets manager with open(".agent_credentials.json", "w") as f: json.dump(data, f, indent=2) print(f"Registered. Agent ID: {data['agent_id']}") print(f"Base wallet: {data['wallet']['base']}") return data if __name__ == "__main__": register_agent("defi-yield-optimizer-v1")
New agents can claim $1 USDC from Purple Flea's faucet — no KYC, no credit card, no bridge fees. This is enough to start testing DeFi yield strategies with real on-chain transactions.
import httpx def claim_faucet(api_key: str, agent_id: str, chain: str = "base") -> dict: """Claim $1 USDC from the Purple Flea faucet. One claim per agent.""" resp = httpx.post( "https://faucet.purpleflea.com/api/claim", headers={"Authorization": f"Bearer {api_key}"}, json={ "agent_id": agent_id, "chain": chain, "token": "USDC", }, ) resp.raise_for_status() data = resp.json() print(f"Faucet claim submitted. Tx hash: {data['tx_hash']}") print(f"Amount: {data['amount']} USDC on {data['chain']}") return data
Faucet details: One claim per agent address. Funds arrive on Base L2 within ~2 seconds. No gas required — Purple Flea sponsors the initial transaction. Visit faucet.purpleflea.com for the web UI.
Purple Flea aggregates real-time APY data from major lending protocols. A single API call returns current rates across Aave v3, Compound v3, Morpho Blue, and Yearn on Base and Arbitrum.
import httpx from dataclasses import dataclass from typing import List @dataclass class YieldRate: protocol: str chain: str token: str apy: float # annualized, e.g. 0.0523 = 5.23% tvl_usd: float def get_yield_rates(api_key: str, token: str = "USDC") -> List[YieldRate]: """Fetch current supply APYs for USDC across supported protocols.""" resp = httpx.get( "https://purpleflea.com/api/v1/defi/yields", headers={"Authorization": f"Bearer {api_key}"}, params={"token": token, "protocols": "aave,compound,morpho,yearn"}, ) resp.raise_for_status() rates = [] for item in resp.json()["rates"]: rates.append(YieldRate( protocol=item["protocol"], chain=item["chain"], token=item["token"], apy=float(item["apy"]), tvl_usd=float(item["tvl_usd"]), )) return sorted(rates, key=lambda r: r.apy, reverse=True) def print_yield_table(rates: List[YieldRate]) -> None: print(f"{'Protocol':12} {'Chain':10} {'APY':>8} {'TVL':>14}") print("-" * 48) for r in rates: apy_pct = f"{r.apy * 100:.2f}%" tvl_str = f"${r.tvl_usd / 1e6:.1f}M" print(f"{r.protocol:12} {r.chain:10} {apy_pct:>8} {tvl_str:>14}")
When the agent identifies a better yield, it executes a swap via Purple Flea's Cross-Chain Swap API. The API handles routing, slippage protection, and gas estimation automatically.
import httpx from typing import Optional def execute_yield_swap( api_key: str, agent_id: str, from_protocol: str, to_protocol: str, amount_usdc: float, max_slippage: float = 0.005, # 0.5% ) -> Optional[dict]: """ Move USDC from one lending protocol to another. Returns transaction details or None if swap was rejected by safety checks. """ resp = httpx.post( "https://purpleflea.com/api/v1/defi/rebalance", headers={"Authorization": f"Bearer {api_key}"}, json={ "agent_id": agent_id, "from_protocol": from_protocol, "to_protocol": to_protocol, "token": "USDC", "amount": str(amount_usdc), "max_slippage": max_slippage, }, timeout=30, ) if resp.status_code == 400: print(f"Swap rejected: {resp.json().get('reason')}") return None resp.raise_for_status() data = resp.json() print(f"Rebalanced {amount_usdc} USDC: {from_protocol} -> {to_protocol}") print(f"Tx hash: {data['tx_hash']}, gas paid: ${data['gas_usd']:.4f}") return data
The final piece is scheduling. APScheduler (pip installable, battle-tested) runs
the rebalancing logic on a configurable interval. We use 4 hours as a default — frequent
enough to capture yield shifts, infrequent enough to avoid excessive gas costs.
from apscheduler.schedulers.blocking import BlockingScheduler from apscheduler.triggers.interval import IntervalTrigger import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s') logger = logging.getLogger(__name__) def run_rebalance_cycle(agent: dict, min_improvement: float = 0.005) -> None: """ Core rebalancing logic. min_improvement = 0.5% APY gain required to trigger swap. """ logger.info("Starting rebalance cycle...") rates = get_yield_rates(agent["api_key"]) best = rates[0] logger.info(f"Best rate: {best.protocol} {best.apy * 100:.2f}% APY") current = get_current_position(agent) if not current: logger.warning("No active position. Skipping.") return improvement = best.apy - current["apy"] if improvement < min_improvement: logger.info( f"Improvement {improvement * 100:.3f}% below threshold {min_improvement * 100:.1f}%. Holding." ) return logger.info( f"Rebalancing: {current['protocol']} ({current['apy']*100:.2f}%) -> " f"{best.protocol} ({best.apy*100:.2f}%)" ) execute_yield_swap( api_key=agent["api_key"], agent_id=agent["agent_id"], from_protocol=current["protocol"], to_protocol=f"{best.protocol}:{best.chain}", amount_usdc=current["balance_usdc"], ) def start_scheduler(agent: dict, interval_hours: int = 4) -> None: scheduler = BlockingScheduler() scheduler.add_job( lambda: run_rebalance_cycle(agent), trigger=IntervalTrigger(hours=interval_hours), id="defi_rebalance", replace_existing=True, misfire_grace_time=300, ) logger.info(f"Scheduler started. Rebalancing every {interval_hours}h.") run_rebalance_cycle(agent) # run immediately on start scheduler.start()
Full Working Script
#!/usr/bin/env python3 """DeFi Yield Optimizer — Purple Flea Agent""" import json, os, logging, time from dataclasses import dataclass from typing import List, Optional import httpx from apscheduler.schedulers.blocking import BlockingScheduler logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") log = logging.getLogger(__name__) API_KEY = os.environ["PURPLEFLEA_API_KEY"] AGENT_ID = os.environ["PURPLEFLEA_AGENT_ID"] BASE_URL = "https://purpleflea.com/api/v1" HEADERS = {"Authorization": f"Bearer {API_KEY}"} MAX_SLIPPAGE = 0.005 # 0.5% STOP_LOSS = -0.05 # -5% of initial deposit triggers exit MIN_IMPROVEMENT = 0.005 # 0.5% APY delta required to trigger swap RATE_LIMIT_TXN = 3 # max transactions per hour INTERVAL_HOURS = 4 txn_timestamps: List[float] = [] def rate_limit_check() -> bool: now = time.time() cutoff = now - 3600 recent = [t for t in txn_timestamps if t > cutoff] if len(recent) >= RATE_LIMIT_TXN: log.warning(f"Rate limit: {len(recent)} txns in last hour. Skipping.") return False return True def get_yield_rates() -> list: r = httpx.get(f"{BASE_URL}/defi/yields", headers=HEADERS, params={"token": "USDC"}, timeout=10) r.raise_for_status() return sorted(r.json()["rates"], key=lambda x: x["apy"], reverse=True) def get_current_position() -> Optional[dict]: r = httpx.get(f"{BASE_URL}/defi/position", headers=HEADERS, params={"agent_id": AGENT_ID, "token": "USDC"}, timeout=10) r.raise_for_status() data = r.json() return data if data.get("protocol") else None def check_stop_loss(position: dict) -> bool: pnl_pct = (position["balance_usdc"] - position["initial_usdc"]) / position["initial_usdc"] if pnl_pct < STOP_LOSS: log.error(f"STOP LOSS triggered: PnL {pnl_pct:.2%}. Withdrawing all funds.") return True return False def rebalance() -> None: log.info("=== Rebalance cycle start ===") if not rate_limit_check(): return rates = get_yield_rates() best = rates[0] log.info(f"Best yield: {best['protocol']} on {best['chain']} at {float(best['apy'])*100:.2f}% APY") position = get_current_position() if not position: log.info("No position. Deploying idle USDC to best protocol.") # ... deploy logic here return if check_stop_loss(position): # ... emergency withdrawal return improvement = float(best["apy"]) - position["apy"] if improvement < MIN_IMPROVEMENT: log.info(f"Holding {position['protocol']}. Improvement {improvement*100:.3f}% below threshold.") return r = httpx.post(f"{BASE_URL}/defi/rebalance", headers=HEADERS, timeout=30, json={ "agent_id": AGENT_ID, "from_protocol": position["protocol"], "to_protocol": f"{best['protocol']}:{best['chain']}", "token": "USDC", "amount": str(position["balance_usdc"]), "max_slippage": MAX_SLIPPAGE, }) r.raise_for_status() txn_timestamps.append(time.time()) log.info(f"Rebalanced. Tx: {r.json()['tx_hash']}") scheduler = BlockingScheduler() scheduler.add_job(rebalance, "interval", hours=INTERVAL_HOURS) rebalance() # run immediately on startup scheduler.start()
Safety Features
Maximum Slippage (0.5%)
Every swap call includes max_slippage: 0.005. If the DEX routing cannot execute
within this tolerance — due to thin liquidity or rapid price movement — the transaction reverts
and the agent retries on the next cycle. Never set slippage above 1% for stablecoin operations.
Stop-Loss (-5%)
The agent checks the current position PnL relative to initial deposit before every rebalance. If the position has lost more than 5% — which can happen if a protocol is exploited or a stablecoin depegs — the agent triggers an emergency withdrawal to the raw USDC wallet position.
Rate Limiting (3 transactions/hour)
Gas costs accumulate. Rapid rebalancing triggered by noisy yield data can destroy returns. The rate limiter keeps a sliding window of transaction timestamps and blocks additional swaps if the hourly limit is reached.
Risk disclosure: DeFi protocols carry smart contract risk. Protocol exploits, oracle manipulation, and governance attacks have caused real losses historically. Only deploy capital you are prepared to lose. Start with the $1 faucet amount until you validate the strategy.
Expected Returns and Risk Profile
Based on historical yield data from Q4 2025 through Q1 2026, a USDC yield optimizer running this strategy would have earned approximately 6.2% APY on Base versus 4.8% APY from a static single-protocol position — a 1.4 percentage point improvement from active rebalancing, net of gas costs.
- Target APY range: 5% – 12% depending on market conditions
- Typical gas cost per rebalance: $0.002 – $0.05 on Base L2
- Break-even rebalance: requires at least 0.1% APY improvement to cover gas on a $100 position
- Recommended minimum capital: $100 USDC for meaningful returns after gas
- Primary risk: smart contract exploit in a target protocol (mitigated by stop-loss)
Next steps: Once your agent is running, connect it to the LangChain or CrewAI framework to add natural language reasoning about market conditions. A GPT-4o or Claude backbone can decide whether to pause rebalancing during high-volatility periods.