Арбитражная система для начинающих, часть 1

Лет 7 назад, имел опыт написания терминала для Московской биржи. Часть команды увлекалась алгоритмической торговлей, в том числе и я. Однако никогда не воспринимал это дело, как реальный источник дохода, хотя были в этом небольшие успехи. Понятно, что конкурировать с банками и различными фондами, с их командами математиков и программистов сложно и проще реализоваться в других областях.

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

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

Навскидку план работы должен быть такой:

  • Создание базы данных, где будут храниться цены валютных пар.
  • Сервер который будет сохранять данные в базу.
  • Первичный анализ.

Исходники доступны по ссылке arb_analysis.

Создание базы данных


Для того чтобы хранить данные, понадобится 3 таблицы.

В этой таблице будет храниться список бирж.

CREATE TABLE `exchange` (
      `id`   int(11)     NOT NULL AUTO_INCREMENT,
      `name` varchar(50) NOT NULL,
	  PRIMARY KEY (id)
    ) ENGINE=MyISAM;

Список криптовалютных пар.

CREATE TABLE `market` (
  `id`          int(11)  NOT NULL AUTO_INCREMENT,
  `name`        char(50) NOT NULL,
  `id_exchange` int(11)  NOT NULL,
  PRIMARY KEY (id)
) ENGINE=MyISAM;
CREATE INDEX id_exchange ON market (id_exchange);

Таблица со сделками, также здесь будет храниться информация, по данным из биржевого стакана.

CREATE TABLE `ticker` (
  `id`         int(11) NOT NULL AUTO_INCREMENT,
  `id_market`  int(5) NOT NULL,
  `local_time`  int(9)    NOT NULL,
  `timestamp`  int(9)    NOT NULL,
  `last`       DECIMAL (11,11) NOT NULL,
  `low`        DECIMAL (11,11),
  `high`       DECIMAL (11,11),
  `bid`        DECIMAL (11,11),
  `ask`        DECIMAL (11,11),
  PRIMARY KEY (id)
) ENGINE=MyISAM;
CREATE INDEX id_market ON ticker (id_market);

Получение данных с бирж


Для удобного доступа к биржам есть опенсорсный проект ccxt. С помощью которого возможно в едином стиле обращаться к различным биржам. Однако оказалось, что не все так радужно, по ряду бирж информацию не получалось доставать, и некоторые методы не работали.

В файле create_markets.py происходит инициализация, получаем список криптовалютных пар, по биржам. При этом используется метод load_markets(), который возвращает список пар для биржи.

name_exchange = ["acx", "binance", "bitfinex", "bitfinex2", "wex"]
def create_exchange_and_market_in_db():

    exchanges = {}

    for id in name_exchange:

        exchange = getattr(ccxt, id)
        exchanges[id] = exchange()

        id_exchage = db_helper.insert_exchage_to_db(exchanges[id],cnx,cursor)

        markets = exchanges[id].load_markets()

        for mark in markets:
            id_market = db_helper.insert_market_to_db( id_exchage, mark, cnx,cursor)

Далее в файле ex_data_saver.py, запускаем сохранение изменение цен для пар:

def save():

    markets = db_helper.get_data_exch()

    exchanges = {}

    for id in name_exchange:
        exchange = getattr(ccxt, id)
        #isHas = exchange.hasFetchTickers
        #if isHas:
        exchanges[id] = exchange({
            'enableRateLimit': True,  # or .enableRateLimit = True later
        })

    cnx = db_helper.CreateConnection()
    cursor = cnx.cursor()
	
	loop = asyncio.get_event_loop()
    while True:
        start_time = time.time()
        input_coroutines = [fetch_ticker(exchanges, name) for name in exchanges]
        exch_tickers = loop.run_until_complete(asyncio.gather(*input_coroutines, return_exceptions=True))
        
        count_exchange = 0
        
        delta = time.time() - start_time
        
        for tickers in exch_tickers:
            if  tickers is not None:
                count_exchange+=1
        
        inserted_start = time.time()
        db_helper.insert_tick(markets,exch_tickers,cnx,cursor)
        inserted_time = time.time()
        print(count_exchange," ", delta, ' ', inserted_start - inserted_time)

Асинхронное получение тиков для конкретной пары, происходит с помощью метода ccxt fetchTickers().

async def fetch_ticker(exchanges, name):
    item = exchanges[name]

    try:
        ticker = await item.fetchTickers()
       
        return {name:ticker}

Предварительный анализ данных


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

SELECT ex.name as exchange_name, m.name as market_name, count(*) as count_deals 
FROM exchange ex
LEFT JOIN market m ON m.id_exchange = ex.id
LEFT JOIN ticker t ON t.id_market =m.id 
GROUP BY ex.id, t.id_market
ORDER BY m.name
HAVING count_deals > 10000;

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

Следующая статья будет посвящена созданию этого робота. И тестового сервера — эмулирующего работу реальной биржи.В тестовый сервер хочу заложить следующие моменты:

  • Задержки.
  • Проскальзывание.
  • Комиссии.
Поделиться публикацией

Комментарии 6

    +2
    Несколько абзацев и куски простейшего кода. Для чего статья — не понятно. Мода наверно такая, попробовать новый язык и сразу запилить статейку чтобы не забыть его потом.

    Хотя бы PEP8 для начала прочитали перед тем как писать статьи на Хабре.
      0
      Предполагается серия статей, где попробую показать подход к анализу данных биржи. Статья пробная, и если людям понравиться, буду публиковать продолжение.
      +1
      Совет, вместо бесполезных тиков и bid/ask (они не дают ответа на вопрос СКОЛЬКО можно обменять по этой цене), собирайте у бирж стаканы — depth, все они в криптоэкономике такой выдают, иногда лимитированный до некоторой глубины, но для нужд арбитража хватит.

      Еще больше пользы вы получите, если вместо запроса информации по http REST api, подключайтесь к серверу биржи по websocket (или fix если есть) и восстанавливайте стаканы самостоятельно в реальном времени, без задержек (rest api обычно лимитированы количеством запросов, в конечном счете секунды между ними в среднем), ведь за секунды содержимое стакана изменится, а вы об этом не узнаете.
      p.s. я сильно сомневаюсь, что вы сумеете используя sql, делать все это достаточно оперативно, слишком уж много оно накладных расходов дает.

      По стакану вы сможете оценить объем сделки и ее реальную стоимость, т.е. например если вы можете на одной бирже по цене 10 купить 100 монет, а на другой по цене 12 продать всего 5, то ваш бот должен на первой купить тоже только пять, иначе на второй бирже вы выставите продажу по 12, и этот ордер на 95 монет останется лимитным, сдвинув курс на ней вниз.
        0
        с ccxt есть еще ряд проблем: лимиты (минимумы) бирж, «сверхмалые» числа при переводе одной валюты в другую, и некоторые биржи, помимо того что не реализуют все методы еще и возвращают нестандартные ответы…
          0
          Спасибо за статью, я как раз новичок в этом деле. Понемногу, с помощью таких статей начал разбираться в криптовалюте. И кстати нашел сайт на котором удобно следить за курсом cmc2.pro. Сервис не зависает, можно читать новости и выбрать себе выгодную биржу.
            0
            Вы зря думаете, что банки и фонды вам конкуренты. У них своя специфика. С их большими деньгами им может не хватать ликвидности рынка для тем стратегий, которые можете попробовать вы.
            Интересно будет прочитать в продолжении как вы видите основные идеи, опираясь на которые вы собираетесь строить свои стратегии. Будет ли это только межбиржевой арбитраж на криптоплощадках или например торговля парами?

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое