Продолжаю в свободное время писать сканер рынка.
В первую очередь оказалось, что ответы на запросы к TWS приходят асинхронно, поэтому линейно работать затруднительно. Это проявлялось в том, что некоторые ответы прилетали позже и терялись, т. к. я уже переходил к следующему тикеру в случае каких-либо ошибок. Поэтому я разделил программу на два потока — один из них регулярно делает запросы, а второй ждёт когда появятся ответы и записывает данные на диск. Собственно такая архитектура поддерживается API IB, т. к. при получении данных вам передаётся request id, по которому можно сориентироваться, куда записывать пришедшие данные, и этот id может быть ответом не на последний запрос, а например, на предпоследний.
Изменил вывод текста в консоль. Решил, что хорошая идея не выводить вперемешку в общий output сообщения из разных потоков, а разделить консольный вывод на две части, и привязать к каждой из них соответствующий поток. Пришлось отказаться от стандартных функций POSIX по выводу строк в консоль, и написать свой собственный класс консоли, который управляет выводом в screen buffer. Выглядит это примерно так:
Была решена проблема составления списка тикеров для биржи NASDAQ. Оказалось, что список торгуемых компаний можно найти на FTP самого насдака:
ftp://ftp.nasdaqtrader.com
В директории SymbolDirectory есть файл nasdaqlisted.txt, который обновляется регулярно. Я написал текстовый парсер, который считывает из этого файла тикеры и названия компаний и заносит их в ассоциативный контейнер. При этом некоторое время пришлось потратить на фильтр, отсеивающий из названия компании или торгуемого на бирже фонда «левые» словосочетания. Например «Avalanche Biotechnologies, Inc. — Common Stock» преобразуется просто в «Avalanche Biotechnologies, Inc». Разных вариантов там много, пришлось их все фиксить.
Работаю над проблемой исключения pacing violation. Я внедрил простой алгоритм, который учитывает кол-во отосланных запросов и отсчитывает время для нового запроса таким образом, чтобы не нарушать historical data request limitations от Interactive Brokers. Учитываются ограничения:
1) не более 6 запросов в 2 секунды.
2) не более 60 запросов за 10 минут.
Мне кажется, что второе правило не соблюдается сервером IB, реально получается отправить 55-57 запросов, после которых прилетает pacing violation. Будем уточнять «характер» работы сервера на практике и подстраиваться.
Как видно из лога, сейчас в листинге биржи NASDAQ находится 3088 компаний. Если исходить из пресловутых 60 запросов в течении 10 минут, понадобится 3088 / 60 ~= 52 * 10 = 520 мин (~9 часов) чтобы обновить дневные данные по всем компаниям NASDAQ. В принципе это не является проблемой, если программа работает постоянно и без перерывов. Если нужны часовые данные, список придётся уже сужать. На данный момент удалось получить дневные данные для большинства компаний из списка, размер базы данных — около 50 Мбайт.
Что в планах: стабильная работа без pacing violation, продуманный консольный вывод, timestamp на каждый запрос, стабильная круглосуточная работа программы без вылетов.