Что такое Алгопак я уже писал, как и то, как можно сделать для библиотеки на Python moexalgo документацию из докстрингов – ведь пока никакого хорошего пособия с “разжеванными” примерами от Мосбиржи не существует.
На данный момент я поставил задачу – вытащить исторические данные по российским акциям и в дальнейшем их регулярно обновлять. Это позволит мне при изучении Backtrader использовать данные Мосбиржи для компонента DataFeeds, а также разрабатывать и тестировать на исторических данных собственные торговые стратегии.
Приступим. Отправная точка – раздел moexalgo на Гитхабе. Файл samples/quick_start.ipynb начинается с примера:
Сразу же встает вопрос: что такое stocks?
На самом деле это алиас. Механизм алиасов в коде библиотеки на Python позволяет создавать псевдонимы для модулей или их функций, классов и переменных. Это полезно, когда нужно сократить длинные имена или импортировать функции или классы из модуля с более удобными именами. В библиотеке moexalgo в модуле market.py определены алиасы:
_ALIASES= { ‘index’: (‘index’, ‘SNDX’), ‘shares’: (‘shares’, ‘TQBR’), ‘stocks’: (‘shares’, ‘TQBR’), ‘EQ’: (‘shares’, ‘TQBR’), }
Это означает, что объекты Market(“stocks”) и Market(“shares/TQBR”) эквивалентны и создают объект для работы с данными по акциям в режиме основных торгов TQBR. Первый вариант с использование “stocks” по замыслу разработчиков является более удобным и коротким способом написания кода.
Выражение Market(‘shares/TQBR’) создает объект класса Market из библиотеки moexalgo, который представляет раздел рынка акций Московской биржи (TQBR).
TQBR позволяет осуществлять сделки с акциями.
BOARDID на Московской бирже – это уникальный цифровой идентификатор торговой площадки или режима торгов, в рамках которого проходят торги различными финансовыми инструментами.
Каждой торговой площадке на Мосбирже присваивается свой BOARDID:
- BOARDID=TQBR – основная площадка для акций и облигаций (Торгово-Quotation Board)
- BOARDID=TQTF – площадка для биржевых ETF и паев ПИФов
- BOARDID=EQBR – площадка для торговли акциями малой капитализации
- BOARDID=FQBR – площадка для корпоративных и муниципальных облигаций и т.д.
Зная BOARDID конкретной ценной бумаги, можно однозначно определить на какой площадке и в рамках какого режима она обращается. Этот идентификатор широко используется в отчетности и API Московской биржи.
Вернемся к нашему коду Market(“shares/TQBR”).
Market – это класс в moexalgo, представляющий раздел биржевого рынка. При создании экземпляра класса Market нужно указать название раздела и режим торгов. В нашем случае shares – название раздела акций и TQBR – режим основных торгов акциями на Московской бирже.
А например другой вариант Market(“index/SNDX”) создаст объект класса Market для работы с индексами фондового рынка Московской биржи. Здесь index – раздел фондовых индексов, SNDX – режим основных торгов индексами Мосбиржи.
Мы же сосредоточимся на акциях.
Далее мы уже можем получить справочную информацию о всех инструментах рынка.
from moexalgo import Market stocks = Market("shares/TQBR") all_stocks = stocks.tickers() # Вызоваем метод tickers() на экземпляре класса Market print(type(all_stocks)) # <class 'pandas.core.frame.DataFrame'> или <class 'list'> в зависимости от того, где вызвали метод (в интерактивной среде или нет)
Здесь мы создаём объект stocks, который будет представлять раздел акций Московской биржи в режиме основных торгов TQBR. И далее вызываем метод stocks.tickers(), который возвращает информацию по всем инструментам (тикерам акций) с их метаданными в этом разделе рынка. Результат сохраняем в переменной all_stocks. Можем вывести на печать тип и увидим, что это будет список словарей с данными по каждой акции.
Тут есть одно важное замечание(!): метод tickers возвращает нам тип данных в зависимости от того, где выполняется код программы. Если программа выполняется в интерактивной среде (например, в Jupyter Notebook), то функция tickers возвращает результат в виде pandas DataFrame, потому что в интерактивных средах удобно работать с DataFrame. Если код выполняется не в интерактивной среде, то функция tickers возвращает результат в виде итератора объектов данных (в данном случае TradeStat), потому что вне интерактивных сред это видимо более эффективный формат.
Так как в конечном итоге нам нужен код для обычной программы, и мне все же хочется оперировать с данными именно через pandas, то давайте полученную информацию в виде списка list отправим в Dataframe pandas.
(Замечу, что Pandas для знакомства и начала работы с ним очень простой. Главное понять, что DataFrame в pandas – это по сути плоская таблица данных, практически как Excell. Все остальное можно узнавать и изучать по мере необходимости.)
немного изменим код:
import pandas as pd from moexalgo import Market stocks = Market("shares/TQBR") all_stocks = pd.DataFrame(stocks.tickers()) print(all_stocks)
и получаем таблицу Dataframe, включающую 248 акций
Вот как она выглядит, если программу запустить как обычный скрипт:
27 колонок со следующими названиями [‘SECID’, ‘BOARDID’, ‘SHORTNAME’, ‘PREVPRICE’, ‘LOTSIZE’, ‘FACEVALUE’, ‘STATUS’, ‘BOARDNAME’, ‘DECIMALS’, ‘SECNAME’, ‘REMARKS’, ‘MARKETCODE’, ‘INSTRID’, ‘SECTORID’, ‘MINSTEP’, ‘PREVWAPRICE’, ‘FACEUNIT’, ‘PREVDATE’, ‘ISSUESIZE’, ‘ISIN’, ‘LATNAME’, ‘REGNUMBER’, ‘PREVLEGALCLOSEPRICE’, ‘CURRENCYID’, ‘SECTYPE’, ‘LISTLEVEL’, ‘SETTLEDATE’]
и вот так выглядит таблица, если программу запустить в интерактивной среде в Jupyter Notebook:
только 9 колонок со следующими названиями [‘ticker’, ‘shortname’, ‘lotsize’, ‘decimals’, ‘minstep’, ‘issuesize’, ‘isin’, ‘regnumber’, ‘listlevel’]
Таблица с акциями в сохраненном pdf файле.
Вы заметили, что получаемые dataframe немного отличаются (!) друг от друга. Обращайте внимание на названия колонок. Например символьное обозначение акции в первом варианте трактуется как SECID, а во втором как ticker. Это очень важно при написании программы. И если вы пишите программу модулями в Jupyter Notebook и обращаетесь в ней к названиям колонок, то скорее всего этот код в скрипте при запуске в обычном режиме выдаст ошибку.
SECID – уникальный идентификатор инструмента, присваивается биржей. Тикер – буквенно-цифровой код, также идентифицирующий инструмент. SECID всегда уникален для каждого финансового инструмента на бирже. Тикер уникален только в рамках одного рынка. Тикер является “внешним” идентификатором – он используется для отображения в торговых системах. А SECID больше предназначен для внутреннего использования.
Пройдемся по заголовкам полученного Dataframe:
Остановимся на последнем параметре. На Московской бирже параметр listlevel обозначает уровень листинга, то есть котировальный список, в который включены ценные бумаги (акции, облигации).
Выделяют 3 уровня листинга:
Чем выше уровень листинга, тем выше требования к эмитенту и качество его ценных бумаг с точки зрения надежности, информационной прозрачности и ликвидности. Все голубые фишки относятся к первому уровню листинга.
Ну и ниже приведен код программы, которая создает списки акций с сортировкой по листингам.
Я думаю это удобный функционал, чтобы в дальнейшем пользователь мог скачать все исторические данные не только по конкретной акции, а например сразу по всем, включенным в конкретный уровень листинга. Дополнительно отчет сохраним в текстовый файл.
Давайте детально разберем код, чтобы любой начинающий с нуля “питонист”, собственно говоря как и я, мог в нем разобраться.
Мы имеем all_stocks – это dataframe с информацией по всем акциям.
Выражение listlevels = sorted(all_stocks[“LISTLEVEL”].unique())
Здесь метод .unique() используется для возврата уникальных значений в колонке “LISTLEVEL” нашего датафрейма all_stocks. В результате будет создан массив (listlevels) из уникальных значений в колонке listlevel. И сразу же методом sort() отсортируем значения в массиве listlevels. Получим [1, 2, 3].
with open(“stock_listing.txt”, “w”, encoding=”utf-8″) as file:
в этом коде используется оператора with в сочетании с функцией open(). Оператор with используется для автоматического управления ресурсами, такими как файлы или соединения с базами данных. Он гарантирует, что после выполнения блока кода ресурсы будут корректно закрыты или освобождены.
В этом коде функция open() используется для открытия файла с именем “stock_listing.txt” в режиме записи (“w”). Оператор with используется для гарантии правильного закрытия файла после того, как он больше не нужен. Если файла stock_listing.txt нет, то он будет создан, если есть – перезаписан. Благодаря with, нам не нужно вызывать метод file.close(), так как он автоматически будет выполнен после выхода из блока кода.
print(f”Всего в Алгопаке доступны данные по {all_stocks.shape[0]} акциям Мосбиржи”, file=file)
print выводит сообщение в терминал, а print с параметром file – записывается в файл. В фигурных скобках выполняется выражение {all_stocks.shape[0]}, в котором применен атрибут shape у объекта all_stocks, чтобы узнать количество строк в нем. Атрибут shape возвращает кортеж с двумя элементами: количество строк и количество столбцов в датафрейме, соответственно элемент [0] содержит количество строк. Фактически мы узнали сколько строк в нашем DataFrame с информацией по акциям, т.е. узнали количество акций.
Далее запускаем цикл for level in listlevels: , т.е. выполняется итерация по элементам переменной listlevels. На каждой итерации значение level будет содержать очередной элемент из listlevels, т.е. на каждой итерации в переменную level будет попадать очередное значение из listlevels, которая содержит уникальные уровни листинга.
В цикле происходит следующее:
stocks_level = all_stocks[all_stocks[“LISTLEVEL”] == level]
Здесь происходит фильтрация данных. Мы берем весь датафрейм all_stocks и отбираем из него только те строки, в которых значение в столбце LISTLEVEL равно текущему значению переменной level, например 1,2 или 3. В результате в stocks_level будут записаны данные только по тем акциям, которые имеют текущее значение LISTLEVEL.
print(f”Для {level} уровня листинга отобрано {stocks_level.shape[0]} акций:”)
Выводим сообщение в терминал (в файл с параметром file) о том, какое количество акций отобрано для каждого уровня листинга.
list_tickers = stocks_level[“SECID”].tolist()
Здесь из отфильтрованных данных по акциям stocks_level (датафрейм) извлекаем столбец с тикерами акций (‘SECID’) и преобразуем его в список с помощью tolist().
.tolist() – это метод, который используется для преобразования массива в список (list).Таким образом в list_tickers попадают тикеры всех акций с текущим уровнем листинга.
Аналогично создаем список list_shortnames с перечнем SHORTNAME всех акций текущего уровня листинга.
Далее выводим список тикеров и названий акций из двух списков. Для этого используем функцию zip для объединения двух списков list_tickers и list_shortnames в один итератор, который возвращает кортежи из элементов с одинаковыми индексами. В каждом кортеже будут содержаться элементы из соответствующих позиций исходных итерируемых объектов (списков). Это удобно, когда нужно проходить по нескольким спискам одновременно и выполнять операции с элементами из одной и той же позиции в каждом списке.
Выводим с помощью цикла for в терминал и файл список из пар элементов (ticker и shortname) из созданного функцией zip кортежа.
На каждой итерации цикла мы будем получать данные для очередного уникального LISTLEVEL.
Собственно говоря выше детально описан весь код. Надеюсь, что в нем все понятно)) Результат работы программы будет выведен в терминал и продублирован в текстовый файл, можете посмотреть сохраненную копию stock_listing.txt .
Теперь я буду писать вторую часть кода для непосредственного скачивания исторических данных с выбором:
— какой-либо конкретной акции,
— либо всех акций заданного уровня листинга,
— или вообще всех 248 акций
— за определенный период
— с заданным таймфреймом.
И это тема для следующей публикации. Продолжение следует..
Чтобы быть в курсе Telegram-канал Алготрейдинг на Python
Видео по теме: