В этом примере показаны методы управления, обработки и визуализации больших объемов финансовых данных в MATLAB ®. Это часть серии связанных примеров машинного обучения для статистического арбитража (см. Приложения машинного обучения).
Финансовые рынки, где электронные биржи, такие как NASDAQ, выполняют порядки во временной шкале миллисекунд, генерируют огромное количество данных. Потоки данных могут быть добыты для возможностей статистического арбитража, но традиционные методы обработки и хранения динамической аналитической информации могут быть перегружены большими данными. К счастью, появились новые вычислительные подходы, и MATLAB имеет массив инструментов для их реализации.
Основная память компьютера обеспечивает высокоскоростной доступ, но ограниченную емкость, в то время как внешняя память обеспечивает низкоскоростной доступ, но потенциально неограниченную емкость. Расчет происходит в памяти. Компьютер вызывает данные и результаты из внешнего хранилища.
Этот пример использует один торговый день данных обмена NASDAQ [2] на одной безопасности (INTC) в выборке, предоставленной LOBSTER [1] и включенной с Financial Toolbox™ в zip- файла LOBSTER_SampleFile_INTC_2012-06-21_5.zip
. Извлеките содержимое zip- файла в текущую папку. Расширенные файлы, включая два файла данных CSV и текстовый файл LOBSTER_SampleFiles_ReadMe.txt
, потребляют 93,7 МБ памяти.
unzip("LOBSTER_SampleFile_INTC_2012-06-21_5.zip");
Данные описывают внутридневное развитие книги лимитных порядков (LOB), которая является записью рыночных порядков (лучшая цена), лимитных порядков (обозначенная цена) и получаемых закупок и продаж. Данные включают точное время этих событий с отслеживаемыми порядками от прибытия до отмены или выполнения. В каждый момент торгового дня порядки как на стороне покупки, так и на стороне продажи LOB существуют на различных уровнях вдали от средней цены между самым низким спросом (порядок на продажу) и самой высокой ставкой (порядок на покупку).
Данные уровня 5 (пять уровней вдали от среднего уровня с каждой стороны) содержатся в двух файлах CSV. Извлеките торговую дату из имени файла сообщения.
MSGFileName = "INTC_2012-06-21_34200000_57600000_message_5.csv"; % Message file (description of data) LOBFileName = "INTC_2012-06-21_34200000_57600000_orderbook_5.csv"; % Data file [ticker,rem] = strtok(MSGFileName,'_'); date = strtok(rem,'_');
Ежедневные потоки данных накапливаются и должны храниться. Datastore является репозиторием для наборов данных, которые являются слишком большими, чтобы помещаться в памяти.
Использование tabularTextDatastore
для создания хранилищ данных для файлов сообщений и данных. Поскольку файлы содержат данные с различными форматами, создайте хранилища данных отдельно. Игнорируйте типовые заголовки столбцов (для примера, VarName1
) путем установки 'ReadVariableNames'
аргумент пары "имя-значение" в false
. Замените заголовки описательными именами переменных, полученными из LOBSTER_SampleFiles_ReadMe.txt
. Установите 'ReadSize'
аргумент пары "имя-значение" в 'file'
позволяет добавлять файлы аналогичного формата к существующим хранилищам данных в конце каждого торгового дня.
DSMSG = tabularTextDatastore(MSGFileName,'ReadVariableNames',false,'ReadSize','file'); DSMSG.VariableNames = ["Time","Type","OrderID","Size","Price","Direction"]; DSLOB = tabularTextDatastore(LOBFileName,'ReadVariableNames',false,'ReadSize','file'); DSLOB.VariableNames = ["AskPrice1","AskSize1","BidPrice1","BidSize1",... "AskPrice2","AskSize2","BidPrice2","BidSize2",... "AskPrice3","AskSize3","BidPrice3","BidSize3",... "AskPrice4","AskSize4","BidPrice4","BidSize4",... "AskPrice5","AskSize5","BidPrice5","BidSize5"];
Создайте комбинированный datastore путем выбора Time
и данные уровня 3.
TimeVariable = "Time"; DSMSG.SelectedVariableNames = TimeVariable; LOB3Variables = ["AskPrice1","AskSize1","BidPrice1","BidSize1",... "AskPrice2","AskSize2","BidPrice2","BidSize2",... "AskPrice3","AskSize3","BidPrice3","BidSize3"]; DSLOB.SelectedVariableNames = LOB3Variables; DS = combine(DSMSG,DSLOB);
Можно предворительно просмотреть первые несколько строк в комбинированном datastore, не загружая данные в память.
DSPreview = preview(DS); LOBPreview = DSPreview(:,1:5)
LOBPreview=8×5 table
Time AskPrice1 AskSize1 BidPrice1 BidSize1
_____ _________ ________ _________ ________
34200 2.752e+05 66 2.751e+05 400
34200 2.752e+05 166 2.751e+05 400
34200 2.752e+05 166 2.751e+05 400
34200 2.752e+05 166 2.751e+05 400
34200 2.752e+05 166 2.751e+05 300
34200 2.752e+05 166 2.751e+05 300
34200 2.752e+05 166 2.751e+05 300
34200 2.752e+05 166 2.751e+05 300
Предварительный просмотр показывает запросы и предложения на ощупь, что означает данные уровня 1, который ближе всего к середине периода. Временные модули указаны через секунды после полуночи, ценовые модули указаны в долларах США, умноженных на 10 000, и модули размера указаны количества акций (см. LOBSTER_SampleFiles_ReadMe.txt
).
Длинные массивы работают с данными , которыми не помещаютсями в память,, поддерживаемыми datastore с помощью метода MapReduce (см. «Длинные массивы для данных , которых не помещаютсь в память,»). Когда вы используете MapReduce, длинные массивы остаются недооцененными, пока вы не выполните определенные расчеты, которые используют данные.
Установите окружение выполнения для MapReduce в локальный сеанс сеанс работы с MATLAB вместо использования Parallel Computing Toolbox™ путем вызова mapreducer(0)
. Затем создайте длинный массив из datastore DS
при помощи tall
. Предварительный просмотр данных в длинный массив.
mapreducer(0) DT = tall(DS); DTPreview = DT(:,1:5)
DTPreview = Mx5 tall table Time AskPrice1 AskSize1 BidPrice1 BidSize1 _____ _________ ________ _________ ________ 34200 2.752e+05 66 2.751e+05 400 34200 2.752e+05 166 2.751e+05 400 34200 2.752e+05 166 2.751e+05 400 34200 2.752e+05 166 2.751e+05 400 34200 2.752e+05 166 2.751e+05 300 34200 2.752e+05 166 2.751e+05 300 34200 2.752e+05 166 2.751e+05 300 34200 2.752e+05 166 2.751e+05 300 : : : : : : : : : :
Расписания позволяют вам выполнять операции, характерные для временных рядов (см., Создание расписаний). Поскольку данные LOB состоят из параллельных временных рядов, преобразуйте DT
к длинное расписание.
DT.Time = seconds(DT.Time); % Cast time as a duration from midnight.
DTT = table2timetable(DT);
DTTPreview = DTT(:,1:4)
DTTPreview = Mx4 tall timetable Time AskPrice1 AskSize1 BidPrice1 BidSize1 _________ _________ ________ _________ ________ 34200 sec 2.752e+05 66 2.751e+05 400 34200 sec 2.752e+05 166 2.751e+05 400 34200 sec 2.752e+05 166 2.751e+05 400 34200 sec 2.752e+05 166 2.751e+05 400 34200 sec 2.752e+05 166 2.751e+05 300 34200 sec 2.752e+05 166 2.751e+05 300 34200 sec 2.752e+05 166 2.751e+05 300 34200 sec 2.752e+05 166 2.751e+05 300 : : : : : : : : : :
Отобразите все переменные в рабочем пространстве MATLAB.
whos
Name Size Bytes Class Attributes DS 1x1 8 matlab.io.datastore.CombinedDatastore DSLOB 1x1 8 matlab.io.datastore.TabularTextDatastore DSMSG 1x1 8 matlab.io.datastore.TabularTextDatastore DSPreview 8x13 4515 table DT Mx13 4950 tall DTPreview Mx5 2840 tall DTT Mx12 4746 tall DTTPreview Mx4 2650 tall LOB3Variables 1x12 780 string LOBFileName 1x1 234 string LOBPreview 8x5 2203 table MSGFileName 1x1 230 string TimeVariable 1x1 150 string date 1x1 156 string rem 1x1 222 string ticker 1x1 150 string
Поскольку все данные находятся в datastore, рабочая область использует мало памяти.
Длинные массивы допускают предварительную обработку или постановку расчетов в очередь перед их оценкой, что улучшает управление памятью в рабочей области.
Средние S
и индекс дисбаланса I
используются для моделирования динамики LOB. Чтобы поставить их расчеты в очередь, задайте их и временную основу с точки зрения DTT
.
timeBase = DTT.Time; MidPrice = (DTT.BidPrice1 + DTT.AskPrice1)/2; % LOB level 3 imbalance index: lambda = 0.5; % Hyperparameter weights = exp(-(lambda)*[0 1 2]); VAsk = weights(1)*DTT.AskSize1 + weights(2)*DTT.AskSize2 + weights(3)*DTT.AskSize3; VBid = weights(1)*DTT.BidSize1 + weights(2)*DTT.BidSize2 + weights(3)*DTT.BidSize3; ImbalanceIndex = (VBid-VAsk)./(VBid+VAsk);
Индекс дисбаланса является взвешенным средним значением объемов запросов и предложений по обеим сторонам средней цены [3]. Индекс дисбаланса является потенциальным показателем будущих изменений цен. Переменная lambda
является гиперпараметром, который является параметром, заданным до обучения, а не оцененным алгоритмом машинного обучения. Гиперпараметр может повлиять на эффективность модели. Инженерия функций - это процесс выбора специфичных для области гиперпараметров для использования в алгоритмах машинного обучения. Вы можете настроить гиперпараметры, чтобы оптимизировать торговую стратегию.
Чтобы принести предварительно обработанные выражения в память и вычислить их, используйте gather
функция. Этот процесс называется отсроченной оценкой.
[t,S,I] = gather(timeBase,MidPrice,ImbalanceIndex);
Evaluating tall expression using the Local MATLAB Session: - Pass 1 of 1: Completed in 2.3 sec Evaluation completed in 2.5 sec
Один вызов для gather
вычисляет несколько предварительно обработанных выражений с одним проходом через datastore.
Определите размер выборки, которая является количеством тактов или обновлений в данных.
numTicks = length(t)
numTicks = 581030
Ежедневные данные LOB содержат 581 030 такты.
Можно сохранить как недооцененные, так и оцененные данные во внешнем хранилище для дальнейшего использования.
Подготовьте временную основу к дате и приведите результат к массиву datetime. Сохраните полученный массив datetime, MidPrice
, и ImbalanceIndex
в MAT-файл в указанном расположении.
dateTimeBase = datetime(date) + timeBase; Today = timetable(dateTimeBase,MidPrice,ImbalanceIndex)
Today = 581,030x2 tall timetable dateTimeBase MidPrice ImbalanceIndex ____________________ __________ ______________ 21-Jun-2012 09:30:00 2.7515e+05 -0.205 21-Jun-2012 09:30:00 2.7515e+05 -0.26006 21-Jun-2012 09:30:00 2.7515e+05 -0.26006 21-Jun-2012 09:30:00 2.7515e+05 -0.086772 21-Jun-2012 09:30:00 2.7515e+05 -0.15581 21-Jun-2012 09:30:00 2.7515e+05 -0.35382 21-Jun-2012 09:30:00 2.7515e+05 -0.19084 21-Jun-2012 09:30:00 2.7515e+05 -0.19084 : : : : : :
location = fullfile(pwd,"ExchangeData",ticker,date); write(location,Today,'FileType','mat')
Writing tall data to folder /tmp/BR2021ad_1655202_180016/mlx_to_docbook15/tp0c5070d3/finance-ex97702880/ExchangeData/INTC/2012-06-21 Evaluating tall expression using the Local MATLAB Session: - Pass 1 of 1: Completed in 3.3 sec Evaluation completed in 3.5 sec
Файл записывается один раз, в конце каждого торгового дня. Код сохраняет данные в файл в папке с меткой даты. Серия ExchangeData
подпапки служат историческим данным репозиторием.
Кроме того, можно сохранить переменные рабочей области, оцененные с помощью gather
непосредственно в MAT-файл в текущей папке.
save("LOBVars.mat","t","S","I")
В процессе подготовки к валидации модели позднее оцените и добавьте цены рыночного порядка к тому же файлу.
[MOBid,MOAsk] = gather(DTT.BidPrice1,DTT.AskPrice1);
Evaluating tall expression using the Local MATLAB Session: - Pass 1 of 1: Completed in 2.1 sec Evaluation completed in 2.2 sec
save("LOBVars.mat","MOBid","MOAsk","-append")
В оставшуюся часть этого примера используется только недооценённое длинное расписание DTT. Очистить другие переменные из рабочей области.
clearvars -except DTT whos
Name Size Bytes Class Attributes DTT 581,030x12 4746 tall
Чтобы визуализировать большие объемы данных, необходимо каким-либо образом суммировать, интервал, или отобразить данные, чтобы уменьшить число точек, нанесенных на экран.
Одним из методов визуализации является оценка только выбранной подвыборки данных. Создайте снимок LOB в определенное время суток (11 часов утра).
sampleTimeTarget = seconds(11*60*60); % Seconds after midnight sampleTimes = withtol(sampleTimeTarget,seconds(1)); % 1 second tolerance sampleLOB = DTT(sampleTimes,:); numTimes = gather(size(sampleLOB,1))
Evaluating tall expression using the Local MATLAB Session: - Pass 1 of 1: Completed in 2.4 sec Evaluation completed in 2.5 sec
numTimes = 23
В течение одной секунды с 11:00 до 23 тактов. Для моментального снимка используйте такт ближе всего к середине времени.
sampleLOB = sampleLOB(round(numTimes/2),:);
sampleTime = sampleLOB.Time;
sampleBidPrices = [sampleLOB.BidPrice1,sampleLOB.BidPrice2,sampleLOB.BidPrice3];
sampleBidSizes = [sampleLOB.BidSize1,sampleLOB.BidSize2,sampleLOB.BidSize3];
sampleAskPrices = [sampleLOB.AskPrice1,sampleLOB.AskPrice2,sampleLOB.AskPrice3];
sampleAskSizes = [sampleLOB.AskSize1,sampleLOB.AskSize2,sampleLOB.AskSize3];
[sampleTime,sampleBidPrices,sampleBidSizes,sampleAskPrices,sampleAskSizes] = ...
gather(sampleTime,sampleBidPrices,sampleBidSizes,sampleAskPrices,sampleAskSizes);
Evaluating tall expression using the Local MATLAB Session: - Pass 1 of 2: Completed in 2.4 sec - Pass 2 of 2: Completed in 2.3 sec Evaluation completed in 5.2 sec
Визуализируйте ограниченную выборку данных, возвращенную gather
при помощи bar
.
figure hold on bar((sampleBidPrices/10000),sampleBidSizes,'r') bar((sampleAskPrices/10000),sampleAskSizes,'g') hold off xlabel("Price (Dollars)") ylabel("Number of Shares") legend(["Bid","Ask"],'Location','North') title(strcat("Level 3 Limit Order Book: ",datestr(sampleTime,"HH:MM:SS")))
Некоторые функции визуализации работают непосредственно с длинные массивы и не требуют использования gather
(
см. «Визуализация длинных массивов»). Функции автоматически дискретизируют данные, чтобы уменьшить плотность пикселей. Визуализация уровня 3 внутридневной глубины рынка, которая показывает временную эволюцию ликвидности, с помощью plot
с длинное расписание DTT
.
figure hold on plot(DTT.Time,-DTT.BidSize1,'Color',[1.0 0 0],'LineWidth',2) plot(DTT.Time,-DTT.BidSize2,'Color',[0.8 0 0],'LineWidth',2) plot(DTT.Time,-DTT.BidSize3,'Color',[0.6 0 0],'LineWidth',2) plot(DTT.Time,DTT.AskSize1,'Color',[0 1.0 0],'LineWidth',2) plot(DTT.Time,DTT.AskSize2,'Color',[0 0.8 0],'LineWidth',2) plot(DTT.Time,DTT.AskSize3,'Color',[0 0.6 0],'LineWidth',2) hold off xlabel("Time") ylabel("Number of Shares") title("Depth of Market: Intraday Evolution") legend(["Bid1","Bid2","Bid3","Ask1","Ask2","Ask3"],'Location','NorthOutside','Orientation','Horizontal');
Для отображения подробных данных ограничьте временной интервал.
xlim(seconds([45000 45060]))
ylim([-35000 35000])
title("Depth of Market: One Minute")
Этот пример представляет основы работы с большими данными, как в памяти, так и из памяти. В нем показано, как настроить, объединить и обновить внешние хранилища данных, а затем создать длинные массивы для предварительной обработки данных без выделения переменных в рабочем пространстве MATLAB. The gather
функция переносит данные в рабочую область для расчетов и последующего анализа. Пример показов, как визуализировать данные с помощью выборки данных или функций построения графика MATLAB, которые работают непосредственно с данной , которой не помещаютсей в память,.
[1] LOBSTER Limit Order Book Data. Берлин: frischedaten UG (haftungsbeschränkt).
[2] Данные NASDAQ Historal TotalView-ITCH. Нью-Йорк: The Nasdaq, Inc.
[3] Рубисов, Антон Д. «Статистический арбитраж с использованием дисбаланса книги предельных порядков». Магистерская диссертация, Университет Торонто, 2015.