Простой бот для IB

В этот раз напишем на Python простого бота для торговли через платформу Interactive Brokers TWS. Торговать будем внутри дня на американском фондовом рынке акциями и ETF. Наш код сможет работать в среде Jupyter Notebook вместе с пакетом IB.API. Для примера, реализуем стратегию на основе пересечений SMA (простых скользящих средних).

Стратегия очень проста. Используем комбинацию скользящих средних (SMA) разных периодов. Например, 26-периодной (длинная) и 9-периодной (короткая). Когда короткая средняя выше длинной, это показатель восходящего тренда. Если короткая средняя пересекает длинную сверху вниз, считаем это сигналом на продажу и наоборот, если короткая пересекает длинную снизу вверх, сигнал на покупку. На основании пересечений алгоритм будет давать сигнал терминалу TWS от Interactive Brokers на покупку или продажу актива.

«Больше дела — меньше слов»

1️⃣ Этап 1. Открытие демо-аккаунта и установка TWS

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

 

Все шаги предоставлены для операционной системы Linux (Debian). Код робота будет работать в любой операционной системе, для которой существует Jupyter Notebook.

После регистрации скачайте платформу Trader Workstation (текущая версия TWS) и следуйте инструкциям ниже.

Откроем терминал и перейдем в папку, где находится установочный файл:

Сделаем инсталлятор исполняемым:

Запустим инсталлятор:

В окне инсталлятора нажимаем Далее и Завершить.

Если всё прошло успешно, тогда в списке приложений появится ярлык Trader Workstation, запустим его.

Выберите вкладку Тренировочная торговля и введите ваш логин и пароль. Затем нажмите Paper Log in:

Вы увидите подобный интерфейс TWS:

Главный экран IB TWS

На этом установка TWS закончена. Позднее мы вернемся к платформе, чтобы настроить соединение.

2️⃣ Этап 2. Настройка окружения Python

Установим библиотеку TA-Lib. Для этого сначала подготовим систему.

В терминале вводим:

Скачаем архив с кодом и распакуем его:

Перейдем в папку ta-lib, скомпилируем библиотеку и установим:

Установим pip и virtualenv:

Создадим папку проекта SMAint и перейдём туда:

Создадим новое виртуальное окружение и активируем его:

Дальнейшие действия необходимо выполнять с активным виртуальным окружением и без использования прав супер-пользователя (sudo).

Установим необходимые пакеты в окружении с помощью pip:

ib_insync это асинхронная библиотека позволяющая работать с TWS и IB Gateway в привычном синхронном подходе. Можете почитать статью о том, как работать с голым IB.API и сравнить с тем, как это делается через ib_insync.

Во время импорта библиотеки TA-Lib может возникнуть ошибка “ImportError: libta_lib.so.0: cannot open shared object file: No such file or directory”. Для решения необходимо ввести команду в терминале:

Установим Jupyter Notebook:

Запустить Jupyter можно командой в терминале:

Теперь необходимо установить пакет IB.API для работы с TWS из Python. Сначала необходимо скачать архив с IB.API. Для этого нужно перейти по ссылке, и нажать кнопку I Agree.

Нажимаем на IB API Latest for Mac/Unix Version API 9.76. Одно уточнение, версия API 9.72 не подойдет, т.к. в ней нет поддержки Python.

Введем в терминале перечисленные ниже команды.

Перейдём в папку загрузок:

Разархивируем скачанный архив:

Перейдем по пути:

Запустим установщик (Важно, запустить команду внутри виртуального окружения!):

IB.API должен установиться в папку site-packages виртуального окружения. Например:

Проверьте командой:

Если все прошло успешно, вернёмся к платформе TWS. Если есть ошибки и вы не видите надписи Success, задавайте вопросы в комментариях к статье.

Далее настроим платформу для API соединения:

Включение доступа для IB.API в TWS

В верхнем меню нажмем Файл — Глобальная конфигурация:

В левой части окна разверните меню API. В нём выберите пункт Настройки.

  • Поставьте галочку на пункте Активировать ActiveX и сокеты-клиенты;
  • Снимите галочку с пункта API, только чтение;
  • Необходимо запомнить пункт Сокет-порт (в данном случае 7497);
  • Введите в пункт ID главного API-клиента: 1.

Настройки API в TWS

Применим изменения и вернёмся в Jupyter Notebook.

Блокноты Jupyteг — это очень мощный инструмент, который позволяет работать с исходным кодом и документацией, видеть ре­зультаты выполнения кода и визуализировать данные графиками. Документация поддерживает синтаксис упрощенной разметки Maгkdown и мощный синтаксис формул LaTeX. Jupyteг активно используется для анализа данных.

После запуска Jupyter, открываем меню New и выбираем Python 3:

Будет создан блокнот, в котором и продолжим работу. По умолчанию создается файл под названием Untitled с расширением .ipynb, но собственное название можно задать кликнув на Untitled или в меню File — Rename.

Скопируем данный код для проверки соединения, здесь необходимо прописать сокет-порт (7497) и ID главного API-клиента (clientId=1) согласно вашим настройкам:

Если вы видите, что успешно подключились к TWS, значит этап настройки полностью закончен и можно переходить к разработке алгоритма.

Jupyter: успешное подключение к TWS

3️⃣ Этап 3. Создание бота

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

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

Настройка бота

В самом начале работы от пользователя требуется ввести тикер(ticker) и размер позиции(amount), больше никаких настроек не потребуется. Ввод наименования тикера регистронезависимый, только латиница, размер позиции целым числом.

Получение минутных данных

Загрузка данных осуществляется с помощью сервиса Alpha Vantage. Здесь, с определенными ограничениями, можно бесплатно взять биржевые данные американских компаний. Ограничения заключаются в лимите обращений к данным, не более пяти раз в минуту и не более пятисот в сутки. Но так как робот будет делать один запрос в минуту, AV отлично для этого подойдет. Чтобы начать обращаться к API сервиса, необходимо получить API Key.

Функция загрузки данных представлена ниже:

В случае необходимости, Alpha Vantage можно заменить другим источником данных. Для этого просто следует заменить url-запрос на адрес вашего сервиса. Однако формат данных должен соответствовать csv, иначе корректная работа робота не гарантируется. Например, даже если взять json данные из того же Alpha Vantage, робот не сможет прочитать их и выдаст исключение. Однако, если загрузить json данные из биржи IEX, то все будет работать и ничего переделывать не придется.

Из этого следует сделать вывод, что лучшим вариантом для данного робота, будет использование данных в формате csv:
  • csv имеет одинаковую структуру данных;
  • на обработку csv требуется меньше ресурсов. Хоть в нашем случае это не так важно, но при чтении объемных файлов это сыграет роль.

Alpha Vantage позволяет загружать минутные (TIME_SERIES_INTRADAY — как в нашем случае), дневные (TIME_SERIES_DAILY), недельные (TIME_SERIES_WEEKLY) и месячные данные (TIME_SERIES_MONTHLY).

Поддерживаются следующие значения временных интервалов: 1 мин, 5 мин, 15 мин, 30 мин, 60 мин. Как наверное уже заметили ранее, данный робот использует интервал в 1 минуту.

Сервис позволяет получать данные в двух форматах: csv и json. По умолчанию выдается json, поэтому для получения csv, следует явно это указать в запросе: datatype=csv.

Еще следует учитывать, что в Alpha Vantage есть два размера файла: compact и full. По умолчанию идет compact и означает, что файл включает в себя 100 последних значений, в нашем случае это 100 последних минут. При указании опции full, AV представит всю историю данных за сутки, если используем TIME_SERIES_INTRADAY.

Необходимый файл с данными выглядит так:

Вспомогательные методы

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

Всего выделяют четыре состояния рынка:
  • премаркет;
  • регулярная сессия;
  • постмаркет;
  • торги закрыты.

Не буду углубляться в подробности каждого состояния, т.к. для торговли внутри дня подойдет только регулярная сессия. Например на Нью-Йоркской фондовой бирже она начинается в 9:30, а заканчивается в 16:00 по US/Eastern. Необходимо узнать, совпадает ли местное время с торговыми часами, а для этого потребуется часовой пояс в котором работает биржа.

В ib_insync есть очень полезный метод reqContractDetails, способный помочь узнать в какие дни и часы работает биржа, так же там можно найти информацию о часовом поясе для торгуемого актива. Данный метод возвращает timeZoneId=’EST (Eastern Standard Time)’ для Нью-Йорка. Что в общем-то верно, однако это не совсем нам подойдет. EST не учитывает переход на летнее время, т.е. его придется осуществлять самостоятельно. Это привело бы к усложнению программы, что не желательно.

В этом случае нам снова пригодится Alpha Vantage, но уже его json файл, т.к. он содержит метаданные для каждого актива.

В этом разделе содержатся общие сведения о данных, тикер, время обновления, интервал, размер файла и часовой пояс. Часовой пояс US/Eastern, полученный при загрузке данных для SPY, учитывает переход на летнее время.

Функция получения часового пояса из файла get_tz довольно проста. Для этого потребуется так же отправить запрос в AV, но только для получения JSON файла. После записи данных в файл, его необходимо прочитать методом read_json из пакета pandas и затем удалить, так как он больше не пригодится.

К сожалению, робот не получает сигнала от терминала о том, работает ли сейчас биржа или нет, поэтому приходится сделать это самостоятельно в виде функции time_check, которая:
  • определит торговые дни и часы;
  • разделит торговые часы на состояния рынка (премаркет, регулярная сессия, постмаркет);
  • определит местное время (для этого прежде получали часовой пояс);
  • сопоставит местное время с состоянием рынка;
  • вернет текущее состояние рынка.

Функция time_check представлена ниже:

Проверка баланса и позиций

В алгоритм встроена проверка баланса и открытых позиций. Функция balance позволяет проверить наличие кэша, но она не учитывает наличие залоговых средств по открытым коротким позициям. Если таковые имеются, необходимо учитывать это самостоятельно и рассчитать каким размером позиции собираетесь торговать.

После того как узнали баланс, можно перейти к проверке покупательной способности с помощью функции check_balance. В начале из файла считывается цена закрытия с помощью функции read_data, затем цена закрытия умножается размер позиции, который ввел пользователь и в случае, если средств достаточно, возвращается True.

Функция чтения истории цен read_data выглядит так. После того, как загрузили данные в переменную data, файл можно удалить:

Далее понадобится список открытых позиций, получаемый с помощью метода positions из IB.API.

Данный код возвращает наименование тикера, количество и стоимость каждой позиции:

Завершающей в этом разделе будет функция list_orders, показывающая список активных ордеров использующая метод openTrades.

Описание логики пересечений

Расчет скользящих средних, реализацию логики пересечений и подачу сигнала на покупку/продажу осуществляет функция algorithm. Эта функция является основной, т.к. вызывает практически все остальные функции робота по мере необходимости.

Получим цены закрытия за весь период с использованием функции read_data и, с помощью метода fillna, заполняем пропуски в данных путем копирования предыдущего значения. Это бывает редко.

Обработка исключений здесь требуется в связи с имеющимися ограничениями в Alpha Vantage. Может получиться так, что инвестор сразу после запуска алгоритма захочет прекратить его работу и запустить снова (например, в случае изменения торгуемого актива) и т.к. сервис не позволяет делать более пяти запросов в минуту, мы можем упереться в это ограничение.

Теперь в переменные ma_long и ma_short запишем значения скользящих средних, полученные путем использования библиотеки Ta-Lib. В данном случае использованы 26-ти минутные для длинной и 9-ти минутные для короткой периоды, но вы можете заменить их на свои.

Стоит помнить, что мы использовали данные за последние 100 минут, поэтому значения SMA не должны превышать 100. Но если хотите использовать бОльшие периоды, то в функции load_data, в переменной URL, необходимо указать параметр outputsize=full, например:

Если используешь небольшие периоды, нет смысла ради нескольких минут таскать данные за последние 24 часа.

Теперь у нас имеются необходимые данные по двум скользящим средним. Можно переходить к написанию логики их пересечений. Основной нюанс здесь заключается в недостаточности просто указать, что если короткая средняя окажется выше длинной, то отдать приказ на покупку и наоборот. В этом случае при запуске алгоритма бот сразу отправит приказ в терминал на покупку или продажу (это будет зависеть от положения средних в тот момент), а нам это не нужно. Для отправки приказа необходимо чтобы соблюдался принцип противоположности сигналов, т.е. положения скользящих средних за последнюю и предпоследнюю минуты должны быть противоположными.

В случае выполнения условия, когда короткая средняя оказывается выше длинной, в переменную contract записывается результат выполнения функции contract. Данная функция инициализирует контракт в TWS путем передачи тикера в метод Stock, способа передачи ордера(SMART) и валюты. SMART означает, что IB автоматически отправляет ордер на несколько бирж тем самым осуществляя маршрутизацию самостоятельно.

После инициализации контракта отрабатывает функция buy, название которой говорит само за себя. В случае если торгуемого актива нет с списке открытых позиций и открытых ордеров, то с помощью метода placeOrder выставляется рыночный ордер на покупку того количества актива, которое ввел инвестор на стадии настройки.

Когда актив уже куплен и, если короткая SMA опустилась ниже длинной, вызывается функция contract и sell. sell проверяет есть ли актив в списке открытых позиций, нет ли его в списке активных ордеров и передает ордер на продажу. После исполнения приказа TWS покажет уведомление в правом нижнем углу экрана.

Код функции algorithm полностью:

Основной цикл работы

Управляющей конструкцией бота является цикл, который с периодичностью в одну минуту вызывает функции load_data и algorithm. Т.е. сначала загружаются данные и затем они используются.

Однако перед тем как алгоритм начнет работу необходимо удостовериться, что актив доступен в регулярной сессии. Для этого используем функцию time_check, описанную ранее. Если она возвращает ‘Regular session’, то алгоритм переходит к следующей проверке, иначе прекращает работу.

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

В случае прохождения всех проверок работу начинает основной цикл. Все это осуществляет функция run_algorithm.

Как запустить и остановить алгоритм

Для запуска робота, необходимо убедиться что работает терминал TWS, затем поочередно запустить все ячейки в Jupyter. Если сейчас регулярная торговая сессия, тогда начнется загрузка данных и расчет индикаторов, иначе будет выведена надпись с просьбой дождаться начала торгов.

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

С полной версией кода можно ознакомится в репозитории.

🏁 Заключение

По мере написания данного робота я старался максимально упростить алгоритм, чтобы было понятно начинающим алготрейдерам. Данная статья показывает, как взаимодействовать с IB.API используя ib_insync, передавать ордер, работать с библиотекой Ta-Lib и биржевыми данными.

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

Если имеются какие-либо замечания или предложения прошу писать их в комментариях.