События и метаданные: как рынки собираются в матчи
Один реальный матч — это обычно несколько рынков: «кто победит», «тотал убийств», «первый Рошан». On-chain это независимые pm_market_object, но клиент (Forecaster) показывает их одной карточкой события со ставками по исходам и ссылкой «ещё N линий». Склейка происходит только через метаданные — специальных «родительских» объектов в консенсусе нет, и это осознанно: протокол остаётся минимальным, а группировку задаёт создатель рынка при создании.
Короткая модель
- Каждый рынок несёт свободное текстовое поле
metadata(JSON-строка вpm_create_market). - Нода парсит из него белый список ключей и строит индексы; всё остальное игнорирует.
- Рынки с одинаковым
event— «одно событие». Рынок безchild— лицевой (родительский), сchild: 1— дочерняя линия (проп). - Forecaster: таб «События» группирует активные рынки по
event, лицевым показывает винлайн-рынок, дочерние прячет из общих лент и раскрывает на странице события.
Ключи метаданных, которые индексирует нода
Нода извлекает из metadata только эти поля (остальные ключи не индексируются, но остаются в сыром JSON — клиенты могут читать их сами):
| Ключ | Тип | Зачем |
|---|---|---|
title | строка | Человекочитаемый вопрос рынка (заголовок карточки). |
category | строка | Раздел листингов (esports, sports, crypto…) — индекс by_category. |
subcategory | строка | Уточнение раздела (опционально). |
tags | массив или CSV | Теги для фильтров; клиент читает массив market.metadata.tags, который нода пересобирает сама. |
image | строка (URL) | Обложка карточки (ссылка, не хостится on-chain). |
description | строка | Краткие правила резолва — «как оракул решит исход». |
event | строка (slug) | Ключ склейки события. Все рынки одного матча ставят одинаковый event. |
event_title | строка | Человекочитаемое имя события («Dota 2: MOUZ vs Vici — TI 2026»). |
child | 1 / true | Дочерняя линия (проп). Прячется из лент по категории/тегу; видна на странице события. |
banned_jurisdictions | массив или CSV | Юрисдикционный фильтр клиентов. |
condition_id | строка | Дедуп-идентификатор источника (для зеркалирующих парсеров). |
Правила разбора: metadata должен быть валидным JSON-объектом (не-JSON просто не индексируется); tags/banned_jurisdictions принимаются и массивом, и CSV-строкой; child принимается как true, 1 или "1". Теги матчатся без учёта регистра.
Как указать «родительский» рынок
Родитель не указывается явно — он вычисляется отсутствием child:
- Всем рынкам матча проставьте одинаковый
event(стабильный slug: латиница, дефисы — напримерdota2-mouz-vg-2026-07-12) и одинаковыйevent_title. - Главному рынку матча («кто победит» / moneyline) — не ставьте
child. Это и есть родитель: он остаётся видимым во всех лентах и станет лицом карточки события. - Всем остальным линиям (тоталы, форы, спецрынки) —
child: 1. Они исчезают из общих лент (не шумят в категориях), но полностью доступны на странице события и по прямой ссылке.
Минимальный пример metadata для трёх рынков одного матча:
// Родитель (винлайн) — БЕЗ child
{"title":"MOUZ победит Vici Gaming?","category":"esports","tags":["dota-2"],
"event":"dota2-mouz-vg-2026-07-12","event_title":"Dota 2: MOUZ vs Vici Gaming"}
// Дочерняя линия 1
{"title":"Тотал убийств больше 45.5 (карта 1)?","category":"esports","tags":["dota-2"],
"event":"dota2-mouz-vg-2026-07-12","event_title":"Dota 2: MOUZ vs Vici Gaming","child":1}
// Дочерняя линия 2
{"title":"Первый Рошан — MOUZ?","category":"esports","tags":["dota-2"],
"event":"dota2-mouz-vg-2026-07-12","event_title":"Dota 2: MOUZ vs Vici Gaming","child":1}Важно: event иммутабелен на практике — клиенты склеивают по точному совпадению строки, поэтому выберите ключ до создания рынков и используйте его во всех линиях матча одинаково (регистр и дефисы имеют значение).
Что делает нода
- Строит мета-объект рынка (
pm_market_meta) с распарсенными полями и индексами: по категории, тегам и по событию (by_meta_event); мета едет в снапшот. list_markets_by_category(...)по умолчанию прячет дочерние (hide_children = true, 8-й аргумент) — ленты показывают только родителей; передайтеfalse, чтобы увидеть все линии.list_markets_by_event(event, from, limit)отдаёт все рынки события — родителя и детей, без фильтра. Это API страницы события.- В строках листингов
event_titleотдаётся на верхнем уровне, в полной карточке рынка — внутриmetadata;tagsнода пересобирает в массив.
Что делает Forecaster с этим
- Таб «События» (спорт-бук вид): активные рынки группируются по
event; лицевым выбирается рынок, чей заголовок похож на винлайн (winner/moneyline/to win), иначе — первый бинарный; на карточке — исходы с текущими коэффициентами (тап кладёт ногу в купон) и ссылка «ещё N линий». - Страница события
#/event/<ключ>— все линии матча одним списком (list_markets_by_event). - Карточки показывают
event_titleнад вопросом; крошки «Категория › теги» ведут в листинги. - Ленты по категории/тегу не показывают дочерние рынки — пропы живут за карточкой события.
Практический вывод для создателя: правильно проставленные event/event_title/child — это разница между «десять разрозненных рынков шумят в ленте» и «одна аккуратная карточка матча со всеми линиями внутри». Ошиблись в ключе события — линии не склеятся; забыли child — пропы засорят общие ленты.
Смотрите также
- Создатель рынка — стартовая ликвидность, оракул, комиссия.
- Мульти-исходные рынки — когда вместо нескольких бинарных лучше один LMSR.
- Спецификация — формальная модель объектов и индексов.