На крипторынке есть редкий класс стратегий, где не нужно угадывать направление цены. Нет анализа свечей, индикаторов или прочей тяжелой математики.
Одна из таких стратегий — арбитраж ставок финансирования (funding rate arbitrage).
Этот материал — не обещание лёгких денег. Это разбор реальной рабочей системы, которую я сначала писал для себя, а позже обернул в Telegram-бота. Ниже я разберу:
как именно зарабатываются деньги на арбитраже ставок финансирования
почему ручной арбитраж не работает
как устроена архитектура алгоритмической системы
как код собирает данные, считает спреды и фильтрует мусор;
какие риски остаются и как их контролировать.
Автоматизированная торговая система может кратко упростить поиск грамотных связок между биржами и присылать уведомления. Я обернул эту систему в телеграм бота @Fandyng_Bot, логику которого мы сегодня и разберём — от получения данных с нескольких бирж, до обработки этой информации и практической значимости.
Funding rate — это периодический платёж между участниками рынка бессрочных фьючерсов. Он нужен, чтобы цена perpetual-контракта не отклонялась слишком сильно от спота.
Механика простая:
если funding положительный — платят держатели long-позиций;
если funding отрицательный — платят держатели short-позиций.
Ключевой момент: ставка финансирования отличается от биржи к бирже. Причём иногда — значительно.
Например, в один и тот же момент времени:
MEXC: -0.25%
Bitget: +0.0035%
Разница между ними — это и есть арбитражный спред, на котором мы можем заработать.
В классическом подходе трейдер выбирает направление и рискует капиталом, надеясь, что цена пойдёт в нужную сторону.
В арбитраже фандинга используется хедж-позиция:
long на одной бирже;
short на другой;
одинаковый объём;
В результате движение цены компенсируется, PNL по цене будет приблительно 0, а итоговый PNL позиции будет сформирован только фандингом.
Процесс выглядит так:
Найти актив, где funding на разных биржах сильно отличается.
Открыть long там, где funding отрицательный.
Открыть short там, где funding положительный.
Дождаться выплаты фандинга.
Закрыть обе позиции.
Прибыль = (Funding_short − Funding_long) − комиссии
Вот пример одной из связок, которая была найдена с помощью моей системы:
mexc
kucoinСуммарная прибыль составила 400$ и комиссионные съели около 112$ (учтено в PNL). Это пример хорошей удачной связки, которая отработала менее чем за несколько часов.
Чем больше объём позиции — тем больше абсолютный доход. Поэтому стратегия особенно интересна при крупных депозитах, где классический трейдинг становится сложным в психологии и управлении рисками.
На бумаге всё выглядит просто, но в реальности бирж много, на каждой из них по несколько сотен разных тикеров, значения могут меняться за минуты
Ручной поиск не масштабируется, требует full-time мониторинга и мы физически не можем контролировать всё сразу = получим упущенные возможности, фомо, трудности с эмоциями.
Именно здесь появляется необходимость в автоматизации.
Система решает одну задачу — быстро находить качественные арбитражные связки.
Логика следующая:
Собрать funding rate со всех бирж.
Привести данные к единому формату.
Отфильтровать неактуальные и шумовые значения.
Посчитать разницу между биржами.
Отдать трейдеру только те связки, где есть экономический смысл.
Вся архитектура построена вокруг этой последовательности.
Архитектура намеренно простая — без баз данных, брокеров сообщений и лишней магии.
Для каждой биржи существует отдельный скрипт, который обращается к API биржи, получает список активных контрактов (фьючерсы), запрашивает ставку финансирования, сохраняет результат в текстовый файл, чтобы потом работать с этими данными.
Примеры файлов:
<code>Binance.txt ByBit.txt KuCoin.txt Hyperliquid.txt ...</code>
Давайте разберём, как получать данные на некоторых биржах. Для примера возьмём kucoin и hyperliquid. Со второй ситуация довольно интересная, ведь это не классический CEX.
async def get_active_symbols():
symbols = []
async with aiohttp.ClientSession() as session:
async with session.get(active_contracts_url, timeout=10) as response:
data = await response.json()
for contract in data.get('data', []):
symbol = contract.get('symbol')
if symbol and symbol.endswith('M'):
symbols.append(symbol[:-1])
return symbolsЧто здесь происходит:
Используем асинхронные запросы — это важно, так как работаем с большим количеством инструментов. Также работаем только с USDT-M/USDC-M контрактами, т.е. с активами к стейблкоинам. Добавляем все символы в список и возвращаем их.
async def fetch_funding_rate(session, symbol):
url = funding_rate_url.replace('{symbol}', symbol + 'M')
async with session.get(url, timeout=10) as response:
data = await response.json()
funding_rate = float(data['data']['value']) * 100
return funding_rateЗдесь мы можем получить фандинг. Для удобства переводим его в проценты. Получаем сам фандинг мы, извлекая его из массива данных с помощью ключей ['data']['value']. Делаем всё также асинхронно, обрабатываем таймауты и не забываем передать в функцию правильно сам символ токена.
Hyperliquid — это децентрализованная биржа perpetual-фьючерсов на собственной L1-цепочке. В отличие от классических CEX вроде Binance или KuCoin, здесь нет привычного REST-API для получения списка контрактов и funding rates. Вместо этого используется единый эндпоинт /info, куда отправляются POST-запросы с JSON-payload'ом, указывающим тип запроса.
В системе используется специальная функция для отправки запросов:
def _post_info(payload: dict, timeout: int = 10) -> Any:
data = json.dumps(payload).encode("utf-8")
req = Request(
HL_INFO_API,
data=data,
method="POST",
headers={
"Content-Type": "application/json",
"User-Agent": "curl/8",
},
)
with urlopen(req, timeout=timeout) as resp:
raw = resp.read().decode("utf-8")
try:
return json.loads(raw)
except Exception:
return rawДалее идёт запрос мета-данных и контекста активов:
def fetch_meta_and_asset_ctxs() -> Tuple[Dict[str, Any], List[Dict[str, Any]]]:
resp = _post_info({"type": "metaAndAssetCtxs"})
if not (isinstance(resp, list) and len(resp) >= 2):
raise RuntimeError("Неверный ответ metaAndAssetCtxs: ожидался список из 2 элементов")
meta_part = resp[0]
ctxs_part = resp[1]
if not (isinstance(meta_part, dict) and isinstance(ctxs_part, list)):
raise RuntimeError("Неверный формат metaAndAssetCtxs: meta=dict, ctxs=list")
ctxs: List[Dict[str, Any]] = [c for c in ctxs_part if isinstance(c, dict)]
return meta_part, ctxsОтвет содержит два элемента: мета-информацию (список всех активов с их именами, szDecimals и т.д.) и массив asset contexts. Funding rate для каждого актива находится в поле funding внутри соответствующего asset context (это значение в decimal, обычно очень маленькое, поэтому его умножаем на 100 для перевода в проценты). Также там есть previousFundingTimestamp и интервал (обычно 1 час), по которым рассчитывается время до следующей выплаты.
Такой подход позволяет одним запросом получить данные по всем активам сразу — это эффективно и быстро.
После того как все скрипты бирж отработали и заполнили свои текстовые файлы (Binance.txt, ByBit.txt, KuCoin.txt и т.д.), запускается центральный скрипт, который собирает всё воедино и считает спреды.
Ключевые шаги скрипта:
Чтение файлов — функция read_funding_file парсит строки вида BTC: 0.01% | Time: 12:00:00 для каждой биржи.
Объединение данных — все записи по одному символу (например, BTCUSDT, ETHUSDT) собираются в один словарь.
Приведение символов к единому виду — функция get_base_symbol отрезает USDT/USDC, чтобы связать, например, BTCUSDT с BTCUSDC или с BTC/USDT на разных биржах (это важно, потому что один и тот же актив может называться по-разному).
Фильтрация по времени — учитывается только актуальный funding, соответствующий ближайшему часу выплаты (целевой час в UTC+3). Это отсекает устаревшие или несовпадающие по времени данные.
Поиск экстремальных значений — для каждого актива находим максимально положительный и максимально отрицательный funding (в пределах разумных границ, например ±0.002%).
Расчёт всех значимых спредов — сортируем отрицательные и положительные rates, перебираем пары и сохраняем только те, где разница ≥ порога (по умолчанию 0.01%).
Запись в Raznitsa.txt — итоговый отчёт с удобным форматированием: символ, long-биржа (отрицательный funding), short-биржа (положительный), спред и время до выплаты.
Давайте разберём код — сердце скрипта, который отвечает именно за расчёты и поиск спредов, для начала зададим константы фильтров и основные имена файлов для хранения фандинга
# Константы фильтров
EXT_MIN_NEG = -0.002 # Считаем «отрицательным» только ≤ -0.002% (отсекаем шум)
EXT_MAX_POS = 0.002 # Считаем «положительным» только ≥ +0.002%
SP_THRESHOLD = 0.01 # Минимальный спред для раздела «Все спреды» (можно менять)
FILENAMES = [
"Binance.txt", "Bitget.txt", "ByBit.txt",
"CoinEx.txt", "KuCoin.txt", "Gate.txt",
"Hyperliquid.txt", "BingX.txt", "MEXC.txt", "OKX.txt"
]
OUTPUT_FILE = "Raznitsa.txt"Самая частая проблема — разные биржи называют один и тот же контракт по-разному:
BTCUSDT на Binance
BTCUSDT на ByBit
BTC:USDT на Hyperliquid
BTCUSD на некоторых DEX
Функция get_base_symbol отрезает всё после USDT/USDC/USD и приводит к единому виду:
<code>def get_base_symbol(sym: str) -> str:
sym = sym.strip().upper()
suffixes = ("USDT", "USDC", "BUSD", "TUSD", "USD")
for s in suffixes:
if sym.endswith(s):
return sym[:-len(s)]
return sym</code>Благодаря этому BTCUSDT, BTCUSD и 100BTCUSDT попадают в одну группу.
Не все биржи платят фандинг в одно и то же время. Большинство — каждые 8 часов (00:00, 08:00, 16:00 UTC), но есть и ежечасные (Hyperliquid, ByBit Inverse и др.).
Скрипт определяет ближайший «целевой час» в UTC+3 (Москва) и оставляет только те ставки, у которых время выплаты совпадает с этим часом.
def get_target_hour_str() -> str:
now_utc = datetime.now(timezone.utc)
tz3 = timezone(timedelta(hours=3))
now_tz3 = now_utc.astimezone(tz3)
# Округляем до следующего целого часа
if now_tz3.minute != 0 or now_tz3.second != 0:
target_hour = (now_tz3.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1)).hour
else:
target_hour = now_tz3.hour
return f"{target_hour:02d}:00:00"Это критически важно: если у вас на одной бирже фандинг через 5 минут, а на другой — через 7 часов, спред будет ложный.
Для каждого актива скрипт сначала ищет просто максимальный положительн
def filter_extremes(entries, min_neg, max_pos):
max_ent = None # самая положительная ставка
min_ent = None # самая отрицательная ставка
for exch, rate, raw, t in entries:
if rate >= max_pos and (max_ent is None or rate > max_ent[1]):
max_ent = (exch, rate, raw, t)
if rate <= min_neg and (min_ent is None or rate < min_ent[1]):
min_ent = (exch, rate, raw, t)
return max_ent, min_entый и минимальный отрицательный funding (в разумных пределах ±0.002%). Это самые очевидные и обычно самые прибыльные связки.
Если экстремальных нет, но есть несколько бирж с отрицательным и несколько с положительным — скрипт перебирает все возможные пары и сохраняет те, где разница ≥ 0.01%.
def find_spreads_extended(entries, threshold):
results = []
negs = sorted([e for e in entries if e[2] < 0], key=lambda x: x[2]) # от самого отрицательного
poss = sorted([e for e in entries if e[2] > 0], key=lambda x: x[2]) # от самого маленького положительного
for group in (negs, poss):
for i in range(len(group)):
le = group[i]
for j in range(i+1, len(group)):
se = group[j]
diff = se[2] - le[2]
if diff >= threshold:
results.append({ ... })
return resultsЭто даёт больше возможностей, особенно когда на рынке мало экстремальных расхождений.
Файл Raznitsa.txt выглядит так:
# Отчёт обновлён: 2026-01-06 12:34:56 (целевой час UTC+3: 15:00:00) ## Экстремальные funding (min/max) | BTCUSDT | 📈 Long 🟢 MEXC: -0.124500% | 🕘15:00:00 📉 Short 🔴 ByBit: +0.003100% | 🕘15:00:00 Spread: 0.127600% ## Все спреды (diff ≥ 0.010000%) | ETHUSDT | 📈 Long 🟢 BingX: -0.045612% | 🕘— 📉 Short 🔴 OKX: +0.012345% | 🕘15:00:00 Spread: 0.057957%
Бот читает именно этот файл — никаких баз данных не нужно.
if __name__ == "__main__":
save_combined() # первый запуск сразу
while True:
time.sleep(60) # каждуб минуту
save_combined()Скрипт никогда не падает: если у одной биржи файл пустой или битый — просто пропустит её, остальные продолжат работать.
Эту часть можно легко модифицировать под себя:
изменить SP_THRESHOLD на 0.03%, если хотите только жирные связки;
добавить свои биржи в FILENAMES;
поменять пороги EXT_MIN_NEG/EXT_MAX_POS, если торгуете альткоины с дикими ставками.
Всё вместе даёт полностью автономную систему, которая 24/7 ищет деньги там, где 99% трейдеров даже не знают, что они есть.
Чтобы архитектура бота не была жёстко привязана к одной площадке, на практике важно понимать, какие инструменты доступны для работы с другими централизованными биржами. Ниже — краткий обзор наиболее популярных CEX и их API-стека, с которыми можно реализовать аналогичную логику мониторинга фандинга и открытия хеджированных позиций.
Тип API: REST + WebSocket
Фандинг: доступен через отдельные эндпоинты, включая историю и будущие ставки
Инструменты: python-binance, ccxt
Особенности: высокая ликвидность, быстрые WebSocket-потоки, но строгие лимиты и агрессивный rate-limit
Тип API: REST + WebSocket
Фандинг: удобный доступ к текущему и ожидаемому funding rate
Инструменты: pybit, ccxt
Особенности: хорошая документация, стабильная работа с деривативами, популярна для funding-стратегий
Тип API: REST + WebSocket
Фандинг: отдельные методы для perpetual-свопов
Инструменты: официальный SDK, ccxt
Особенности: сложнее модель аккаунта, но широкий выбор инструментов и пар
Тип API: REST
Фандинг: доступен, но с меньшей детализацией
Инструменты: неофициальные Python-клиенты, прямые REST-запросы
Особенности: проще инфраструктура, подходит для мониторинга и вспомогательных стратегий
Во всех случаях общая логика остаётся одинаковой: получение funding rate, агрегация данных, фильтрация по порогам и принятие торгового решения. Разница заключается в формате данных, лимитах и надёжности инфраструктуры, что важно учитывать при масштабировании стратегии.
Финальный этап — Telegram-бот (@Fandyng_Bot). Он читает Raznitsa.txt, хранит настройки пользователей (порог спреда, автоматические уведомления) в отдельном файле.
Возможности:
Установка личного порога (например, уведомлять только о спредах ≥ 0.05%).
Ручной запрос текущих связок.
Автоматические push-уведомления, как только появляется подходящая возможность.
Мониторинг подписки (чтобы бот работал только для платных пользователей).
Это решает главную проблему ручного арбитража — скорость. Вы получаете алерт за минуты до выплаты, открываете хедж-позиции и фиксируете прибыль.
Даже в хеджированной позиции риски остаются:
Изменение funding → до выплаты ставка может резко поменяться. Решение: входить за 5–10 минут до funding time.
Ликвидация → сильное движение цены может ликвидировать одну из сторон при высоком плече. Решение: использовать низкое плечо (1x–3x) или cross-маржу.
Комиссии и проскальзывание → съедают спред, особенно на низколиквидных активах. Решение: считать net profit с учётом taker/maker fees и открывать позиции частями.
Разные времена выплат → не все биржи платят в 00:00/08:00/16:00 UTC. Код фильтрует по совпадению времени.
В целом, при дисциплине и достаточном капитале (от $10k+) это одна из самых стабильных стратегий на крипто-рынке без directional риска.
Если вы хотите попробовать — начните с бота @Fandyng_Bot, протестируйте на небольших объёмах и убедитесь, что понимаете все нюансы. В боте хотя и есть подписка, но она символичная, чтобы не перегружать системы и не падать от ботов. Для абсолютно всех есть тестовый период, так что этот проект даже тяжело назвать коммерческим. Удачных связок!