python

Программный сбор данных о котировках

  • вторник, 11 июля 2017 г. в 03:12:26
https://habrahabr.ru/post/332700/
  • Машинное обучение
  • Python
  • Data Mining


Заголовок обязывает перейти непосредственно к программному коду… Но, думаю, все же необходима вводная часть. А зачем, собственно, это нужно?

Эффективные действия на бирже связаны с тщательным анализом происходящего на рынке. Что кроется за динамикой цифр, котировок?

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

Дилинговые залы брокерских контор… там существует своя, особая атмосфера. Атмосфера общения, обмена опытом, эмоциями. Мне нравятся дилинговые залы. По тому как человек входит в сделку, трейдеров можно разделить на две группы. Я буду говорить о тех, чей результат, как правило, печален. И таких трейдеров — большинство. Итак — описываю процесс входа в рынок трейдера соответствующей группы. В дилинговый зал вбегает мужчина лет 20-60 выкрикивает: «Куда идем?! Вверх?! Вниз?!» Со стороны встречающих слышаться неоднозначные выкрики «Вверх! Вниз!» Новоприбывший присоединяется к наиболее громко крикнувшей группе и… делает ТЫЦ. ТЫЦ по кнопке покупки или продажи. Все. Теперь человек в рынке. С этого момента он рискует своими деньгами. С этого момента трейдер не похож на трейдера. Он похож на болельщика. Вувузела в руках такого трейдера, думаю, была бы уместным инструментом торговли.


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

Результат таких сделок вполне предсказуем. Но… есть ли более счастливый исход? Конечно. И связан он с анализом данных котировок. Как получить эти данные? Как получить эти данные в больших объемах? Как здорово, что есть такая замечательная компания «ФИНАМ» и их интернет-ресурс finam.ru! Сервера «ФИНАМ» предоставляют замечательную возможность — скачивать котировки, например вот по такой форме (например):



Однако, таким образом предоставляется возможность скачать лишь один файл за одну загрузку. А что если мы хотим получить больше данных для анализа? Гораздо больше? Практически по всем инструментам! По всем периодам! Это даст богатейшие возможности для анализа данных. Оу… возможно ли такое? Ответ: да возможно.

Пока же определимся с перечнем бумаг (инструментов), а также с основными принципиальными моментами, которые позволят нам получить данные о котировках. Перечень бумаг (инструментов) которые предоставляться компанией «ФИНАМ» будем брать отсюда:



Эта страница интересна для нас тем, что на ней есть, во-первых, большая часть инструментов которые дает «ФИНАМ»; во-вторых, веб-ссылки, по которым можно перейти непосредственно на страницу каждой ценной бумаги (инструмента).

Ссылки имеют следующий вид:

www.finam.ru/profile/moex-akcii/polymetal-international-plc/export
www.finam.ru/profile/moex-akcii/pllc-yandex-n-v/export
www.finam.ru/profile/moex-akcii/alrosa-ao/export

Пропарсив соответствующую станицу получим файл ссылок. Теперь мы знаем где «живут» инструменты. Файл можете скачать по этой ссылке. Зачем нам место жительства каждого инструмента? Этот параметр нам еще пригодится. Запаситесь терпением. Пока имеем ссылки по 6131 бумаге (инструменту).

Что требует сервер «ФИНАМ»? Какие параметры для получения данных? Давайте попробуем получить один файл, и посмотрим параметры запроса. Скачивая котировки компании Polymetal, имею вот такой GET запрос:
__http://export.finam.ru/POLY_170620_170623.txt?market=1&em=175924&code=POLY&apply=0&df=20&mf=5&yf=2017&from=20.06.2017&dt=23&
mt=5&yt=2017&to=23.06.2017&p=8&f=POLY_170620_170623&e=.txt&cn=POLY&dtf=1&tmf=1&
MSOR=1&mstime=on&mstimever=1&sep=1&sep2=1&datf=1&at=1

Среди всего перечня хотелось бы акцентировать внимание на параметрах em, market, code. Параметр em следует понимать как индекс, своеобразную метку бумаги (инструмента). Если мы хотим скачивать не один инструмент, а массив данных по нескольким бумагам (инструментам) мы должны знать em каждого из них. Переменная market говорит о том, где вращается данная бумага (инструмент) – на каком рынке? Маркетов много: МосБиржа топ***, МосБиржа пифы***, МосБиржа облигации***, Расписки и т.д. Параметр code – это символьная переменная по инструменту.

Итак, для получения файла котировок нам нужно добыть эти три параметра: em и market и code. По всем бумагам (инструментам). Вопрос — где их взять? Ответ: вспоминаем о файле со ссылками. В файле есть, например, такая ссылка:

www.finam.ru/profile/moex-akcii/polymetal-international-plc/export

Зайдем на нее и в исходном коде страницы увидим то, что нам нужно — в элементах javascript сидят наши искомые параметры, которые относятся к данной бумаге (инструменту):

Finam.IssuerProfile.Main.issue = {"quote": {"id": 175924, "code": "POLY", "fullUrl": "moex-akcii/polymetal-international-plc", "title": "Polymetal", "decp": 1, "testDriveEnabled": false, "market": {"id": 1, "title": "МосБиржа акции", "volumeEnabled": true},"info": {"decp": 1, "last": 680, "pchange": 1.87266, "change": 12.50001, "bid": null, "ask": null, "open": 668, "high": 686, "low": 666, "close": 667.5, "volume": 53037, "date": "05.07.2017 18:47:18", "weekMin": 653.5, "weekMax": 688, "monthMin": 653.5, "monthMax": 753, "yearMin": 572, "yearMax": 1009.5,"currency": "руб.","volumeCode": "шт."},"
/*…тут еще куча важных параметров, но они нам не нужны …*/
 175924, "url": "/profile/moex-akcii/polymetal-international-plc/secondary/", }, "corporativeEvents": {"quote": 175924, "url": "/profile/moex-akcii/polymetal-international-plc/corporate/", }, "blogsAndGraphs": {"quote": 175924, "url": "__http://whotrades.com/markets/instrument/polymetal-international-plc", "count": "1", "pageSize": 1, "pageNumber": 1, "pagesCount": 1}}};

Заметим, что в данном кусочке кода id — это и есть em; имеется параметр code, а также параметры маркета – id и его русскоязычное название. Данный кусок кода с вариациями присутствует у каждого бумаги (инструмента). Сходим, например, на:

www.finam.ru/profile/moex-akcii/pllc-yandex-n-v/export
www.finam.ru/profile/moex-akcii/alrosa-ao/export

и увидим все то же самое. Теперь, думаю, общая цепочка получения данных понятна: в цикле перебираем ссылки, где живут отдельные бумаги (инструменты). Парсим кусочки javascript, собирая параметры em, market и code для каждой позиции. Имея на руках эти данные, можем программно заходить на сервер «ФИНАМ» и получать файлы котировок. Осталось дело за техникой исполнения.

Чем будем парсить? Парсить будем, используя Java. И… из всех велосипедов я выбираю тот, который стоит у меня в гараже. А именно Jsoup. Хотя можно было бы использовать и htmlunit.



Небольшое уточнение. При парсинге страницы мною были получены также данные – русскоязычное название бумаги (1) и раздел, в который «ФИНАМ» определили данную бумагу (инструмент) (2). Таким образом, на входе парсера имеется три файла. Напомню, имеем 6131 позиций — бумаг (инструментов). Всю эту информацию, а также результаты парсинга объединим в один файл. Код парсера можно скачать по этой ссылке.

В результате выполнения имеем файл function_parameters.csv. Каждая строка файла при построчном считывании может использоваться как перечень параметров для функции обращения к серверу «ФИНАМ» за котировками. Файл function_parameters.csv можно скачать по этой ссылке.

Для того чтобы написать функцию обращения к серверу «ФИНАМ» (а писать мы будем ее на Python), еще раз рассмотрим параметры GET запроса:
__http://export.finam.ru/POLY_170620_170623.txt?market=1&em=175924&code=POLY&apply=0&df=20&mf=5&yf=2017&from=20.06.2017&dt=23&
mt=5&yt=2017&to=23.06.2017&p=8&f=POLY_170620_170623&e=.txt&cn=POLY&dtf=1&tmf=1&
MSOR=1&mstime=on&mstimever=1&sep=1&sep2=1&datf=1&at=1

POLY_170620_170623 – очевидно, что данная строка представляет параметр code, а также временные характеристики.

.txt – расширение файла; расширение упоминается в параметре e; при написании функции следует помнить об этом нюансе.

Примем также во внимание содержимое исходного кода страницы типа www.finam.ru/profile/moex-akcii/gazprom/export внутри тэга form (где name=«exportdata»). Характеризуем показатели.

market, em, code – об этих параметрах, упоминал ранее, при обращении к функции их значения будут приниматься из файла.
df, mf, yf, from, dt, mt, yt, to – это параметры времени.
p — период котировок (тики, 1 мин., 5 мин., 10 мин., 15 мин., 30 мин., 1 час, 1 день, 1 неделя, 1 месяц)
e – расширение получаемого файла; возможны варианты — .txt либо .csv
dtf — формат даты (1 — ггггммдд, 2 — ггммдд, 3 — ддммгг, 4 — дд/мм/гг, 5 — мм/дд/гг)
tmf — формат времени (1 — ччммсс, 2 — ччмм, 3 — чч: мм: сс, 4 — чч: мм)
MSOR — выдавать время (0 — начала свечи, 1 — окончания свечи)
mstimever — выдавать время (НЕ московское — mstimever=0; московское — mstime='on', mstimever='1')
sep — параметр разделитель полей (1 — запятая (,), 2 — точка (.), 3 — точка с запятой (;), 4 — табуляция (»), 5 — пробел ( ))
sep2 — параметр разделитель разрядов (1 — нет, 2 — точка (.), 3 — запятая (,), 4 — пробел ( ), 5 — кавычка ('))
datf — Перечень получаемых данных (#1 — TICKER, PER, DATE, TIME, OPEN, HIGH, LOW, CLOSE, VOL; #2 — TICKER, PER, DATE, TIME, OPEN, HIGH, LOW, CLOSE; #3 — TICKER, PER, DATE, TIME, CLOSE, VOL; #4 — TICKER, PER, DATE, TIME, CLOSE; #5 — DATE, TIME, OPEN, HIGH, LOW, CLOSE, VOL; #6 — DATE, TIME, LAST, VOL, ID, OPER).
at — добавлять заголовок в файл (0 — нет, 1 — да)

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

# -*- coding: utf-8 -*-
"""
Created on Sat Jun 24 01:46:38 2017

@author: optimusqp
"""
import urllib


code='POLY';
e='.txt';
market='1'
em='175924';
e='.txt';
p='3';
yf='2017';
yt='2017';
month_start='05';
day_start='20';
month_end='06';
day_end='20';
dtf='1';
tmf='1';
MSOR='1';
mstimever='0'
sep='1';
sep2='3';
datf='1';
at='1';


year_start=yf[2:];
year_end=yt[2:];
mf=(int(month_start.replace('0','')))-1;
mt=(int(month_end.replace('0','')))-1;
df=(int(day_start.replace('0','')))-1;
dt=(int(day_end.replace('0','')))-1;


def quotes(code,year_start,month_start,day_start,year_end,month_end,day_end,e,market,em,df,mf,yf,dt,mt,yt,p,dtf,tmf,MSOR,mstimever,sep,sep2,datf,at):
    
    page = urllib.urlopen('http://export.finam.ru/'+str(code)+'_'+str(year_start)+str(month_start)+str(day_start)+'_'+str(year_end)+str(month_end)+str(day_end)+str(e)+'?market='+str(market)+'&em='+str(em)+'&code='+str(code)+'&apply=0&df='+str(df)+'&mf='+str(mf)+'&yf='+str(yf)+'&from='+str(day_start)+'.'+str(month_start)+'.'+str(yf)+'&dt='+str(dt)+'&mt='+str(mt)+'&yt='+str(yt)+'&to='+str(day_end)+'.'+str(month_end)+'.'+str(yt)+'&p='+str(p)+'&f='+str(code)+'_'+str(year_start)+str(month_start)+str(day_start)+'_'+str(year_end)+str(month_end)+str(day_end)+'&e='+str(e)+'&cn='+str(code)+'&dtf='+str(dtf)+'&tmf='+str(tmf)+'&MSOR='+str(MSOR)+'&mstimever='+str(mstimever)+'&sep='+str(sep)+'&sep2='+str(sep2)+'&datf='+str(datf)+'&at='+str(at))
    f = open("company_quotes.txt", "w")
    content = page.read()
    f.write(content)
    f.close()

qq = quotes(code,year_start,month_start,day_start,year_end,month_end,day_end,e,market,em,df,mf,yf,dt,mt,yt,p,dtf,tmf,MSOR,mstimever,sep,sep2,datf,at)

Код функции можно скачать также по этой ссылке.

Что дальше? Теперь возможно использовать данную функцию в цикле по имеющимся у нас позициям. Всего имеем, напомню, 6131 позицию. Из файла function_parameters.csv подгружаем параметры, указываем дату, выбираем нужный формат. И, используя данный код, не забудьте о правилах хорошего тона – поставьте задержку в пару секунд в итерацию цикла, дабы не перегружать сервер-источник.

Данных для анализа рынка, думаю, у вас будет предостаточно. Искренне надеюсь, что клиентов у компании «ФИНАМ» после написания данной статьи только прибавится!