A deep technical guide to autonomous NFT market analysis: rarity scoring, wash-trade detection, floor-sweep execution, listing sniping, lending market arbitrage, and profit tracking — with working code throughout.
The NFT market is, structurally, one of the most agent-friendly trading environments that has ever existed. It is open 24/7, its order books are entirely on-chain and thus freely readable, its pricing signals are noisy enough that systematic edge is abundant, and its retail participants are among the most sentiment-driven of any financial market. The combination is almost tailor-made for a disciplined, data-driven autonomous agent.
Where a human trader burns out monitoring ten Discord channels, a dozen Twitter accounts, and five marketplace interfaces simultaneously, an agent does so indefinitely at zero marginal cost. Where a human hesitates on a listing that just appeared at 40% below floor, an agent can evaluate, decide, and execute within seconds — often before the listing is even indexed by aggregators.
There are five structural advantages that agents hold over human NFT traders:
This guide assumes an agent that controls a wallet with USDC and/or ETH/SOL, has programmatic access to at least one NFT marketplace API, and can sign and broadcast transactions autonomously. The Purple Flea Wallet API (/docs/wallet/) covers the USDC custody layer for agents that want to park idle capital between NFT trades.
In equity markets, the dominant edge comes from private information, order-flow internalization, or low-latency co-location — all inaccessible to most agents. In NFT markets, a surprisingly large fraction of exploitable edge comes from correctly computing public information that others have not computed yet, or correctly weighting signals that others are over- or under-weighting. That is a domain where agents with good data pipelines genuinely outperform.
Trait rarity mispricings persist for hours — sometimes days — in illiquid collections because human buyers anchor to floor price and do not bother to calculate that a specific trait combination makes a token worth 3x floor to the right buyer. An agent that has pre-computed the full trait-value surface for every token in a 10,000-item collection can bid opportunistically the moment a mispriced listing appears.
Before building any strategy, an agent needs a rigorous mental model of what NFT "markets" actually are. They differ from conventional financial markets in several important ways that change which metrics matter and which are noise.
The floor price is the lowest ask currently listed for any token in a collection. It is the most widely cited metric but also the most easily manipulated: a single seller willing to take a loss, or a single wash-trader, can move it. An agent should never treat the raw floor as a reliable anchor; instead it should compute a robust floor — typically the 5th or 10th percentile of listed prices after filtering suspected wash trades, normalized over a rolling 6-hour window.
Floor price in ETH and floor price in USD can diverge sharply during crypto volatility. Agents that hold USDC between trades and denominate risk in USD need to track both and convert correctly when setting bids.
Most NFT collections are PFP (profile picture) projects with 7–15 trait categories, each with multiple values of varying rarity. Tokens with extremely rare trait combinations command premiums above floor, sometimes extreme ones. The market for these "1/1" or rare-trait tokens is thinner and more volatile than the floor market, but the premiums are more stable over time because they reflect genuine scarcity rather than sentiment.
Raw 24h volume in ETH is a lagging, manipulation-susceptible metric. More useful signals for agents are:
NFT liquidity is fundamentally different from fungible-asset liquidity. Even "blue chip" collections like CryptoPunks can take hours to sell at mid-market if the buyer pool is thin that day. An agent must model liquidation time, not just spread. A reasonable heuristic is:
| Liquidity Tier | Daily Sales Volume | Est. Time to Sell at Floor | Agent Strategy |
|---|---|---|---|
| Tier 1 (Blue Chip) | >200 ETH/day | Minutes to hours | Swing trade, floor sweeps |
| Tier 2 (Mid-cap) | 20–200 ETH/day | Hours to 1–2 days | Selective trait plays, bids only |
| Tier 3 (Long tail) | <20 ETH/day | Days to weeks | Avoid unless conviction is very high |
| Dead collection | <1 ETH/week | Indeterminate | Do not enter; exit if holding |
Tier 3 and dead collections should be avoided by agents that need capital turnover. Getting stuck in an illiquid position ties up USDC that could be deployed in higher-EV opportunities. Always compute expected liquidation time before entering a position.
An agent operating across the NFT ecosystem needs reliable, low-latency data from multiple sources. Here is a practical survey of the major APIs and what each provides best.
Reservoir is an open NFT data aggregator that normalizes order books from OpenSea, Blur, LooksRare, X2Y2, and others into a single API. It is the best single starting point for an agent because it provides cross-marketplace best prices, unified sales history, and floor price series in one place.
# Reservoir v5 API — no auth needed for basic endpoints (rate-limited)
# Replace CONTRACT with the ERC-721 address
CONTRACT="0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d" # BAYC
curl -s "https://api.reservoir.tools/collections/v7?id=${CONTRACT}&includeTopBid=true" \
-H "x-api-key: YOUR_RESERVOIR_KEY" | jq '{
name: .collections[0].name,
floor_eth: .collections[0].floorAsk.price.amount.native,
floor_usd: .collections[0].floorAsk.price.amount.usd,
top_bid_eth: .collections[0].topBid.price.amount.native,
top_bid_usd: .collections[0].topBid.price.amount.usd,
volume_24h: .collections[0].volume["1day"],
unique_buyers: .collections[0].ownerCount
}'
import httpx, asyncio, time
RESERVOIR_KEY = "YOUR_RESERVOIR_KEY"
HEADERS = {"x-api-key": RESERVOIR_KEY}
async def poll_floor(contract: str, interval_s: int = 30):
prev_floor = None
url = f"https://api.reservoir.tools/collections/v7?id={contract}&includeTopBid=true"
async with httpx.AsyncClient(timeout=10) as client:
while True:
resp = await client.get(url, headers=HEADERS)
data = resp.json()
coll = data["collections"][0]
floor = coll["floorAsk"]["price"]["amount"]["native"]
top_bid = coll["topBid"]["price"]["amount"]["native"]
spread_pct = ((floor - top_bid) / floor) * 100
if prev_floor is not None:
change_pct = (floor - prev_floor) / prev_floor * 100
if abs(change_pct) > 2.0:
print(f"[ALERT] Floor moved {change_pct:+.2f}% "
f"Floor={floor:.4f} ETH Bid={top_bid:.4f} ETH Spread={spread_pct:.1f}%")
prev_floor = floor
await asyncio.sleep(interval_s)
asyncio.run(poll_floor("0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"))
Blur has captured the majority of NFT trading volume on Ethereum among professional traders and bots. Its key advantage for agents is the native bid pool mechanism: you can post collection-wide bids that automatically match against incoming listings. Blur also surfaces real-time listing events via websocket, making it the fastest source for listing-snipe data on Ethereum.
Blur does not have a fully public REST API; however, Reservoir aggregates Blur order books, so you can read Blur bids and asks through Reservoir and execute via Blur's NFT router contracts directly on-chain.
OpenSea remains the largest platform by user count and has the deepest NFT API documentation. It provides collection statistics, individual NFT metadata (traits), offer events, and a listing stream. Key endpoints for agents:
GET /api/v2/collections/{slug}/stats — floor, volume, holder countGET /api/v2/collections/{slug}/nfts — full token list with traitsGET /api/v2/events/collection/{slug} — real-time sale, listing, offer eventsFor Solana NFT collections and Bitcoin Ordinals, MagicEden is the dominant marketplace. Its REST API exposes collection stats, token listings, and activity feeds. Tensor is the alternative on Solana with better programmatic bid support (similar to Blur's role on Ethereum).
# MagicEden v2 API for Solana
COLLECTION="degods"
curl -s "https://api-mainnet.magiceden.dev/v2/collections/${COLLECTION}/stats" | jq '{
floor_sol: .floorPrice,
listed_count: .listedCount,
volume_all: .volumeAll,
avg_price: .avgPrice24hr
}'
Tensor provides a GraphQL API and on-chain programs that allow agents to place collection-wide bids and auto-accept incoming listings on Solana. It is the Blur equivalent for the Solana NFT ecosystem and is particularly useful for agents running automated bidding loops on Solana-native collections.
Rarity scoring is the foundation of any NFT trading edge based on fundamental value. The core insight: a token's market price should reflect not just which collection it belongs to, but how its specific trait combination compares to the rest of the collection. Agents that correctly price trait rarity can identify tokens listed at floor whose true "fair value" is significantly higher.
The most statistically rigorous rarity score uses the information content of each trait, defined as -log2(trait_frequency). Rare traits contribute more to the score; common traits contribute little. The total score for a token is the sum of IC values across all its traits.
import math
from collections import defaultdict
from typing import List, Dict, Any
def compute_rarity_scores(tokens: List[Dict[str, Any]]) -> Dict[int, float]:
"""
tokens: list of dicts like {"token_id": 1, "traits": {"Background": "Blue", "Eyes": "Laser"}}
Returns: {token_id: ic_score}
"""
n = len(tokens)
# Count occurrences of each (category, value) pair
trait_counts: Dict[str, Dict[str, int]] = defaultdict(lambda: defaultdict(int))
for token in tokens:
for category, value in token["traits"].items():
trait_counts[category][value] += 1
# Compute information content: -log2(freq)
scores: Dict[int, float] = {}
for token in tokens:
score = 0.0
for category, value in token["traits"].items():
freq = trait_counts[category][value] / n
score += -math.log2(freq)
scores[token["token_id"]] = round(score, 4)
return scores
# Example usage
tokens = [
{"token_id": 1, "traits": {"Background": "Blue", "Eyes": "Normal", "Mouth": "Grin"}},
{"token_id": 2, "traits": {"Background": "Gold", "Eyes": "Laser", "Mouth": "Grin"}},
# ... 9,998 more tokens
]
scores = compute_rarity_scores(tokens)
ranked = sorted(scores.items(), key=lambda x: x[1], reverse=True)
print("Top 5 rarest tokens:", ranked[:5])
IC scores tell you which tokens are rare; they do not tell you which traits the market is actually paying a premium for. Those can differ. A trait that is statistically rare (1% occurrence) may not command much premium if the market does not find it aesthetically desirable. Conversely, a moderately rare trait (5%) in a category that the community values highly can command a large premium.
The correct tool here is a hedonic regression: regress observed sale prices against dummy variables for each (category, value) combination, controlling for market conditions. The coefficients on each trait dummy give you the market's implicit price for that trait, independent of overall floor movement.
import pandas as pd
import numpy as np
from sklearn.linear_model import Ridge
def fit_trait_prices(sales_df: pd.DataFrame) -> pd.Series:
"""
sales_df columns: token_id, price_eth, trait_Background, trait_Eyes, ...
Returns Series of trait premium estimates (ETH above base).
"""
trait_cols = [c for c in sales_df.columns if c.startswith("trait_")]
X = pd.get_dummies(sales_df[trait_cols], drop_first=True)
y = sales_df["price_eth"].values
model = Ridge(alpha=0.1)
model.fit(X, y)
return pd.Series(model.coef_, index=X.columns).sort_values(ascending=False)
# Trait premium series tells you: "Laser Eyes adds ~0.8 ETH above base"
# Use this to estimate fair value = base_floor + sum(premiums for token's traits)
Compute estimated fair value for every token currently listed. Sort by (fair_value - list_price) / list_price descending. Tokens at the top of this list are the best rarity-adjusted buys. Set a threshold (e.g., listed at >15% below estimated fair value) and alert for immediate action.
Wash trading — where the same entity buys and sells a token to themselves or a coordinated partner to inflate volume statistics — is endemic in NFT markets. For an agent relying on volume as a signal for collection health, failing to filter wash trades leads to systematically wrong conclusions: entering "hot" collections that are actually dead, or overpaying based on fake momentum.
No single heuristic is definitive, but the following combination catches the large majority of wash-trade volume:
from datetime import timedelta
def is_wash_trade(sale: dict, all_sales: list, funding_graph: dict) -> bool:
"""Returns True if the sale shows wash-trade signatures."""
buyer = sale["buyer"]
seller = sale["seller"]
token = sale["token_id"]
ts = sale["timestamp"]
# Rule 1: Self-trade
if buyer == seller:
return True
# Rule 2: Common funder (simplified — check if buyer funded seller or vice versa)
buyer_funders = funding_graph.get(buyer, set())
seller_funders = funding_graph.get(seller, set())
if buyer in seller_funders or seller in buyer_funders:
return True
if buyer_funders & seller_funders: # shared funder
return True
# Rule 3: Round-trip within 48 hours for same token
window = timedelta(hours=48)
for other in all_sales:
if (other["token_id"] == token
and other["buyer"] == seller
and other["seller"] == buyer
and abs(other["timestamp"] - ts) < window):
return True
return False
def clean_volume(sales: list, funding_graph: dict) -> float:
return sum(
s["price_eth"]
for s in sales
if not is_wash_trade(s, sales, funding_graph)
)
For collections where you track historical volume, a sudden spike in daily volume that is more than 3 standard deviations above the rolling 30-day mean, combined with a low unique-buyer count, is a strong wash-trade signal even without wallet-graph analysis.
Floor sweeping — buying multiple tokens at or near the listed floor price — is one of the most common mechanical strategies in NFT trading. Done well, it can profit from momentum, thin listing stacks, and impending price discovery. Done poorly, it creates expensive bags that take months to sell.
A floor sweep is likely to be profitable when all of the following conditions hold simultaneously:
Rather than actively sweeping the floor, agents can post collection-wide bids below floor and wait for distressed sellers to hit them. This is a lower-alpha but lower-risk strategy that works best in sideways or mildly declining markets where motivated sellers appear regularly.
import { getClient, Execute } from '@reservoir0x/reservoir-sdk'
import { createWalletClient, http } from 'viem'
const COLLECTION = '0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d'
const FLOOR_ETH = 15.2 // fetch this fresh before each run
const BID_DISCOUNT = 0.93 // bid at 93% of floor
const BID_QUANTITY = 3 // fill up to 3 tokens
async function placeCollectionBid(walletClient) {
const bidPrice = (FLOOR_ETH * BID_DISCOUNT).toFixed(6)
await getClient().actions.placeBid({
bids: [{
weiPrice: BigInt(Math.floor(parseFloat(bidPrice) * 1e18).toString()),
orderbook: 'blur',
orderKind: 'blur',
collection: COLLECTION,
quantity: BID_QUANTITY,
expirationTime: String(Math.floor(Date.now() / 1000) + 3600), // 1h TTL
}],
signer: walletClient,
onProgress(steps) {
steps.forEach(s => console.log(s.action, s.status))
}
})
console.log(`Bid placed: ${BID_QUANTITY}x at ${bidPrice} ETH (floor: ${FLOOR_ETH})`)
}
Never size a floor sweep by "how much it feels right to spend." Use the Kelly criterion, adapted for the discrete NFT case. You need an estimate of win probability p (based on backtest or market-structure signals) and the expected payoff ratio b (expected sell price / buy price minus 1). The Kelly fraction of total capital to risk is f* = (bp - q) / b where q = 1 - p.
In practice, use a fractional Kelly (half-Kelly) to account for estimation error in p and b. For a collection that historically shows 60% win rate at 20% expected return, half-Kelly suggests risking ~7.5% of NFT-allocated capital per sweep — not your entire wallet.
Listing sniping is the practice of identifying and buying a token that has been listed at a price significantly below its fair value — typically due to a seller error, automated delisting/relisting at wrong price, or simple ignorance of recent floor movement — before any other buyer can act. It requires fast data pipelines, near-zero latency execution, and a pre-computed "fair value" for every token in the target collection.
A production snipe agent requires three components running in parallel:
token_id → fair_value_eth stored in memory. This is updated every few minutes from the latest sales and the hedonic regression model. Must be in-memory for sub-100ms lookup.fair_value / list_price > threshold (e.g., 1.20), and if so, fire the buy transaction without any human confirmation step.import asyncio, json, httpx
import websockets
RESERVOIR_WS = "wss://ws.reservoir.tools?api_key=YOUR_KEY"
COLLECTION = "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"
SNIPE_RATIO = 1.15 # buy if listed at <= 87% of fair value
fair_value_map = {} # pre-loaded: {token_id: fair_value_eth}
async def execute_buy(token_id: str, list_price: float):
print(f"[SNIPE] Buying token #{token_id} at {list_price} ETH")
# Call your execution layer (e.g., Reservoir execute/buy API or direct contract call)
async with httpx.AsyncClient() as client:
await client.post("https://api.reservoir.tools/execute/buy/v7",
headers={"x-api-key": "YOUR_KEY"},
json={
"items": [{"token": f"{COLLECTION}:{token_id}"}],
"taker": "YOUR_AGENT_WALLET_ADDRESS"
}
)
async def snipe_listener():
subscribe_msg = {
"type": "subscribe",
"event": "ask.created",
"filters": {"contract": COLLECTION}
}
async with websockets.connect(RESERVOIR_WS) as ws:
await ws.send(json.dumps(subscribe_msg))
async for raw in ws:
event = json.loads(raw)
if event.get("type") != "ask.created":
continue
ask = event["data"]
tid = ask["tokenId"]
price = float(ask["price"]["amount"]["native"])
fv = fair_value_map.get(tid)
if fv and fv / price >= SNIPE_RATIO:
print(f"SNIPE OPPORTUNITY: #{tid} list={price:.4f} fv={fv:.4f} ratio={fv/price:.3f}")
await execute_buy(tid, price)
asyncio.run(snipe_listener())
On high-profile collections, snipe opportunities at >15% discount are competed for by dozens of bots simultaneously. The winning bot is often determined by gas price (for EVM chains) or priority fees. Budget for aggressive gas settings on high-confidence snipes. On Solana, Jito bundles provide similar prioritization.
Rather than sniping all underpriced floor listings, agents can specialize in specific high-value traits. If your regression shows Laser Eyes commands a 2.5 ETH premium, monitor for any Laser Eyes token listed within 1.5 ETH of floor. The target buyer pool for that specific token is people who want Laser Eyes, and your maximum selling price is limited only by the demand from that group — often substantially above the "floor" that general buyers track.
NFT lending protocols allow token holders to borrow against their NFTs without selling. For agents that hold NFTs as inventory, this creates a significant capital efficiency opportunity: rather than waiting weeks to sell an illiquid token, an agent can borrow against it immediately and redeploy that capital.
| Protocol | Chain | Model | Typical LTV | APR |
|---|---|---|---|---|
| Blend (Blur) | Ethereum | Peer-to-peer, no oracle | 50–80% of floor | 10–40% |
| NFTfi | Ethereum | Peer-to-peer offers | Negotiated | Negotiated |
| Arcade.xyz | Ethereum | Peer-to-peer | 30–60% of appraisal | Negotiated |
| Sharky.fi | Solana | Peer-to-protocol pools | ~50–70% of floor | 60–200% APR |
| BendDAO | Ethereum | Peer-to-pool | 30–40% of floor | Variable |
Blur's Blend protocol introduced a novel "perpetual loan" mechanic. Unlike fixed-term loans, Blend loans have no expiry: they continue indefinitely at the agreed rate until a lender triggers a "dutch auction" refinancing. The borrower then has 30 hours to find a better rate or repay; if they fail, the NFT is seized.
For an agent holding an NFT as inventory, this means you can borrow against it indefinitely at the current market rate for lenders in that collection, giving you USDC to redeploy without a forced liquidation timer — as long as you monitor for refinancing triggers and respond within the 30-hour window.
# Get active loans (as borrower) for a wallet address
WALLET="0xYOUR_AGENT_WALLET"
curl -s "https://api.reservoir.tools/loans/v1?borrower=${WALLET}&status=active" \
-H "x-api-key: YOUR_KEY" | jq '.loans[] | {
token_id: .token.tokenId,
collection: .token.collection.name,
borrowed_eth: .principal,
rate_apr: .rate,
lender: .lender
}'
Agents with excess ETH can also be the lender in these protocols. By providing loans to blue-chip NFT holders, an agent earns 15–40% APR backed by collateral that is typically worth 2x the loan amount. The primary risk is a sudden floor collapse that makes the collateral worth less than the outstanding loan before the agent can trigger a liquidation auction.
A well-designed lending strategy monitors collateral LTV in real time and triggers early repayment requests the moment the floor drops to a predetermined threshold (e.g., collateral at 115% of loan principal), giving the borrower time to top up before the agent is at risk.
NFT marketplace fragmentation creates persistent arbitrage opportunities. The same token can be listed at different prices on different platforms, or a collection's best bid on one platform can exceed the floor ask on another. Agents that monitor multiple order books simultaneously can capture these spreads.
import httpx, asyncio
from dataclasses import dataclass
from typing import Optional
@dataclass
class ArbOpportunity:
token_id: str
buy_price: float
buy_market: str
sell_price: float
sell_market: str
net_profit_pct: float
async def scan_arb(
contract: str,
fee_rate: float = 0.015 # combined buy + sell fees
) -> list[ArbOpportunity]:
async with httpx.AsyncClient() as client:
# Fetch cheapest 50 asks across all markets
asks_r = await client.get(
f"https://api.reservoir.tools/orders/asks/v5?token={contract}&sortBy=price&limit=50",
headers={"x-api-key": "YOUR_KEY"}
)
# Fetch best 50 bids across all markets
bids_r = await client.get(
f"https://api.reservoir.tools/orders/bids/v6?token={contract}&sortBy=price&limit=50",
headers={"x-api-key": "YOUR_KEY"}
)
asks = asks_r.json()["orders"]
bids = bids_r.json()["orders"]
# Build token→best_bid map
bid_map = {}
for bid in bids:
tid = bid.get("tokenSetId", "").split(":")[-1]
price = bid["price"]["amount"]["native"]
if tid not in bid_map or bid_map[tid]["price"] < price:
bid_map[tid] = {"price": price, "source": bid["source"]["name"]}
opportunities = []
for ask in asks:
tid = ask["tokenSetId"].split(":")[-1]
ask_price = ask["price"]["amount"]["native"]
ask_source = ask["source"]["name"]
if tid in bid_map:
bid_price = bid_map[tid]["price"]
bid_source = bid_map[tid]["source"]
gross_pct = (bid_price - ask_price) / ask_price
net_pct = gross_pct - fee_rate
if net_pct > 0.01: # at least 1% net profit
opportunities.append(ArbOpportunity(
token_id=tid,
buy_price=ask_price, buy_market=ask_source,
sell_price=bid_price, sell_market=bid_source,
net_profit_pct=net_pct
))
return sorted(opportunities, key=lambda x: x.net_profit_pct, reverse=True)
The primary risk in NFT arb is that the bid you planned to sell into is cancelled or filled by someone else between the moment you identify the opportunity and the moment you execute the buy. Unlike fungible markets where you can execute both legs atomically, NFT arb is inherently sequential: buy first, then sell. If the sell-side evaporates, you are left holding an NFT you overpaid for on a relative basis.
Mitigate this by: (a) only executing arb where the sell-side bid has been stable for >60 seconds, (b) verifying the bid on-chain (not just via API) immediately before executing the buy, and (c) setting a maximum position-hold time after which you accept the floor price even at a loss.
Without rigorous P&L tracking, it is impossible to know whether your agent's NFT strategy is actually profitable. The illusion of profit is particularly common in NFT trading because gains are often denominated in ETH while losses are denominated in USD (or vice versa depending on market direction), and because fees are paid in small increments that are easy to undercount.
from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional
import json
@dataclass
class NFTPosition:
collection: str
token_id: str
entry_eth: float
entry_usd: float
entry_ts: datetime
entry_gas_eth: float
entry_fee_eth: float # marketplace + royalty
entry_tx: str
exit_eth: Optional[float] = None
exit_usd: Optional[float] = None
exit_ts: Optional[datetime] = None
exit_gas_eth: Optional[float] = None
exit_fee_eth: Optional[float] = None
exit_tx: Optional[str] = None
def cost_basis_eth(self) -> float:
return self.entry_eth + self.entry_gas_eth + self.entry_fee_eth
def net_proceeds_eth(self) -> Optional[float]:
if self.exit_eth is None: return None
return self.exit_eth - (self.exit_gas_eth + self.exit_fee_eth)
def pnl_eth(self) -> Optional[float]:
proceeds = self.net_proceeds_eth()
if proceeds is None: return None
return proceeds - self.cost_basis_eth()
def pnl_usd(self) -> Optional[float]:
if self.exit_usd is None: return None
return self.exit_usd - self.entry_usd # simplified; gas costs in ETH terms
def roi_pct(self) -> Optional[float]:
pnl = self.pnl_eth()
if pnl is None: return None
return pnl / self.cost_basis_eth() * 100
def holding_hours(self) -> Optional[float]:
if self.exit_ts is None: return None
return (self.exit_ts - self.entry_ts).total_seconds() / 3600
class NFTPnLLedger:
def __init__(self): self.positions: list[NFTPosition] = []
def summary(self):
closed = [p for p in self.positions if p.pnl_eth() is not None]
winners = [p for p in closed if p.pnl_eth() > 0]
total_pnl = sum(p.pnl_eth() for p in closed)
print(f"Closed trades: {len(closed)} | Winners: {len(winners)} "
f"({len(winners)/max(len(closed),1)*100:.1f}%) | Net PnL: {total_pnl:.4f} ETH")
Open positions (NFTs currently held) should be marked to market regularly using the current floor price for that collection and the token's rarity premium estimate. The mark-to-market value minus cost basis gives unrealized P&L, which the agent should treat as a soft limit: if unrealized loss exceeds a predefined threshold (e.g., 20% of cost basis), consider exiting rather than waiting for recovery.
NFT trading is capital-intensive but also lumpy: there are periods of intense activity (new collection mint, floor breakout, snipe opportunity) punctuated by periods of waiting. During waiting periods, an agent's USDC allocation should not sit idle.
The Purple Flea Wallet API supports USDC deposits, withdrawals, and balance queries via simple REST calls. An NFT trading agent can maintain its "dry powder" USDC in a Purple Flea wallet, earning yield on idle capital via the Purple Flea yield infrastructure, and withdraw when an NFT opportunity requires immediate action.
# Register your agent (first time only)
curl -s -X POST https://purpleflea.com/api/v1/agents/register \
-H "Content-Type: application/json" \
-d '{"name": "nft-arb-agent-01", "type": "nft_trader"}' | jq .
# Check USDC balance
curl -s https://purpleflea.com/api/v1/wallet/balance \
-H "Authorization: Bearer YOUR_AGENT_TOKEN" | jq '{usdc: .balances.usdc, eth: .balances.eth}'
# Request withdrawal to fund an NFT purchase (converts to ETH via integrated swap)
curl -s -X POST https://purpleflea.com/api/v1/wallet/withdraw \
-H "Authorization: Bearer YOUR_AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"currency": "USDC",
"amount": "2500.00",
"destination_address": "0xYOUR_ETH_WALLET",
"note": "NFT floor sweep — BAYC"
}' | jq .
If you are bootstrapping a new NFT trading agent with no initial capital, claim $1 free USDC from the Purple Flea Faucet to cover initial API costs and test the wallet integration end-to-end before deploying real capital. The faucet is rate-limited to one claim per agent identity.
The Purple Flea wallet API makes it trivial to implement dynamic position sizing based on total agent capital. Before each sweep or snipe decision, fetch current USDC balance, apply the Kelly fraction, and size the order accordingly. This ensures the agent never over-leverages during a drawdown period.
const API_BASE = 'https://purpleflea.com/api/v1'
const PF_TOKEN = 'YOUR_AGENT_TOKEN'
const ETH_PRICE = 3200 // update from price feed
async function computeMaxBuyETH(winProb = 0.55, payoffRatio = 0.20) {
const res = await fetch(`${API_BASE}/wallet/balance`, {
headers: { 'Authorization': `Bearer ${PF_TOKEN}` }
})
const { balances } = await res.json()
const totalUSDC = parseFloat(balances.usdc)
const totalETH = totalUSDC / ETH_PRICE
// Half-Kelly fraction
const q = 1 - winProb
const kellyFull = (payoffRatio * winProb - q) / payoffRatio
const halfKelly = Math.max(0, kellyFull / 2)
const maxBuyETH = totalETH * halfKelly
console.log(`Capital: ${totalUSDC.toFixed(2)} USDC | Half-Kelly: ${(halfKelly*100).toFixed(1)}% | Max buy: ${maxBuyETH.toFixed(4)} ETH`)
return maxBuyETH
}
NFT trading is among the riskiest asset classes in digital finance. An agent operating in this space must have explicit, hard-coded risk controls — not soft heuristics. The following categories of risk need dedicated countermeasures.
The single greatest risk in NFT trading is buying an asset that subsequently becomes impossible to sell at any reasonable price. Collections that appear liquid during their hype cycle can lose 90%+ of trading volume within weeks. An agent must treat liquidity as a first-order constraint, not a secondary consideration.
Mitigation: Only enter positions in collections with >20 ETH/day clean volume over the prior 14 days. Hard circuit breaker: if a collection's 7-day clean volume drops below 50% of the 30-day baseline, exit all positions in that collection at market within 24 hours regardless of current P&L.
Project teams can abandon collections, drain treasury funds, or simply stop developing, causing floor prices to collapse. Agents should monitor the following on-chain signals as early-warning indicators:
Beyond wash trading (covered earlier), agents must watch for floor painting — where a coordinated group buys and holds floor tokens to artificially maintain or inflate the floor price while selling off rare tokens into retail demand. The tell: unusual uniformity of floor listings (many tokens all listed at exactly the same price from different wallets), combined with large rare-token sales at seemingly good prices.
Sweep-and-dump schemes are the opposite: a group sweeps the floor publicly (sometimes announced in Discord to create FOMO), rapidly relists 10–30% higher, and dumps into the FOMO buyers. If an agent's sweep logic triggers on these setups, it becomes the exit liquidity for the manipulators. Countermeasure: add a 15-minute delay after any anomalously large sweep detected in the order history before placing your own floor bids.
Interacting programmatically with marketplace contracts introduces smart contract risk. Agents should only approve specific marketplace router addresses, never grant unlimited approvals, and revoke approvals for contracts they are not actively using. Use a dedicated agent wallet with limited ETH exposure rather than a central treasury wallet for NFT trading activity.
Every NFT trading agent should hard-code the following limits that cannot be overridden by strategy logic: (1) maximum single-position size as % of total capital, (2) maximum aggregate NFT exposure as % of total capital, (3) maximum loss per collection before circuit-break exit, (4) maximum holding period (time-based stop-loss), (5) daily loss limit triggering full trading halt for manual review.
On Ethereum, NFT transactions are subject to front-running and sandwich attacks. When you broadcast a high-value buy transaction, searchers monitoring the mempool may front-run your transaction (buying the token before you) or extract MEV by reordering transactions. Mitigation: use Flashbots bundles for sensitive transactions, set appropriate slippage tolerances, and consider using private mempools (Flashbots Protect RPC, MEV Blocker) for your agent's transaction broadcasts.
An AI agent running a disciplined NFT trading strategy needs the following components in place before going live with real capital:
| Component | Description | Priority |
|---|---|---|
| Data pipeline | Reservoir / OpenSea API + WebSocket listener for live events | Critical |
| Fair-value oracle | Hedonic regression on recent sales, in-memory lookup per token | Critical |
| Wash-trade filter | Wallet-graph analysis + round-trip detection | High |
| Rarity scorer | IC-based + trait regression, updated after each sale | High |
| Execution engine | Reservoir SDK or direct contract calls, with gas management | Critical |
| P&L ledger | Per-trade cost basis, realized + unrealized tracking in ETH and USD | High |
| Risk controls | Position limits, daily stop, liquidity circuit breaker, hold-time stop | Critical |
| Capital management | Purple Flea wallet for USDC, Kelly sizing, idle yield | Medium |
| Monitoring & alerts | Floor movement >2%, volume anomaly, rug-risk signals | High |
NFT markets remain one of the few corners of digital finance where a well-engineered autonomous agent can consistently outperform human traders — not because the agent is smarter, but because it is faster, more disciplined, and more data-driven. The edge is real. The risks are also real. Build the controls first, then the strategies.
Explore the full Purple Flea API suite at /docs/, integrate your agent wallet at /api/v1, and claim your free bootstrap capital at faucet.purpleflea.com.