Чтение данных текущего стакана цен
После успешного выполнена функция MarketBookAdd, MQL-программа может запрашивать состояния стакана с помощью функции MarketBookGet по приходу событий OnBookEvent. Функция MarketBookGet заполняет передаваемый по ссылке массив структур MqlBookInfo записями стакана цен указанного символа.
bool MarketBookGet(string symbol, MqlBookInfo &book[])
Под приемный массив можно заранее распределить память для достаточного количества записей. Если динамический массив имеет нулевой или недостаточный размер, терминал сам выделит под него память.
Функция возвращает признак успеха (true) или ошибки (false).
Обычно MarketBookGet используется непосредственно в коде обработчика OnBookEvent или в функциях, вызываемых из него.
Отдельная запись о ценовом уровне стакана хранится в структуре MqlBookInfo.
struct MqlBookInfo
|
Перечисление ENUM_BOOK_TYPE содержит следующие элементы.
Идентификатор |
Описание |
---|---|
BOOK_TYPE_SELL |
Заявка на продажу |
BOOK_TYPE_BUY |
Заявка на покупку |
BOOK_TYPE_SELL_MARKET |
Заявка на продажу по рыночной цене |
BOOK_TYPE_BUY_MARKET |
Заявка на покупку по рыночной цене |
Массив стакана отсортирован таким образом, что в верхней его половине расположены заявки на продажу, а в нижней — на покупку. Как правило, это приводит к соблюдению последовательности элементов от больших цен к малым. Иными словами, под 0-м индексом идет самая высокая цена, в последней записи — самая низкая, а между ними цены постепенно уменьшаются. При этом минимальный шаг цен между уровнями составляет SYMBOL_TRADE_TICK_SIZE, однако уровни с нулевыми объемами не транслируются, то есть соседние элементы могут отстоять и на большую величину.
В пользовательском интерфейсе терминала, в окне стакана имеется опция для включения/отключения Расширенного режима, в котором уровни с нулевыми объемами начинают отображаются, но по умолчанию, в стандартном режиме, такие уровни скрыты (пропускаются в таблице).
На практике содержимое стакана может иногда противоречить озвученным правилам. В частности, может оказаться, что некоторые заявки на покупку или продажу попадают в противоположную половину стакана (вероятно, кто-то поставил покупку по невыгодно высокой цене или продажу по невыгодно низкой, однако не исключены и технические ошибки при агрегировании данных у поставщика). В результате, из-за соблюдения приоритета "сверху все заявки на продажу, снизу все заявки на покупку", последовательность цен в стакане окажется нарушенной (см. пример ниже). Кроме того, могут обнаружиться повторяющиеся значения цен (уровней) как в одной половине стакана, так и в противоположных.
В принципе, совпадение цен на покупку и продажу в середине стакана — корректно. Оно означает нулевой спред. Однако, к сожалению, задваивание уровней случается и на большей глубине стакана.
Когда мы говорим "половина" стакана, это не следует понимать буквально. В зависимости от ликвидности количество уровней предложения и спроса может не совпадать. В общем случае, стакан несимметричен.
MQL-программа должна проверять корректность стакана (в частности, порядок сортировки цен) и быть готовой обработать потенциально возможные отклонения.
К менее серьезным нештатным ситуациям (которые, тем не менее, следует учесть в алгоритме) можно отнести:
- Последовательные одинаковые стаканы, т.е. без изменений
- Пустой стакан
- Стакан с одним уровнем
Ниже показан фрагмент реального стакана цен, полученного от брокера. Буквами 'S' и 'B' помечены, соответственно, цены заявок на продажу и покупку.
Обратите внимание, что уровни покупок и продаж фактически перекрываются: визуально это не сильно заметно, потому что все записи 'S' в стакане специально вынесены вверх (начало приемного массива), а записи 'B' — вниз (конец массива). Однако приглядитесь: цены для покупок в элементах 20 и 21 равны 143.23 и 138.86, соответственно, а это больше всех предложений на продажу. И в то же время, цены для продаж в элементах 18 и 19 равны 134.62 и 133.55, а это ниже всех предложений на покупку.
...
|
Кроме того, цена 138.39 встречается и в верхней половине под номером 16, и в нижней под номером 22.
Ошибки в стакане наиболее вероятны в экстремальных условиях: при сильной волатильности или недостатке ликвидности.
Проверим получение стакана с помощью индикатора MarketBookDisplay.mq5. Он будет подписываться на события стакана для заданного символа в параметре WorkSymbol (если там оставить пустую строку, подразумевается рабочий символ текущего графика).
input string WorkSymbol = ""; // WorkSymbol (if empty, use current chart symbol)
|
Для обработки событий в коде определен обработчик OnBookEvent, в котором вызывается MarketBookGet, и все элементы полученного массива MqlBookInfo выводятся в многострочный комментарий.
void OnBookEvent(const string &symbol)
|
Поскольку стакан меняется довольно быстро, следить за комментарием не очень удобно. Поэтому добавим в индикатор пару буферов, в которые в виде гистограмм будем выводить содержимое двух половин стакана: раздельно продажи и покупки. Нулевой бар будет соответствовать центральным уровням, формирующим спред. С увеличением номеров баров происходит увеличение "глубины рынка", то есть там отображаются все более и более отдаленные ценовые уровни: в верхней гистограмме это означает более низкие цены с заявками на покупку, а в нижней — более высокие с заявками на продажу.
#property indicator_separate_window
|
Предусмотрим возможность визуализировать стакан в стандартном и расширенном режиме (то есть пропускать или показывать уровни с нулевыми объемами), а также отображать сами объемы в долях лотов или единицах. Обе опции имеют аналоги во встроенном окне стакана.
input bool AdvancedMode = false;
|
Настройку буферов и получение некоторых свойств символа (которые нам потребуются в дальнейшем) выполним в OnInit.
int depth, digits;
|
В обработчик OnBookEvent добавим заполнение буферов.
#define VOL(V) (ShowVolumeInLots ? V / contract : V)
|
Следующее изображение демонстрирует работу индикатора с настройками AdvancedMode=true, ShowVolumeInLots=true.
Содержимое стакана в индикаторе MarketBookDisplay.mq5 на графике USDCNH
Покупки выводятся как положительные величины (синяя гистограмма вверху), продажи — как отрицательные (красная внизу). Для наглядности справа размещено стандартное окно стакана с такими же настройками (в расширенном режиме, объемы в лотах), так что можно убедиться в совпадении значений.
Следует отметить, что индикатор может не успевать перерисовываться достаточно оперативно, чтобы сохранять синхронизацию со встроенным стаканом. Это не значит, что MQL-программа не получила вовремя событие, а лишь побочный эффект асинхронной отрисовки графиков. В рабочих алгоритмах для стакана, как правило, производится аналитическая обработка и выставление приказов, а не визуализация.
В данном случае обновление графика неявным образом запрашивается в момент вызова функции Comment.