CEX-DEX Arbitrage
What it is
Centralized exchanges quote prices on internal order books; decentralized exchanges quote prices algorithmically from on-chain liquidity pools (Uniswap v2/v3, Curve, PancakeSwap, etc.). The two surfaces don't always agree. When the gap exceeds gas + bridge + fee costs, the trade is: buy where it's cheap, sell where it's expensive, settle the leg — usually by swapping on the DEX and unwinding the CEX leg, or vice versa.
This is the strategy MEV searchers grind on-chain alongside; doing it as a non-MEV trader means you're competing with the bots on the DEX side and the market makers on the CEX side. The remaining alpha is in:
- Long-tail tokens that majors don't market-make.
- Cross-chain inefficiencies where bridge friction prevents arbs from closing immediately.
- DEX-listing pre-CEX-listing windows where price discovery happens on-chain first.
When it works
- The asset has liquidity on both surfaces — DEX pool TVL ≥ your trade size × 10 to keep AMM slippage tolerable.
- Bridge to/from the relevant chain is fast and cheap relative to spread. For an Ethereum L1 round-trip, gas alone can be $20–200 — minor arbs aren't worth attempting.
- You hold pre-positioned inventory on both sides (USDC on a CEX + USDC on the chain). Bridging per-trade is rarely profitable.
- The MEV ecosystem hasn't already eaten the gap. If a Flashbots searcher can atomically rebalance the pool, your latency advantage is zero.
Data you need
- DEX trade snapshot —
/api/v1/dex/trade— most recent swap prices and quote rates across DEX pools. - DEX pools —
/api/v1/dex/pools— liquidity, fee tier, token pair per pool. Critical for slippage modelling. - DEX chains —
/api/v1/dex/chains— supported networks. - DEX exchanges —
/api/v1/dex/exchanges— supported DEX venues per chain. - DEX candles —
/api/v1/dex/candle— historical price for backtesting. - CEX ticker —
/api/v1/ticker— live CEX mark to compute the gap against. - CEX wallet status —
/api/v1/wallet-status— confirm on-chain deposit/withdrawal is enabled on the network you'll bridge over. - CEX trading fees —
/api/v1/cex/fees— CEX-side leg cost.
DataMaxi+ surfaces DEX swap data, not full pool-state snapshots (reserves/sqrtPriceX96). Accurate execution-quote modeling for v3 concentrated-liquidity pools usually requires reading the pool contract directly via RPC. Use these endpoints for opportunity detection; do the precise quote on-chain at execution time.
API recipe
Compare a DEX swap rate against the CEX ticker for the same asset. First, latest DEX trades for a token on Ethereum:
- cURL
- Python
- Go
- TypeScript
curl -G 'https://api.datamaxiplus.com/api/v1/dex/trade' \
-H 'X-DTMX-APIKEY: '"$YOUR_API_KEY" \
--data-urlencode 'chain=ethereum' \
--data-urlencode 'exchange=uniswap-v3' \
--data-urlencode 'pool=0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640'
import os
import requests
HEADERS = {"X-DTMX-APIKEY": os.environ["DTMX_API_KEY"]}
BASE = "https://api.datamaxiplus.com"
dex = requests.get(f"{BASE}/api/v1/dex/trade", headers=HEADERS, params={
"chain": "ethereum",
"exchange": "uniswap-v3",
"pool": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
}, timeout=10).json()
cex = requests.get(f"{BASE}/api/v1/ticker", headers=HEADERS, params={
"exchange": "binance",
"symbol": "ETH-USDC",
}, timeout=10).json()
print("DEX last:", dex)
print("CEX last:", cex)
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
)
func fetch(path string, q url.Values) ([]byte, error) {
req, _ := http.NewRequest("GET",
"https://api.datamaxiplus.com"+path+"?"+q.Encode(), nil)
req.Header.Set("X-DTMX-APIKEY", os.Getenv("DTMX_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil { return nil, err }
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
func main() {
dex, _ := fetch("/api/v1/dex/trade", url.Values{
"chain": {"ethereum"}, "exchange": {"uniswap-v3"},
"pool": {"0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640"},
})
cex, _ := fetch("/api/v1/ticker", url.Values{
"exchange": {"binance"}, "symbol": {"ETH-USDC"},
})
var out any
json.Unmarshal(dex, &out); fmt.Println("DEX:", out)
json.Unmarshal(cex, &out); fmt.Println("CEX:", out)
}
import axios from "axios";
const headers = { "X-DTMX-APIKEY": process.env.DTMX_API_KEY! };
const base = "https://api.datamaxiplus.com";
const [dex, cex] = await Promise.all([
axios.get(`${base}/api/v1/dex/trade`, {
headers,
params: {
chain: "ethereum",
exchange: "uniswap-v3",
pool: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
},
}),
axios.get(`${base}/api/v1/ticker`, {
headers,
params: { exchange: "binance", symbol: "ETH-USDC" },
}),
]);
console.log("DEX:", dex.data);
console.log("CEX:", cex.data);
Risks & caveats
- Gas volatility. A 50 bps gap on Ethereum L1 becomes negative if base fee spikes 5x mid-trade. Model gas as a stochastic input, not a constant.
- MEV. Public-mempool swaps get sandwiched. Use private RPC (Flashbots Protect, MEV Blocker) or batch through CoW Swap / 1inch Fusion when sizing matters.
- Bridge risk. Bridges have been the largest source of crypto loss ever (~$2B+ cumulative). Native bridges are slow (7-day Optimism, 12s LayerZero, etc.). Choose deliberately.
- Oracle / pricing lag. The DEX trade endpoint reflects last-confirmed swap; concentrated-liquidity pool state can change with the next block. Quote on-chain at execution time, not from cached REST data.
- CEX withdrawal-network mismatch. Many CEXs disable specific networks intermittently (e.g., Binance pausing Arbitrum withdrawals for 30 min). Always check wallet status pre-trade.
- Atomicity asymmetry. Your on-chain leg is atomic; your CEX leg isn't. If the CEX leg fails (margin call, kill switch, latency), you're left with unhedged exposure.
- Token-tax tokens. Some long-tail tokens charge transfer tax on-chain. Your effective DEX execution is worse than the quoted pool price.
Further reading
- DEX API reference — pools, swap, candles, chains, exchanges.
- Premium page (UI) — UI roadmap mentions DEX premiums as a future addition.
- CEX ticker reference — opposite-leg pricing.
- Wallet status reference — network availability check.