Версионирование протоколов маркет-даты

Версионирование протоколов маркет-даты

Sehnsucht (https://t.me/cxx95)
Нейрослоп

Одна из особенностей биржевой разработки - большое количество разнообразных протоколов, главным образом для "маркет-даты", то есть об информации о том, что происходит на бирже. Эти протоколы еще меняются со временем - выходят новые версии под обновления биржевого движка или финансовых регуляций. Так что даже если несколько бирж изначально используют один протокол (по лицензии или историческим причинам), в процессе развития появляется биржевый "диалект", часто развитие приводит к "невзаимопонятным" версиям якобы одного и того же протокола, то есть одним и тем же кодом нельзя работать с данными разных бирж.


Если данные только начинают трогать на базовом уровне, то создать C++ структуры под бинарные месседжи еще имеет смысл, чтобы не тратить много времени. Для биржи, где условно регулярно (раз в год-два) происходят релизы, уже требуется другой подход для эффективного версионирования.


На мой взгляд, наиболее эффективным является написание кодогенератора на Python, который по "схеме" месседжей (в xml или json формате, изредка биржа сама предоставляет их) генерирует C++ хедер со структурами для схемы. В момент нового релиза надо сдавать PDF'ку обновленной спеки в LLM агента, чтобы оно наслопало обновленную схему после релиза. Таким образом, для каждой версии протокола есть полный набор C++ структур в отдельном файле, соответствующий этой версии. Это конечно типо "избыточно", так как поменяться могло 1-2 месседжа из 50, но в долгосроке это более удобно, чем вручную микроменеджить каждый месседж. Чтобы не было конфликта C++ имен, используются отдельные неймспейсы.


При этом весь код, который работает с протоколом (читатка маркет-даты, симулятор, и так далее), от версии глобально не меняется - разве что могут где-то быть if constexpr на проверку версии при серьезном изменении.


Как это обеспечить? Можно в кодогенерированном файле завести traits-структуру с перечислением соответствующих структур месседжей, и по желанию с еще какой-то дополнительной инфой (например датой начиная с которой действует):

struct MessageTraits {
  inline static const auto kBeginDate = Date{2025, 3, 20};

  using AddOrder = AddOrder;
  using DeleteOrder = DeleteOrder;
  // ...
};

Далее заводим функцию-хелпер, которая помогает выбрать для задачи (пресловутая маркет-даты или симулятор) нужный traits в зависимости от даты и возможно еще чего-нибудь (например биржи или отдела рынка), чтобы оно понимало какими структурами щас надо читать сырые байты:

decltype(auto) with_message_traits(Date date, auto&& func) {
  if (date >= ver_3_10::MessageTraits::kBeginDate) {
    return func(TypeTag<ver_3_10::MessageTraits>{});
  } else if (date >= ver_3_09::MessageTraits::kBeginDate) {
    return func(TypeTag<ver_3_09::MessageTraits>{});
  } else {
    return func(TypeTag<ver_3_08::MessageTraits>{});
  }
}

(Здесь TypeTag это идиома для того чтобы менее страшно передавать инфу про типы)

template<typename T>
struct TypeTag {
  using type = T;
};

Это сильно облегчает вопрос с апдейтами маркет-даты, и надо писать меньше кода.

Report Page