Внутренний арбитраж - часть 2. Охота на сусликов

Андрей К…
Последнее изменение:
18
0
0

В первой части статьи мы проверяли на живучесть легенду о том, что внутренний арбитраж способен работать и приносить плоды. Скрипт, который был написан на питоне, проверял текущую обстановку и моделировал цепочки купли-продажи – в теории прибыль была. На практике же бот не успевал проворачивать сделки, т.к. получал и просчитывал информацию с опозданием, а также много времени уходило на выставление ордеров и проверку их состояния.

Немного поразмыслив, я решил кардинально изменить код, да и подход в целом, ну и в итоге написал рабочую программу, которая реально проворачивает прибыльные сделки. Я поднял несколько миллионов и сейчас выбираю яхту в Сиднее. Оказалось, не так всё радужно.

Изменения с точки зрения программирования

В процессе подготовки прошлой статьи стало ясно, что это одна из тех задач, с которой питону справиться сложновато – тут нужна большая скорость просчета и получения данных, и более-менее шустрая многопоточность. Это нормальный цикл производства, с моей точки зрения –сначала ты пишешь быстро, просто и весело на Питоне, проседаешь в скорости и производительности, но проверяешь работу алгоритма и ценность идеи вообще, добавляешь вские фишки и ходишь по всевозможным граблям, о которых раньше и подумать не мог, а потом, когда и если ты упираешься в производительность языка (обычно до этого не доходит, т.к. большинство идей сами по себе отстойные), переводишь свои наработки на что-то, похожее на C++. В данном случае чем-то похожим на C++ стал Golang.

Мне давно хотелось опробовать этот язык в бою, т.к. у него есть некоторые киллер-фичи:

  1. Скорость разработки на нем довольно высока
  2. Умеет хорошо параллелиться
  3. Компилируемый
  4. Встроенные средства для работы с вебом, разбора JSON, шифрования и т.п.
  5.  На нем круто делать всякие микросервисы
  6. Он модный
  7. Он кроссплатформенный (можно писать код на винде а компилировать под линуксом и наоборот)
  8. Самый главный пункт – мне было любопытно

Забегая вперед – если вы хотите писать софт на заказ, то можете присмотреться к этому языку – на выходе вы будете отдавать клиенту единый *.exe файл, который не раскрывает деталей реализации кода, алгоритмов и не требует установки дополнительного ПО.

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

  1. На старте для каждой пары выделяем память, в этой памяти держим структуру данных: Название пары, лучшую цену и объем ask, лучшую цену и объем bid, стакан ask, стакан bid и прочую служебную информацию (об этом ниже).
  2. Нет смысла на лету искать цепочки торговли. На старте программы нужно получать список всех пар, строить цепочки и работать в дальнейшем с ними. Тут может возникнуть довольно маловероятный риск, что какие-то пары будут запрещены во время работы бота, поэтому нужно этот список время от времени актуализировать. Каждый элемент цепочки ссылается на пару, для которой уже выделена память (и которая содержит актуальные курсы), а также содержит тип сделки – BUY или SELL.
  3. Для упрощения и ускорения программы я сделал так, что каждая сделка начинается с покупки (Например, имея BTC мы покупаем монету X) и заканчивается продажей (Мы продаем купленную посередине монету Y и выручаем обратно BTC).
  4. Все, абсолютно все сделки должны быть проведены по рынку. Нет смысла ждать у моря погоды, когда курс пойдет в нужную сторону. С этим связано сразу несколько моментов, которые нужно учесть:

Выставление ордеров на Binance осуществляется через запрос к rest api, и сделка может произойти:

      1. Моментально – Мы отправили запрос к rest api, сервер нам вернул ответ, что сделка уже прошла, и её результат. В этом случае нет необходимости запрашивать уточненную информацию по ордеру.
      2. Протяженно по времени – мы отправили запрос через rest api, сервер биржи создал ордер и вернул нам номер, а через несколько миллисекунд этот ордер исполнился. В этом случае можно было бы бесконечно опрашивать биржу на предмет состояния ордера, но можно поступить оптимальнее – подписаться через web-сокеты на события изменения ордеров. Теперь, каждый раз, когда ордер создастся, и/или как-либо изменится, биржа кинет нам клич, а программа его подхватит. Это быстрее, чем опрашивать через rest. Так же возможны ситуации, когда мы отправляем запрос на создание ордера, и, прежде чем получаем ответ от сервера, уже по сокету прилетает информация об исполнении.
  1. Необходимо держать актуальными все стаканы по всем парам. При этом наиболее интересной информацией будет лучшая цена и объем, который этой цене соответствует. Для этого нужно имплементировать стаканы для каждой пары в своей программе, и обновлять их через сокеты: как только на бирже происходит изменение стакана, информация прилетает в программу, и мы у себя производим корректировки. При этом информация может прилететь какая-угодно: например, кто-то отменил ордер в середине или в хвосте стакана, либо увеличился/уменьшился объем по какой-то позиции и т.п. Поэтому у меня сложился такой алгоритм: на старте программы получаем топ1000 записей по каждому стакану каждой пары (bid и ask) считаем отдельно и строим бинарное дерево по каждому стакану. Подписываемся на сокеты, и, как только прилетает информация, обновляем дерево – либо добавляем запись, если такой цены не было, либо обновляем лист, если изменился объем.  После изменения дерева пробегаемся по нему, и получаем лучшую цену (и соответствующий объем, берем только записи с объемом больше 0). Стаканы и прочее держим в памяти пары – цены и объемы для поиска оптимальной цепочки, стаканы – что бы всегда иметь возможность получить актуальную лучшую цену, а так же, что бы уйти если что в глубину  - см. след пункт.
  2. Для того, что бы провернуть сделку, должны совпасть несколько условий:
    1. нам должно хватить денег на покупку,
    2. каждое лучшее предложение в цепочке должно содержать объем равный или выше нужному (не должно получиться так, что по лучшей цене продают одну сатошу, грубо говоря). Возможно, нужный объем будет достигнут, если брать не самое лучшее предложение, а несколько предложений сверху стакана, и суммировать объемы. При этом цены будут всё более невыгодными и нам придется считать среднезвешенную. Вообще это не работает, т.к. слишком быстро всё меняется, верхних цен и объемов достаточно.
    3. Каждая сделка на каждом этапе должна удовлетворять правилам биржи, особенно касательно размера сделки – нужно избегать таких ситуаций, где мы покупаем монету X на мин. разрешенное кол-во, потом пытаемся продать X за монету Y, и эта сделка кажется бирже слишком маленькой.
    4. На каждой сделке мы теряем комиссию 0.075%, поэтому на выходе мы должны получать больше BTC чем потратили с учетом 0.075% * длину цепи. При этом зачастую после покупки/продажи будет оставаться т.н. «пыль» - остатки монет, которые были отрезаны в результате необходимого округления. Поэтому на каждом этапе мы можем терять чуть больше. Пыль конечно останется, но может быть обесценена со временем.
  3. Важный момент – для покупки/продажи по рынку нужно указывать кол-во монет. Но обстоятельства меняются довольно быстро, и тот расчет, который был перед первой покупкой может быть уже неактуальным перед второй покупкой в цепи. Для примера, мы рассчитали цепь, в которой на 1 BTC закупаем ETH по курсу 1:10 (получим 1 ETH), потом на 10ETH покупаем 100X по курсу 1:10, потом 100X продаем за BTC.  Мы выставляем ордер на покупку 10ETH по рынку, получаем пускай эти 10 ETH, и… теперь нам нужно купить 100X по нашему расчету, но курс X:ETH изменился, и на 10 ETH можно купить только 99 X. А при покупке по рынку, как сказано выше, нужно указывать кол-во монет, так что после сделок нужно пересчитывать новое кол-во монет с учетом актуального курса.
  4. Еще есть фишка для ETH (и, вероятно BNB), что деньги на балансе оказываются не сразу – вы можете купить ETH, и не сможете купить на него ничего в течении секунды или двух. Видимо, там работает какой-то процесс по расписанию. Что бы обойти этот вопрос, нужно на балансе держать ETH – вы покупаете ETH, тратите свой ETH на дальнейшее движение по цепи, а потом купленный ETH падает вам на баланс, восполняя траты. Уот так уот.

В общем, все эти моменты я реализовал в коде, и этот осьминог в куче потоков получает все стаканы по всем парам, ищет цепочки, создает заказы, отслеживает заказы и т.п. Щас заживем.


Реклама


Результаты

Вообще, иногда такие возможности попадаются, и даже можно заработать несколько сатош, но, во-первых, эти ситуации довольно редкие – от 1 до 4 сделок в сутки по BTC паре.

Как только появляется такая возможность, она сразу же пропадает – я так понимаю, работают чужие роботы по тому же алгоритму. Поэтому очень важно успеть первым – приятно осознавать, что я часто успеваю :) Но, т.к. сделки идут по рынку, то если не успел, продаешь в минус.

Иногда бывает так, что цена в стакане и объем какие надо, бот покупает, но цена неадекватно растет шагами, вот так:

Тут толи какие-то трюки биржи, которая сама продает, то ли чей-то бот противодействует, отменяя лоты и повышая цену, я не понял. Но ситуация мною замечена – в стакане все ок, денег хватает, при покупке не совсем ок, потом стакан возвращается. Это очень любопытно и стоит отдельного разбирательства, но об этом в другой раз.

Golang меня порадовал, он реально работает очень быстро и потребляет мало памяти. Не то, что бы я решил на него переходить, но в некоторых ситуациях пользоваться буду. Я еще использовал его в других проектах – например, я делал веб-сервис, отлаживал его на винде, добавлял всякие штуки, проверял, потом этот же код без каких-либо изменений закинул на сервер с убунтой, там скомпилировал и запустил, вот он работает уже хз сколько. Не могу сказать, что он лучше C++ - его я тоже использую, но своя ниша у него есть – просто и быстро писать код, ориентированный на веб с хорошей производительностью.

Насчет арбитража – я думаю, история еще не закончена, но тем не менее вы можете взять последнюю версию кода на GitHub. Сильно не пинайте, если что, это практически мой первый проект на Golang. У меня есть подозрения, что зря я выбрал Binance – она конечно самая популярная сегодня, но именно поэтому оккупирована ботами всех возможных мастей и направлений. Если взять биржу попроще, то и без всех этих оптимизаций алгоритмов, работ с сокетами и т.п. могло бы выгореть… возможно.

Ну, и в любом случае это было увлекательно, и я сейчас всё еще тестирую работу. К сожалению, на яхту заработать таким подходом не удается, придется придумать что-то ещё.

Может быть, у вас получится? :)

Это статья из цикла "Популярно о бирже"

Комментарии: (18)
16.01.2019 04:40
Здравствуй Андрей! Мне понравилась твоя задумка про арбитраж на бинансе. Мог ли ты выложить сам скрипт написанный на golang? Хотелось бы поэкспериментировать на других более мелких биржах, а так задумка очень интересная! Заранее спасибо!
21.01.2019 08:52
Я планирую сделать это чуть позже, т.к. нужно пройти еще несколько шагов:
1. Всё же погонять еще хотя бы месяц и снять статистику
2. Оформить комментариями, расписать в них логику
3. Сделать какой-то вывод текущей информации, что бы непосвященный человек мог мониторить работу.
4. Написать сопроводиловку, как запускать, как компилировать, и т.п.

Сейчас это выглядит как, как некий загадочный процесс, который работает, потребляет немного ресурсов компа, при этом ничего не происходит внешне, потом крекс-пекс-фекс, и на бирже цепочка ордеров сформировалась и в логах появилась куча загадочного.
Кроме того, если я сейчас выложу этот код, и его запустят многие, то они возможно будут мешать друг другу и вообще ничего происходить не будет.
А может быть наоборот, одни люди будут создавать волну для других, а это чем-то тоже интересно.
В общем, мне кажется будет правильным на основе этого проекта сделать что-то более доступное и массовое и выложить, а там делайте что хотите )
Но это будет позже )
19.01.2019 07:24
почему только внутренний, почему не пространственный?
21.01.2019 06:39
Всему свое время :)
21.01.2019 21:04
здравствуйте Го-ланг действительно приятно удивил , как к нему GUI прикрутить ?
вот это подойдет ? https://habr.com/ru/post/420035/
 
п.с.  https://evilinside.ru/pishem-torgovogo-robota-dlya-birzhi-exmo/# Ваша статья? или "одолжили" ))
22.01.2019 11:42
Хах, плагиатор детектед )
Вообще true way - это в отдельном потоке поднять веб-сервер в пару строк и управлять через страницу - просто, быстро и кроссплатформенно, можно управлять ботом на удаленном сервере. И кстати веб-сервер тоже может приятно удивить, он заточен на то, что бы обойтись без nginx, работать с хорошей скоростью на слабых серверах и т.п.
Но если уж нужен монолитный интерфейс, то я бы наверное сразу ушел в сторону C++ с QT например, ну или там WPF какой-нибудь.
26.01.2019 15:42
Спасибо, Андрей, за статью. С большим интересом слежу за вашим блогом (в особенности за статьями по внутрибиржевому арбитражу). Жду с нетерпением 3 части (не терпится посмотреть на код).
29.01.2019 11:21
Добрый день!
Код для арбитража выложен на гитхаб
https://github.com/Bablofil/golang_inner_arb
29.01.2019 17:22
проводил анализ внутреннего арбитража на Бинанас, весной и летом 2018 года... весной приход был маломальский, а летом они подняли % со сделки и всё накрылось медным тазом... теперь прихода вообще нет... использовал самопальные проги на делфе собственного производства...
06.02.2019 19:06
Приветствую! А е пробовали на других биржах использовать свои наработки. Например на EXMO. Там то объемы не такие, как на бинансе, поэтому думаю что из данной затеи может что то выйти!
07.03.2019 12:20
Тоже потратил кучу времени на реализацию такой стратегии. Вот прям как будто сам статью писал)) Тоже надеялся, что скорость golang что то даст. Пробовал 3 биржи в итоге (poloniex, hitbtc, bittrex), везде результат одинаковый, биржа скорее всего закрывает такие сделки сама. 

Очень часто возникает ситуация с задержкой ответа от биржи когда данные по вебсокету приходят позже или вообще зависают (из-за этого иногда возникают забавные моменты когда bid > ask). Само собой при попытке торговать - это не получается, так как реально на бирже такой цены уже нет. При моих замерах оказалось что эта задержка при получении данных ордербука еще и очень нестабильная и в моменты большой нагрузки на бирже может изменяться от 50ms до 3 секунд! Плюс возникают проблемы при постановке ордера, например на bittrex был лаг при выставлении через REST по 15-20 секунд, а оборвать соединение тоже нельзя, биржа в конце-концов ордер выставляет. 

Получается при расчетах прибыли мы изначально оперируем невалидными и устаревшими данными. В общем при кажущейся простоте очень много подводных камней которые не дают выйти в плюс в итоге.
10.03.2019 14:43
Увы (

Кстати насчет биттрекса, когда-то я возлагал на него большие надежды, пока не выяснил, что официальное api кешировало данные на 2 секунды, кажется, а неофициальное отдавало как есть, но работало нестабильно - иногда присылало данные для другой пары. Правда это было пару лет назад и что-то могло измениться, но я в итоге оттуда ушел. Там еще был интересный момент, когда в истории сделок в интерфейсе писались покупки а через апи возвращались продажи с теми же курсами, и наоборот, а бОльшей частью всё совпадало.

Вот на hitBTC я думал что могло бы выгореть, если использовать FIX, но наверное и правда пользы от этого не будет )
01.04.2019 05:08
Вот тут утверждают что эта тема рабочая и будет работать всегда. https://www.redirect.am/?http://o-s-a.net/posts/robot-perelivshchik-sponger.html и даже робота под это дело продают!
02.04.2019 13:08
Если бы еще доказательства этого были... История торгов, история расчетов с ценами, которые можно было бы проверить.
Продают то и рекламируют что угодно.
Я сходил на сайт, не впечатлило, искать позиции это одно, а реально получать прибыль - совсем другое. таких ботов обычно не продают (
02.04.2019 16:13
Ну так и тоже так думаю - зачем это продавать, если оно приносит неплохую прибыль. Я так полагаю (из их подсказок), что бы на этом заработать, нужно в каждом из стаканов всей цепочки "болтаться" лимитками по обе стороны спреда (причем цены выставления должны просчитываться исходя из цены на других парах) и как только произойдет отклонение цены на одной из пар (кто то жахнул по рынку бешеным объемом и "продавил стакан") и цепляет нашу лимитку, на других парах мы сразу все перекрываем маркет ордерами и закрываем цепочку. Проблема тут как раз в том, что нужно иметь оч. много разной крипты на балансах всех пар. По сути ты являешься еще одним поставщиком ликвидности для биржи. Только вот на битмексе платят рибейт тебе за исполнение твоей лимитки, а на бинансе хрен - что маркет, что лимит - один комис для всех.
02.04.2019 16:17
Да и еще забыл добавить - за постоянное перевыставление лимиток (без их исполнения - UFR - (Total Filled Quantity / Total Submitted Order Quantity)) биржа штрафует баном на 5 минут. и за этим тоже нужно следить постоянно и если ты какую то пару "задрочил" переходить на другую...
03.05.2020 16:08
Приветствую!

Запустил бота, для теста не меняя настройки.

В логах вижу что он погнал анализировать цепочки, а потом начал сыпать вот так

2020/05/03 15:35:34.721696 rest.go:33: Get "https://api.binance.com/api/v1/exchangeInfo": dial tcp 52.85.243.193:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
-
dial:websocket: bad handshake
-

Скорее всего из за

{"code":-1003,"msg":"Way too much request weight used; IP banned until 1588520566956. Please use the websocket for live updates to avoid bans."}

Насколько я понял мы же в этом случае итак через websocket запросы делаем, не пойму что не так =)
07.09.2020 10:12
Добрый день!
Да тоже запустил код на golang и вижу dial:websocket: bad handshake после запроса пар...

BNBBTC BUY ASK 0.00000000 BID 0.00000000 (NEED COINS +Inf, ASK VOL 0.00000000, BID VOL 0.00000000) MF (0.00000000) LastUpdated 
 COSBNB BUY ASK 0.00032700 BID 0.00032300 (NEED COINS +Inf, ASK VOL 58.00000000, BID VOL 324620.00000000) MF (0.00000000) LastUpdated 2020-09-07 13:11:12.727824200
 COSBTC SELL ASK 0.00000000 BID 0.00000000 (NEED COINS NaN, ASK VOL 0.00000000, BID VOL 0.00000000) MF (0.00000000) LastUpdated 
  !!! NaN% (  MF 0.00000000,  NaN) !!! 
dial:websocket: bad handshake
Пожалуйста, Авторизуйтесь что бы оставить свой комментарий