Введение: что такое арбитраж по ставке финансирования

На крипто рынке у бессрочных фьючерсов существует специальный механизм: ставка финансирования (funding rate) - периодический платёж между держателями длинных (long) и коротких (short) позиций, который служит для выравнивания цены фьючерса с ценой спота.

Арбитраж по ставке финансирования - стратегия, цель которой не столько угадать движение цены, сколько извлечь выгоду из разницы в ставках финансирования на разных площадках или между контрактом и спотом.
Например: если фьючерс на актив торгуется с положительной ставкой +0.03 % за период, то держатели short получают оплату от long. Арбитражер может занять длинную позицию на споте и короткую на фьючерсе, тем самым оставаясь почти нейтральным к движению цены, и получать платёж по ставке. Или - если ставка отрицательная (short платят long) - можно действовать наоборот: short спот и long фьючерс.

Зачем это нужно и когда работает

  • Это стратегия с относительно низкой ориентированной на движение цены риском

  • Возможности возникают, когда ставка финансирования отклоняется от нуля или есть разница между площадками.

  • Нужно учитывать: сборы, проскальзывание, ликвидационный риск, требования маржи, переключения механизмов ставок.

  • Для начинающих важно понимать: это не «гарантированная» прибыль, а механизм, требующий мониторинга, автоматизации и расчёта.

Для автоматизации нахождения таких "расхождений" ставок финансирования я решил написать скрипт, который будет это делать за меня. Взял биржи: binance, bybit, mexc, okx. Торговля будет ручная, так как тут тоже важна насмотренность и анализ чарта - допустим, если монета улетела на сотни процентов, то лучше стоит ее просто пропустить.

Подробное описание скриптов и логики их работы

В этой части мы разберём внутреннюю архитектуру проекта — как устроены скрипты, какие API используются, как рассчитывается потенциальная прибыль и как всё это работает вместе. Проект состоит из менеджера скринеров и четырёх отдельных скринеров для бирж. Каждый скринер автономен, но следует единой логике.

1. main.py — Менеджер скринеров

Для начала создаем сердце проекта - main.py будет отвечать за координацию скриптов и логирование. Сразу импортируем будущие скрипты - MexcScreener.py и т.п. для удобства, чтобы не возвращаться сюда позже.

import logging 
from screeners.mexc_screener import MexcScreener
from screeners.bybit_screener import BybitScreener
from screeners.okx_screener import OkxScreener
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class ScreenerManager:
    def __init__(self):
        self.screeners = [MexcScreener(), BybitScreener(), OkxScreener(), BinanceScreener()]

    def run_screeners(self):
        all_results = []
        for screener in self.screeners:
            data = screener.run()
            data = data[:20]  # Берём только топ-20

            result = {
               'ex_name': screener.name,
               'coins': data
            }
            all_results.append(result)
        
        # Запись в файл
        with open('results.txt', 'w') as f:
            for exchange in all_results:
               f.write(f"============ Top {len(exchange['coins'])} coins in {exchange['ex_name']} ============\n")
               for coin in exchange['coins']:
                  f.write(f"{coin['ticker']}. Funding rate: {coin['funding_rate']} - Potential Profit: {coin['potential_profit']:.6%}\n")
        
        return all_results

Что делает:

  1. Инициализирует все скринеры.

  2. Запускает .run() у каждого.

  3. Обрезает результат до топ-20 по potential_profit.

  4. Формирует человекочитаемый отчёт в results.txt.

  5. Логирует процесс через logging.

2. Базовая логика всех скринеров (общее ядро)

Каждый скринер следует одинаковому алгоритму:

Шаг

Описание

1

Получить список всех перпетульных контрактов

2

Для каждого — получить текущую ставку финансирования

3

Рассчитать потенциальную прибыль = funding_rate − комиссии

4

Отсортировать по убыванию прибыли

5

Вернуть топ-N (в нашем случае — 20)

3. bybit_screener.py — Bybit

Ключевые эндпоинты для запросов к api:

Эндпоинт

Что делает

GET /v5/market/instruments-info?category=linear

Все линейные перпетульные контракты (USDT-маржа)

GET /v5/market/funding/history?symbol=BTCUSDT&limit=1

Последняя ставка финансирования

Базовый url для формирования запросов - "https://api.bybit.com"

Важно: Bybit не отдаёт все funding rates одним запросов, нужно запрашивать по символу.

Блок 1: Импорты и настройка логирования

import requests, time, decimal, logging
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime
from typing import Dict, Any, List
import re

Что делает: Подключает библиотеки для работы с API (requests), параллельного выполнения (ThreadPoolExecutor), точных вычислений с денежными величинами (decimal), работы с датами и логирования.

Блок 2: Класс BybitScreener и инициализация

class BybitScreener:
    BASE_URL = "https://api.bybit.com"
    FUNDING_RATE_ENDPOINT = "/v5/market/funding/history"
    INSTRUMENTS_INFO_ENDPOINT = "/v5/market/instruments-info"

    def __init__(self):
        self.name = 'Bybit'
        self.contracts = self.get_all_contracts()
        logger.info(f"Initialized with Bybit Screener{len(self.contracts)} contracts")

Что делает:

  • Определяет скринер для Bybit с базовым URL и необходимыми endpoint.

  • При создании объекта автоматически загружает список всех контрактов

  • Логирует количество доступных контрактов.

Блок 3: Получение списка контрактов

def get_all_contracts(self) -> List[Dict[str, Any]]:
        url = f"{self.BASE_URL}{self.EXCHANGE_INFO_ENDPOINT}"
        response = requests.get(url)
        response.raise_for_status()
        contracts = []
        for symbol in response.json()['symbols']:
            if symbol['contractType'] == 'PERPETUAL':
                contracts.append({
                    'symbol': symbol['symbol'],
                    'takerFeeRate': decimal.Decimal(symbol.get('takerFee', '0')),
                    'makerFeeRate': decimal.Decimal(symbol.get('makerFee', '0'))
                })
        return contracts

Что делает:

  • Отправляет запрос к API Bybit для получения всех линейных контрактов.

  • Исключает фьючерсы с датой экспирации (не подходят для арбитража).

  • Для каждого контракта сохраняет символ и комиссии (makerFeeRate и takerFeeRate).

  • Возвращает список контрактов для дальнейшего анализа.

Блок 4: Проверка экспирации контрактов

def is_future_dated_contract(self, symbol: str, current_date: datetime) -> bool:
      match = re.search(r'-(\d{2})([A-Z]{3})(\d{2})$', symbol)
      if match:
         day, month, year = match.groups()
         contract_date = datetime.strptime(f"20{year}-{month}-{day}", "%Y-%b-%d")
         return contract_date > current_date
      return False

Что делает:

  • Проверяет тикер контракта на наличие даты экспирации.

  • Использует регулярные выражения, чтобы понять, является ли контракт будущим фьючерсом.

  • Возвращает True, если контракт истечёт позже текущей даты.

Блок 5: Получение текущей funding rate

    def get_current_funding_rate(self, symbol: str) -> decimal.Decimal:
        url = f"{self.BASE_URL}{self.FUNDING_RATE_ENDPOINT}"
        params = {"symbol": symbol, "category": "linear", "limit": 1}
        try:
            response = requests.get(url, params=params)
            response.raise_for_status()
            funding_rate_data = response.json()['result']['list'][0]
            return self.format_funding_rate(funding_rate_data)
        except (requests.exceptions.HTTPError, IndexError, KeyError) as e:
            logger.warning(f"Error fetching funding rate for {symbol}: {e}")
            return decimal.Decimal('0')

Что делает:

  • Отправляет запрос к API Bybit, чтобы получить последнюю ставку финансирования.

  • Обрабатывает ошибки запроса и пустые данные.

Блок 6: Форматирование funding rate и расчёт прибыли

    def format_funding_rate(self, funding_rate_data):
        funding_rate = funding_rate_data.get('fundingRate', '0')
        return decimal.Decimal(funding_rate).quantize(decimal.Decimal('1E-6'))

    def calculate_potential_profit(self, funding_rate: decimal.Decimal, contract: Dict[str, Any]) -> decimal.Decimal:
        total_fee = contract['makerFeeRate'] * 2
        return funding_rate - total_fee

Что делает:

  • Преобразует строковое значение funding rate в Decimal.

  • Округляет до 6 знаков после запятой для удобства отображения и расчётов.

  • Учитывает комиссии биржи (двойную maker fee) при расчёте потенциальной прибыли.

  • Возвращает реальный доход, который можно получить после оплаты комиссий.

Блок 7: Анализ funding rates

    def analyze_funding_rates(self):
        funding_rates = []
        with ThreadPoolExecutor(max_workers=5) as executor:
            futures = {executor.submit(self.get_current_funding_rate, contract['symbol']): contract for contract in self.contracts}
            for future in as_completed(futures):
                contract = futures[future]
                try:
                    rate = future.result()
                    potential_profit = self.calculate_potential_profit(rate, contract)
                    funding_rates.append({
                        'ticker': contract['symbol'],
                        'funding_rate': rate,
                        'potential_profit': potential_profit
                    })
                except Exception as e:
                    logger.error(f"Error analyzing {contract['symbol']}: {e}")
        
        sorted_rates = sorted(funding_rates, key=lambda x: x['potential_profit'], reverse=True)
        return sorted_rates

Что делает:

  • Параллельно запрашивает funding rate для всех контрактов через ThreadPoolExecutor.

  • Вычисляет потенциальную прибыль для каждого контракта.

  • Сортирует контракты по potential_profit по убыванию.

  • Возвращает список самых прибыльных контрактов.

Блок 9: Запуск скринера

def run(self):
    logger.info("Starting BybitScreener")
    try:
        best_contracts = self.analyze_funding_rates()
        logger.info(f"BybitScreener completed. Found {len(best_contracts)} contracts.")
        return best_contracts
    except Exception as e:
        logger.error(f"An error occurred during BybitScreener execution: {e}")
        return []

Что делает:

  • Объединяет все функции: загружает контракты, анализирует их funding rate, сортирует по доходности.

  • Логирует успешное завершение или ошибки выполнения.

  • Возвращает готовый список контрактов с потенциальной прибылью.

Остальные скринеры

Все остальные скрипты следуют аналогичной логике - разве что отличаются эндпоинты и подобные ньюансы. Для того, чтобы не томить читателей кодом, все остальные скринеры загружены на github - https://github.com/kir1l/Funding-Arbitrage-Screener

ВыводПо итогу, запустив скрипт мы получим вывод с лучшими монетками на биржах, и уже там можно будет собирать фандинг.

Ключ к успешному арбитражу — хеджирование: открытие противоположной позиции минимизирует ценовой риск, а прибыль формируется только за счёт funding rate. При этом важно учитывать комиссии, проскальзывание и требования маржи.

Аарбитраж ставок финансирования позволяет извлекать доход с относительно низким риском, используя автоматизированные скринеры для мониторинга возможностей на разных биржах. Хотя и от вас будет зависить многое - важна скорость принятия решений и возможность находится у терминала, но всё же такой терминал сильно упростит работу по арбитражу ставок финансирования.

Комментарии (1)


  1. dyadyaSerezha
    10.11.2025 21:17

    Ещё одна статья о том "как заработать на крипте/трейдинге". Ещё одна статья без сути: "я вложил Х килобаксов и за N месяцев заработал в среднем M баксов в месяц со средним отклонением в O баксов и максимальным в P баксов.

    Где все эти данные? А если их нет или стыдно публиковать, зачем все эти "зарабатывающие" скрипты? Не понимаю.