Как обойти блокировки по IP при парсинге стакана Raydium на Python

Как обойти блокировки по IP при парсинге стакана Raydium на Python ИНВЕСТИЦИИ, ВКЛАДЫ и СБЕРЕЖЕНИЯ
Пошаговое руководство от Senior Quant Developer по обходу IP-блокировок при сборе данных стакана (Orderbook) с DEX Raydium на Python с использованием ротации RPC и прокси.
Суть: Для обхода блокировок IP при сборе стакана Raydium необходимо отказаться от публичных RPC-узлов Solana, внедрить пул ротации приватных RPC-эндпоинтов (Helius, QuickNode) совместно с резидентными прокси, а также перейти от частого HTTP-поллинга к WebSocket-подпискам на аккаунты OpenBook (Bids/Asks), привязанные к пулу Raydium.

Исходный код

При парсинге DEX на Solana (в частности, Raydium) классический HTTP-поллинг быстро приводит к лимитам (Rate Limits) и бану IP-адреса. Raydium использует ликвидность OpenBook (Serum) для лимитных ордеров. Чтобы получить актуальный стакан, нам нужно напрямую читать данные аккаунтов marketBids и marketAsks. Ниже представлен асинхронный скрипт на Python, реализующий ротацию RPC-узлов и резидентных прокси для безопасного получения сырых данных стакана.

Для построения отказоустойчивой архитектуры сбора данных рекомендуется использовать брокеры сообщений. Подробнее об этом читайте в статье Интеграция Kafka для обработки потоковых тиковых данных на Python. Если вы планируете масштабировать сбор на сотни торговых пар, ознакомьтесь с материалом про Мультиплексирование WebSocket Binance Futures на Python.

import asyncio
import aiohttp
import random
from typing import List, Dict, Any, Optional

# Список ваших приватных или публичных RPC-узлов Solana для ротации
RPC_ENDPOINTS: List[str] = [
    "https://api.mainnet-beta.solana.com",
    "https://solana-api.projectserum.com",
    "https://rpc.ankr.com/solana"
]

# Список резидентных прокси (формат: http://user:pass@ip:port)
PROXY_POOL: List[str] = [
    "http://user1:pass1@residential.proxyprovider.com:9000",
    "http://user2:pass2@residential.proxyprovider.com:9000",
    "http://user3:pass3@residential.proxyprovider.com:9000"
]

class RaydiumOrderbookParser:
    def __init__(self, rpc_urls: List[str], proxies: List[str]):
        self.rpc_urls = rpc_urls
        self.proxies = proxies

    def _get_route(self) -> tuple[str, Optional[str]]:
        """Возвращает случайный RPC-узел и прокси для запроса."""
        rpc = random.choice(self.rpc_urls)
        proxy = random.choice(self.proxies) if self.proxies else None
        return rpc, proxy

    async def fetch_account_data(self, session: aiohttp.ClientSession, account_pubkey: str) -> Optional[Dict[str, Any]]:
        """Получает сырые данные аккаунта Solana с использованием ротации."""
        rpc_url, proxy = self._get_route()
        
        payload = {
            "jsonrpc": "2.0",
            "id": 1,
            "method": "getAccountInfo",
            "params": [
                account_pubkey,
                {"encoding": "base64"}
            ]
        }
        
        try:
            async with session.post(rpc_url, json=payload, proxy=proxy, timeout=10) as response:
                if response.status == 200:
                    result = await response.json()
                    if "result" in result and result["result"]["value"]:
                        return result["result"]["value"]
                    else:
                        print(f"Аккаунт {account_pubkey} не найден или пуст.")
                else:
                    print(f"Ошибка HTTP {response.status} на узле {rpc_url}")
        except Exception as e:
            print(f"Сбой запроса через {rpc_url} (Прокси: {proxy}): {e}")
        return None

async def main():
    # Адрес аккаунта Bids (аски/биды) для конкретного рынка OpenBook/Raydium
    # Например, SOL/USDC OpenBook Market Bids
    sol_usdc_bids_address = "5jF7K88ZhhBsDMkWST787UcH68766gV6T67mDq98g" 
    
    parser = RaydiumOrderbookParser(RPC_ENDPOINTS, PROXY_POOL)
    
    async with aiohttp.ClientSession() as session:
        print("Запуск парсинга стакана Raydium...")
        for iteration in range(5):
            print(f"Итерация {iteration + 1}: запрос данных...")
            data = await parser.fetch_account_data(session, sol_usdc_bids_address)
            if data:
                raw_bytes_info = data.get("data", [])
                print(f"Успешно получено байт: {len(raw_bytes_info[0]) if raw_bytes_info else 0}")
            await asyncio.sleep(2)

if __name__ == "__main__":
    asyncio.run(main())

Разбор параметров

  • RPC_ENDPOINTS: Список эндпоинтов нод Solana. Для продакшена рекомендуется использовать платные тарифы от Helius, QuickNode или Triton, так как публичные ноды имеют жесткие лимиты на количество запросов в секунду (RPS).
  • PROXY_POOL: Список резидентных или мобильных прокси-серверов. Резидентные прокси мимикрируют под обычных домашних пользователей, что делает их блокировку со стороны Cloudflare (защищающего API Raydium) практически невозможной.
  • getAccountInfo: Метод JSON-RPC Solana для получения состояния аккаунта. Мы запрашиваем данные в формате base64, чтобы затем локально декодировать структуру стакана (Slab) OpenBook.
  • _get_route: Внутренний метод класса, реализующий простейшую балансировку нагрузки (Load Balancing) путем случайного выбора пары RPC + Proxy для каждого отдельного запроса.

Как запустить

Для запуска скрипта вам понадобится интерпретатор Python версии 3.8 или выше и библиотека aiohttp для выполнения асинхронных HTTP-запросов.

Установите необходимую зависимость в вашем терминале:

pip install aiohttp

Замените демонстрационные адреса в RPC_ENDPOINTS и PROXY_POOL на ваши реальные данные. Запустите скрипт:

python main.py

Полученные бинарные данные стакана необходимо декодировать в соответствии с макетом структуры OpenBook Market (используя библиотеки construct или готовые парсеры). Если вам требуется передавать полученные сигналы в торговые модули на C++ с минимальной задержкой, изучите руководство Как связать Python и C++ через ZeroMQ для передачи сигналов.

Оцените статью
FinFluct