Торговый алгоритм на Python

Торговый алгоритм на Python для API TWS от Interactive Brokers

Данной статьей мы открываем серию материалов по использованию API от брокера Interactive Brokers и написанию простых торговых алгоритмов.

Цель — описать все процессы: от установки и настройки необходимого программного окружения до создания базовых алгоритмов, которые выполняют полезный функционал в связке с IB Trader Workstation.

В отличии от тысяч других мануалов данная серия статей проведет вас от самого начала, показав, как ставить все программное окружение на машину, до логического завершения — готового алгоритма, который даст понимание, как и что надо написать на ?Python 3+, чтобы получить желаемое от терминала.

??Важные замечания

В данной серии статей будут применяться следующие аббревиатуры, привыкайте к ним сразу:
IB — Interactive Brokers — название компании-брокера, терминалом которого будем пользоваться.
TWS — Trader Workstation — торговый терминал компании IB.
API или IB API — Application Programming Interface (программный интерфейс) и одноимённый пакет для Python 3.4+, обеспечивающий взаимодействие между нашим приложением и TWS от IB.
Приложение — наш код на языке Python 3.
Клиент — окружение вне терминала TWS. В большинстве случаев — это наше приложение на Python3.

Также я постарался максимально уйти от ООП. Сделал это умышленно, чтобы максимально «раскрыть» все механизмы API и не перегружать код, представленный в статьях. Сам API создан исключительно на ООП, имейте это ввиду.

В статьях будут рассмотрены:
Установка и настройка программного окружения:
Языка программирования Python, версии 3.6+.
Терминала Trader Workstation, версии 969.2+.
API от Interactive Brokers, версии 9.72+.
Установка подключения к Trader Workstation.
Определение и организация контрактов.
Запрос исторических данных у Trader Workstation.

Все это и много другое будет организовано средствами Python 3.

Пара слов о TWS

TWS (или Trader Workstation) — это кросс-платформенное приложение, созданное на языке Java программистами компании Interactive Brokers.

Выражаясь на сленге: «запилен на Яве, лобает на всех осях».

Из личного опыта: работал с этим терминалом на macOS High Sierra (версия 10.13.2) и на Ubuntu (версия 16.04.3 LTS) — проблем и сбоев замечено не было. Сам терминал был версии 969.2.

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

Как работать с терминалом TWS?

Не ошибусь, если отвечу: «Как угодно!». И это правда. Вы можете создавать любой код на Python 3, в любой момент времени подключаться к терминалу и запрашивать все, что вам необходимо.

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

Шаг 1: Подключение IB API

Это одна из легких задач. Организовать подключение можно всего лишь в несколько строк, при помощи парочки пакетов из библиотеки.

Шаг 2: Организация подключения к TWS

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

Шаг 3: Обработчики событий и контракты

Суть работы с терминалом — это организация двух потоков: отправки запросов в TWS и получения ответов (сообщений) от него. Делается это с помощью обработчиков событий.

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

Шаг 4: Отправить запрос и получить ответ

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

?Заключение

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

И это только введение!

Далее мы пошагово разберем работу с TWS и продемонстрируем создание «законченных» приложений на Python 3, которые будут осуществлять различный полезный функционал.

Торговый алгоритм на Python для IB.API: установка и настройка

Это вторая статья в серии про работу с терминалом Trader Workstation от брокера Interactive Brokers.

Здесь рассмотрим пошаговую установку всего программного комплекса необходимого для работы с терминалом.

В конце статьи вы будете обладать программным окружением, которое позволит создавать программы на ?Python3, работающие с терминалом TWS.

Установка и настройка:
Oracle VirtualBox — виртуальная машина, на которую поставим операционную систему.
Ubuntu Desktop Linux — операционная система, в которой будем работать с Python.
Python — интерпретатор для языка программирования.
TWS Software (Trade Workstation) — терминал от брокера Interactive Brokers.
IB API — API, который позволит работать с терминалом TWS .

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

?Шаг 1: Установка VirtualBox

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

Ссылка для скачивания: https://www.virtualbox.org/wiki/Downloads
Актуальная версия (на момент написания статьи): 5.2.2

Переходим по ссылке выше и выбираем операционную систему. Загружаем дистрибутив VirtualBox.

Следующим шагом скаченный файл надо запустить и пройти процесс установки, который начнется вот с такого окна:

Торговый алгоритм на Python


Нажимаем кнопку Продолжить. Инсталятор перейдет к шагу “Тип установки”:



В этом окне предлагается выбрать: установить VirtualBox для всех пользователей компьютера или только для вас. Нажимаем кнопку Установить.

Примечание! Если за Вашим компьютером работают другие пользователи под своими учетными записями, Вы можете нажать кнопку Изменить размещение установки…, далее выбрать установку только для Вашего пользователя. Но данный вопрос выходит за рамки данной статьи.

Начнется процесс установки:



Данный процесс может занять от нескольких секунд до нескольких минут. Зависит от мощности вашего компьютера. После установки откроется последнее окно инсталятора:



Нажимаем кнопку Закрыть.

На этом установка VirtualBox завершена. Установкой и настройкой виртуальной машины мы займемся позже, сразу как только получим дистрибутив операционной системы Ubuntu.

?Шаг 2: Загрузка дистрибутива Ubuntu

Ссылка для скачивания: https://www.ubuntu.com/download/desktop
Актуальная LTS версия (на момент написания мануала): 16.04.3 LTS

Переходим по ссылке выше. Далее есть два сценария закачки:

Нажмите кнопку Download. На следующей странице промотайте вниз и нажмите на ссылку Not now, take me to the download. Начнется процесс закачки и откроется страница с надписью Thank you for downloading Ubuntu Desktop.
Нажмите на ссылку Alternative downloads and torrents. На следующей странице можно выбрать альтернативные способы закачки, включая закачку с помощью Torrent-клиентов.

Теперь можно создавать и настраивать виртуальную машину.

??Шаг 3: Создание виртуальной машины VirtualBox.

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

Открываем ранее установленный VirtualBox. При первом запуске он встретит нас окном приветствия:



Нажимаем кнопку Создать. Откроется следующее окно:



Вводим:
Имя — Ubuntu 16.04 Algotrading
Тип — Linux
Версия — Ubuntu (64-bit)

Нажимаем кнопку Продолжить. Откроется следующее окно:



Оставляем 1024 МБ.

Нажимаем кнопку Продолжить. Откроется следующее окно:



Оставляем отметку Создать новый виртуальный жесткий диск.

Нажимаем кнопку Создать. Откроется следующее окно:



Оставляем VDI (Virtual Disk Image).

Нажимаем кнопку Продолжить. Откроется следующее окно:



Оставляем Динамический виртуальный жесткий диск.

Нажимаем кнопку Продолжить. Откроется следующее окно:



В текстовом поле указывается имя файла, который будет представлять из себя жесткий диск виртуальной операционной системы. Оставляем Ubuntu 16.04 Algotrading.

Горизонтальным бегунком или в текстовом поле справа от него можно настроить объем данного файла. Оставляем его равным 10,00 ГБ.

Нажимаем кнопку Создать. VirtualBox вернется к своему первоначальному окну. Теперь в списке (в левой части окна) появится наша машина:



На этом создание виртуальной машины завершено. Теперь остается подготовить ее к установке Ubuntu.

??Шаг 4: Подготовка виртуальной машины

Виртуальная машина готова к запуску, но не готова к установке Ubuntu. Исправим это!

Кликаем в списке на нашу машину Ubuntu 16.04 Algotrading и нажимаем кнопку Настроить. Появится меню настроек нашей виртуальной машины:



В верхней части данного меню расположены разные вкладки.
Переходим на вкладку Система -> Материнская плата.



В окне Порядок загрузки снимаем галочку с пункта Гибкий диск.

Пункт Оптический диск с помощью стрелок справа от окна переносим в самый верх.

Пункт Жесткий диск с помощью тех же стрелок переносим вверх, но так, чтобы он оказался под пунктом Оптический диск.

Все остальные настройки оставляем без изменений. Финальные настройки:



Разделы Процессор и Ускорение менять не надо.

Переходим на вкладку Дисплей. На вкладке Экран меняем значение Видеопамяти на 128 МБ. Вкладка должна выглядеть вот так:



Переходим на вкладку Носители:



В левой части окна указаны носители, которые доступны сейчас нашей виртуальной машине. Идем снизу вверх:
Контроллер: SATA — тут указан файл созданного ранее жесткого диска. Оставляем как есть.
Контролер: IDE — тут надо внести важное изменение: нажимаем на надпись пусто — справа отобразиться окно настройки оптического привода:



Находим пункт Оптический привод. В выпадающем списке указан Вторичный мастер IDE, не трогаем это. Еще правее изображен CD-диск. Нажимаем на него и в появившемся меню выбираем пункт Выбрать образ оптического диска… В появившемся окне выбираем дистрибутив Ubuntu, который скачивали на шаге 2. Теперь в окне появились данные по нашему образу Ubuntu. Окончательный вид:



Нажимаем кнопку ОК в нижнем правом углу. VirtualBox снова вернется к своему первоначальному окну.

На этом настройка нашей виртуальной машины завершена.

?Шаг 5: Установка Ubuntu

В первоначальном окне VirtualBox:



Выбираем слева нашу виртуальную машины и нажимаем кнопку Запустить. Откроется новое окно (рабочий стол виртуальной машины) и после небольшой загрузки начнется установка Ubuntu:



В первом же окне установщик Ubuntu предлагает выбрать язык для операционной системы и возможность попробовать (либо установить) Ubuntu.

В списке слева оставляем язык English.

Нажимаем на кнопку Install Ubuntu. Откроется следующее окно:



Download updates while installing Ubuntu — ставим галочку, чтобы ОС скачала все обновления в процессе установки.

Install third-party software for graphics and Wi-Fi hardware, Flash, MP3 and other media — ставим галочку, чтобы установились драйвера сторонних разработчиков для Wi-Fi и видеокарты.

Нажимаем кнопку Continue. Откроется следующее окно:



Оставляем Erase disk and install Ubuntu.

Нажимаем кнопку Install Now. Откроется такое предупреждение:



Нажимаем Continue. Откроется следующее окно:



Проверяем название города.

Нажимаем Continue. Откроется следующее окно.



Оставляем все как есть.

Нажимаем Continue. Откроется последнее окно:



Заполняем текстовые поля:
Your name — Algotrader
Your computer’s name — algomachine
Pick a username — algo
Choose a password — 1234
Confirm your password — 1234

Примечание: при вводе пароля появится красная надпись Short password — игнорируем эту надпись.

Оставляем выбранным пункт: Require my password to log in

После всех изменений окно выглядит таким образом:



Нажимаем Continue. Начнется процесс установки:



Процесс установки может занять разное время, это зависит от мощности Вашего компьютера.

По завершению процесса установки выйдет следующее окно:



Нажимаем Restart Now.

После перезагрузки машины появится окно авторизации:


Сейчас нам надо отключить машину, чтобы внести кое-какие настройки, поэтому нажимаем на шестеренку в верхнем правом углу и в появившемся меню нажимаем Shut Down.



Машина полностью отключиться.

Теперь заходим в настройки нашей виртуальной машины непосредственно в VirtualBox. Нужна вкладка Носители. Проверяем параметр Контроллер: IDE. Если в нем по-прежнему указан образ Ubuntu в качестве CD-диска, его надо отключить.

После всех манипуляций должно быть так:



Нажимаем ОК. VirtualBox вернется к своему первоначальному экрану.

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

?Шаг 6: Установка Python.

Снова открываем VirtualBox и запускаем ранее созданную виртуальную машину.

В момент авторизации указываем пароль для пользователя Algotrader. Если вы следовали данной статье, пароль должен быть — 1234.

Загрузив систему, первым делом проверяем, как работает интернет. Нажмите на иконку FireFox Web Browser и посетите любой популярный сайт, допустим youtube.com. Если сайт открылся, значит, все в порядке, идем далее. Если интернета нет — проверьте интернет на вашей физической машине, далее обратитесь к настройкам VirtualBox и Ubuntu Desktop. Настройка доступа в интернет выходит за рамки данной статьи и здесь не рассматривается.

Теперь открываем терминал: нажмите на значок Ubuntu в верхнем левом углу экрана и начните вводить “Terminal”. Появится иконка терминала, нажмите ее.

Дальнейшая работа будет вестись именно в терминале.

Обновление пакетов системы

Первым делом надо обновить список пакетов программного обеспечения, это делается с помощью команды:

sudo apt-get -y update


В ответ на введению команду система запросить пароль от вашего пользователя. Введите его.

Теперь надо обновить пакеты. Введите команду:

sudo apt-get -y upgrade


Система попросит пароль, введите.

Начнется процесс обновления всех пакетов системы. Это займет некоторое время. Зависит от мощности компьютера и скорости интернета. Дождитесь окончания этого процесса.

Установка Python

По большому счету Python уже идет вместе с дистрибутивом Ubuntu, то есть он уже стоит в нашей операционной системе, надо только проверить это:

python --version


Должно выйти:

Python 2.7.12


Проверим наличие Python 3:

python3 --version


Должно выйти:

Python 3.5.2


Далее можно войти в интерпретатор Python и попробовать ввести несколько команд:

python3

>>> 42%8
2
>>> exit()


Примечание! В примерах выше, указаны версии языка интерпретатора Python актуальные на момент написания статьи. Если ваши версии будут отличаться от указанных — ничего страшного.

Мы проверили: Python установлен в нашу систему.

?Шаг 7: Установка IB API.

Ссылка для скачивания: http://interactivebrokers.github.io
Актуальная версия (на момент написания мануала): API 9.73

Загрузка API

Переходим по указанной выше ссылке. Нажимаем кнопку I Agree. Откроется окно, в котором будет предложено скачать разные версии IB API:



Нажимаем кнопку IB API Latest for Mac / Unix. Начнется закачка пакета для языка Python3.

Будьте внимательны! Не ошибитесь с кнопками! Поддержка языка Python реализована в API версии 9.73 и выше. Если нажмете на IB API Stable for Mac/Unix, скачается дистрибутив версии 9.72 — без API для Python, в нем будут API только для Java и C++.

Установка API от IB

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

Переходим в папку Загрузки командой:

cd ~/Downloads/


В папке лежит наш архив twsapi_macunix.973.06.zip. Его надо разархивировать:

unzip twsapi_macunix.973.06.zip


После этого в папке Загрузки должна появится папка IBJts, в ее недрах лежит нужный нам установщик пакета. Перейдем к нему:

cd ~/Downloads/IBJts/source/pythonclient/


Запускаем установщик пакета командой:

sudo python3 setup.py install


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

Теперь можно проверить наличие пакета в библиотеке Python3:

python3 -c "help('modules ibapi')"


В ответ система должна показать очень много строк, которые будут начинаться так: “ibapi. …“. Это список модулей, доступных в пакете нашего API.

На этом установка IB API завершена.

?Шаг 8: Установка терминала Trader Workstation.

Ссылка для загрузки: https://www.interactivebrokers.com/en/index.php?f=16040
Актуальная версия (на момент написания статьи): 969.2c
Загрузка и установка TWS

Перейдя по ссылке выше, нажимаем кнопку DOWNLOAD. Начнется закачка файла и откроется страница с инструкцией по установке.

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

Сделав все по инструкции, вы установили Trader Workstation на свою систему. Теперь его надо настроить для дальнейшей работы через API на Python3.

Нажмите на иконку Ubuntu (в верхнем левом углу) и в поисковой строке начните вводить: Trader Workstation. Появится иконка терминала:



Кликните по ней. Терминал начнет загружаться.

В самом начале он покажет вот такое окно авторизации:



Нажмите на ссылку в правой части окна: Try the demo.

Откроется следующее окно:



Введите свой E-mail и нажмите Login.

Терминал после недолгой загрузки начнет свою работу:



В верхнем меню нажмите File -> Global Configuration… Откроется окно глобальных настроек TWS:



В левой части окна найдите и раскройте меню API. В нем выберите пункт Settings.
Поставьте галочку на пункте Enable ActiveX and Socket Clients.
Снимите галочку с пункта: Read-Only API.
Введите в пункт Master API client ID: 100.
Запомните, чему равен пункт Socket port. Это очень важно (далее поймете почему).

Окно после всех настроек должно выглядеть вот так:



Нажмите Apply. Затем нажмите OK. Программа вернется к своему первоначальному виду.

На этом настройка терминала завершена.

?Заключение

Теперь у вас есть все, чтобы создавать приложения на Python3, которые смогут работать с терминалом Trader Workstation от брокера Interactive Brokers.

Торговый алгоритм на Python для IB.API: подключение к TWS

Это третья статья в данной серии. В ней мы создадим алгоритм, который будет подключаться к TWS и получать «первичные сообщения».

Создавая данный алгоритм, мы рассмотрим реализацию двух обязательных классов EClient и EWrapper. И в конце заглянем «за кулисы» и посмотрим, что происходит, когда мы подключаемся к TWS.

Подключение к Trader Workstation

Это один из самых простых процессов, которые можно организовать с помощью IB API.

В самом простом случае для организации подключения нам понадобятся всего два класса: EWrapper и EClient.

EClient и EWrapper — в каждом вашем приложении

Как я уже писал, два данных класса являются обязательными участниками любого приложения на Python3, где задействован TWS. И это не просто так. Без этих двух классов вы не сможете даже подключиться к терминалу, про всё остальное можно даже не говорить.

В первой статье мы рассмотрели роли данных классов. Теперь давайте поговорим об их применени.

Все начинается с импорта модулей, содержащих эти классы:

import ibapi.wrapper
import ibapi.client


После подключения данных классов займемся созданием их объектов. Тут будьте внимательны, есть одна хитрость:

wrp = ibapi.wrapper.EWrapper()
cln = ibapi.client.EClient(wrp)


Ничего странного не увидели? Ничего страшного, читайте далее.

Обратите внимание на порядок создания объектов, он должен быть именно таким, как написано в примере: сначала объект EWrapper, потом EClient. Почему? Потому что при инициализации объекта EClient мы должны передать в его инициализатор объект класса EWrapper(!). Вот почему важно создать объект EWrapper первым.

В следующих статьях я объясню почему API задуман таким образом. А пока запомните такой порядок и двигаемся дальше.

?Организация подключения к TWS

Связь с терминалом использует сокетное подключение. Если вы знаете как работать через сокеты в Python3, то понимаете на сколько это страшная конструкция из кода (что для организации сервера, что для организации клиента). Но при использовании IB API это всего лишь одна строчка кода:

cln.connect("127.0.0.1", 7496, 1)


Разберем параметры, передоваемые в этот метод. Вот они по порядку:
«127.0.0.1» — host (тип str) — название хоста или IP-адресс. Можно указать IP-адрес компьютера, если TWS запущен на другом компьютере в локальной сети. Можно отправить пустую строку. Можно отправить «localhost». В данном примере используется IP-адрес «локальной петли», то есть вы обращаетесь к TWS на том же компьютере, на котором создаете своё приложение.
7496 — port (тип int) — порт сокетного подключения, естественно. Не менее важная вещь для сокетного подключения. Не поленитесь, залезьте сейчас в настройки вашего TWS и посмотрите пункт Socket port. Используйте порт, который там указан.
1 — clientId (тип int) — идентификационный номер клиента. В самой первой статье я писал, что один TWS способен поддерживать связь с 32 клиентами одновременно, благодаря этому номеру TWS и различает клиентов. В будущем уделяйте пристальное внимание этому номеру, когда будете писать многопоточное приложение. Ну, и разумеется, в настройках TWS указывается Master API client ID — это «главный клиент». Измените настройку на единицу, как указано в коде. Потом можете менять на любое число (что в настройках, что в коде).

Небольшая ремарка: если вы работаете с WEB-сервером или экспериментировали на вашей машине с локальной сетью, посмотрите ваш файл host. Очень важно, чтобы там была строка: «127.0.0.1 localhost», в противном случае наступите на те же грабли, что и я. У меня localhost’у соответствовал IP-адрес другого компьютера в локальной сети — API на отрез отказывался подключаться к TWS, выкидывая ошибку.

Ну, а отключение от TWS еще проще:

cln.disconnect()


Еще одна ремарка: если вы — опытный Python-разработчик, то знаете, что надо обязательно отключать сокетное подключение. Если же нет, то объясняю: при открытии сокетного подключения (равно как и при открытии файла), интерпретатор Python создает специальный дескриптор для работы с подключением, так же TWS, со своей стороны, продолжает «держать» такое подключение, ожидая по нему запросов от IB API, по-этому, чтобы не происходило накопление дескрипторов и TWS не перегружался «пустыми» подключениями, не забывайте закрывать подключение из вашего кода. Тем более, что это всего одна строчка кода.

С подключением разобрались, но как-то «жиденько» получилось… Если запустить представленный код, он просто подключиться к терминалу, а потом отключиться от него. Давайте хотя бы спросим у него «как дела», а точнее попробуем получить «первичные сообщения».

?Получение сообщений от TWS

Любой запрос от вашего приложения к терминалу и любой ответ в обратную сторону, IB API считает «сообщениями». Они бывают разными и по сложности данных и по сложности обработки, но сейчас самое важное — это то, что они протекают в неком «канале обмена сообщениями» и, чтобы получить от терминала первичные сообщения, нам надо «подключиться» к такому «каналу». В конце статьи рассказывается про процессы, протекающие в момент подключения, поэтому сейчас давайте сосредоточемся на коде.

Для «подключения к каналу» нам потребуются еще два метода класса EClient:
.isConnected() — возвращает True, если IB API подтвердит, что подключен к TWS.
.run() — отвечает за прием и накопление сообщений от TWS.

Отдельного внимания заслуживает метод .run(). Вообще, метод .run() как и метод .connect() является самым сложным по коду и самым важным в классе EClient. Без этих двоих все остальное лишено практического смысла. Они две главные шестеренки, которые запускают и вращают весь механизм под названием «IB API». В следующих статьях я постараюсь детально описать, что делают эти методы, а пока возврашаемся к коду.

if cln.isConnected():
    print("Успешно подключились к TWS")
    tws.th = threading.Thread(target=tws.run)
    tws.th.start()
    tws.th.join(timeout=5)


Первая строка — метод .isConnected() — проверяет подключены ли мы к TWS, и, если подключение установлено, исполнится блок кода далее.

Во второй строке мы «принтуем» обычное сообщение. Я эту строку всегда держу в коде — потом легче анализировать результат работы: сразу видно подключился мой алгоритм к TWS или нет.

А вот с третьей строки начинается самая магия! Здесь мы запускаем параллельный поток с помощью модуля threading и класса Thread() внутри него. Данный модуль входит в стандартную библиотеку Python, поэтому он всегда доступен.

В четвертой строке мы запускаем только что созданный поток.

В пятой строке мы заставляем основной поток подождать, пока выполнится организованный нами параллельный поток. Обратите внимание на параметр, который передается в метод .join(), — это timeout, в нашем примере равный 5 секундам. Данный параметр ограничивает время ожидания параллельного потока до 5 секунд, то есть, у нашего параллельного потока, работающего с методом .run(), есть 5 секунд, чтобы сделать все свои дела. Если метод .run() затянет выполнение на большее время, поток принудительно завершится. На досуге вы можете поэкспериментировать с timeout, задавая другое значение. Только не ставьте одну секунду, по своему опыту скажу — это слишком мало, чтобы получить от TWS сообщения.

?Код в студию!

А вот и весь код целиком:

# Импортируем необходимые библиотеки
import ibapi.wrapper                             # Wrapper - класс, обрабатывающий сообщения от TWS
import ibapi.client                              # Client - класс, подключающий приложение к TWS
import threading                                 # Модуль по работе с потоками

# Создаем необходимые объекты для работы с IB API
wrp = ibapi.wrapper.EWrapper()                   # Объект класса Wrapper
cln = ibapi.client.EClient(wrp)                  # Объект класса Сlient, передаем wrapper в инициализатор

# Подключаемся к Trader Workstation
cln.connect("127.0.0.1", 7496, 1)                # Передаем хост, номер порта и ID клиента

# Работаем с TWS, подключаем стандартные сообщения
if cln.isConnected():                            # В случе успешного подключения к TWS
    print("Успешно подключились к TWS")          # Печатаем статус подключения
    cln.th = threading.Thread(target=cln.run)    # Организовываем поток
    cln.th.start()                               # Запускаем поток
    cln.th.join(timeout=5)                       # Ожидаем окончание выполнения потока или выходим через 5 сек.

# Отключаемся от Trader Workstation
cln.disconnect()                                 # Подключение закрывать обязательно!


При запущенном TWS код выше должен отобразить в панели вот это:
Успешно подключились к TWS

ERROR:root:ERROR -1 2104 Market data farm connection is OK:usfuture
ERROR:root:ERROR -1 2104 Market data farm connection is OK:cashfarm
ERROR:root:ERROR -1 2104 Market data farm connection is OK:usfarm
ERROR:root:ERROR -1 2106 HMDS data farm connection is OK:ushmds


Если у вас так же — примите мои поздравления! Вы создали алгоритм, который подключился к TWS и получил от него сообщения! УРА вам!

Спешу вас успокоить!
Порядок сообщений не важен, мы же работаем с асинхронными потоками в нашем коде! Не пугайтесь если у вас сообщения вышли в другом порядке.
Не пугайтесь 4 сообщения про ошибки — это не «настоящие» ошибки! По крайней мере так пишут в мануале. Но на самом деле их идентификатор (-1) и коды 2104 и 2106 говорят о том, что терминал успешно подключен к дата-центрам и серверам Interactive Brokers и мы можем запрашивать разные данные и отправлять разные торговые приказы на рынок.
Не пугайтесь, если в конце увидете исключение: AttributeError: ‘NoneType’ object has no attribute ‘close’. Это специфика нашего кода. В следующих статьях наш код будет другим и ошибка уйдет.

Подтвержденное подключение API

Когда вы запустите код, приведенный выше, в TWS можете увидеть вот такое окно:



Это TWS обнаружил подключение IB API к нему и просит вас подтвердить входящее подключение. Так будет происходить каждый раз, когда вы будете запускать свой код.

Что бы TWS не надоедал своими «принять входящее подключение?», посмотрите в настройках пункт «Allow connection from localhost only«. Поставьте галочку на против этого пункта. Более TWS не будет запрашивать ваше подтверждение при каждом подключении.

?Заглянем за кулисы

Этот раздел для самых любопытных. Здесь описывается процесс взаимодействия между API и TWS. Чтобы создавать алгоритмы, вам не обязательно знать, что тут написано, поэтому можете его пропустить.

Итак, связь с TWS устанавливается с помощью сокетов. В данном подключении TWS выполняет роль сервера, который принимает все запросы от API, обрабатывает их и направляет обратно ответы. Один экземпляр TWS способен поддерживать до 32 сокетных подключений (то есть способен вести до 32 ваших приложений) одновременно. При этом один экземпляр TWS способен принимать до 50 запросов в секунду от всех подключенных в эту секунду клиентов.

В свою очередь, API в данном сокетном подключении выполняет роль клиента, который «запускает» подключение к TWS, направляет ему запросы и принимает ответы. Один API способен принять бесконечное количество ответов от сервера, но из-за ограничений последнего, это количество не может быть больше 50 в секунду.

В момент вызова метода .connect() IB API запрашивает у операционной системы подключение по параметрам, указанным в методе. Если по каким-то причинам подключение невозможно, операционная система вернёт ошибку. В свою очередь IB API вернёт в ваше приложение ошибку 502, что будет означать, что TWS не запущен или ждёт подключение по другому порту и/или хосту.

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

Далее устанавливается «канал обмена сообщениями», организованный на двух потоках Python (модуль threading). Через первый поток IB API направляет запросы в TWS. Через второй поток читает сообщения из сокета, декодирует их и принимает дальнейшие действия, в зависимости от типа сообщения. Более подробно механизм приема сообщений я опишу в следующих статьях.

И наконец, терминал присылает свои первые сообщения через установленный канал. Это я и называл «первичными сообщениями» в тексте выше. Данные сообщения, как и все другие, проходящие через поток получения сообщений, IB API передает в наше приложение. Их мы и видим в панели терминала или IDE. Это просто статусы подключения TWS к серверам Interactive Brokers в интернете. Те самые 4 сообщения-ошибки, которые не являются ошибками. Это просто TWS рапортует, что подключился к своим источникам данных и готов к дальнейшей работе.

??Осторожно грабли!

Напоследок, расскажу про один эксперимент с методом .run().

Как-то мне надоело штудировать мануал IB API и я взялся поэкспериментировать. Из уже прочтенного в мануале я знал, что надо запустить этот метод, чтобы начать процесс обмена сообщениями.

Я так и сделал. Создал код, запустил его и… ничего не произошло… Немного посмотрел на панель, там ничего не было. И с мыслью «Ну, и хрен с тобой!» пошел заваривать себе кофе (шёл третий час ночи, нужно было топливо, чтобы не уснуть).

Через несколько минут я уже летел голопом к ноутбуку, потому что гул его кулера (из комнаты) смог «переорать» шум закипающего чайника (производитель ноутбука уверял, что сделал кулер на 30% тише).

Подлетев к монитору, я снова ничего не увидел на панели, но гул кулера меня сильно напрягал. Поэтому я аварийно завершил процесс Python, закрыл IDE, закрыл TWS и перезагрузил операционную систему. После того, как кулер успокоился, я решил разобраться «в чем дело».

Дальнейшее (уже внимательное) чтение мануала и анализ кода метода .run() показали, что данный метод управляет всеми входящими от терминала сообщениями в бесконечном цикле(!), при этом цикл завершается только если очередь сообщений полностью опустошается или, если происходит отключение от TWS. Поэтому для нормальной работы приложения требуется организовать отдельный поток, в котором и надо запускать метод .run(). Разумеется я этого не знал и запустил его в основном потоке своего кода. В ответ на это, метод .run() «подвесил» выполнение всей моей программы в бесконечном цикле, на каждой итерации которого проверял очередь на предмет входящих сообщений от TWS.

Будьте внимательны, не наступайте на грабли, на которые наступил я. Если вы не знаете про процессы (process) и потоки (threading) в Python — изучите их. Если вы не разбираетесь в очередях (queue), как способе синхронизации данных между потоками — так же изучите их. В противном случае, вам будет очень сложно разобраться в коде IB API самостоятельно.

?Заключение

Ух! Получилось много слов и мало кода! Понимаю вас, однако ничего сделать не могу. Слишком большой пласт информации пришлось изучить, осознать и подать.

Но несмотря на это в данной статье мы начали знакомство с IB API: узнали про два обязательных класса — EWrapper и EClient (центр всего внутри IB API), создали с их помощью скрипт, который подключается к терминалу и получает от него сообщения со статусом подключения. Рассмотрели сам процесс подключения, а в конце я рассказал, как нагрузил «боевого товарища».

И это всё только про подключение. Далее мы поговорим про контракты. К своему удивлению, вы узнаете, что это не торговые приказы терминалу. Про закачку истории, отправку торговых приказов через терминал и многое другое.
Источник / https://quantrum.me/
При копировании ссылка http://elitetrader.ru/index.php?newsid=378730 обязательна
Условия использования материалов