tgindex
Один микросек - C++, low latency, concurrency, HFT

Один микросек - C++, low latency, concurrency, HFT

Статистика
Последний пост
22 апр. 2025 г.
Последнее чтение
14 авг.
Постов за неделю
0
Всего постов
20
Тип
открытый
Язык
русский
Категория
Технологии (по похожим)
В каталоге с
13 авг.
Подписчики
883
+1 за 4 дн.
Сутки
+2
+0,23%
Неделя
 
Месяц
 
Просмотров на пост
2 899
20 постов
Вовлечённость
328,3%
к подписчикам
Постов в день
0,0
всего 20
Упоминаний
1
каналов
Охват размещения
оценка
1/24сутки в ленте
1/48двое суток
1/72трое суток

Оценка по просмотрам недавних постов: пост набирает почти всё за первые сутки.

Посты

  • Месяц-два назад начал изучать новый язык, чего не делал уже несколько лет. Предыдущим новым языком был Golang аж в 2018-м, но он меня не вштырил, хотя были приятные моменты и откровения. Что за язык то? Вы угадаете его с 2х слов: blazingly fast. Подсказываю: 100% memory safe. Правильно, это Ржавый. Не буду ничего больше писать, а то получу сотни отписок от недовольных коллег по плюсам. Но мне он нравится. Параллельно вникаю в Solana.

  • ⚡️Видео When Nanoseconds Matter: Ultrafast Trading Systems Довольно интересная лекция от David Gross из Optiver о том, какие фишки используются в проектировании комплексных торговых систем: - как лучше представить L3 ордербук в памяти, и как искать в нем. - немного о проектировании и коде SPMC bounded очереди в shared memory. - идея spawn'ить perf прямо из своего процесса. - немного о likely/unlikely/cold секциях в коде. - упомнянул о том, какой выигрыш даёт kernel bypass при работе с сетью. - использование clang xray для добавления профилирования в рантайме.

  • Когда перешел в перспективный крипто HFT стартап... (автор: KNADCORE)

  • В декабре прошлого года я не продлил домен eyeleo.com для моего самого общественно-полезного проекта в жизни. Зашли посмотреть по ссылке? А зачем, домен то не продлён. Где-то году в 2011-м в новогодние каникулы я написал Windows программу, напоминающую делать перерывы, чтобы глаза отдыхали от дисплея. Мне самому такая была нужна, а существующие вариации мне не нравились. EyeLeo была небольшой бесплатной программой, но делающей важное дело, поэтому она быстро стала весьма популярной, и долгое время я получал небольшой доход с донатов (через Adsense и Patreon). Однако из-за очень низкого качества кода и общей игрушечности, я буквально скрывал принадлежность к EyeLeo. Мне не хотелось, чтобы кто-то из коллег (или потенциальных работодателей) увидел код и сделал неверные выводы. Но сейчас решил: да пофиг, сделаю coming out. Программа-то полезная, и благодарных пользователей было много. Короче, это моя прога, и мне пофиг на качество кода, уже не стыдно. Сейчас развивать её нет времени и желания (другие приоритеты), поэтому я перестал оплачивать домен. На Linux дистрибутивах сейчас есть Safe Eyes, и я рекомендую им пользоваться. Когда приходит время для перерыва, смотрите вдаль секунд на 20. И в долгосрочной перспективе это сильно поможет вам снизить нагрузку на зрение. Чем из софтовых и физических решений для здоровья работников клавы и монитора вы пользуетесь?

  • Что не так с float'ом и спасет ли double? По каким таким причинам биржи не уважают floating-point numbers? Смотришь в документацию, а они в json'ах цену и количество лотов шлют не number type, а string type. Не {"price":36.12}, а {"price":"36.12"}. Они так делают, если: - практически: в их кодовой без используется какой-нибудь BigDecimal, который так сериализует числа. - теоретически: они не могут гарантировать отсутствие чисел, непредставимых в double типе (например, 9007199254740993.0), даже, если сейчас такого не видно на горизонте. Проблема не в «маленьком диапазоне» double, а в невозможности точной двоичной репрезентации десятичных дробей. - для некоторых полей такую гарантию они могут дать, и тогда они используют uint64 (сравни поля ts и v). - они заботятся о пользователе, вводя проблему в сознательную область (т.к. теперь просто as_float() в парсере не вызовешь) и снижая вероятность ошибки. - упрощают пользователю возможность прокинуть строку в свой кастомный парсер (не часть json либы). - оставляют себе микро-возможность вписать пустую строку, либо "Nan" или "Inf" (и такое бывало). С точки зрения спецификации json тип Number не ограничен в размере и точности, но на практике юзерские либы же как-то должны с этим работать, поэтому на эту особенность спеки положиться нельзя. Ладно, с биржами понятно. Допустим, прислали нам "amount":"36.12". В чем ваш trading engine будет это хранить? Это фундаментальный вопрос для торговой системы. Ни ужели точности double не хватит, ведь он может показывать числа от 2.22507e-308 до 1.79769e+308, что уже довольно дофига? Как будто бы может и хватить, но все же легко наступить на грабли: - Операции не ассоциативны: (a + b) + c может быть не равно a + (b + c). - Различные платформы могут давать различные результаты операций. - Ошибки округления накапливаются при последовательных математических операциях (см. 0.1 + 0.2). - Сравнивать два floating-point числа напрямую чревато. - Что-то еще наверянка забыл, дополните, плз. Практический пример: вы посылаете торговый ордер на quantity 0.3, вам приходит три сделки по 0.1. Вы их складываете, чтобы понять полностью ли заполнен ордер, и тут вы и приехали, ибо: (0.1 + 0.1 + 0.1) == 0.3 вычисляется в false Да, вы можете сравнить числа через epsilon (abs(a-b) < eps), но потенциально легко где-то забыть вызвать правильную функцию. Что тогда, вон из профессии? По итогу, используется либо double с дополнительными приседаниями (обвязками, проверками и т.п.), либо кастомный decimal тип (пример), либо изначально величины хранятся в самых мелких единицах (пример из Ethereum).

  • Решение задачи Злоумышленник не знает, какую половинку он взял - левую или правую. По BIP-39 последние 4 бита последнего слова в фразе являются контрольной суммой. И контрольную сумму как вариант перебирать не нужно, её нужно вычислить. Поэтому мы должны рассмотреть две ветки: - Если он нашел правую часть, тогда перебирать нужно левую часть. Каждое слово может быть одним из 2048 вариантов из специального словаря, или 2^11, а всего 6 слов, то есть 2^(11x6) = 2^66. - Если он нашел левую часть, то перебирать надо меньше, т.к. биты контрольной суммы перебирать не нужно. Из 66 бит энтропии (66 это 11 бит на слово x 6 слов) мы вычитаем 4 бита чексуммы, получаем 62, то есть 2^62 варианта. Т.к. ему нужно перебрать оба варианта, то суммируем числа, получаем 2^66 + 2^62 = 2^62 x (2^4 + 1) = 2^62 x 17 или 78398662313265594368.

  • Один криптобро решил сохранить свою seed фразу (12 слов по стандарту BIP-39) от кошелька на бумажке. Он схитрил и разделил последовательность посередине, записав первую часть на одну бумажкуу, оставшуюся - на другую, и спрятал их в разных местах. К сожалению, злоумышленник нашел одну из бумажек, и теперь хочет перебрать недостающие 6 слов, чтобы забрать свой куш. Какое максимальное количество комбинаций ему может понадобиться перебирать? hint #1: каждая позиция в фразе имеет 2048 возможных вариантов (слов), что составляет 11 бит энтропии

  • Интересная статья 2014 года, в которой автор пытается увидеть манипуляции на рынке через визуализацию ордербука во времени. К сожалению, картинки сжатые, но понять можно (прищурившись). Для новичков будет интересна первая часть, где объясняется принципы устройства ордербука, а для бывалых - вторая. Интересно, что это происходило 10 лет назад, а все перечисленные паттерны до сих можно увидеть в ордербуках. https://parasec.net/blog/order-book-visualisation/

  • Что мне нравится в API крипто бирж: — наличие batch запросов (несколько запросов отправляется одной пачкой) — таймстемпы в микросекундах, а лучше даже в наносекундах (у некоторых только в миллисекундах, а встречались даже секунды) — несколько таймстемпов из различных этапов ордера (время приема, время матчинга, время отправки отчета) — когда можно задать окно, позже которого биржа не примет моё сообщение — наличие запросов: order info, get open orders, cancel all, kill switch on disconnect — в ответе есть request id и тип запроса — api ключи с гранулярным доступом — доки не устаревшие и не содержат банальных ошибок (редкость) Не нравится: — когда json ответа слишком глубокий: {"channel":"post","data":{"id":167248,"response":{"type":"action","payload":{"status":"ok","response":{"type":"cancel","data":{"statuses":["success"]}}}}}} — когда ордербук передают через json объекты: "bids":[{"p":65332,"q":1.35},...], вместо массива массивов — когда различные рынки (spot/perps) одной биржи делают разные команды (и появляются различия в api) — когда выкатывают обновления в субботу

  • Что почитать по C++? https://www.saminiir.com/lets-code-tcp-ip-stack-1-ethernet-arp/ - серия статей "Let's code a TCP/IP stack" https://pthorpe92.dev/programming/systems/common-misunderstandings/ - серия статей "Common misunderstandings" по processes, tasks, async I/O. https://eli.thegreenplace.net/2017/concurrent-servers-part-1-introduction/ - базовое "Concurrent servers" про select/epoll. https://accu.org/journals/overload/32/182/teodorescu/ - базовый гайд про атомики "In an atomic world" от accu. https://www.think-cell.com/en/career/devblog/cpp-needs-undefined-behavior-but-maybe-less - про UB и будущий erroneous behavior. https://lemire.me/blog/2024/09/09/replace-stdstring-by-stdstring_view-when-you-can/ - один mega-string вместо кучи мелких от Daniel Lemire. https://habr.com/ru/companies/pvs-studio/articles/846532/ - std::array не медленнее C-массива от PVS studio. https://www.studyplan.dev/pro-cpp/concepts - серия доступных уроков по C++20 concepts, если еще не вкатились.

  • Если подумать, каких провалов я опасаюсь в работе, то это такой тип сбоев, в которых ваша торговая система набирает очень большую и нежелательную позицию. Она начинает неконтролируемо и быстро слать ордера на биржу, превышая заложенные в систему величины рисков. Такое произошло с Knight Capital (в блоге и на хабре на русском), что впоследствии привело к продаже фирмы. Всё произойдет за минуты или секунды, и уже после случившегося вы будете какое-то время только осознавать "эээ а что происходит". Либо, как вариант, баг в логике, неверные подсчёты или парсинг ответов приводит к тому, что подсчитанная позиция расходится с реальной. Вы думаете, у вас лонг на 100k, а у вас шорт на 100k, и рынок, зараза, идет вверх. Подобное случалось и со мной и с коллегами. Второй неприятный тип сбоев: когда из-за бага торговая система простаивает. Представьте, что ваша, с любовью разработанная, фича остановила весь терминал, и фирма не зарабатывает, скажем, 5k в минуту, во всех рабочих чатах пишут ваше имя (а вы в отпуске). Понятно, что такая ситуация есть в других IT сферах, Crowdstrike подтвердит. Третим в списке поставлю утечку приватных ключей и паролей (в результате небрежности или злого умысла сотрудников или внешней атаки). Вот вам любопытный случай c 3commas. Предлагаю порассуждать, какие бы вы ввели механизмы, практики, правила в своем стартапе для защиты от подобных провалов? А точно хватит времени на это (в стартапе)? А точно не будет багов в самих механизмах защиты (если речь о коде)?

  • GDB позволяет вводить команды из файла Приведу несколько примеров. Поставить breakpoint с условием: > cat 1.gdb break my_func if (var > 10000) commands print var info locals info registers backtrace continue end Поставить одноразовый catch на write syscall: tcatch syscall write commands printf "Syscall write\n" printf "File descriptor: %d\n", $rdi printf "Buffer: %p\n", $rsi printf "Bytes: %d\n", $rdx continue end Объявить функцию, которая принимает адрес и ставит условный watch на этот адрес: define my_int_watch watch *(int*)$arg0 if *(int*)$arg0 % 3 == 0 commands printf "New value: %d\n", *(int*)$arg0 continue end end Реальные сценарии как правило чуть сложнее, т.к. дебаг символов может и не быть, условия посложнее, но для демонстрации идеи эти подходят. Далее команды можно запустить из файла: gdb -p 1234 -x 1.gdb или прямо в cli: > source 1.gdb Да, то же самое можно ввести вручную поэтапно в cli, однако ручной ввод комманд требует времени, что может быть непозволительно в уже запущенных программах, которые нельзя прерывать. GDB также умеет в Python скрипты, но на питоняке я писал только pretty-printer'ы. Кто-то использовал python в gdb?

  • Что я понял про ведение телеграм канала (с января 2024) - Можно ссылку на свой канал положить в свой профиль. Смотри пример: @yukigaru. - Очень хорошо для меня сработала публикация в Linkedin, она принесла несколько десятков новых подписчиков. Чувствую, что в этом способе привлечения для меня еще есть большой потенциал. Взаимопиар похожих каналов работает, но мне принесло умеренно (до 20, насколько помню, у "on the way to 10x engineer"). Платная реклама в инфо-канале @seniorcpp принесла мне очень мало, это того не стоило, но попробовать хотелось. - Если вы премиум юзер, то вы можете оставлять комментарии в чужих каналах от имени своего канала. - Простые комментарии в чужих каналах дают мне примерно +1 подписчик на комментарий. Качественные расписанные комментарии могут дать больше, но я не уделяю этому время. - Однако некоторые каналы заранее запрещают писать от имени своего канала (в них просто нельзя выбрать свой канал). Существуют так же боты, удаляющие комментарии от чужих каналов. - Легко отличить какой канал создан для души, а какой чисто ради заработка. Вторые пишут общую якобы полезную информацию, но по факту инфо-мусор. Используют chat gpt для контента, содержат ошибки, но при этом имеют в десять раз больше подписчиков. Доказательств нет, но возможно, что часть подписчиков - это боты, и нужны для повышения цен на рекламу. - Развить свой блог всё еще можно, и даже без вложений. Пробуйте, если вам интересно! Если вы начинающий, то могу написать о вас. Да, у меня пока аудитория небольшая, но если у вас до 50 человек, то это имеет смысл. - По наблюдениям, высоко востребовано высоко-профессиональное уникальное чтиво, где автор показывает мастерство, и чего нигде больше не найдешь, и хорошо заходят подборки с полезными материалами. - Я не зарабатываю на канале, и пока не планирую. Если бы начал монетизацию, вам бы полилась реклама курсов, а мне этого не хочется. - У меня много идей, о чем я могу написать, но не хватает времени на реализацию. Чего я не понял, но хотел бы понять: - Я вижу статистику по постам, например, пост со ссылками на интересные мне материалы (https://t.me/hft_dev/49) расшарили 27 раз и в это число не входит "Saved messages". Спасибо, что делитесь постами! Но все же, кто и куда forward'ит мои посты?

  • Взял себе новую клаву (Nuphy Airv2, brown switches, low profile) на замену предыдущей. Теперь кайфую. Пора снова писать после долгого перерыва.

  • Обнаружил, что Алиса теперь использует YandexGPT для ответа на несценарные вопросы. Пока ещё не придумал, как это использовать, но вот вам Алиса, пишущая на плюсах

  • На работе завал, придётся делегировать. Что стоит почитать по C++? (новые статьи, заметки, новости, блогпосты, видео)

  • Наверное, уже все заметили: красный цвет на всех рынках. Свечи вниз, другими словами. Слив. Дамп. Крипта приняла на себя первый удар, т.к. классические фондовые биржи закрыты в выходные. Краткосрочного резкого падения рынка HFT'шники не боятся, они этого ждут: деньги зарабатываются на движениях в любую сторону. Однако, торговая инфра переходит в такое время в режим повышенной нагрузки (опасности): - Повышенный трафик с рынка, выжирается RAM, CPU в сотку, сервисы могут начать не вывозить, очереди наполняются и переливаются, начинают тормозить сопутствующие сервисы, и т.п. - Вы хотите много наторговать, и выжираете API rate limits. - Логи сыпятся так быстро, что не успеваешь вычитывать. Разбираешь несколько уже часов после движения. - API бирж начинает присылать новые виды ошибок/ответов/состояний, на которые, возможно, вы не рассчитывали. - События с биржи могут начать вести себя непредсказумо: молчать некоторое время, иметь увеличивенные задержки, идти в другом порядке и т.п. - Из-за большого количества событий повышаются шансы нароллить маловероятные события: спящие баги, коллизии, переполнения, совпадение событий по времени и неожиданные реордеры.

  • Alber Blanc, где я работаю, активно нанимает, и у нас несколько вкусных вакансий. Например: — C++ developer (Core team), core классы и сервисы, инфраструктура для быстрой торговли, обработка и пересылка маркет даты, оптимизация, взаимодействие с FPGA и т.п. — C++ developer (Gates team), коннекторы к биржам, оптимизация сетевых подключений, работа с маркет датой, метрики и т.п. — Network/SRE engineer, работа с различными сетями, тюнинг линукса, отлаживание проблем ОС, автоматизация сетевых задач, работа с AWS, хостами, инфра сервисами. Нанимаем на Кипр (Лимассол), но есть офисы и в других локациях. Особенности: — Нет bullshit практик и траты времени, мало бюрократии, доступы выдаются быстро. — Топы открыты, доступны, и работают наравне с нами (программируют, например), плоская структура компании. — По оплате HFT сфера, пожалуй, самая привлекательная: приятная ЗП, полугодовые бонусы по результатам работы, оплата дежурств, если участвуешь. — Четкие критерии успешности, быстрый результат от твоей работы. — Работа в офисе, удалёнки нет. Не всем это подходит, но мне вполне. — Мало документации, кое-где не хватает комментариев и читаемости/вылизанности кода. Вы можете податься самостоятельно, а можете через меня: я посмотрю ваше резюме, дам базовые советы, если нужно, и сделаю вам рефку. Потом, если проходите, то я получаю бонус. Чего я не делаю: не рассказываю, что конкретно спрашивают на собесе (только в общих словах). Советую не пропускать такую возможность. Полный список тут (нужны также кванты, трейды, QA, аналитики, фронтендеры)

  • Про ИИ на собесах Хотя это всё еще редкость, но всё же появляются люди, которые пользуются ИИ (Chat GPT и аналоги) во время собеседований для получения преимуществ. Подавляющее большинство собесов сейчас проводятся дистанционно, и организовать это несложно. Один раз и нам такой чел попался (хотя доказательств нет). Но как с этим бороться? У меня нет хороших решений, но вот несколько средне-всратых идей: 1) Спрашивать сложные и редкие вопросы, на которые Chat GPT не знает ответ? Однако есть сомнение, что и кандидат обязан знать ответ на такой специфичный вопрос. 2) Спрашивать вопрос, на который Chat GPT знает ответ, а нормальный кандидат не должен знать ответа. 3) Проводить серию вопросов, требующих коротких ответов (и предупредить, что ответы должны быть короткие). Мысль в том, что ИИ склонен выдавать очень длинные ответы. 4) Смотреть, как кандидат пишет программу. Делает ли мелкие ошибки? Улучшает ли программу постепенно? Корявое ли у него форматирование? Прогоняет ли тестовые данные в уме? Если да, то он нормальный. 5) Внимательно смотреть на глаза, читает ли собеседник с экрана при ответе на вопросы? Всегда просить включать камеру (и предупреждать об этом до старта собеса). 6) Звать на финальный технический собес в офис, заодно можно будет лично познакомиться, и офис показать. 7) Увольнять с испытательного срока, если не справляется с задачами. А к вам приходили AI-cheating энтузиасты? Нужно ли с этим бороться? А вы сами пробовали так читерить?

  • Про логирование и как его ускорить Как-то раз я ускорял логгер через io_uring, т.к. скорость логгера не позволяла совмещать активную торговлю и достаточное количество информации для расследования случившихся инцидентов. Поэтому расскажу что понял на эту тему. Простое логирование состоит из 2х частей: форматирование и вывод. Различные throttle'ры, примочки, фильтры, окрашиватели рассматривать не будем. Форматирование: 1) std::stringstream - не стоит, не надо: это медленно. 2) std::snprintf - не стоит, не надо: медленно, не современно. 3) std::format, поддерживается только с C++20. Я его не смотрел, но подозреваю, что в текущем виде libfmt будет быстрее. 4) libfmt - базовый выбор, достаточно быстрый. Для самых критических областей добавляем FMT_COMPILE, но не повсюду. 5) Не форматировать вообще. Можно складывать бинарные данные в файл (подход Binlog), либо отправлять куда-то (но это получается уже телеметрия). Но тогда файл требует декодирования отдельной тулзой. Но это можно автоматизировать. 6) Выносить форматирование в отдельный поток. Интересный подход из Quill. Они encode'ят аргументы в буфер, и предполагается, что это быстрее, чем отформатировать. Попробую эту либу, и позже поделюсь впечатлениями. 7) Еще крайне желательно не вычислять формат аргументы, если текущий verbosity все равно не выведет эту строку. Это приводит нас к тому, что нам подходят только макросы в том месте, где юзеры вызывают логирование. Какие есть варианты по выводу сформированной строки на file storage: 1) Вывод в файл через: - std::ofstream::operator << и flush - fwrite + fflush (glibc) - один write syscall Выдают примерно одинаковые величины задержек. По логике можно представить, что дёрнуть просто syscall дешевле, но по факту разницу сложно увидеть. В первых двух вариантах можно не делать flush на каждую строку, это сильно быстрее (т.к. данные буферятся в юзерспейсе), но тогда: - Если долго нет новой строчки, то последнюю можно долго не видеть в логе. Но можно дополнительно flush'ить по таймеру. - Что-то потеряется при креше, т.к. останется в памяти процесса. Однако это можно достать из кордампа gdb-скриптюней (такое не писал, но выглядит осуществимым). Проблемы: средняя скорость min/avg: до 1 микроса, но периодически write может сделать серьезный пик, уходя в запись на устройство на 1-5мс (это на nvme). Можно попробовать понастраивать механизм сбрасывания dirty pages (см. sysctl -a | grep vm.dirty_). 2) Передаём задачу записи в отдельный поток. Так делает, например, Quill. Это очень быстро (скорость: от сотни наносекунд, если используете простую очередь, mutex и cv, и несколько наносекунд, если пишете в ring buffer (если не боитесь переполнения и нет параллельных вызовов)), но имеет свои недостатки: - Этому отдельному потоку тоже нужно какое-то (неторговое) ядро. - При креше вы можете потерять несколько последних строк (а именно они обычно нужны при креше). Снова можно пробовать выковыривать из крешдампа, но это неудобно. Скорее всего, можно дождаться дозаписи в крэшхэндлере. 3) Запись в ядре через io_uring: выглядит интересно, но имеет свои сложности: код надо очень аккуратно писать, понимать как настраивать kworker'ы (куда их пиннить, какие там размеры буферов и т.п.). Подходит для kernel'ов старше 5.1. В случае креша без специальных приседаний ядро не дозапишет строки. Я эту штуку использовал, и было интересно познакомиться с механизмом. Ждал дозаписи строк в крэш-хэндлере. 4) Запись в отдельном своём сервисе. Как ему максимально быстро передать строку - отдельный непростой вопрос, который может сильно всё усложнить. Сервисом тоже нужно управлять, деплоить, он тоже может упасть. 5) Вариант предыдущего пункта: выводить в stdout и писать в файловую систему через стандартный сервис (journald, dockerd). Тоже один syscall, поэтому по скорости как запись в файл, но без пиков в несколько миллисов. Довольно быстро, стандартно. Легко поддержать ротацию. Если процесс крашнется сразу после лог строчки, то демон всё дозапишет. Дополняйте, если что, или поправьте.