Называется Контур Оракул. 🔮 За первый день после запуска он собрал сотни реакций сотрудников, и сразу показал, что корпоративные прогнозы могут быть не развлечением, а рабочим инструментом.

Сделали сервис студенты ФИИТ (это программа УрФУ, которую поддерживает Контур). А в этой статье пришли и рассказали, как появилась идея Оракула, как он выглядит внутри, что за технологии использовали в серверной и клиентской частях, и как в основе сервиса работает алгоритм LMSR для расчётов.

Как появилась идея

Всё началось с Проектного практикума на ФИИТ УрФУ. Это обязательный курс, в котором команда студентов решает реальную задачу от заказчика, а в конце представляет результат перед комиссией.

Мы хотели найти интересный проект, который не пойдёт «в стол», и наткнулись на Prediction Market  — первое название Контур Оракула.

В описании проекта было указано, какая функциональность требуется на выходе, пара ссылок на описание математического алгоритма рынка предсказаний и условие, что проект должен быть написан на внутренней инфраструктуре Контура. Именно последнее условие нас привлекло больше всего. Нам не хотелось писать своих велосипедов.

Немного про то, как организовали работу и с какими трудностями столкнулись

Мы делаем не первый студенческий проект вместе, и это наш главный козырь. Четыре года в одной группе в институте научили нас понимать сильные и слабые стороны друг друга. Исходя из этого мы организовали процессы приоритезации и декомпозиции задач, научились договариваться, разделили зоны ответственности. 

Работали по спринтам. Всё началось с четырёх встреч: сначала мы сами сформировали видение, затем уточнили его с заказчиком, а потом четыре часа проектировали архитектуру. Всю коммуникацию вели в Telegram, но на втором спринте, когда проект разросся, перешли на корпоративный YouTrack для управления задачами. Регулярно собирались с заказчиком по окончанию спринтов, чтобы демонстрировать результаты и сверять курс.

Главной сложностью разработки стал ограниченный ресурс. В нашей команде все не только учатся, но и работают, а это самый большой учебный проект за семь семестров. Спасала только сработанность: каждый чётко знал свою роль: PM, тимлид, backend, frontend, math и чётко выполнял задачи в своей зоне ответственности.

Что такое Контур Оракул и зачем он нужен

Контур Оракул — это сервис, в котором можно делать предсказания на события. Под капотом он использует алгоритм LMSR (Logarithmic Market Scoring Rule) для вычисления исхода предсказания. Сервис задумывался, в первую очередь, для внутрикорпоративных нужд, чтобы собирать коллективный прогноз сотрудников по каким-то событиям. Например, предсказание может выглядеть так: N человек зайдут в Контур Оракул, чтобы потестировать его.

Как выглядит сервис

При первом входе в сервис пользователя встречает экран со всеми предсказаниями. На нём можно увидеть историю предсказаний, количество участников, динамику и исходы. Если нужно найти конкретные предсказания, можно воспользоваться фильтрами и строкой поиска.

На карточке каждого предсказания можно увидеть, в какую сторону, по мнению участников, оно склоняется. Зелёные цифры показывают процентное отношение «Сбудется», красные — «Не сбудется». Также на карточке видно, есть ли твои ставки на предсказание. Они тоже используют механику зелёного и красного цветов.

Любой пользователь может создавать свои предсказания. Для этого ему нужно нажать на кнопку «+Предсказание» в правом верхнем углу и заполнить карточку. Выглядит внутри вот так:

После создания предсказание уходит на верификацию администраторам. При необходимости опубликовать предсказание на определённую группу людей, например, только на команду руководителей, можно с помощью принципалов за пару нажатий.

При переходе в карточку предсказания открывается больше информации о нём: полное описание и условие завершения события, динамика изменения исхода и история ставок, которая позволяет узнать вклад в исход предсказания каждого участника.

Внутри каждого предсказания пользователь может внести часть своей кармы в один из исходов — «Сбудется» или «Не сбудется». Этот вклад сдвигает текущую оценку вероятности события: чем больше суммарная карма участников в выбранном исходе, тем выше его отображаемый шанс на карточке. Пересчёт происходит по алгоритму LMSR, поэтому влияние нелинейное: ранние ставки меняют прогноз сильнее, а по мере роста объёма рынка для заметного сдвига требуется больший вклад. Перед подтверждением ставки сервис сразу показывает ожидаемую выплату: если прогноз окажется верным, пользователь получает карму обратно и прибыль, если нет — теряет внесённый вклад. Полученную карму можно использовать в следующих предсказаниях, а ещё по ней строится рейтинг лучших предсказаний. 

Чтобы просматривать историю взаимодействий с предсказаниями, нужно перейти в личный профиль.

А увидеть самых точных предсказателей в компании можно в разделе «Рейтинг пользователей».

Какие технологии мы использовали для Оракула

Серверная часть 

Для backend мы выбрали C# и .NET 10 — это был не случайный выбор. Во-первых, C# — основной язык разработки в Контуре, а значит, вся корпоративная инфраструктура «из коробки» поддерживает .NET. Во-вторых, у нас уже был производственный опыт с этим стеком, что позволило сосредоточиться на продукте, а не на изучении нового инструмента.

Главным преимуществом разработки внутри Контура стал доступ к готовым инфраструктурным сервисам. И вместо того, чтобы тратить время на настройку базы данных, написание авторизации с нуля и изобретение деплой-пайплайна, мы взяли готовые проверенные сервисы и сфокусировались на бизнес-логике. И вот чем мы воспользовались:

  • Zebra — внутренняя распределённая база данных Контура, построенная поверх собственной файловой системы компании — Kanso. В ней хранится всё: предсказания, ставки, балансы пользователей, история транзакций и статистика. Про то, как устроена Kanso, можно почитать в статье на Хабре.

  • Корпоративные инструменты аутентификации и авторизации — поскольку мы разрабатываем продукт внутри Контура, мы могли использовать уже существующую инфраструктуру управления доступом. Пользователь входит в Оракул тем же корпоративным аккаунтом, что и во все остальные сервисы компании, а права доступа управляются централизованно. Нам не пришлось писать ни страницу входа, ни собственный auth-слой.

  • Houston — внутренняя система хостинга и деплоя приложений. Через неё в проде происходит масштабирование, конфигурирование и мониторинг.

  • Vostok —  корпоративный набор инструментов для разработки микросервисов на .NET. Он берёт на себя межсервисное взаимодействие, service discovery, управление конфигурацией и автоматически собирает логи, метрики и трассировки. Благодаря ему высокое эксплуатационное качество достигается без написания дополнительного инфраструктурного кода.

  • ZooKeeper — сервис координации распределённых систем, который использовали для распределённых блокировок, чтобы гарантировать корректность ставок при одновременных запросах от разных пользователей.

Готовые сервисы закрыли инфраструктурный слой, но архитектуру и бизнес-логику мы проектировали сами: 

  • доменную модель под Zebra, 

  • механизм блокировок на ZooKeeper, чтобы параллельные ставки не ломали расчёт LMSR, 

  • конфигурацию деплоя и мониторинга. 

По сути, задачи были те же, что и на рабочих проектах, только без легаси и «так исторически сложилось». 

Мы сами выбирали структуру данных, определяли контракты между сервисами, решали, как обрабатывать конкурентные запросы. Нечасто получается спроектировать продакшн-сервис с чистого листа.

Клиентская часть 

Для написания клиентской части приложения мы использовали стандартный набор: typescript, react, redux. Для генерации контрактов на фронте по спецификации api серверной части выбрали openapi-generator, а для отображения графиков — Chart.js. Такие решения выбрали исходя из технологий, которые есть в Контуре, а также нашего опыта и навыков.

Ещё использовали внутреннюю библиотеку ui-компонентов Контура и пакет со стандартизированными стилями и цветами — это продиктовало необходимость использования less в качестве альтернативы css в нашем проекте. 

Для разработки proof-of-concept функциональности и вёрстки задействовали Cursor, после чего код дорабатывали уже сами. Такой подход позволил нам сэкономить время и силы, и при этом добиться высокого качества кода. Хотя, конечно, переписывание кода, написанного ИИ, иногда раздражало. 😡 

Проблема сгенерированного кода в том, что без редактуры и проверки со стороны человека он со временем становится нечеловекочитаемым. Из-за ограниченных сроков мы сначала сгенерировали вёрстку клиентской части, а потом уже начали её редактировать и рефакторить. В итоге, пришлось работать с кодом, который был весьма запутан, не использовал общепринятые паттерны, не был разделён на модули и имел большое количество самоповторов. Он работал, но поддерживать его всё-таки было неприятно.

В процессе разработки нам пришлось освоить новые технологии: например, раньше мы никогда не работали с автоматической генерацией контрактов и не пользовались отдельными библиотеками для построения графиков. Также наш опыт программирования в тандеме с курсором был весьма ограничен. Пришлось многое вспомнить: redux, детали react, вёрстку.

Но в целом разработка веб-интерфейса нашего сервиса прошла достаточно гладко и без серьёзных издержек.

Как настраивали расчёты в сервисе

В основе всех математических расчётов заложено правило Logarithmic Market Scoring Rule. Но для нашего сервиса мы внесли ряд доработок, отличающих его от рынков предсказаний в чистом виде.

Немного внутренних терминов 

  • Токен исхода (q_i) — условная единица, похожая на акции фондовых рынков, отражает количество приобретённых «акций» конкретного исхода.

  • Logarithmic Market Scoring Rule (LMSR) — правило (функция), задающее оценку стоимости на рынке:

Формула расчёта выигрыша
Формула расчёта выигрыша

Классические рынки предсказаний

В них пользователи покупают определённое целое количество токенов исхода по рыночной цене, которую образует LMSR следующим образом: предположим, q1 — это состояние чистых проданных токенов до совершения сделки, q2 — состояние после. Тогда стоимость приобретения вычисляется так:

Цена за один токен, как правило, в таких рынках предсказаний, например, в  Polymarket, находится в диапазоне от 0 до 1$. В блоке с расчётом вероятностей покажем, почему это удобно.
Цена за один токен, как правило, в таких рынках предсказаний, например, в  Polymarket, находится в диапазоне от 0 до 1$. В блоке с расчётом вероятностей покажем, почему это удобно.

При этом в классических рынках дополнительно существует механизм торгов приобретёнными токенами между другими участниками. Итоговые выплаты при разрешении предсказания пользователям выплачивается как за выкуп токенов случившегося исхода по цене = 1.

Наша версия получения вероятностей

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

Из формулы нетрудно заметить, что предельная цена всегда находится в диапазоне от 0 до 1 (не включительно), а сумма предельных цен всех исходов равна 1. В контексте рынков предсказаний эту предельную цену обычно интерпретируют как оценку рынком вероятности наступления этого события, а единицу токена исхода продают по этой предельной цене.

Что получилось и какие дальнейшие планы

В первом квартале этого года мы опубликовали сервис на весь Контур. Статья о релизе собрала больше тысячи просмотров и сотни лайков. В день запуска мы фиксили баги и обращения, а на выходных собирали обратную связь.

Сейчас сервис находится в стадии активного развития. Мы не бросаем его, а дорабатываем по обратной связи. Наши планы — вывести его на стадию поддержки, когда сформируется стабильное комьюнити, которое само генерирует контент. 


Пишите свои комментарии и вопросы, будем рады пообщаться!