Мнение специалиста от 29 июня 2013 года | Конструктивная кибернетика

Извлечение данных о финансовых индикаторах из среды Web


Допустим Вы, также как и автор данной заметки, ведёте научные исследования в области приложения математических методов к финансовой сфере. И Вам для анализа понадобились какие-то данные, которые доступны в сети Интернет. Вы можете конечно поступить по "старинке" – взять и скопировать эти данные из интернет браузера в какой-нибудь текстовый редактор, там их распарсить, структурировать и скопировать файл в нужное место. У этого подхода есть несколько "но", основные из них это: трудности при большом объёме данных или при сложной структуре их организации; низкая эффективность труда при многократных, однотипных запросах. Путь второй – написать на каком-нибудь универсальном языке программирования высокого уровня (например C/C++, C#, Java, Python, PHP, Perl, Ruby, Haskell – читатели продолжают...) собственное приложение (скрипт), которое запросит данные, распарсит их, структурирует и сохранит их в файл или в базу данных. Тогда основные недостатки первого подхода аннулируются, но появляются другие, а именно: скорость разработки, и затраты на отладку. Есть и ещё одна проблема – знание этих самых языков и умение решать на них требуемые задачи. Автор заметки, например, знает С/С++, но в объёме, необходимом для разработки консольных вычислительных приложений (OpenMP, MPI, Intel MKL) для высокопроизводительных кластеров. Как делать запрос к сайту, и парсить HTML на C/C++ – для него это великая тайна. К счастью, есть третий путь.

Для начала вспомним, для чего нам нужны эти самые финансовые (или иные) данные, доступные в сети Интернет. Правильно – для математических манипуляций с ними. Если Вам вполне хватает табличных редакторов (типа Microsoft Excel), тогда по всей видимости Вам вполне подойдёт описанный выше первый путь. Если считате в чём-то помощней (например PTC MathCAD), то у Вас появляются аргументы для перехода на ещё более мощные решения (см. далее). Если Вы все свои математические расчёты делаете в программах, собственноручно написанных на каком-нибудь из универсальных языков программирования высокого уровня, то это конечно хардкор, но насколько это эффективно? Встречный вопрос: а что эффективно? Автор считает что эффективно – это Wolfram Mathematica. Это и есть третий путь.

Ниже мы продемонстрируем, как с помощью этой программы можно организовать многократную выгрузку данных из Web, их парсинг, структурирование и сохранение. Что касается нативной математической обработки, то её естественно можно (и нужно) проводить в этой же мощной среде. В качестве ремарок. Во-первых, эта заметка возникла не на пустом месте. Автор использовал этот подход (код программы см. ниже) для получения результатов, кратко изложенных в докладе: A.V. Makarenko, Symbolic CTQ-analysis – a new method for studying of financial indicators // International Conference "Advanced Finance and Stochastics" / Book of Abstracts – Moscow, 24-28 June 2013, Steklov Mathematical Institute, pp. 63-64. Презентация доступна по ссылке. Во-вторых, для хардкодеров (а также и халиварщиков) будет полезен пост: Jon McLoone, Code Length Measured in 14 Languages. Текст поста доступен по ссылке.

Итак, организуем выгрузку данных, с официального web-сайта Центрального Банка РФ, о курсах 5-ти базовых валют (USD [Доллар США], EUR [Евро], JPH [Японская йена], CHF [Швейцарский франк], GBP [Фунт стерлингов Соединённого королевства]) по отношению к RUB [рублю РФ]. Для начала сконфигурируем среду и зададим ряд исходных констант:

(* Конфигурируем среду *)
emove["Global`*"];
$HistoryLength = 0;

(* Введём начальную и конечную даты периода выгрузки данных, в формате ДД.ММ.ГГГГ *)
BeginDate = "01.01.2000"; (* начальная дата *)
EndDate = "31.05.2013"; (* конечная дата *)

(* формат ГГГГ, ММ, ДД – это точка отсчёта для относительных отметок времени, см. ниже *)
AbsoluteDateStart = {2000, 1, 1};

Далее изучим структуру web-страницы, содержащую необходимый нам запрос. Он имеет вот такой вид (на момент написания заметки): http://www.cbr.ru/currency_base/dynamics.aspx?
VAL_NM_RQ=R01010&date_req1=01.06.2013&r1=1&date_req2=10.06.2013&
C_month=06&C_year=2013&rt=1&mode=1&x=23&y=6.

Важное замечание: ЦБ РФ сравнительно редко, но меняет интерфейс доступа к данным, поэтому вид запроса может измениться и потребуется модификация кода связанного с организацией запроса и первичным парсингом html страниц.

Присвоим ряду констант их значения:

StrMainURL = "http://www.cbr.ru/currency_base/dynamics.aspx";
Currency = {
{"USD", "R01235"},
{"EUR", "R01239"},
{"JPH", "R01820"},
{"CHF", "R01775"},
{"GBP", "R01035"}};

Далее получим дополнительные данные, необходимые для корректного выполнения запроса:

(* Это текущие год и месяц, переведённые в string тип *)
{CurrentYear, CurrentMonth} = ToString /@ DateList[TimeZone -> $TimeZone][[1 ;; 2]];

И, наконец, получим текстовое содержимое HTML страниц:

(* Строковые переменные внутри списка Parameters - это фрагменты запроса *)
TextRaw = ImportString[URLFetch[StrMainURL, "Content",
"Parameters" -> {"VAL_NM_RQ" -> Last@#,
"date_req1" -> BeginDate, "date_req2" -> EndDate, "r1" -> "1",
"C_month" -> CurrentMonth, "C_year" -> CurrentYear,
"rt" -> "1", "mode" -> "1"}], "HTML"] & /@ Currency;

Примечание: Весьма мощная функция URLFetch[...] появилась в 9-й версии Wolfram Mathematica и в ответ на запрос выдаёт сырой HTML, поэтому дополнительно к ней используется функция ImportString[...], которая из сырого HTML извлекает "чистый" текст.

Что делать, если 9-й версии Wolfram Mathematica у Вас нет, а работать всё же необходимо? Тогда скачать последнюю полнофункциональную версию программы Вы можете отсюда (на момент написания заметки, это 9.0.1). Если скачивать Вы не хотите (не можете), то тогда у Вас два пути: первый – см. начало заметки; второй – вместо функции URLFetch[...] использовать функцию Import[...]. Эта функция, кстати, доступна в Wolfram Mathematica начиная c 4-й версии (последняя модификация произведена в 7-й версии).

Для удобной работы с функцией Import[...] присвоим ряду констант их значения:

URLStr1 = StrMainURL <> "?VAL_NM_RQ=";
URLStr3 = "&date_req1=";
URLStr5 = "&date_req2=";
URLStr7 = "&r1=1&C_month=";
URLStr9 = "&C_year=";
URLStr10 = "&rt=1&mode=1";

Далее, сформируем полный URL адрес запроса, и выполним сам запрос:

FullURL = (URLStr1 <> Last@# <> URLStr3 <> BeginDate <> URLStr5 <>
EndDate <> URLStr7 <> CurrentMonth <> URLStr9 <> CurrentYear <>
URLStr10) & /@ Currency;
TextRawOld = Import[#, "HTML"] & /@ FullURL;

Ради эксперимента проверим идентичность данных, полученных с помощью функций URLFetch[...] и Import[...]:

TextRaw === TextRawOld

True

Как видно из вышеизложенного, доступ к содержимому web-сайта осуществляется почти в одну строку (в одну функцию). То есть если знать синтаксис языка Wolfram и понимать что делаешь, то загружать html страницы средствами Mathematica практически также просто, как вбивать запрос в адресную строку браузера и нажимать Enter. Остальное делается фактически также легко и красиво.

После получения сырых данных, отфильтруем их и подготовим к распознаванию:

DataRaw = StringTrim@StringReplace[
StringCases[#, "Дата Единиц Курс"~~str:___~~"История валюты" -> str],
"," -> "."] & /@ TextRaw;

Важное замечание: ЦБ РФ периодически меняет интерфейс доступа к данным, может потребоваться модификация кода.

Далее начинается собственно этап распознавания (по идее этот код, и тот что ниже, уже не зависит от интерфейса доступа к данным, но связан только со структурой и представлением данных):

(* Получаем построчное представление таблицы *)
DataSolidStr = (Flatten@StringSplit[#, "\n"]) & /@ DataRaw;
(* Разбиваем строки на столбцы *)
DataSeparatedStr = Map[StringSplit[#, " "] &, DataSolidStr, {2}];
(* Переводим в машиночитаемый формат, элементарная запись имеет формат: Дата Единиц Курс *)
SrcData = Map[{DateList[{#[[1]], {"Day", "Month", "Year"}}][[1 ;; 3]],
ToExpression@#[[2]], ToExpression@#[[3]]} &, DataSeparatedStr, {2}];

Что мы видим? Парсинг таблицы из трёх столбцов и двух типов данных, простого – Real и сложного – Date, осуществлён в три строки. Просто, эффективно, и элегантно. На самом деле код можно ещё сократить, но уже с потерей читаемости и понимания. Поэтому мы этого делать не будем, не тот случай, как говорится.

Извлекаем список дат, проверяем их сопоставимость:

DateOnData = Map[First@# &, SrcData, {2}];
QuantityCurrency = Length@DateOnData;
And@@Flatten@Table[DateOnData[[i]] === DateOnData[[j]],
{i, 1, QuantityCurrency - 1}, {j, i + 1, QuantityCurrency}]

True

Очищаем память от всего объёмного и ненужного:

Remove[TextRaw, TextRawOld, DataRaw, DataSolidStr, DataSeparatedStr];

Далее осуществляем ряд сервисных операций:

(* Формируем выделенный список дат *)
ListDate = First@DateOnData;
(* Получаем кол-во отсчётов временного ряда *)
QuantityCount = Length@ListDate;
(* Формируем относительные временные отметки для временных рядов *)
(* Именно здесь используется введённая в самом начале константа AbsoluteDateStart *)
ListRelativeDate = Accumulate[
DateDifference@@@Partition[Prepend[ListDate, AbsoluteDateStart], 2, 1]];
(* Формируем наборы данных удобные для дальнейшей работы *)
DateRateOnData = Map[{First@#, Last@#} &, SrcData, {2}];
DateCountOnData = Map[{First@#, #[[2]]} &, SrcData, {2}];
RateOnData = Flatten /@ Map[{Last@#} &, SrcData, {2}];

Отобразим полученные данные:

ColorSeq = {Red, Blue, Darker@Green, Orange, Brown};
VisData = DateListPlot[DateRateOnData, Joined -> True, PlotStyle -> (Directive[Thick, #] & /@ ColorSeq),
ImageSize -> 800, AspectRatio -> 1/3, PlotLabel -> "Курс валют по отношению в рублю РФ",
PlotLegends -> Placed[SwatchLegend[ColorSeq, First@# & /@ Currency,
LegendLayout -> "Row", LegendMarkerSize -> {30, 25}], Bottom]];

Если Вы сделали всё правильно (и web-сайт ЦБ РФ доступен), то должны увидеть вот такой красивый и информативный рисунок:

VisData

Курс валют по отношению в рублю РФ.

Далее данные можно сохранить либо в файл с помощью функций Export[...], Write[...] – зависит от Ваших задач, либо в базу данных – используя Database Connectivity.

И самое главное. С полученными данными возможно производить самые разнообразные вычисление не выходя из Wolfram Mathematica: встроенные в неё средства могут удовлетворить практически любые научные (и не только научные) любопытство и потребности, а если Вы умеете писать расширения к Mathematica – то Ваши возможности ограничиваются только законами нашей (а может Вашей) Вселенной. :)

Естественно, данный код, при его надлежащей модификации, можно адаптировать и для других наборов данных и для других web-сайтов и даже для других задач, находящихся весьма далеко от области финансовой математики и её приложений. Данная заметка только демонстрирует подход и показывает некоторые приёмы работы с Wolfram Mathematica. Дальше дело за вами. Но начало, положено!

Скачать данную заметку в формате Wolfram CDF:
Powered by Wolfram CDF  GetDataFromWeb.cdf


Данный документ распространяется на условиях лицензии: Creative Commons «Attribution-NonCommercial-ShareAlike» («Атрибуция-Некоммерческое использование-На тех же условиях») 3.0 Непортированная.


29 июня 2013 года.

Андрей Макаренко,
группа «Конструктивная Кибернетика».

Обсуждение: contact@rdcn.ru

Ключевые слова: Финансовые индикаторы, Центральный Банк РФ, Web-сайт, извлечение данных, Wolfram Mathematica.