Parkowanie płynności na rynku krypto to wieczny ból głowy. Trzymasz stablecoiny na spocie, czekając aż BTC albo ETH zaliczą spadek – kapitał zżera ci inflacja. Wrzucasz w lending – dostajesz marne 2–4% APY, a do tego ładujesz się w ryzyko smart kontraktów.
Spotowy Grid Trading na parach typu USDT/USDC czy USDC/DAI pozwala wycisnąć 3–8% APR na czystym trendzie bocznym. Bez dźwigni, bez ryzyka likwidacji i bez bajek od kołczów finansowych o 50% w skali roku na „bezpiecznym” flacie.
Cała strategia opiera się na jednym fakcie: stablecoiny nigdy nie kosztują równo $1.0000. Przez ciągłe przepływy kapitału między protokołami, arbitraż i cash-out do fiatów, kurs pary USDT/USDC nieustannie krąży w wąskim korytarzu $0.9980–$1.0020. Siatka zleceń limit po prostu spija śmietankę z każdego takiego mikro-wahania.
Pułapka prowizyjna: na czym wywalają się świeżaki
Główny powód, dla którego 90% botów siatkowych na stable’ach kręci minusy, to Fee Drag, czyli zjadanie zysków przez prowizje.
Wyobraź sobie sytuację: ustawiasz siatkę z krokiem 0.01% (czyli zlecenia co $0.0001). Twoja prowizja Maker na giełdzie to standardowe 0.02%. Kupujesz po $0.9999, sprzedajesz po $1.0000. Na papierze masz 0.01% zysku. W rzeczywistości – płacisz 0.02% za wejście i 0.02% za wyjście. Bilans: plecy na 0.03% przy każdym kółku.
Zapamiętaj żelazną zasadę: krok siatki musi być co najmniej 2.5–3 razy wyższy niż łączna prowizja za pełen obrót (Maker Buy + Maker Sell).
Z tego wynika prosty wniosek: kręcenie Grida na stable’ach ma sens tylko w dwóch przypadkach:
- Na parach USDT/USDC lub USDC/DAI śmiga akurat promocja Zero Fee (0% Maker / 0% Taker).
- Masz wyrobiony konkretny poziom VIP na CEX-ie z zerową lub ujemną prowizją Maker (Rebate).
Realna ekonomia i parametry siatek
Zapomnij o tabelkach obiecujących 30–50% APY na spocie. Oto realne liczby, które wypluwa rynek przy obecnej głębokości orderbooka i dobowej zmienności:
| Para | Typ pokrycia | Zakres roboczy | Optymalny krok | Realny APR na czysto |
|---|---|---|---|---|
| USDT/USDC | Fiat / Fiat | 0.9985 - 1.0015 | 0.015% - 0.02% | 3% - 6% |
| USDC/DAI | Fiat / Zabezpieczenie w krypto | 0.9970 - 1.0030 | 0.02% - 0.03% | 4% - 8% |
| USDT/USDE | Fiat / Syntetyk delta-neutralny | 0.9930 - 1.0070 | 0.05% | 8% - 13% |
Jeśli ktoś obiecuje ci wyższe stopy zwrotu na czystym spocie – albo masz do czynienia ze scamem, albo dana para wisi na krawędzi kaskadowego depegu.
Przykładowa kalkulacja dla depo $10,000
Weźmy parę USDT/USDC. Kurs stoi idealnie na $1.0000.
- Rozstawiamy zakres $0.9990 – $1.0010 (rozpiętość 0.2%).
- Liczba poziomów (Grids): 20 poziomów.
- Krok siatki:
($1.0010 - $0.9990) / 20 = $0.0001(0.01%). - Wielkość zlecenia na poziom:
$10,000 / 20 = $500. - Zysk brutto z 1 obrotu (Buy + Sell):
$500 * 0.01% = $0.05.
Przy normalnej zmienności bot domyka od 60 do 140 kółek na dobę. Przekłada się to na $3.0–$7.0 czystego zysku dziennie przy kapitale $10,000 i zerowej prowizji. W ujęciu rocznym daje to bardzo rozsądne 4–6% APR, i to bez mrożenia środków w niesprawdzonych smart kontraktach.
Gotowy bot w Pythonie do siatki na spocie
Poniższy skrypt działa asynchronicznie po WebSocketach (ccxt.pro). Pobiera aktualny orderbook, rozstawia siatkę limitów od obecnej ceny, a potem na żywo nasłuchuje realizacji i od razu wystawia przeciwne zlecenie krok wyżej lub niżej.
import asyncio
import logging
from decimal import Decimal
import ccxt.pro as ccxt
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s'
)
class StableGridBot:
def __init__(
self,
symbol: str,
lower_price: float,
upper_price: float,
grids: int,
amount_per_grid: float,
api_key: str,
api_secret: str
):
self.symbol = symbol
self.lower_price = Decimal(str(lower_price))
self.upper_price = Decimal(str(upper_price))
self.grids = grids
self.amount_per_grid = amount_per_grid
self.step = (self.upper_price - self.lower_price) / Decimal(str(grids))
self.exchange = ccxt.binance({
'apiKey': api_key,
'secret': api_secret,
'enableRateLimit': True,
'options': {
'defaultType': 'spot'
}
})
self.active_orders = {}
async def initialize_grid(self):
await self.exchange.load_markets()
market = self.exchange.market(self.symbol)
min_cost = (
market
.get('limits', {})
.get('cost', {})
.get('min')
)
if min_cost:
order_cost = (
float(self.amount_per_grid)
* float(self.lower_price)
)
if order_cost < min_cost:
raise ValueError(
f"Order cost {order_cost} below minimum {min_cost}"
)
open_orders = await self.exchange.fetch_open_orders(
self.symbol
)
existing_prices = set()
if open_orders:
logging.info(
f"Found {len(open_orders)} open orders. "
f"Restoring state from exchange..."
)
for order in open_orders:
price_str = self.exchange.price_to_precision(
self.symbol,
order['price']
)
self.active_orders[order['id']] = (
order['side'],
float(price_str)
)
existing_prices.add(price_str)
ticker = await self.exchange.fetch_ticker(
self.symbol
)
current_price = Decimal(
str(ticker['last'])
)
logging.info(
f"Grid initialized. "
f"Current price for {self.symbol}: {current_price}"
)
for i in range(self.grids + 1):
raw_price = (
self.lower_price +
(Decimal(str(i)) * self.step)
)
price_str = self.exchange.price_to_precision(
self.symbol,
str(raw_price)
)
if price_str in existing_prices:
continue
price = float(price_str)
if Decimal(price_str) < current_price:
order = await self.place_order(
'buy',
price
)
if order:
self.active_orders[order['id']] = (
'buy',
price
)
elif Decimal(price_str) > current_price:
order = await self.place_order(
'sell',
price
)
if order:
self.active_orders[order['id']] = (
'sell',
price
)
async def place_order(
self,
side: str,
price: float
):
try:
precise_price = float(
self.exchange.price_to_precision(
self.symbol,
price
)
)
precise_amount = float(
self.exchange.amount_to_precision(
self.symbol,
self.amount_per_grid
)
)
order = await self.exchange.create_order(
symbol=self.symbol,
type='limit',
side=side,
amount=precise_amount,
price=precise_price
)
logging.info(
f"Placed {side.upper()} order at "
f"{precise_price}"
)
return order
except Exception as e:
logging.error(
f"Failed to place "
f"{side} order at {price}: {e}"
)
return None
async def handle_order_fill(
self,
filled_order
):
order_id = filled_order['id']
order_info = self.active_orders.pop(
order_id,
None
)
if order_info is None:
return
side, price = order_info
logging.info(
f"Filled {side.upper()} "
f"order at price: {price}"
)
dec_price = Decimal(str(price))
if side == 'buy':
new_price_dec = dec_price + self.step
if new_price_dec > self.upper_price:
logging.info(
f"Skipping SELL order: "
f"{new_price_dec} is above "
f"upper grid boundary"
)
return
new_price = float(
self.exchange.price_to_precision(
self.symbol,
str(new_price_dec)
)
)
new_order = await self.place_order(
'sell',
new_price
)
if new_order:
self.active_orders[new_order['id']] = (
'sell',
new_price
)
else:
new_price_dec = dec_price - self.step
if new_price_dec < self.lower_price:
logging.info(
f"Skipping BUY order: "
f"{new_price_dec} is below "
f"lower grid boundary"
)
return
new_price = float(
self.exchange.price_to_precision(
self.symbol,
str(new_price_dec)
)
)
new_order = await self.place_order(
'buy',
new_price
)
if new_order:
self.active_orders[new_order['id']] = (
'buy',
new_price
)
async def start(self):
await self.initialize_grid()
while True:
try:
orders = await self.exchange.watch_orders(
self.symbol
)
for order in orders:
if (
order.get('status') == 'closed'
and order.get('id') in self.active_orders
):
await self.handle_order_fill(
order
)
except Exception as e:
logging.error(
f"WebSocket error: {e}"
)
await asyncio.sleep(5)
async def close(self):
try:
await self.exchange.close()
except Exception:
pass
async def main():
bot = StableGridBot(
symbol='USDC/USDT',
lower_price=0.9985,
upper_price=1.0015,
grids=30,
amount_per_grid=500,
api_key='YOUR_API_KEY',
api_secret='YOUR_API_SECRET'
)
try:
await bot.start()
finally:
await bot.close()
if __name__ == '__main__':
try:
asyncio.run(main())
except KeyboardInterrupt:
logging.info(
"Bot shut down by user"
)Jedyne ryzyko, które wyczyści ci konto
Grid trading na stablecoinach nie niesie ryzyka dźwigni, ale wisi nad nim ryzyko kaskadowego depegu.
Jeśli jeden ze stablecoinów w parze zacznie tracić powiązanie z dolarem i poleci w stronę $0.90, bot będzie z żelazną dyscypliną uśredniał w dół i kupował każdy spadek za całe dostępne depo. W efekcie zostaniesz ze 100% zdekapitalizowanego aktywa w portfelu.
Jak nie dać się usadzić:
- Sztywny Stop-Loss: Ustaw awaryjnego stopa poniżej dolnej granicy siatki. Dla pary USDC/USDT idealny będzie poziom $0.9940. Czasem lepiej uciąć 0.5% straty na depo niż utknąć na pół roku w śmieciowym tokenie.
- Monitoring Curve 3pool: Zachwiania w pulach DeFi zawsze wyprzedzają dumpy na giełdach zcentralizowanych. Jeśli udział jednego ze stable’i w Curve przekracza 60–65% – masz jasny sygnał, żeby natychmiast ubijać bota.
- Zero marginesu: Próba odpalenia siatki na stable'ach z dźwignią 10x pod "wysoki APY" zamienia nudne parkowanie płynności w kasyno, gdzie byle tąpnięcie rynku czyści cię z pozycji przez likwidację.
Trzymaj się tych trzech zasad, pilnuj prowizji giełdowych, nie przesadzaj sa szerokością zakresu, a spotowy grid stanie się stabilną maszynką do generowania pasywnego dochodu.