IQFeed - это не самый дешёвый (но и не самый дорогой) провайдер исторических (и real-time) данных финансовых бирж и разнообразных trading venues. Со своими плюсами и минусами.
В этой короткой статье расскажу, как закачать исторические данные из IQFeed при минимальном знании языка Python.
Disclosure: я заинтересованное лицо, хоть и не в коммерческом смысле, т.к. ищу тех с кем можно расшарить сервис IQFeed вскладчину. См. подробнее: smart-lab.ru/blog/439522.php
Из плюсов IQFeed – вполне сносное API для доступа к данным (уступающее по простоте RESTful сервисам, но минус REST – крайне низкая производительность, тяжело качать более или менее объёмные данные). Огромный выбор (акции, фьючерсы, опционы...). Минусы -тики только 180 дней (по SPY, кстати, это примерно 2.5GB). Adjusted данных по акциям нет.
Скачивание сводится к формированию запроса -“хочу такие-то данные с такой-то по такую-то дату”. Запрос — это текстовая строка, отправленная в канал коммуникации (сокет).
Вот примеры строки запроса на интрадей (минутки): «HIT,SPY,60,20160226 093000,20170226 160000,,093000,160000,1,,»
Расшифровка: дайте интрадей (HIT), по SPY, с барами в 60 секунд (т.е. минутки), с 20160226 093000 (YYYYMMDD HHMMSS), по 20170226 160000, без ограничения кол-ва выдаваемых строк, в торговую сессию с 093000 по 160000 (т.е. без extra-hours), с “прямой” (от старого к новому) сортировкой по датам (1), без RequestID, без DATAPOINTSPERSEND.
Формально запрос выглядит так:
HTT,SYMBOL,BEGINDATE BEGINTIME,ENDDATE ENDTIME,MAXDATAPOINTS,BEGINFILTERTIME,ENDFILTERTIME,DIRECTION,REQUESTID,DATAPOINTSPERSEND<CR><LF>
<CR><LF> это перевод строки “по-windowsовски” (\r\n). Говорю специально для тех, кто работает на Linux.
Запрос нужно послать на предварительно открытый сокет localhost:9100 или 127.0.0.1:9100 (под Linux может иметь значение), на котором “висит” работающий клиент IQFeed (в Windows запустить вручную через IQLink Launcher), а дальше вычитывать с него порции (блоки) данных (формат текстовый, кстати там не OHLC, а HLOC) соединяя вместе. Чтение производить до появления слова “!ENDMSG!” к конце очередного блока.
Грубо говоря – всё. Это минимально достаточный набор действий.
Попробуем сделать это на Python:
import sys import socket def read_historical_data_socket(sock, fname, recv_buffer=4096): buffer = "" data = "" cont=True while cont: buffer = sock.recv(recv_buffer).decode('ascii') data += buffer if "!ENDMSG!" in data[-12:]: data = data[:-12] cont=False firstlines=buffer.split("\n",2) if len(firstlines)>1: sys.stdout.write("Download progress(%s): %s \r" % (fname,firstlines[1][0:19]) ) sys.stdout.flush() data = "".join(data.split("\r")) data = data.replace(",\n","\n")[:-1] f = open(fname, "w") f.write(data) f.close() return if __name__ == "__main__": host = "127.0.0.1" port = 9100 syms = ["SPY", "AAPL", "GOOG", "AMZN"] for sym in syms: message = "HIT,%s,60,20140101 000000,20160101 000000,,,,1\n" % sym sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, port)) sock.sendall(message.encode()) read_historical_data_socket(sock,"%s.csv" % sym) sock.close sys.stdout.write("Downloaded symbol: %s " % sym) sys.stdout.flush() print
Собственно – всё. Пример проверен и на Linux (Python 2.7.12) и на Windows (Python 3.5.4).
Примечания:
Из недостатков скрипта — всё сначала закачивается в память. Тому есть причины: логика работы с сокетом — в получении равномерно нарезанных порций данных (4096 байт по умолчанию). Но сообщение !ENDMSG! может прийтись на границу двух блоков, потому быть «разрезанным» и при последовательном чтении с записью в файл (write-append block by block) просто неотдетектироваться. Для сохранения в файл on-the-fly без больших затрат памяти, нужна слегка усложнённая логика с хранением предыдущего считанного блока и детектом !ENDMSG! в них обоих сразу (в их конкатенации). Это усложняет скрипт. Для минуток особо морочиться не нужно (в худшем случае сотни мегабайт). Кроме того, нужно удалять запятые с конца строк (сделано) и реформатировать сами строки под конкретную программу ТА (не сделано).
На Linux скрипт работает хорошо.
И ещё. Не забывайте, что клиент IQFeed вылетает по timeout inactivity. Т.е. запустив клиент IQConnect, через IQLink, и потом поигравшись с скриптом вы долго можете мучиться с «неработающим сокетом», а причина банальна — iqfeed клиент закрылся по timeout inactivity (не пользовались долго).