Парсинг данных Open Interest с бирж
Open Interest (OI) — суммарный объём открытых фьючерсных/опционных позиций. Это один из ключевых индикаторов для аналитики деривативов: резкий рост OI при падении цены означает открытие новых шортов, рост OI при росте цены — набор лонгов. Но чтобы это считать, нужны надёжные данные, а у каждой биржи свой API со своими quirks.
Источники данных: где брать OI
Централизованные биржи деривативов публикуют OI через REST и WebSocket API:
| Биржа | Endpoint | Особенность |
|---|---|---|
| Binance | GET /fapi/v1/openInterest (perp), /futures/data/openInterestHist (история) |
История только за 30 дней, гранулярность 5 мин/15 мин/1 час |
| Bybit | GET /v5/market/open-interest |
Параметр intervalTime: 5min, 15min, 30min, 1h, 4h, 1d |
| OKX | GET /api/v5/rubric/open-interest |
Поддерживает futures, swap, options |
| dYdX v4 | GraphQL API или Indexer REST | On-chain, данные публичны без ключей |
| GMX v2 | On-chain через контракт Reader |
Нет централизованного API |
Для агрегированной картины рынка нужны все крупные площадки. Dominance одной биржи смещает картину: в 2022 году FTX давал ~30% OI рынка, после его краха метрики по всем индексам просели.
Архитектура коллектора
Ключевое решение: polling vs WebSocket. Большинство бирж предоставляют OI только через REST (не WebSocket — OI не такой high-frequency сигнал как цена). Оптимальный подход — scheduled polling каждые 1-5 минут.
import asyncio
import aiohttp
from datetime import datetime
from decimal import Decimal
class OICollector:
def __init__(self, db, symbols: list[str]):
self.db = db
self.symbols = symbols
self.session: aiohttp.ClientSession = None
async def collect_binance_oi(self, symbol: str) -> dict:
url = f"https://fapi.binance.com/fapi/v1/openInterest"
async with self.session.get(url, params={"symbol": symbol}) as resp:
data = await resp.json()
return {
"exchange": "binance",
"symbol": symbol,
"oi_value": Decimal(data["openInterest"]),
"oi_usd": Decimal(data["openInterest"]) * await self.get_price(symbol),
"timestamp": datetime.utcfromtimestamp(data["time"] / 1000),
}
async def collect_bybit_oi(self, symbol: str) -> dict:
url = "https://api.bybit.com/v5/market/open-interest"
async with self.session.get(url, params={
"category": "linear",
"symbol": symbol,
"intervalTime": "5min",
"limit": 1,
}) as resp:
data = await resp.json()
item = data["result"]["list"][0]
return {
"exchange": "bybit",
"symbol": symbol,
"oi_value": Decimal(item["openInterest"]),
"timestamp": datetime.utcfromtimestamp(int(item["timestamp"]) / 1000),
}
async def collect_all(self):
tasks = []
for symbol in self.symbols:
tasks.extend([
self.collect_binance_oi(symbol),
self.collect_bybit_oi(symbol),
])
results = await asyncio.gather(*tasks, return_exceptions=True)
valid = [r for r in results if not isinstance(r, Exception)]
await self.db.bulk_insert(valid)
Rate limits и обход ограничений
Каждая биржа имеет rate limits на API. Binance fapi: 2400 weight/минуту, openInterest = 1 weight. Bybit: 600 req/5 сек. При большом числе символов (50+ пар) polling каждую минуту легко упирается в лимиты.
Стратегии:
Приоритизация символов. BTC и ETH — каждую минуту, топ-20 по объёму — каждые 5 минут, остальные — каждые 15-30 минут.
Параллельный сбор с rate limit guard:
from asyncio import Semaphore
class RateLimitedCollector:
def __init__(self, max_concurrent: int = 10):
self.semaphore = Semaphore(max_concurrent)
self.last_request_times = {} # exchange -> deque of timestamps
async def throttled_request(self, exchange: str, coro):
async with self.semaphore:
await self.enforce_rate_limit(exchange)
return await coro
IP ротация — если объём данных требует > 1 инстанса коллектора, каждый инстанс работает с отдельным IP. Используйте residential proxies или разные VPS для разных бирж.
Биржевые WebSocket для price feed — цену берём из WebSocket (высокая частота), OI — из REST по расписанию. Избегаем лишних REST-запросов для цен.
Нормализация данных
Разные биржи возвращают OI в разных единицах:
- Binance BTCUSDT perp — в BTC (число контрактов × 1 BTC за контракт)
- Bybit BTCUSDT — в USD (base currency × price)
- OKX BTC-USDT-SWAP — в контрактах (1 контракт = 0.01 BTC)
- CME Bitcoin Futures — в контрактах (1 контракт = 5 BTC)
Для сопоставимого агрегата всё приводим к USD-номиналу:
def normalize_to_usd(oi_value: Decimal, unit: str, btc_price: Decimal) -> Decimal:
match unit:
case "BTC":
return oi_value * btc_price
case "USD" | "USDT":
return oi_value
case "contracts_0.01BTC":
return oi_value * Decimal("0.01") * btc_price
case "contracts_5BTC": # CME
return oi_value * Decimal("5") * btc_price
case _:
raise ValueError(f"Unknown OI unit: {unit}")
Хранение и агрегация
TimescaleDB — оптимально для time-series OI данных:
CREATE TABLE open_interest (
time TIMESTAMPTZ NOT NULL,
exchange TEXT NOT NULL,
symbol TEXT NOT NULL,
oi_contracts NUMERIC(30, 8),
oi_usd NUMERIC(30, 2),
PRIMARY KEY (time, exchange, symbol)
);
SELECT create_hypertable('open_interest', 'time');
-- Continuous aggregate: агрегированный OI по всем биржам
CREATE MATERIALIZED VIEW oi_aggregate_5m
WITH (timescaledb.continuous) AS
SELECT
time_bucket('5 minutes', time) AS bucket,
symbol,
SUM(oi_usd) AS total_oi_usd,
jsonb_object_agg(exchange, oi_usd) AS by_exchange
FROM open_interest
GROUP BY bucket, symbol;
OI change alerts: практические сигналы
Резкое изменение OI — торговый сигнал. Стандартные пороги:
- OI вырос > 5% за 1 час — значительное открытие позиций
- OI упал > 10% за 1 час — ликвидации или массовое закрытие
-- Текущее изменение OI за последние 1 и 4 часа
SELECT
symbol,
total_oi_usd AS current_oi,
LAG(total_oi_usd, 12) OVER (PARTITION BY symbol ORDER BY bucket) AS oi_1h_ago,
(total_oi_usd - LAG(total_oi_usd, 12) OVER (PARTITION BY symbol ORDER BY bucket))
/ LAG(total_oi_usd, 12) OVER (PARTITION BY symbol ORDER BY bucket) * 100 AS change_1h_pct
FROM oi_aggregate_5m
WHERE bucket = (SELECT MAX(bucket) FROM oi_aggregate_5m)
ORDER BY ABS(change_1h_pct) DESC NULLS LAST;
Дополнительные метрики
OI в связке с другими данными даёт более полную картину:
Long/Short ratio — доступен на Binance (/futures/data/globalLongShortAccountRatio), Bybit. Показывает соотношение трейдеров в лонг/шорт.
Funding rate — стоимость удержания перпетуальной позиции. Высокий positive funding + высокий OI = перегретый лонг.
OI-weighted funding — средний funding по всем биржам взвешенный по их OI. Более точный агрегат чем простое среднее.
Разработка коллектора для 5-7 бирж с нормализацией, хранением и базовыми агрегатами — 1-2 недели. Полный analytics pipeline с алертами, API и дашбордом — 3-4 недели.







