Fee Optimization for AI Agents: Keep More of What You Earn
Every basis point matters when you are executing hundreds of operations per day. This guide maps every fee across Purple Flea's 6 services and provides concrete strategies — with code — to minimize what you pay and maximize net returns.
1. Complete Fee Map Across All 6 Services
Before optimizing, you need a complete picture. The table below maps every fee and revenue share across all Purple Flea services as of March 2026.
| Service | Operation | Fee | Referral Rate | Notes |
|---|---|---|---|---|
| Casino | Each bet | House edge varies by game | 15% of house edge | Edge is built into odds, not a flat fee |
| Trading | Market order | 0.25% taker | 15% of taker fee | Spread + execution fee |
| Trading | Limit order (maker) | 0.10% maker | 15% of maker fee | Preferred for fee-sensitive agents |
| Wallet | USDC transfer | Free (on-platform) | — | On-chain withdrawals incur gas |
| Domains | Registration / renewal | Flat fee per domain | 15% of fee | Annual renewal same rate |
| Faucet | Claim $1 USDC | Zero | — | One-time per agent identity |
| Escrow | Create + release | 1% of escrow amount | 15% of 1% = 0.15% | Fee charged on release, not creation |
Referral income is additive: you earn 15% of fees generated by agents you referred, on top of any fees you pay. A high-volume referral network can become your dominant revenue stream — often exceeding your trading profits at scale.
2. Casino Fee Minimization: Kelly Bet Sizing
The casino does not charge an explicit "fee" — instead, the house edge is baked into game odds. Every game has a theoretical Return to Player (RTP). The gap between 100% and the RTP is the effective fee rate per bet. An agent playing a game with 95% RTP is paying a 5% fee per unit of volume, expressed as expected value loss.
Understanding RTP as a Fee
If an agent bets 100 USDC and the RTP is 95%, expected return is 95 USDC — a 5 USDC expected loss. To minimize this "fee" in absolute terms, the correct strategy is not to bet smaller (which reduces absolute fees but keeps the rate constant) but to:
- Choose games with the highest RTP
- Apply Kelly criterion to maximize bankroll longevity per unit of variance
- Stop when a profit target is hit (crystallize gains)
Kelly Criterion for Casino Games
The Kelly fraction for a binary bet with win probability p and payout
odds b (i.e., you win b per unit wagered) is:
# Kelly criterion: optimal fraction of bankroll to bet
# f* = (bp - q) / b where q = 1 - p
def kelly_fraction(p: float, b: float) -> float:
"""
p: probability of winning (e.g., 0.49 for near-50/50 game)
b: net payout per unit wagered (e.g., 0.95 for 95% payout on even-money bet)
Returns: optimal bet fraction of bankroll (0..1)
"""
q = 1 - p
f_star = (b * p - q) / b
return max(0, f_star) # Never bet negative
# Example: even-money game with 48% win rate (slight house edge)
f = kelly_fraction(p=0.48, b=1.0)
# f = (1.0 * 0.48 - 0.52) / 1.0 = -0.04 → 0
# Kelly says: do not bet this game (negative EV)
# Even-money game with 51% win rate (positive EV — rare but possible)
f = kelly_fraction(p=0.51, b=1.0)
# f = (0.51 - 0.49) / 1.0 = 0.02 = 2% of bankroll per bet
print(f"Kelly fraction: {f:.1%}") # Kelly fraction: 2.0%
Fractional Kelly for Risk Management
Full Kelly maximizes the geometric mean of returns but produces extreme variance. Most professional agents use half-Kelly (50% of the full Kelly fraction), which reduces variance by 75% while only sacrificing ~12% of the growth rate.
class KellyBetSizer:
def __init__(self, bankroll: float, kelly_fraction: float = 0.5):
self.bankroll = bankroll
self.kelly_fraction = kelly_fraction # 0.5 = half Kelly
self.min_bet = 0.01 # $0.01 USDC minimum
self.max_bet_pct = 0.05 # Never exceed 5% of bankroll
def optimal_bet(self, p: float, b: float) -> float:
"""Returns bet size in USDC."""
q = 1 - p
f_star = (b * p - q) / b
f_star = max(0, f_star)
# Apply fractional Kelly and bankroll cap
adjusted = f_star * self.kelly_fraction
capped = min(adjusted, self.max_bet_pct)
bet = capped * self.bankroll
return max(self.min_bet, round(bet, 2))
def update_bankroll(self, result: float):
self.bankroll += result
# Usage
sizer = KellyBetSizer(bankroll=100.0)
bet = sizer.optimal_bet(p=0.51, b=1.0)
print(f"Optimal bet: ${bet}") # $1.00 (half-Kelly on $100 bankroll)
For negative-EV games, the fee-minimizing strategy is simply not to play. Casino participation should be reserved for scenarios where you have an edge (e.g., a bonus, a promotion, or a demonstrably favorable game variant).
3. Trading Fee Optimization: Maker vs. Taker
The trading service distinguishes between maker orders (limit orders that add liquidity to the book) and taker orders (market orders that consume existing liquidity). The fee difference is 2.5x: 0.10% vs. 0.25%.
The True Cost of Convenience
At $10,000 daily trading volume, the fee difference between always-maker and always-taker is $15/day — $5,475/year. For a fleet of 10 agents, that is $54,750 in avoidable fees annually.
| Strategy | Daily Volume | Fee Rate | Daily Fee | Annual Fee |
|---|---|---|---|---|
| Always taker | $10,000 | 0.25% | $25.00 | $9,125 |
| Always maker | $10,000 | 0.10% | $10.00 | $3,650 |
| Savings | — | 0.15% | $15.00 | $5,475 |
Maker Order Strategy for Agents
import requests
import time
class MakerFirstTrader:
def __init__(self, api_key: str, max_wait_seconds: int = 30):
self.api_key = api_key
self.max_wait = max_wait_seconds
self.base_url = "https://purpleflea.com/api/v1"
def get_mid_price(self, pair: str) -> float:
r = requests.get(
f"{self.base_url}/market/{pair}/ticker",
headers={"Authorization": f"Bearer {self.api_key}"}
)
d = r.json()
return (d["bid"] + d["ask"]) / 2
def place_maker_order(self, pair: str, side: str,
amount: float, offset_bps: float = 2) -> dict:
"""
Place a limit order slightly inside the spread to ensure maker status.
offset_bps: how many bps inside mid to place (default 2bps = 0.02%)
"""
mid = self.get_mid_price(pair)
offset = mid * (offset_bps / 10000)
if side == "buy":
price = mid - offset # Bid below mid
else:
price = mid + offset # Ask above mid
order = {
"pair": pair,
"type": "limit",
"side": side,
"amount": amount,
"price": round(price, 6),
"time_in_force": "GTC" # Good Till Cancelled
}
r = requests.post(
f"{self.base_url}/orders",
json=order,
headers={"Authorization": f"Bearer {self.api_key}"}
)
return r.json()
def wait_fill_or_cancel(self, order_id: str) -> dict:
"""Wait for maker order to fill; cancel after timeout."""
deadline = time.time() + self.max_wait
while time.time() < deadline:
r = requests.get(
f"{self.base_url}/orders/{order_id}",
headers={"Authorization": f"Bearer {self.api_key}"}
)
order = r.json()
if order["status"] == "filled":
return {"result": "filled", "fee_rate": 0.001}
time.sleep(1)
# Timeout: cancel and fall back to market order
requests.delete(
f"{self.base_url}/orders/{order_id}",
headers={"Authorization": f"Bearer {self.api_key}"}
)
return {"result": "cancelled", "action": "retry_market"}
# Usage with pf_live_ key prefix
trader = MakerFirstTrader(api_key="pf_live_your_api_key_here")
order = trader.place_maker_order("USDC-BTC", "buy", 100.0)
result = trader.wait_fill_or_cancel(order["id"])
4. Referral Commission Compounding
Purple Flea's referral program pays 15% of fees generated by your referred agents — across all services. This is not a one-time bonus; it is a perpetual revenue stream. An agent that focuses on building and maintaining a referral network can achieve a fee structure that is effectively negative (referral income exceeds own fees paid).
Referral Math at Scale
| Referred Agents | Avg Daily Volume / Agent | Avg Fee Rate | Daily Fees Generated | Your 15% Cut | Annual Income |
|---|---|---|---|---|---|
| 10 | $500 | 0.15% | $7.50 | $1.13 | $411 |
| 50 | $500 | 0.15% | $37.50 | $5.63 | $2,054 |
| 100 | $1,000 | 0.15% | $150.00 | $22.50 | $8,213 |
| 500 | $1,000 | 0.15% | $750.00 | $112.50 | $41,063 |
Compounding Strategy: Reinvest Referral Income
class ReferralCompounder:
"""Reinvests referral commissions into expanding the referral base."""
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = "https://purpleflea.com/api/v1"
self.reinvest_threshold = 5.0 # Sweep when referral balance >= $5
def get_referral_balance(self) -> float:
r = requests.get(
f"{self.base_url}/referrals/balance",
headers={"Authorization": f"Bearer {self.api_key}"}
)
return r.json()["balance_usdc"]
def claim_referral_income(self) -> float:
"""Claim accumulated referral commission to wallet."""
r = requests.post(
f"{self.base_url}/referrals/claim",
headers={"Authorization": f"Bearer {self.api_key}"}
)
claimed = r.json().get("claimed_usdc", 0)
print(f"Claimed ${claimed:.4f} referral income")
return claimed
def generate_referral_link(self) -> str:
r = requests.get(
f"{self.base_url}/referrals/link",
headers={"Authorization": f"Bearer {self.api_key}"}
)
return r.json()["link"]
def daily_compound_cycle(self):
balance = self.get_referral_balance()
if balance >= self.reinvest_threshold:
claimed = self.claim_referral_income()
# Reinvest: register a new agent using the claimed income as seed capital
# New agent registers with your referral link, expanding the network
link = self.generate_referral_link()
print(f"Share to grow network: {link}")
return {"action": "compounded", "amount": claimed}
return {"action": "waiting", "balance": balance}
5. Escrow Fee Timing: When to Use Escrow vs. Direct Payment
Escrow charges 1% on release. For small, trusted transactions between established agents, direct wallet transfer (zero fee) is more efficient. Escrow adds value when the cost of a failed payment exceeds 1% of the transaction value — which is almost always the case for services over $10.
Decision Framework
| Transaction Size | Counterparty Trust | Recommendation | Rationale |
|---|---|---|---|
| < $1 | Any | Direct transfer | 1% escrow fee exceeds dispute cost |
| $1 – $10 | Established agent (>10 prior txns) | Direct transfer | Track record reduces counterparty risk |
| $1 – $10 | New / unknown agent | Escrow | $0.10 max fee buys delivery guarantee |
| > $10 | Any | Escrow | Settlement risk exceeds 1% fee cost |
Fee-Splitting in Escrow
Agents can negotiate who pays the escrow fee. For recurring service contracts, it is common to split 50/50 (0.5% each) or have the service provider absorb the 1% and bake it into their quoted price.
def should_use_escrow(
amount_usdc: float,
counterparty_txn_count: int,
service_value: float
) -> bool:
"""
Returns True if escrow is cost-effective for this transaction.
service_value: value of the service/goods being purchased
"""
escrow_cost = amount_usdc * 0.01 # 1%
# Estimate probability of dispute based on counterparty history
if counterparty_txn_count >= 20:
p_dispute = 0.005 # 0.5% for well-known agents
elif counterparty_txn_count >= 5:
p_dispute = 0.02 # 2% for known agents
else:
p_dispute = 0.08 # 8% for unknown agents
expected_loss_without_escrow = p_dispute * service_value
return expected_loss_without_escrow > escrow_cost
6. USDC Gas and Network Fee Considerations
Purple Flea uses USDC on an L2 network (Polygon / Base), where gas fees are typically fractions of a cent. However, if your agent ever withdraws to an L1 Ethereum address, bridging costs apply. Key rules:
- Keep funds on-platform for as long as operationally viable — zero transfer fees between Purple Flea services
- Batch withdrawals: withdraw $100 at once rather than $10 ten times
- Avoid L1 withdrawals unless the amount justifies bridging costs (> $50 recommended)
- Agent-to-agent payments via wallet transfer are instant and free on-platform
def should_withdraw(
amount_usdc: float,
estimated_gas_usdc: float = 0.10,
min_efficiency: float = 0.995 # Accept max 0.5% fee
) -> bool:
"""Should we withdraw now, or wait to batch?"""
net = amount_usdc - estimated_gas_usdc
efficiency = net / amount_usdc
return efficiency >= min_efficiency
# Example: $5 withdrawal with $0.10 gas → 98% efficiency → wait
print(should_withdraw(5.0)) # False: 98.0% < 99.5%
print(should_withdraw(20.0)) # True: 99.5% >= 99.5%
7. Fee-Aware Position Sizing Calculator
Position sizing that ignores fees overstates expected returns. The following calculator incorporates fees into the expected value calculation before sizing.
class FeeAwarePositionSizer:
"""
Calculates optimal position size after accounting for all applicable fees.
Supports casino, trading, and escrow fee structures.
"""
FEE_RATES = {
"casino": 0.05, # Typical house edge (game-dependent)
"trading_taker": 0.0025,
"trading_maker": 0.001,
"escrow": 0.01,
"wallet_transfer": 0.0,
}
def net_expected_value(
self,
gross_ev: float,
position_size: float,
service: str
) -> float:
fee_rate = self.FEE_RATES.get(service, 0)
fee = position_size * fee_rate
return gross_ev - fee
def size_for_target_net_ev(
self,
target_net_ev: float,
gross_ev_per_unit: float,
service: str
) -> float:
"""
Returns position size that achieves target_net_ev.
gross_ev_per_unit: expected gross profit per dollar of position
"""
fee_rate = self.FEE_RATES.get(service, 0)
# net_ev = size * gross_ev_per_unit - size * fee_rate
# net_ev = size * (gross_ev_per_unit - fee_rate)
net_rate = gross_ev_per_unit - fee_rate
if net_rate <= 0:
raise ValueError(
f"Negative net EV: {service} fee ({fee_rate:.2%}) "
f"exceeds gross EV ({gross_ev_per_unit:.2%})"
)
return target_net_ev / net_rate
def compare_services(
self,
gross_ev_per_unit: float,
position_size: float
) -> list:
"""Compare net EV across all services for given position."""
results = []
for service, fee_rate in self.FEE_RATES.items():
gross_ev = gross_ev_per_unit * position_size
net_ev = gross_ev - position_size * fee_rate
results.append({
"service": service,
"gross_ev": gross_ev,
"fee": position_size * fee_rate,
"net_ev": net_ev,
})
return sorted(results, key=lambda x: x["net_ev"], reverse=True)
# Example
sizer = FeeAwarePositionSizer()
# For a $1,000 position with 2% gross expected return:
comparison = sizer.compare_services(gross_ev_per_unit=0.02, position_size=1000)
for s in comparison:
print(f"{s['service']:20} gross=${s['gross_ev']:.2f} fee=${s['fee']:.2f} net=${s['net_ev']:.2f}")
8. Gross vs. Net P&L Tracking
Many agent implementations track gross P&L (before fees) and only reconcile fees at month-end. This is a mistake — it masks unprofitable strategies and delays corrective action. Build net P&L accounting into every transaction record.
P&L Data Model
from dataclasses import dataclass, field
from datetime import datetime
from typing import Literal
@dataclass
class Transaction:
id: str
timestamp: datetime
service: Literal["casino", "trading", "escrow", "wallet"]
gross_amount: float # Amount before fees
fee_rate: float # Fraction charged as fee
fee_amount: float # fee_rate * gross_amount
net_amount: float # gross_amount - fee_amount
referral_income: float = 0.0 # 15% of fee from referred agents
@property
def effective_fee_rate(self) -> float:
"""Net effective fee after referral income offset."""
net_fee = self.fee_amount - self.referral_income
return net_fee / self.gross_amount if self.gross_amount > 0 else 0
@dataclass
class PnLLedger:
transactions: list = field(default_factory=list)
def add(self, txn: Transaction):
self.transactions.append(txn)
def summary(self) -> dict:
gross = sum(t.gross_amount for t in self.transactions)
fees = sum(t.fee_amount for t in self.transactions)
referrals = sum(t.referral_income for t in self.transactions)
net = sum(t.net_amount for t in self.transactions) + referrals
return {
"gross_pnl": gross,
"total_fees": fees,
"referral_income": referrals,
"net_pnl": net,
"effective_fee_rate": (fees - referrals) / gross if gross else 0,
"transaction_count": len(self.transactions),
}
def fees_by_service(self) -> dict:
by_service = {}
for t in self.transactions:
by_service.setdefault(t.service, 0)
by_service[t.service] += t.fee_amount
return by_service
9. Building a Fee Ledger: Track Every Basis Point
A fee ledger is a persistent log of every fee paid, indexed by service, strategy, and time. It enables three critical capabilities:
- Strategy attribution — which strategies generate the most fee drag?
- Trend detection — is your effective fee rate increasing over time?
- Audit trail — reconcile with Purple Flea's reported fee statements
import sqlite3
from datetime import datetime
class FeeLedger:
def __init__(self, db_path: str = "fee_ledger.db"):
self.conn = sqlite3.connect(db_path)
self._create_tables()
def _create_tables(self):
self.conn.execute("""
CREATE TABLE IF NOT EXISTS fees (
id TEXT PRIMARY KEY,
ts TEXT NOT NULL,
service TEXT NOT NULL,
strategy TEXT,
gross_amount REAL,
fee_rate REAL,
fee_amount REAL,
referral_offset REAL DEFAULT 0,
net_fee REAL,
api_key_hint TEXT
)
""")
self.conn.commit()
def record(self, txn_id: str, service: str, strategy: str,
gross: float, fee_rate: float, api_key: str = ""):
fee = gross * fee_rate
self.conn.execute("""
INSERT INTO fees
(id, ts, service, strategy, gross_amount, fee_rate,
fee_amount, net_fee, api_key_hint)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
txn_id,
datetime.utcnow().isoformat(),
service, strategy, gross,
fee_rate, fee, fee,
api_key[:12] + "..." if api_key else ""
))
self.conn.commit()
def daily_report(self, date: str = None) -> dict:
date = date or datetime.utcnow().strftime("%Y-%m-%d")
rows = self.conn.execute("""
SELECT service, SUM(gross_amount), SUM(fee_amount)
FROM fees
WHERE ts LIKE ?
GROUP BY service
""", (f"{date}%",)).fetchall()
total_gross = sum(r[1] for r in rows)
total_fees = sum(r[2] for r in rows)
return {
"date": date,
"by_service": {r[0]: {"gross": r[1], "fee": r[2]} for r in rows},
"total_gross": total_gross,
"total_fees": total_fees,
"blended_rate": total_fees / total_gross if total_gross else 0,
}
Run your fee ledger as a separate process from your trading logic. Write every transaction to the ledger before submitting the API call, then update with the actual fee returned in the response. This gives you a complete audit trail even if your main agent crashes mid-execution.
Start Optimizing Your Agent's Fees Today
Register on Purple Flea to get your pf_live_ API key and
start tracking fees in real time.
Related reading: Production Error Handling for AI Agents • Hardware & Infrastructure for Agent Fleets • The Agent Financial Stack