Расчет комиссий мейкера/тейкера (Fee Tiers) в CCXT Python

Расчет комиссий мейкера/тейкера (Fee Tiers) в CCXT Python Python & API
Подробное руководство по расчету комиссий мейкера/тейкера (Fee Tiers) перед отправкой ордера через ccxt на Python, включая примеры кода и лучшие практики для различных бирж.
Суть: Для расчета комиссии мейкера/тейкера перед отправкой ордера через ccxt на Python необходимо использовать методы exchange.fetch_trading_fees() (для комиссий по символам) или exchange.fetch_fees() (для глобальных комиссий). Полученные данные содержат текущие ставки maker и taker, которые зависят от вашего торгового объема или баланса на бирже. Важно учитывать, что структура ответа может отличаться для разных бирж.

Исходный код

Точный расчет комиссий мейкера и тейкера является критически важным аспектом в алготрейдинге, особенно при разработке стратегий, таких как треугольный арбитраж на OKX, где даже небольшие расхождения могут привести к убыткам. Биржи часто используют многоуровневую систему комиссий (Fee Tiers), которая зависит от вашего торгового объема за определенный период или от размера вашего баланса в нативных токенах биржи. ccxt предоставляет удобные методы для получения этой информации.

Ниже представлен Python-скрипт, который демонстрирует, как получить текущие комиссии мейкера и тейкера для заданной биржи и торгового символа. Скрипт пытается сначала использовать fetch_trading_fees() для получения комиссий по конкретному символу, а затем, в случае неудачи или отсутствия символа, переключается на fetch_fees() для получения глобальных комиссий аккаунта.


import ccxt
import os

def get_current_fees(exchange_id: str, symbol: str = None) -> dict:
    """
    Получает текущие комиссии мейкера и тейкера для указанной биржи и символа.

    Args:
        exchange_id (str): ID биржи (например, 'binance', 'okx').
        symbol (str, optional): Торговый символ (например, 'BTC/USDT').
                                 Если None, попытается получить глобальные комиссии.

    Returns:
        dict: Словарь с комиссиями 'maker' и 'taker', или пустой словарь в случае ошибки.
    """
    try:
        exchange_class = getattr(ccxt, exchange_id)
        exchange = exchange_class({
            'apiKey': os.environ.get(f'{exchange_id.upper()}_API_KEY'),
            'secret': os.environ.get(f'{exchange_id.upper()}_SECRET'),
            'password': os.environ.get(f'{exchange_id.upper()}_PASSWORD', ''), # Для OKX и некоторых других
            'enableRateLimit': True,
            'options': {
                'defaultType': 'spot', # Или 'future', 'margin' в зависимости от нужд
            }
        })

        # Загружаем рынки, это часто необходимо для корректной работы с символами
        exchange.load_markets()

        maker_fee = None
        taker_fee = None

        # 1. Попытка получить комиссии по символу (предпочтительный метод)
        if symbol and exchange.has['fetchTradingFees']:
            try:
                trading_fees = exchange.fetch_trading_fees()
                if symbol in trading_fees:
                    maker_fee = trading_fees[symbol]['maker']
                    taker_fee = trading_fees[symbol]['taker']
                    print(f"Комиссии для {symbol} (через fetch_trading_fees): Maker={maker_fee}, Taker={taker_fee}")
                    return {'maker': maker_fee, 'taker': taker_fee}
                else:
                    print(f"Символ {symbol} не найден в fetch_trading_fees. Попытка получить глобальные.")
            except Exception as e:
                print(f"Ошибка при получении fetch_trading_fees для {symbol}: {e}. Попытка получить глобальные.")

        # 2. Попытка получить глобальные комиссии
        if exchange.has['fetchFees']:
            try:
                global_fees = exchange.fetch_fees()
                # Структура global_fees может сильно отличаться.
                # Обычно ищем 'trading' -> 'maker'/'taker' или 'info'
                if 'trading' in global_fees and 'maker' in global_fees['trading'] and 'taker' in global_fees['trading']:
                    maker_fee = global_fees['trading']['maker']
                    taker_fee = global_fees['trading']['taker']
                    print(f"Глобальные комиссии (через fetch_fees): Maker={maker_fee}, Taker={taker_fee}")
                    return {'maker': maker_fee, 'taker': taker_fee}
                elif 'info' in global_fees:
                    # Некоторые биржи возвращают сырые данные в 'info',
                    # которые нужно парсить вручную. Это сильно зависит от биржи.
                    # Пример для OKX: комиссии могут быть в 'info' -> 'data' -> [0] -> 'makerFeeRate'
                    if exchange_id == 'okx' and 'data' in global_fees['info'] and len(global_fees['info']['data']) > 0:
                        data = global_fees['info']['data'][0]
                        maker_fee = float(data.get('makerFeeRate', 0))
                        taker_fee = float(data.get('takerFeeRate', 0))
                        print(f"Глобальные комиссии OKX (через fetch_fees['info']): Maker={maker_fee}, Taker={taker_fee}")
                        return {'maker': maker_fee, 'taker': taker_fee}
                else:
                    print(f"Не удалось найти maker/taker комиссии в fetch_fees для {exchange_id}.")
            except Exception as e:
                print(f"Ошибка при получении fetch_fees для {exchange_id}: {e}")

        if maker_fee is None or taker_fee is None:
            print(f"Не удалось получить комиссии для {exchange_id} (символ: {symbol}).")
            print("Пожалуйста, проверьте документацию биржи и структуру ответа CCXT.")
            print("Возможно, вам потребуется вручную парсить 'info' поле или использовать другой метод.")

        return {'maker': maker_fee, 'taker': taker_fee}

    except AttributeError:
        print(f"Биржа с ID '{exchange_id}' не найдена в CCXT.")
        return {}
    except Exception as e:
        print(f"Произошла непредвиденная ошибка: {e}")
        return {}

if __name__ == "__main__":
    # Установите свои API ключи как переменные окружения
    # Например, в терминале:
    # export BINANCE_API_KEY='YOUR_BINANCE_API_KEY'
    # export BINANCE_SECRET='YOUR_BINANCE_SECRET'
    # export OKX_API_KEY='YOUR_OKX_API_KEY'
    # export OKX_SECRET='YOUR_OKX_SECRET'
    # export OKX_PASSWORD='YOUR_OKX_PASSWORD' # Для OKX требуется пароль

    print("--- Тест для Binance ---")
    binance_fees = get_current_fees('binance', 'BTC/USDT')
    if binance_fees and binance_fees['maker'] is not None:
        print(f"Итоговые комиссии Binance для BTC/USDT: Maker={binance_fees['maker']}, Taker={binance_fees['taker']}")
    else:
        print("Не удалось получить комиссии для Binance.")

    print("\n--- Тест для OKX ---")
    # OKX часто возвращает глобальные комиссии через fetch_fees, даже если запрошен символ.
    # Для OKX также важно указать 'password' в настройках биржи.
    okx_fees = get_current_fees('okx', 'ETH/USDT')
    if okx_fees and okx_fees['maker'] is not None:
        print(f"Итоговые комиссии OKX для ETH/USDT: Maker={okx_fees['maker']}, Taker={okx_fees['taker']}")
    else:
        print("Не удалось получить комиссии для OKX.")

    print("\n--- Тест для несуществующей биржи ---")
    non_existent_fees = get_current_fees('nonexistentexchange', 'BTC/USDT')
    if not non_existent_fees:
        print("Корректно обработана несуществующая биржа.")

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

  • exchange_id: Строковый идентификатор биржи, поддерживаемой ccxt (например, 'binance', 'okx', 'bybit'). Используется для динамического создания экземпляра класса биржи.
  • symbol: Строковый идентификатор торговой пары (например, 'BTC/USDT', 'ETH/USDT'). Этот параметр является необязательным. Если он указан, скрипт сначала попытается получить комиссии, специфичные для этой пары. Если не указан, или если специфичные комиссии недоступны, скрипт попытается получить глобальные комиссии аккаунта.
  • exchange.has['fetchTradingFees']: Флаг, указывающий, поддерживает ли биржа метод fetch_trading_fees(), который обычно возвращает комиссии для каждой торговой пары. Это предпочтительный метод, так как комиссии могут различаться для разных инструментов.
  • exchange.has['fetchFees']: Флаг, указывающий, поддерживает ли биржа метод fetch_fees(), который обычно возвращает глобальные комиссии аккаунта. Структура данных, возвращаемых этим методом, может сильно варьироваться между биржами, особенно поле 'info', которое содержит сырые данные от API биржи. В некоторых случаях, как при создании кастомного адаптера CCXT.Pro, может потребоваться ручной парсинг этих данных.
  • maker: Комиссия, взимаемая за лимитные ордера, которые добавляют ликвидность в стакан ордеров. Обычно она ниже комиссии тейкера или даже может быть отрицательной (rebate).
  • taker: Комиссия, взимаемая за рыночные ордера или лимитные ордера, которые немедленно исполняются, забирая ликвидность из стакана ордеров.
  • Переменные окружения: Для безопасного хранения API-ключей и секретов рекомендуется использовать переменные окружения (os.environ.get()). Это предотвращает жесткое кодирование конфиденциальной информации в коде.

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

Для запуска скрипта выполните следующие шаги:

  1. Установите ccxt: Если у вас еще не установлен ccxt, установите его с помощью pip:

    
    pip install ccxt
    
  2. Настройте API-ключи: Экспортируйте свои API-ключи и секреты как переменные окружения. Замените YOUR_BINANCE_API_KEY, YOUR_BINANCE_SECRET и т.д. на ваши реальные ключи. Для OKX также потребуется PASSWORD (торговый пароль).

    
    export BINANCE_API_KEY='YOUR_BINANCE_API_KEY'
    export BINANCE_SECRET='YOUR_BINANCE_SECRET'
    export OKX_API_KEY='YOUR_OKX_API_KEY'
    export OKX_SECRET='YOUR_OKX_SECRET'
    export OKX_PASSWORD='YOUR_OKX_PASSWORD'
    # Для других бирж аналогично: EXCHANGENAME_API_KEY, EXCHANGENAME_SECRET
    

    В Windows вы можете использовать set вместо export или настроить переменные окружения через системные настройки.

  3. Сохраните скрипт: Сохраните предоставленный Python-код в файл, например, get_fees.py.

  4. Запустите скрипт: Откройте терминал или командную строку, перейдите в директорию, где вы сохранили файл, и выполните:

    
    python get_fees.py
    

Скрипт выведет текущие комиссии для Binance и OKX (если ключи настроены) и продемонстрирует обработку ошибок для несуществующей биржи. Помните, что для оптимизации производительности и снижения нагрузки на API, особенно при работе с большим объемом данных, можно рассмотреть интеграцию Redis для кэширования часто запрашиваемых данных, таких как комиссии, если они не меняются слишком часто.

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