Фьючерсы против спота: заработок на ставке финансирования
Урок 14 курса по арбитражу. Что такое бессрочный фьючерс и его ставка финансирования, как держать монету на спотовом рынке и противоположную позицию во фьючерсе, и как комиссии, отрицательная ставка и ликвидация влияют на результат. Короткий код на Python с числами, которые можно проверить вручную.
Урок 14 курса "Собери своего арбитражного бота". До сих пор бот искал разрывы, которые закрываются за секунды. Сегодня у него появится вторая стратегия: она зарабатывает медленно и стабильно, но несет другой вид риска.
Два способа владеть одной монетой
Монету можно купить на спотовом рынке: вы платите деньги, получаете монету, она ваша.
Можно также торговать фьючерсом на эту монету. Фьючерс является контрактом, цена которого следует за монетой. Саму монету вы не получаете. Вы получаете позицию: если вы в лонге и цена растет на 10, вы зарабатываете 10 на монету, а если падает на 10, теряете 10. Если вы в шорте, все наоборот. Для фьючерса нужна только часть денег в виде залога, его называют маржой, поэтому в нем есть кредитное плечо.
Наиболее распространенные биржи предлагают бессрочные фьючерсы: контракты без даты истечения. Это создает проблему. Срочный фьючерс в последний день принудительно возвращается к спотовой цене. Бессрочный фьючерс никогда не заканчивается, так что же удерживает его цену около спотовой?
Ставка финансирования
Ответом является платеж между трейдерами, который называют ставкой финансирования. Каждые 8 часов, на многих биржах, одна сторона платит другой.
- Если бессрочный фьючерс торгуется выше спота, ставка положительна, лонги платят шортам.
- Если он торгуется ниже спота, ставка отрицательна, шорты платят лонгам.
Платеж равен ставке, умноженной на размер позиции. Когда лонг слишком популярен, его становится дорого держать, часть трейдеров закрывает позиции, и цена возвращается к споту. В этом весь механизм.
Идея стратегии
Держите обе стороны одновременно: купите 1 монету на спотовом рынке и откройте шорт на 1 монету в бессрочном фьючерсе. Теперь цена монеты не важна. Если она растет на 10, монета приносит 10, а шорт теряет 10. Если падает, происходит обратное. Суммарный результат не меняется, трейдеры называют это дельта-нейтральной позицией.
Остается финансирование. Когда ставка положительна, вы держите шорт и получаете платеж каждые 8 часов. Вам платят за удержание позиции без ценового риска. Это называют кэш-энд-керри или арбитражем финансирования.
Прямой подход: посмотреть на годовое число
Предположим, ставка равна 0.01 процента за 8 часов. Это 3 платежа в день, поэтому 3 × 365 × 0.01 процента = 10.95 процента в год. Прямой вывод: вложить 100 и получать 10.95 в год без риска, значит, надо вложить все деньги.
Проверьте это циклом, который учитывает больше. Следующий код открывает пару по цене 100, проходит через 90 платежей финансирования, 30 дней, и добавляет комиссии. Комиссии такие: сделка на споте стоит 0.1 процента, сделка с бессрочным фьючерсом стоит 0.05 процента, обе комиссии платятся при открытии и при закрытии.
SPOT_FEE, PERP_FEE = 0.001, 0.0005 # taker fees, per trade
price = 100.0
# funding rate per 8 hours, 3 payments a day, 30 days = 90 payments
rates = [0.0001] * 45 + [0.00005] * 20 + [-0.0001] * 15 + [0.0001] * 10
def carry(rates, exit_when_negative=False):
cost_in = price * SPOT_FEE + price * PERP_FEE # buy spot, open short
cost_out = price * SPOT_FEE + price * PERP_FEE # sell spot, close short
income = 0.0
for r in rates:
if exit_when_negative and r < 0:
break
income += price * r # the short side receives r * notional
return income - cost_in - cost_out, income
for name, flag in [("hold all 30 days", False), ("leave at first negative", True)]:
net, gross = carry(rates, flag)
print(f"{name:24} funding {gross:6.3f} fees {2*(price*SPOT_FEE+price*PERP_FEE):.3f} net {net:+.3f}")
fees = 2 * (price * SPOT_FEE + price * PERP_FEE)
print("payments to cover fees", round(fees / (price * 0.0001)), "=", round(fees / (price * 0.0001) / 3, 1), "days")
Результат:
hold all 30 days funding 0.500 fees 0.300 net +0.200
leave at first negative funding 0.550 fees 0.300 net +0.250
payments to cover fees 30 = 10.0 days
Разберем его.
- Ставки в тесте не постоянны: 45 платежей по 0.01 процента, затем 20 вдвое меньших, затем 15 отрицательных платежей, затем 10 снова по 0.01 процента. Это похоже на настоящий рынок, где ставка меняется вместе с настроением трейдеров.
- Удержание все 30 дней: финансирование 0.45 + 0.10 − 0.15 + 0.10 = 0.50. Комиссии составляют 0.30, 0.15 при открытии и 0.15 при закрытии. Чистый результат равен +0.20 на 100, то есть 0.2 процента за месяц, около 2.4 процента в год, а не 10.95 процента из заголовка.
- Выход сразу после того, как ставка станет отрицательной, приносит на 0.05 больше: 15 отрицательных платежей стоят 0.15, последние 10 хороших возвращают 0.10.
- Только для безубыточности при полной ставке нужны 30 платежей, 10 дней. Трейдер, который открывает пару на 3 дня при ставке 0.01 процента, теряет деньги.
Итак, 10.95 процента является потолком, который существует, только если ставка остается 0.01 процента и вы долго держите позицию. Цикл показывает, почему первая идея слишком оптимистична: ставка меняет знак, а комиссии платятся с обеих сторон.
Что бот проверяет перед открытием пары
- Текущая и ожидаемая ставка. Среднее за последние дни, а не только последнее значение.
- Комиссии с обеих сторон. Учтите их в сроке безубыточности и сравните с тем, как долго ставка была стабильной.
- Какая биржа платит. У одной и той же монеты ставка различается на разных биржах. Бот может использовать спотовый рынок одной биржи и бессрочный фьючерс другой, это также добавляет стоимость хранения денег в двух местах.
- Правило выхода. Например, "выйти, когда средняя ставка станет отрицательной". Его нужно записать и проверить, как в цикле выше.
Настоящая опасность: шорт открыт на марже
Спотовая монета и шорт находятся на разных счетах. Шорт является позицией с плечом, и биржа принудительно закроет его, ликвидирует, когда убыток съест маржу. Грубая формула без учета небольшой поддерживающей маржи такова: шорт с плечом L ликвидируется, когда цена вырастет примерно на 100 / L процентов:
for L in (2, 5, 10):
print(f"leverage {L:>2}: perp short liquidated near price {price*(1+1/L):6.1f} (+{100/L:.0f}%)")
leverage 2: perp short liquidated near price 150.0 (+50%)
leverage 5: perp short liquidated near price 120.0 (+20%)
leverage 10: perp short liquidated near price 110.0 (+10%)
Теперь картина меняется. Если цена за день вырастет на 20 процентов при плече 5, монета в портфеле принесет 20, но биржа уже закрыла шорт с убытком 20, и у вас остается открытая позиция в монете с ценовым риском, которого вы не планировали. На бумаге общий результат был нулевым, принудительное закрытие его нарушило. Решение скучное, но работает: используйте небольшое плечо, от 1 до 2, держите резерв денег, который бот сможет перевести на маржинальный счет при росте цены, и позвольте лимиту риска из урока 12 остановить все при низком уровне маржи.
Попросите ИИ-агента написать и проверить код
Расплывчатый запрос:
Создай арбитражного бота для ставки финансирования.
Запрос со встроенными проверками:
Напиши carry.py с функцией carry(rates, price, spot_fee, perp_fee, exit_when_negative), которая возвращает (net, gross). При price 100, комиссиях 0.001 и 0.0005 и списке ставок из урока 14, 45 раз 0.0001, 20 раз 0.00005, 15 раз -0.0001, 10 раз 0.0001, функция должна вернуть net 0.2 при удержании всего периода и net 0.25 с exit_when_negative. Добавь break_even_payments(rate, price, spot_fee, perp_fee), она должна вернуть 30 при rate 0.0001. Добавь liquidation_price(entry, leverage) = entry * (1 + 1 / leverage). Никаких сетевых вызовов, никаких реальных ордеров.
Проверьте три числа вручную до того, как доверять коду.
Ваше упражнение
- Запустите код и сравните каждое напечатанное число с уроком.
- Измените комиссию спота на 0.0002 и комиссию бессрочного фьючерса на 0.0002, это скидка для трейдера. Сколько дней полной ставки теперь нужно для безубыточности? Сначала посчитайте вручную: комиссии равны 2 × (0.02 + 0.02) = 0.08.
- Измените список ставок так, чтобы отрицательная часть длилась 40 платежей. Что теперь лучше: удерживать или выйти при первом отрицательном платеже? На сколько?
- При плече 5 цена за один день растет на 20 процентов. Напишите на бумаге, что происходит со спотовой стороной и шортом, и что бот должен был бы сделать до движения, чтобы остаться в безопасности.
Что дальше
Теперь у бота есть две стратегии: быстрый разрыв между биржами и медленный доход от финансирования. Урок 15 даст ему третью, на выбор: торговлю парой монет, которые движутся вместе, статистический арбитраж, или фильтр машинного обучения для уже имеющихся сигналов.