Anatomia spreadu: dlaczego DEX i CEX nie mogą żyć в pełnej synchronizacji
Jeśli myślisz, że arbitraż między DEX a CEX to magiczny „przycisk do robienia kasy”, w którym wystarczy znaleźć 2% różnicy i przerzucić tokeny tam i z powrotem, to czeka cię twardе lądowanie. Spread międzygiełdowy to nie darmowy hajs — to premia za ryzyko, brak płynności i opóźnienia w infrastrukturze.
Na CEX-ach (Binance, Bybit, EXMON) cena kształtuje się w orderbooku (Limit Order Book). Płynność jest tam dynamiczna: animatorzy rynku (market makerzy) przesuwają siatkę zleceń w ułamki mikrosekund, a algorytmy stale przeliczają wartość godziwą na podstawie indeksów kontraktów futures.
Na DEX-ach (Uniswap, Raydium, PancakeSwap) cena zależy od matematycznej krzywej puli płynności. W klasycznym AMM v2 (x · y = k) cena zmienia się tylko тогда, gdy do puli fizycznie wpada transakcja. Jeśli na Binance cena tokena X spadnie o 5% w sekundę przez dużego sprzedawcę, pula na Uniswapie będzie stać w miejscu ze starą, wysoką ceną dopóki:
- Do sieci nie trafi transakcja bota arbitrażowego.
- Transakcja nie przejdzie fazy walidacji i nie trafi do bloku.
I właśnie w tym okienku czasowym (od kilku sekund w szybkich sieciach do kilku bloków na Ethereum) rodzi się spread.
Anatomia w praktyce: skąd właściwie bierze się spread dla tradera ręcznego
Handlując ręcznie, nie masz szans w starciu z botami HFT i MEV searcherami, którzy siedzą na prywatnych węzłach RPC i płacą napiwki walidatorom przez Flashbots, żeby wciąć się do tego samego bloku (backrunning). Drobne spready rzędu 0,2%–0,5% na ETH czy BTC algorytmy zgarniają w mikrosekundy.
Ale trader manualny poluje na zupełnie inny typ odchyleń cenowych:
- Impulsywny dump/pump na altcoinach o średniej kapitalizacji (mid-caps). Do puli na DEX-ie wpada „wieloryb” i zrzuca tokeny za 50 000 USD. W mniej płynnej puli cena natychmiast zalicza spadek o 4–7%. Boty na CEX-ie nie mogą od razu odkupić tego zrzutu, bo w orderbooku na CEX-ie jest gęsto i cena tam nawet nie drgnęła.
- Zatory w sieci i skoki cen gazu (Gas Price). Podczas ostrej rynkowej zadymy mempool jest całkowicie zapchany. Boty MEV tymczasowo odpuszczają albo żądają kosmicznych prowizji, przez co spready na DEX-ach potrafią wisieć nawet przez 2–5 minut.
- Niesynchroniczny listing i wstrzymanie wypłat/depozytów (Deposit/Withdrawal Halt). Giełda zamyka wpłaty lub wypłaty danego tokena w wybranej sieci z powodu aktualizacji węzła. Przepływ płynności między DEX a CEX zostaje całkowicie odcięty. Na CEX-ie tworzy się „zamknięty orderbook”, a cena zaczyna żyć własnym życiem, odjeżdżając od tej na DEX-ie nawet o 15%.
Architektura stanowiska pracy i alokacja kapitału
Główny błąd świeżaka: zauważył spread na DEX-ie, kupił na CEX-ie, kliknął „Wypłać na portfel” i siedzi, czekając 15 minut na potwierdzenie transakcji przez sieć. Zanim tokeny dolecą do portfela, spread nie dość że wyparuje, to jeszcze zamykasz pozycję z gwarantowaną stratą.
Ręczny trading na spreadach wymaga podejścia delta-neutralnego lub gotowego podziału salda (Split Balance).
Model podwojonego salda (Split Balance)
Zapomnij o przelewaniu środków między platformami w trakcie trwania transakcji. Przelewy robi się przed rozpoczęciem handlu.
Twój depozyt dzieli się na dwie równe części:

Przy takim układzie egzekwujesz zlecenia symultanicznie:
- Jeśli token na DEX-ie kosztuje 1,10 USD, a na CEX-ie 1,00 USD: natychmiast sprzedajesz ARB na DEX-ie (zgarniasz 1,10 USD) i kupujesz ARB na CEX-ie (po 1,00 USD).
- Nie ma tu żadnego fizycznego transferu tokenów. Zgarniasz 10% delty (minus prowizje) w 3 sekundy.
- Wyrównanie (rebalancing) depozytu między portfelem a giełdą robisz później — gdy rynek się uspokoi, a opłaty sieciowe spadną do minimum.
Matematyka transakcji: liczymy "czysty" spread i punkt rentowności
Widzisz na ekranie ładne 3% różnicy i już przeliczasz to na pewny zysk? To prosty przepis na wyzerowanie depozytu dla początkujących arbitrażystów. Spread „brutto” (Gross Spread) jest bez znaczenia. Interesuje nas wyłącznie spread „netto” (Net Spread) po odliczeniu wszystkich prowizji opłacanych po drodze w każdej z infrastruktur.
Wzór na rentowność netto
Net_Profit = (Psell · V) - (Pbuy · V) - FeeCEX - FeeDEX - Gas - Impact
Gdzie:
- Psell i Pbuy – realne ceny realizacji zleceń (nie żaden Mid-Price z nagłówka strony, tylko ceny średnie ważone uwzględniające głębokość arkusza).
- V – wolumen transakcji w tokenie bazowym lub stablecoinach.
- FeeCEX – prowizja giełdy za zlecenie typu taker (zazwyczaj 0.05%–0.1%).
- FeeDEX – prowizja puli (0.01%, 0.05%, 0.3% lub 1% w zależności od tieru puli).
- Gas – koszt dołączenia transakcji do bloku, przeliczony na USD.
- Impact – wpływ własnego wolumenu na cenę wewnątrz puli AMM.
Wróg numer jeden – Price Impact na DEX
Jeśli na CEX widzisz w orderbooku 10 000 coinów po $1.00, zgarniasz je idealnie po $1.00 (plus prowizja). Na DEX ze standardową mechaniką Constant Product (x · y = k) każdy kolejny kupowany token jest droższy od poprzedniego.
Wzór na cenę sprzedaży tokena x za token y w puli x · y = k:
yout = (yreserve · xin · (1 - fee)) / (xreserve + xin · (1 - fee))
Jeśli rzucasz zlecenie na $5 000 w pulę z płynnością $100 000, przesuwasz cenę w puli o ~5%. Twój czysty spread 3% błyskawicznie zmienia się w 2% straty, zanim transakcja w ogóle trafi do bloku.
Tabela realnego spreadu przy różnej wielkości zlecenia (Pula $50k, spread brutto 2.5%)
| Wielkość zlecenia ($) | Price Impact (%) | Prowizje (Gas + Fees) | Spread netto (%) | Wynik transakcji |
|---|---|---|---|---|
| $100 | ~0.2% | ~$1.50 (1.5%) | +0.8% | Drobny zysk |
| $500 | ~1.0% | ~$1.50 (0.3%) | +1.2% | Optymalny profit |
| $2,000 | ~3.8% | ~$1.50 (0.075%) | -1.375% | Strata przez Impact |
Wniosek: dla każdej wielkości puli istnieje sztywny, wynikający z matematyki sufit na wolumen transakcji. Pakowanie tam zbyt dużych kwot po prostu rozwala cały rachunek.
Automatyzacja wyszukiwania: piszemy monitor w Pythonie
Przeklikywanie zakładek w przeglądarce, odświeżanie DexScreenera i ręczne podglądanie orderbooka giełdy to przeżytek. Ręczny trader używa softu nie po to, żeby automatycznie egzekwować transakcje (tu ściganie się z botami MEV nie ma najmniejszego sensu), ale po to, żeby dostać natychmiastowy alert dźwiękowy lub powiadomienie na Telegramie/konsoli, gdy pojawi się anomalia rynkowa.
Poniżej znajdziesz w pełni działający, gotowy skrypt w Pythonie 3.10+. Skrypt w czasie rzeczywistym monitoruje bezpośrednie rezerwy puli PancakeSwap v2 (BNB Chain) po RPC i porównuje je z arkuszem zleceń CEX (w tym przykładzie używamy REST API Binance, które bez problemu dostosujesz pod EXMON). Oczywiście to prosty kod i wypadałoby go jeszcze podszlifować, ale spełnia swoje zadanie.
Kod skanera spreadu uwzględniający rezerwy i prowizje
import sys
import time
import requests
from web3 import Web3
# ============================================================
# CONFIG
# ============================================================
BSC_RPC = "https://bsc-dataseed1.binance.org"
POOL_ADDRESS = Web3.to_checksum_address(
"0x16b9a82891338f9bA80E2D6970FddA79D1eb0daE"
)
BINANCE_DEPTH_URL = (
"https://api.binance.com/api/v3/depth"
"?symbol=BNBUSDT&limit=20"
)
TRADE_VOLUME_USD = 300.0
CEX_TAKER_FEE = 0.001
DEX_FEE = 0.0025
MIN_NET_SPREAD_PCT = 0.8
# szacowany koszt gazu za swap
ESTIMATED_GAS_COST_USD = 0.15
REQUEST_TIMEOUT = 3
# ============================================================
# WEB3
# ============================================================
w3 = Web3(Web3.HTTPProvider(BSC_RPC))
if not w3.is_connected():
print("Błąd połączenia z BSC RPC")
sys.exit(1)
# ============================================================
# ABI
# ============================================================
PAIR_ABI = [
{
"constant": True,
"inputs": [],
"name": "getReserves",
"outputs": [
{"name": "_reserve0", "type": "uint112"},
{"name": "_reserve1", "type": "uint112"},
{"name": "_blockTimestampLast", "type": "uint32"},
],
"stateMutability": "view",
"type": "function",
},
{
"constant": True,
"inputs": [],
"name": "token0",
"outputs": [{"name": "", "type": "address"}],
"stateMutability": "view",
"type": "function",
},
{
"constant": True,
"inputs": [],
"name": "token1",
"outputs": [{"name": "", "type": "address"}],
"stateMutability": "view",
"type": "function",
},
]
pair = w3.eth.contract(
address=POOL_ADDRESS,
abi=PAIR_ABI
)
# ============================================================
# KNOWN TOKENS
# ============================================================
WBNB = Web3.to_checksum_address(
"0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"
)
USDT = Web3.to_checksum_address(
"0x55d398326f99059fF775485246999027B3197955"
)
# ============================================================
# VERIFY PAIR
# ============================================================
try:
token0 = Web3.to_checksum_address(
pair.functions.token0().call()
)
token1 = Web3.to_checksum_address(
pair.functions.token1().call()
)
except Exception as e:
print("Błąd odczytu token0/token1")
print(e)
sys.exit(1)
PAIR_LAYOUT = None
if token0 == WBNB and token1 == USDT:
PAIR_LAYOUT = "WBNB_USDT"
elif token0 == USDT and token1 == WBNB:
PAIR_LAYOUT = "USDT_WBNB"
else:
print("Pula nie jest parą WBNB/USDT")
print("token0 =", token0)
print("token1 =", token1)
sys.exit(1)
print("Pair verified:")
print("token0 =", token0)
print("token1 =", token1)
# ============================================================
# BINANCE
# ============================================================
def get_orderbook():
try:
response = requests.get(
BINANCE_DEPTH_URL,
timeout=REQUEST_TIMEOUT
)
response.raise_for_status()
data = response.json()
if "bids" not in data:
return None
if "asks" not in data:
return None
return data
except Exception:
return None
def calculate_execution_buy_price(
asks,
target_usd
):
"""
Zakup BNB za USDT.
Zwraca:
avg_price,
received_bnb
"""
spent_usd = 0.0
received_bnb = 0.0
for price_str, qty_str in asks:
price = float(price_str)
qty = float(qty_str)
level_usd = price * qty
if spent_usd + level_usd >= target_usd:
remaining_usd = target_usd - spent_usd
partial_qty = remaining_usd / price
received_bnb += partial_qty
spent_usd += remaining_usd
break
received_bnb += qty
spent_usd += level_usd
if spent_usd < target_usd:
return None, None
avg_price = spent_usd / received_bnb
return avg_price, received_bnb
def calculate_execution_sell_price(
bids,
sell_bnb
):
"""
Sprzedaż BNB do USDT.
Zwraca:
avg_price,
received_usdt
"""
remaining_bnb = sell_bnb
received_usdt = 0.0
sold_bnb = 0.0
for price_str, qty_str in bids:
price = float(price_str)
qty = float(qty_str)
take_qty = min(
remaining_bnb,
qty
)
received_usdt += take_qty * price
sold_bnb += take_qty
remaining_bnb -= take_qty
if remaining_bnb <= 0:
break
if remaining_bnb > 0:
return None, None
avg_price = received_usdt / sold_bnb
return avg_price, received_usdt
# ============================================================
# DEX
# ============================================================
def get_reserves():
try:
reserves = (
pair.functions
.getReserves()
.call()
)
raw0 = reserves[0]
raw1 = reserves[1]
if PAIR_LAYOUT == "WBNB_USDT":
reserve_bnb = raw0 / 1e18
reserve_usdt = raw1 / 1e18
else:
reserve_usdt = raw0 / 1e18
reserve_bnb = raw1 / 1e18
return (
reserve_bnb,
reserve_usdt
)
except Exception:
return None, None
def amm_out(
amount_in,
reserve_in,
reserve_out
):
if (
amount_in <= 0
or reserve_in <= 0
or reserve_out <= 0
):
return 0.0
amount_in_fee = (
amount_in
* (1 - DEX_FEE)
)
numerator = (
amount_in_fee
* reserve_out
)
denominator = (
reserve_in
+ amount_in_fee
)
return numerator / denominator
# ============================================================
# MONITOR
# ============================================================
def monitor():
print()
print("DEX/CEX Arbitrage Monitor Started")
print("-" * 80)
while True:
book = get_orderbook()
if book is None:
time.sleep(1)
continue
reserve_bnb, reserve_usdt = (
get_reserves()
)
if (
reserve_bnb is None
or reserve_usdt is None
):
time.sleep(1)
continue
bids = book["bids"]
asks = book["asks"]
# ====================================================
# SCENARIO A
# BUY ON DEX
# SELL ON CEX
# ====================================================
bnb_from_dex = amm_out(
TRADE_VOLUME_USD,
reserve_usdt,
reserve_bnb
)
if bnb_from_dex <= 0:
time.sleep(1)
continue
sell_price, usdt_received = (
calculate_execution_sell_price(
bids,
bnb_from_dex
)
)
if usdt_received is None:
time.sleep(1)
continue
usdt_received *= (
1 - CEX_TAKER_FEE
)
usdt_received -= (
ESTIMATED_GAS_COST_USD
)
spread_a = (
(usdt_received - TRADE_VOLUME_USD)
/ TRADE_VOLUME_USD
) * 100
# ====================================================
# SCENARIO B
# BUY ON CEX
# SELL ON DEX
# ====================================================
buy_price, bought_bnb = (
calculate_execution_buy_price(
asks,
TRADE_VOLUME_USD
)
)
if bought_bnb is None:
time.sleep(1)
continue
bought_bnb *= (
1 - CEX_TAKER_FEE
)
usdt_from_dex = amm_out(
bought_bnb,
reserve_bnb,
reserve_usdt
)
usdt_from_dex -= (
ESTIMATED_GAS_COST_USD
)
spread_b = (
(usdt_from_dex - TRADE_VOLUME_USD)
/ TRADE_VOLUME_USD
) * 100
dex_price = (
reserve_usdt
/ reserve_bnb
)
sys.stdout.write(
"\r"
f"DEX={dex_price:.2f} | "
f"A:{spread_a:+.2f}% | "
f"B:{spread_b:+.2f}%"
)
sys.stdout.flush()
if spread_a >= MIN_NET_SPREAD_PCT:
print(
"\n"
f"[SIGNAL] "
f"BUY DEX -> SELL CEX "
f"({spread_a:.2f}%)"
)
elif spread_b >= MIN_NET_SPREAD_PCT:
print(
"\n"
f"[SIGNAL] "
f"BUY CEX -> SELL DEX "
f"({spread_b:.2f}%)"
)
time.sleep(1.5)
# ============================================================
# ENTRY
# ============================================================
if __name__ == "__main__":
try:
monitor()
except KeyboardInterrupt:
print("\nStopped.")Ten skrypt wylicza realną poślizg (slippage), ponieważ zamiast brać cenę „z czapy”, przepuszcza Twój wolumen $300 bezpośrednio przez model matematyczny puli.
Protokol wykonywania ręcznego: Jak nie zostać karmą dla botów sandwich
Kliknięcie dwóch przycisków na różnych giełdach wydaje się prostym zadaniem. W praktyce 80% traderów działających manualnie traci zysk w momencie wysłania transakcji do publicznego mempoola.
Gdy tylko Twoja transakcja zakupu na DEX trafia do publicznego mempoola Ethereum, BNB Chain lub Arbitrum, zostaje błyskawicznie przechwycona przez boty MEV (Maximum Extractable Value). Jeśli masz ustawiony poślizg cenowy (Slippage Tolerance) na więcej niż 0.5%, bot przeprowadza atak typu sandwich (Sandwich Attack):
- Bot kupuje token tuż przed Tobą, windując cenę w górę (Frontrun).
- Twoja transakcja realizuje się po najgorszej możliwej cenie na granicy Twojego limitu poślizgu.
- Bot odsprzedaje token zaraz po Tobie z zyskiem (Backrun).
W efekcie dostajesz token po zmanipulowanej cenie, a cały spread zgarnia bot MEV.
Bezpieczny obwód realizacji transakcji
Żeby ręcznie zgarniać spready i nie karmić botów, egzekucję transakcji konfiguruje się według poniższego schematu:
- Prywatne węzły RPC (MEV-Blocker / Flashbots). Zapomnij o domyślnych ustawieniach sieci w MetaMasku. Wszystkie transakcje na DEX-ach idą wyłącznie przez prywatne przekaźniki, które wysyłają transakcję bezpośrednio do walidatora bloku, omijając publiczny mempool.
Dla Ethereum: https://rpc.mevblocker.io lub https://rpc.flashbots.net
Dla BNB Chain / Polygon / Arbitrum: korzystaj z usług MEV-Protection od QuickNode lub dostawców pokroju BloXroute. - Sztywny Slippage Limit. Żadnych domyślnych 1% czy 2% w interfejsie Uniswap/1inch. Maksymalny dopuszczalny poślizg dla transakcji arbitrażowej to 0.1%–0.3%. Jeśli cena ucieknie ułamek sekundy przed wejściem do bloku, transakcja ma się wywalić z błędem (revert), a nie wykonać ze stratą.
- Optymalizacja parametrów Gas Fee. Konfiguracja prowizji w portfelu Web3 przechodzi w tryb ręczny (Advanced Gas Controls). Max Priority Fee (napiwek dla walidatora) ustawia się nieco powyżej aktualnej mediany sieci, żeby zaklepać miejsce na początku kolejnego bloku. W sieciach L2 (Arbitrum, Base, OP) kluczowe jest monitorowanie L1 Data Fee – podczas obciążenia Ethereum wysyłanie transakcji w L2 może kosztować 5 razy więcej niż standardowo.
Zaawansowana technika: Arbitraż delta-neutralny „DEX Spot vs CEX Futures”
Fizyczne trzymanie altcoina na obu saldach wiąże się z ryzykiem straty na spadku kursu samego aktywa (Market Price Risk), dopóki czekasz na pojawienie się spreadu. Jeśli monety zanurkują o 15%, Twój zablokowany spread na poziomie 2% nie pokryje dołka na depozycie.
Zaawansowana metodologia stosowana w Akademii EXMON to delta-neutralny spread trading z wykorzystaniem kontraktów futures.
Mechanika działania poprzez Short-hedge
Zamiast kupować aktywo spot na CEX, otwierasz tam pozycję krótką (short) na futures z dźwignią 1x.

Algorytm działania:
- Na DEX następuje nagły zjazd ceny: token leci do $0.95, podczas gdy na CEX futures i spot kosztują $1.00 (spread 5.2%).
- Odkupujesz spot na DEX po $0.95.
- Jednocześnie na CEX otwierasz Shorta na futures po cenie $1.00.
- Twoja delta zostaje natychmiast wyzerowana: przy każdym ruchu rynku całkowity ekwiwalent depozytu pozostaje bez zmian.
- Gdy cena na DEX zrównuje się z CEX (np. oba rynki lądują na poziomie $0.98), zamykasz obie pozycje:
- Sprzedajesz spot na DEX po $0.98 (Zysk: +$0.03 na monecie).
- Zamykasz shorta na CEX po $0.98 (Zysk: +$0.02 na monecie).
Łączny czysty zysk: $0.05 z monety (5% do zaangażowanego wolumenu) przy całkowitym braku ryzyka spadku samej ceny tokena.
Dodatkowy bonus: Jeśli futures na CEX handlowane są w kontango (cena kontraktu jest wyższa od spotu), równolegle zgarniasz dodatnią stawkę finansowania (Funding Rate) co każdą godzinę rozliczeniową.
Mało znane pułapki i ryzyka
Jeśli strategia wygląda bezbłędnie, to znaczy, że jeszcze nie dostrzegłeś ukrytych zagrożeń. Ręczny spread trading jest naszpikowany inżynieryjnymi „minami”.
1. Tokeny z wbudowanym podatkiem (Transfer Tax / Reflection Tokens)
Niektóre tokeny ERC-20 / BEP-20 zaszywają w smart kontrakcie prowizję od każdego przelewu (np. 2%–5% na spalanie lub portfel marketingowy).
Jeśli nie sprawdzisz kodu kontraktu przez eksplorator lub skaner (TokenSniffer, GoPlus), wyślesz token z puli, stracisz 5% na mechanice samej monety, kolejne 0.3% na prowizji AMM i zamkniesz transakcję na potężnym minusie.
2. Przesunięcie płynności wirtualnej w Uniswap v3 (Concentrated Liquidity Trap)
W Uniswap v2 płynność rozproszona jest od zera do nieskończoności. W v3 płynność kumuluje się w wąskich przedziałach (Ticks).
Jeśli spread pojawił się dlatego, że cena uciekla poza zakres głównego market makera, wizualna różnica cen może wydawać się ogromna (nawet 10%–20%). Jednak wolumen płynności na bieżącym ticku może wynosić zaledwie $15. Próba wykonania transakcji skończy się natychmiastowym poślizgiem ceny o kilkadziesiąt procent przeciwko Tobie.
3. Nierównowaga płynności i „Zamrożony kapitał” (Inventory Risk)
Wykorzystując spread 5 razy z rzędu w tę samą stronę (np. Zakup na DEX -> Sprzedaż na CEX), całkowicie wyczyścisz zapasy USDT na CEX i zapchasz swój portfel Web3 samymi altcoinami.
Rebalansowanie wymaga czasu i kosztów prowizji. Jeśli rynek pójdź w mocnym trendzie, utkniesz uwięziony w jednej nogi pozycji. Zasada ryzyka: nigdy nie dopuszczaj do sytuacji, w której bilans jednej z platform przekracza relację 70/30.
Checklista tradera przed kliknięciem
Umieść tę checklistę przed swoim monitorem. Wejście w pozycję następuje tylko wtedy, gdy zgadzają się wszystkie 5 punktów:
- Ochrona MEV włączona: W portfelu wybrany jest prywatny węzeł RPC (MEV-Blocker / Flashbots).
- Głębokość puli sprawdzona: Wolumen transakcji nie przekracza 1%–2% całkowitych rezerw puli na DEX (Price Impact < 0.3%).
- Czysty spread potwierdzony: Rozbieżność cenowa przewyższa łączne prowizje CEX + DEX + Gas co najmniej 2.5 raza.
- Smart kontrakt czysty: Token nie posiada ukrytych prowizji za transfer (Transfer Fee = 0%).
- Kapital rozłożony: Na CEX i DEX znajduje się wystarczający balans w obu walutach pary do natychmiastowego wykonania bez czekania na przelewy.
Spread trading między giełdami zdecentralizowanymi a centralizowanymi to czysta dyscyplina matematyczna. Wygrywa tu nie ten, kto zgaduje kierunek trendu, ale ten, kto lepiej liczy prowizje, kontroluje infrastrukturę i potrafi czekać na swoją anomalię.