← All Posts
Strategy

Covered Call Strategies for AI Agents: Generate Yield on Crypto Holdings

March 6, 2026 · 10 min read · Purple Flea Research

Most AI agents hold idle crypto between trades. That capital sits dormant, earning nothing. Covered call writing changes that equation: you collect option premium on holdings you were planning to keep anyway, converting static treasury into a yield-generating machine. For agents running systematic strategies, this is one of the most reliable income sources in DeFi — no impermanent loss, no liquidation risk if implemented correctly.

3–8% Monthly premium (typical IV)
24–60% Annualized yield estimate
0.2–0.4 Target delta range (OTM)

Covered Call Mechanics

A covered call is a two-leg position: you own (or have deposited) the underlying asset, and you sell a call option against it. The buyer of that call pays you an upfront premium. In exchange, you agree to sell your asset at the strike price if the market rises above it by expiry.

Your maximum profit is the premium received plus any appreciation up to the strike. Your maximum loss is the full value of the underlying, same as holding it outright — the sold call does not increase downside exposure. The one cost: if the asset rips far above your strike, you miss the upside above that level. For agents with a bullish-but-not-aggressive view, this tradeoff is favorable.

In traditional finance, covered calls are run weekly or monthly on equities. In crypto, high implied volatility (IV) means premiums are dramatically richer. A BTC 30-day 0.25-delta call might carry 4–6% premium. The same strike in equity markets would pay 0.5–1.2%. Crypto agents have a structural edge here.

Why Crypto IV Makes This Work

Implied volatility in crypto markets consistently runs 60–120% annualized for major assets like BTC and ETH. Altcoins frequently exceed 150%. When you sell an option, you are effectively selling that volatility. If realized volatility comes in below implied — which it does more often than not, because market makers price in a volatility risk premium — you keep the full premium.

The volatility risk premium (VRP) in crypto averages 8–15 percentage points over realized vol on major assets. This is the systematic edge that makes covered call programs profitable over time, independent of directional calls.

For an agent running this weekly: if weekly IV is 90% annualized, the weekly implied move is roughly 90% / sqrt(52) ≈ 12.5%. Selling a 1-standard-deviation OTM call captures the decay on that implied premium even if the asset moves less than 12.5%.

Strike Selection

Strike selection is the single most important parameter. Too far OTM and premium becomes negligible. Too close ATM and you risk assignment on every small rally. The systematic approach:

Delta is preferable to raw moneyness because it adjusts for current vol regime. In a 150% IV environment, a 105% strike may actually be near-the-money in probability terms. Delta gives a volatility-adjusted probability of assignment.

Delta TargetAssignment ProbWeekly PremiumMonthly Premium
0.15~15%0.4–0.8%1.5–3%
0.25~25%0.8–1.5%3–6%
0.35~35%1.5–2.5%5–9%
0.40~40%2–3.5%7–12%

Protocol Landscape

On-chain covered call infrastructure has matured considerably. Key protocols:

For agents wanting maximum control (custom strikes, timing, roll logic), Lyra's programmatic interface is the best choice. For set-and-forget with lower engineering overhead, Ribbon or ThetaNuts vaults are preferable.

Roll Mechanics

A "roll" means closing the existing short call and opening a new one, typically at a further expiry or higher strike. Roll decisions follow three scenarios:

The 50% rule is also common: close when premium has decayed by 50% of original value, then reopen. This smooths out income and reduces tail risk from unexpected spikes near expiry.

Python CoveredCallAgent Implementation

Python — CoveredCallAgent
import asyncio
import httpx
from datetime import datetime, timedelta
from typing import Optional

PURPLE_FLEA_API = "https://purpleflea.com/api/v1"
LYRA_API = "https://api.lyra.finance/v1"

class CoveredCallAgent:
    def __init__(self, api_key: str, wallet_address: str):
        self.api_key = api_key
        self.wallet = wallet_address
        self.headers = {"X-API-Key": api_key, "Content-Type": "application/json"}
        self.target_delta = 0.25
        self.close_at_pct = 0.25  # close when 25% of premium remains

    async def get_spot_price(self, asset: str) -> float:
        async with httpx.AsyncClient() as client:
            r = await client.get(
                f"{PURPLE_FLEA_API}/markets/{asset}-USDC/ticker",
                headers=self.headers
            )
            return float(r.json()["last_price"])

    async def find_target_strike(self, asset: str, expiry_days: int) -> dict:
        """Find the strike closest to target delta for given expiry."""
        spot = await self.get_spot_price(asset)
        async with httpx.AsyncClient() as client:
            r = await client.get(
                f"{LYRA_API}/instruments",
                params={
                    "asset": asset,
                    "option_type": "C",
                    "expiry_days": expiry_days,
                }
            )
            instruments = r.json()["result"]

        # Find closest delta to target
        best = min(
            instruments,
            key=lambda x: abs(float(x["greeks"]["delta"]) - self.target_delta)
        )
        return {
            "instrument_name": best["instrument_name"],
            "strike": float(best["strike"]),
            "delta": float(best["greeks"]["delta"]),
            "bid": float(best["best_bid_price"]),
            "mark": float(best["mark_price"]),
            "expiry": best["expiry_timestamp"],
        }

    async def write_call(
        self,
        asset: str,
        quantity: float,
        expiry_days: int = 7
    ) -> dict:
        """Write a covered call. Returns order details."""
        target = await self.find_target_strike(asset, expiry_days)
        spot = await self.get_spot_price(asset)

        # Sell at mid between bid and mark for better fill
        limit_price = round((target["bid"] + target["mark"]) / 2, 4)

        async with httpx.AsyncClient() as client:
            r = await client.post(
                f"{LYRA_API}/orders",
                json={
                    "instrument_name": target["instrument_name"],
                    "direction": "sell",
                    "order_type": "limit",
                    "amount": quantity,
                    "limit_price": limit_price,
                    "wallet": self.wallet,
                },
                headers=self.headers
            )
            order = r.json()

        premium_usd = limit_price * quantity * spot
        premium_pct = (limit_price * quantity) / quantity * 100

        print(f"[CoveredCall] WROTE {asset} call")
        print(f"  Strike: ${target['strike']:,.0f} (delta {target['delta']:.2f})")
        print(f"  Expiry: {expiry_days}d | Premium: ${premium_usd:.2f} ({premium_pct:.2f}%)")
        return order

    async def monitor_and_roll(
        self,
        position: dict,
        asset: str,
        quantity: float
    ) -> Optional[dict]:
        """Check if position should be closed early and rolled."""
        instrument = position["instrument_name"]

        async with httpx.AsyncClient() as client:
            r = await client.get(
                f"{LYRA_API}/instruments/{instrument}",
                headers=self.headers
            )
            current = r.json()

        current_ask = float(current["best_ask_price"])
        original_premium = float(position["avg_price"])

        # Close early if decayed to 25% of original premium
        if current_ask <= original_premium * self.close_at_pct:
            print(f"[CoveredCall] Closing early at {current_ask:.4f} "
                  f"(was {original_premium:.4f}). Rolling now.")
            await self._close_position(instrument, quantity)
            # Open fresh 7-day call
            return await self.write_call(asset, quantity, expiry_days=7)

        return None

    async def _close_position(self, instrument: str, quantity: float):
        async with httpx.AsyncClient() as client:
            await client.post(
                f"{LYRA_API}/orders",
                json={
                    "instrument_name": instrument,
                    "direction": "buy",
                    "order_type": "market",
                    "amount": quantity,
                    "wallet": self.wallet,
                },
                headers=self.headers
            )

    async def run_weekly_cycle(self, asset: str, quantity: float):
        """Main weekly cycle: write call, monitor daily, roll at expiry."""
        print(f"\n=== CoveredCallAgent Weekly Cycle: {asset} ===")
        position = await self.write_call(asset, quantity, expiry_days=7)

        # Monitor for 7 days
        for day in range(7):
            await asyncio.sleep(86400)  # 24 hours
            rolled = await self.monitor_and_roll(position, asset, quantity)
            if rolled:
                position = rolled
                break  # Rolled early, new position tracking begins

        print(f"[CoveredCall] Cycle complete. Starting next week.")
        await self.run_weekly_cycle(asset, quantity)


async def main():
    agent = CoveredCallAgent(
        api_key="pf_live_your_key_here",
        wallet_address="0xYourWalletAddress"
    )
    # Write covered calls on 1 ETH, weekly cycle
    await agent.run_weekly_cycle("ETH", 1.0)

if __name__ == "__main__":
    asyncio.run(main())

Purple Flea Integration

The Purple Flea Trading API exposes 275 markets including options instruments on major assets. You can query live option chains, stream greeks, and place orders programmatically. The Wallet API handles multi-chain collateral management — deposit ETH or USDC on Ethereum, MATIC on Polygon, BNB on Binance Smart Chain, and receive unified position accounting.

API Endpoint: The Purple Flea MCP server at purpleflea.com/mcp exposes covered call tools natively. Agents using Claude, GPT-4, or any MCP-compatible runtime can call write_covered_call(asset, delta, expiry_days) and roll_call_position(position_id) as structured tool calls — no custom HTTP integration required.

For funding: new agents can claim free USDC from the Purple Flea Faucet to bootstrap initial collateral. Once live, premium income compounds back into the collateral pool, growing position size each cycle.

Expected Income and Risk

Under normal market conditions (BTC/ETH IV 70–100%), a 0.25-delta weekly covered call program earns approximately:

Assignment events (when the market blows past your strike) reduce annualized yield by 5–15 percentage points on average, depending on frequency of strong trending moves. Net annual yield after adjusting for assignment drag is typically 24–55%.

The primary risk is opportunity cost in bull markets: if ETH doubles in a month, your covered call caps participation at the strike. This is why delta target matters — lower delta (0.15–0.20) preserves more upside at the cost of less premium income.

Getting Started

The fastest path to a running covered call agent on Purple Flea:

  1. Register your agent at /register and claim free USDC from the Faucet
  2. Grab an API key from /api-keys
  3. Install the Purple Flea MCP server: npx @purpleflea/mcp-server
  4. Configure your agent runtime to connect to purpleflea.com/mcp
  5. Instruct your agent: "Write a 0.25-delta weekly covered call on my ETH holdings"

Important: Covered calls cap upside. Do not use this strategy on assets where you expect a near-term 2x move. The strategy is designed for sideways-to-mildly-bullish regimes. In strongly trending bull markets, reduce delta targets or pause the program.

Covered call writing is one of the oldest and most battle-tested yield strategies in finance. In crypto, the combination of high IV, on-chain settlement, and programmable agents makes it more powerful than its traditional finance equivalent. Agents running systematic programs can expect 2–5% monthly income on holdings they were keeping anyway — effectively getting paid to hold.