Продолжаю блуждать в направлении цели по тропе алготрейдинга — разрабатываю для себя очередной велосипед для автоматизированной торговли.
На данном этапе увлекло меня создание транзитного сервера для данных. Что подразумевается под транзитным сервером?
Допустим, есть сторонний датафид. И мы хотим логически (а при необходимости и физически) разнести инфраструктуру для торговли.
Наш сервер будет получать Live-данные от датафида, кэшировать, сохранять на диск и в то же время ретранслировать видоизмененный поток данных клиенту, где бы тот ни находился — на той же машине или на удаленной.
Что это нам дает?
Будем исходить из того, что, даже если датафид позволяет запрашивать исторические данные, их содержимое может существенно уступать по детализации Live-данным, поставляемым тем же датафидом.
К примеру, IQFeed в виде тиков дает историю трейдов с лучшими Ask и Bid только на момент сделки, в то время как Live-данные транслируют весь поток L1. Если торговая система L1 не использует, то разница для нас значения не имеет.
В противном случае (а также если мы позже захотим использовать эти данные для тестирования ТС на инструментах, где за день проходит малое количество сделок — малоликвидные акции или опционы), отсутствие данных послужит досадной помехой.
И вот тут критическую роль сыграют обрывы связи (или перебои электропитания), если вся торговля ведется на нашем локальном компьютере. Решение очевидное — перенести программную инфраструктуру на удаленный сервер.
Пойдем немного в сторону — создадим программную прокладку, упомянутую выше. Таким образом, мы обезопасим себя (или, во всяком случае, минимизируем риски) от пробелов данных, связанных с потерей соединения или питания, и в то же время можем разместить торговую логику на другом компьютере.
К примеру, на домашнем. Зачем так делать? Допустим, торговая логика использует очень «тяжелые» вычисления или нуждается в огромном количестве памяти, а оплачивать машину в облаке с такими параметрами выльется в немалую «копеечку». А дома уже стоит системник с подходящими характеристиками и стоимостью как аренда в облаке за полгода аналогичного. Да и если торговый алгоритм из-за ошибки при разработке «сожрет» всю доступную оперативу, это не приведет к падению сервера данных, так как они на разных машинах.
Понятно, что такой подход всего лишь решает вопрос целостности данных, но в данном случае задача состоит именно в этом.
Таким образом, убедив самого себя, что изобретать велосипеды — это совсем ни разу не потеря времени, приступил к основам.
Как я уже писал, выбор пал на Windows Registered I/O.
Учитывая, что знаний в области сетевых коммуникаций у меня было не так и много, кроме самых общих, то процесс вливания в разработку транзитного сервера сопровождался созданием некоторого количества тестовых программ для уверенности, что все будет работать так, как я ожидаю.
Что привело меня к «открытию» таких чудес, как
алгоритм Нагеля и
delayed ACK… В связи с чем появилась надобность новых тестов.
И вот здесь мы, наконец, подходим к теме данного топика. Все далее описанное предполагает использование протокола TCP/IP, ОС Windows Server 2019.
RIO позволяет работать с множеством буферов для отправки и получения данных. И меня заинтересовало, какими рассуждениями следует руководствоваться при выборе количества буферов и их размера.
Если вы думаете, что это не имеет значения, попробуйте отослать несколько мегабайт с сервера, имеющего один буфер размером 64 байта. Я попробовал.
Итак, покопавшись и интернете в попытках разобраться в причинах такого результата, я приобщился к таинствам вуду протокола TCP/IP. С учетом приобретенных знаний, логически рассуждая, ответ на вопрос о количестве и размере буферов становится достаточно очевидным.
Но убедиться все равно надо. Тем более, что нам нужно остановиться на разумном количестве, обеспечивающем желаемую пропускную способность сервера.
Что используем: два сервера, физически разнесенных как можно дальше друг от друга для обеспечения максимальной латенси — один в США, другой в Европе. Пинг между серверами показал 157мс, хотелось бы побольше, но и так сойдет.
На одном сервере клиент для считывания данных (со статичными значениями размера и количества буферов чтения), на другом сервер. Батником в консольном режиме запускается сервер, в командной строке параметрами передаются значения размера буфера отправки и их количество.
В течении одной минуты сервер отсылает подготовленные данные клиенту, завершает работу, затем батник запускает сервер со следующим набором параметров. В отдельном потоке логгер каждые 160 мс считывает значение счетчика отправленных байт за этот промежуток времени, при завершении работы сервера сохраняет последовательность в файл на диске.
Синхронизация не используется, просто запись-чтение (данные выровнены по 8-байтной границе). Почему 160мс? Выбирал значение, кратное 16мс, чтобы получить несколько отсчетов за 1 секунду.
Логика теста пропускной способности: сервер сначала отправляет данные из всех буферов, дожидается подтверждения первого отправленного пакета, затем активирует логгер. Как только какой-либо буфер становится свободным — сервер отправляет из него данные величиной в размер всего буфера.
Клиент после закрытия соединения ждет 3 секунды, прежде чем приконнектиться к серверу — как уже говорил, конструировалась максимально простая связка сервер-клиент для теста в пакетном режиме пропускной способности при заданных параметрах и латенси.
На мой взгляд, одной минуты достаточно, чтобы получить представление — получается около 400 отсчетов.
Проводилось три типа тестов:
1) Размер буфера фиксирован — 8192 байта; количество меняется от 1 до 8192, являясь степенью двойки (общий размер до 64 Мб)
2) Суммарный размер (<размер буфера> * количество) фиксирован 64 Мб, размер и количество меняются, являясь степенями двойки
3) Размер буфера фиксирован — 65536 байт; количество меняется от 1 до 1024, являясь степенью двойки (общий размер до 64 Мб)
Тесты проводились дважды, клиент для считывания использовал 16 буферов размером 4096 байт в первый проход, 256 буферов размером 8192 во второй. Картина наблюдалась аналогичная при соответствующих параметрах сервера.
Тестирование не предполагало получение исчерпывающих, точных результатов, поэтому не исключено влияние внешних факторов, таких как: работа планировщика потоков ОС, нагрузка на сеть сторонних приложений или клиентов, балансировка максимальной пропускной способности софтом облака (если таковой существует) и т.п.
И шо вы таки себе думаете? Этот хитрый делец Билли Гейтс ворует нашу пропускную способность!

Пояснения к графикам:
по оси Х — количество отсчетов от запуска сервера (каждый отсчет — 160 мс)
по оси Y — количество байт, переданных ОС «на отправку» за время отсчета
имя графика Srv_aaa_bbb — aaa — размер буфера в байтах, bbb — количество буферов
Соответственно, чтобы получить пропускную способность сервера в байтах/секунду нужно значение по Y разделить на 0.16, в битах/секунду — еще домножить полученное значение на 8
Как легко догадаться, координата 18 000 000 дает 900МБит/секунду — близко к потолку гигабитной сети
Итак, что же мы видим?
Тест1. Фиксированный размер буфера 8192, количество меняется.
Результат предсказуем:
повторный проход (приводится только часть графиков — остальные не имеют заметных отличий):
Тест3. Фиксированный размер буфера 65536, количество меняется.
повторный проход (приводится только часть графиков)
Тест2. Суммарный размер фиксирован — 64 Мб



повторный проход



Какие предварительные выводы можно сделать?
— пока нагрузка на канал связи не достигает близкой к максимально возможной,
расход топлива скорость отправки стабильна
— при некоторых условиях пропускная способность сервера может быть нестабильной — в одном из тестов, достигнув потолка гигабитной сети, значение упало в 100 раз и держалось на таком уровне полминуты.
Хорошая новость — на практике мы вряд ли будем иметь такой поток данных, для которого понадобится гигабитная сеть.
Исходя из результатов тестов, можно предположить, что причиной нестабильных результатов является одна из трех возможностей:
а) когда канал забивается до допустимого потолка
б) если суммарный размер одновременно используемых буферов превышает максимальный размер внутреннего буфера ОС (точное значение размера не сообщается, судя по информации с сайта Майкрософта, и может варьироваться в зависимости от ОС).
Если присмотреться к графикам, можно предположить, что проблемы начинаются, когда суммарный размер превышает 8 Мб.
в) мне просто не повезло, и именно во время тестирования конкретных параметров сеть приходилось делить с кем-то еще (может, ОС решила что-то подгрузить?). Во время обоих проходов.
Вопрос к залу: что, по вашему, послужило причиной «расколбаса» пропускной способности сервера? «Индусский код» в качестве причины пока вынесем за рамки обсуждения.
Вывел на главную
Не строят такие системы на винде. Если конечно хочется не получить очередными граблями под конец тяжких трудов.
Ну и главное — нет смысла в ретрансляции данных. Есть смысл в заливке их в быструю среду, наложение на них необходимых функций, предагрегация и упаковка в быструю БД с компрессией. Тогда можно работать с этими данными в реальном времени, обставляя 99% наколенной бытовухи на винсерверах.