Продолжаю блуждать в направлении цели по тропе алготрейдинга — разрабатываю для себя очередной велосипед для автоматизированной торговли.
На данном этапе увлекло меня создание транзитного сервера для данных. Что подразумевается под транзитным сервером?
Допустим, есть сторонний датафид. И мы хотим логически (а при необходимости и физически) разнести инфраструктуру для торговли.
Наш сервер будет получать 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% наколенной бытовухи на винсерверах.
В сетевых коммуникациях я разбираюсь, можем голосом обсудить
По поводу второго — там немного не так. Батник запускает программу-сервер раз минуту с новыми параметрами, а вот несколько раз в секунду считывается значение счетчика отправленных байт, т.е. мы получаем минуту измерений одного набора параметров с частотой замеров несколько раз в секунду.
По поводу голосового обсуждения — благодарю, но не вижу смысла кого-то беспокоить. Думал, может это типичная ситуация с сетью, и кто-то отпишется, к примеру: «да, такие чудеса бывают, когда используешь канал в облаке на полную»
мне просто захотелось иметь возможность еще и сохранять L1 (полагаю, многие датафиды, как и IQFeed при обрыве связи позволяют подкачать только трейды).
Дед Нечипор, всё продается, L1, L2, full order log
PS Но сам я коплю, а не покупаю )
Так то да, может тут тоже кто в теме откликнется. Мне просто казалось что «бытовые алгоритмисты» обычно более высокоуровнеными инструментами пользуются, тут же слишком приземлённо и многие вопросы ценны сами по себе, без привязки к торговле.
(имхо лезть в такое имеет смысл чтобы набить руку и зарабатывать прикладными навыками в другой области, или на дядю работать за зарплату. Ну может конечно еще и просто как развлечение если пассивный доход уже позволяет в игрушечки играться).
А по поводу ваших вопросов, данные не телепортируются. Они бьются на пакеты, и идут по проводам/оптике, через маршрутизаторы, чем длиннее этот путь, тем больше задержки и меньше пропускная способность.
И вторая проблема, что Windows RIO это достаточно сложная технология тем более прибитая к винде(да есть io uring, но сути не меняет). Поэтому для начала хорошо бы было сравнить WRIO со стандартными сокетами. И посмотреть, есть ли у WRIO какие-то реальные преимущества на ваших задачах.
В среднем раз в несколько десятков тысяч-сотен тысяч запросов вылезал Access violation чтение по адресу $00020. Путем долгих и мучительных отладок и вставок где только можно логирования удалось локализовать проблему до одной команды. Ею оказался вызов обертки Delphi вокруг команды WinSock на предмет готовности сокета (наличия данных). Не могу представить, что проблему вызывало что-то другое, где бы я набокопорил: идут три команды подряд — 1) запись в лог «вызов ххх» 2) сама функция Delphi ххх 3) запись в лог «завершено ххх». Так вот при ошибке AV в логе было «вызов ххх» и все.
Или еще — примерно с той же частотой запросов получал неполные данные (начало отсутствовало).
Понимаю, что у любого опытного программиста естественной реакцией будет — ищи ошибку в своем коде. Но я реально перелопатил там все несколько раз. И в будущем разбираться, где проблема — то ли в сетевом компоненте Delphi, то ли в Windows, то ли в самом IQFeed не особо хочется. Поэтому и появилось желание закодить самому что-то низкоуровневое, чтобы отсечь хотя бы часть потенциальных проблем.
Delphi, кстати, новые выпускают постоянно. Даже на бесплатную версию расщедрились. Хоть и не без косяков, я лично один в какой-то из предыдущих версий нашел.
И спасибо за совет по поводу операций с памятью, я так и стараюсь делать.
поэтому пропускная способность линка выходит на пиковое значение в диапазоне 1300-1500 байт на пакет. Если вы начинаете повышать сайз, то сразу попадает на фрагментацию (нарезка по 1500б), а это тоже время и задержки
Тут, видимо, у нас разночтение что именно тестировалось (пропускная способность чего). Я не сам канал тестировал, а сколько программный сервер может протолкнуть данных при заданной латенси и параметрах (размер/количество буферов). Условно говоря, толку от того, что у меня гигабитная сеть, если я сконфигурирую параметры неудачно и сервер будет пыхтеть с практическим потолком пару килобайт в секунду
Т.е. Вы хотите сказать, что существующие решения (напр. небезизвестная Гидра stocksharp.ru/store/%D1%81%D0%BA%D0%B0%D1%87%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BC%D0%B0%D1%80%D0%BA%D0%B5%D1%82-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85/)
настолько плохи (лично я разное про это слышал), что нужно свое делать с нуля?