Дві поверхні. Одне трейдингове рішення.
Кількісна трейдингова фірма мала справу зі справжнім, але буденним роздратуванням: дані, потрібні їм для одного рішення, лежали у двох окремих інструментах, які гадки не мали про існування одне одного.
З одного боку — календар звітностей. Чотири основні публічні джерела, кожне трохи інакше — інші тикери, інші конвенції щодо часу дзвінка, час від часу дублікати, час від часу суперечності. Команда вручну звіряла їх між собою, перш ніж щось торгувати.
З іншого боку — дилерська гамма-експозиція SPX. Індикатор структури опціонного ринку, що показує, де концентрується систематичний тиск хеджування — які страйки стають магнітними, коли ринок рухається до них, де дилери купуватимуть слабкість і продаватимуть силу, який буфер має індекс, перш ніж увімкнеться механічний продаж. Для цього немає поля Bloomberg. Ви виводите його. Свіжим, із опціонного ланцюга біржі, з гаммою за Блеком-Шоулзом, обчисленою за страйком і агрегованою через дельта-коригування. І ви робите це кожні п'ятнадцять хвилин протягом касової сесії, інакше число, яке у вас є, уже неправильне.
Бриф був конкретним: один екран, обидва сигнали, синхронізовані. Наведіть курсор на подію звітності. Побачте гамма-картину для цієї дати. Готові продукти роблять одне або інше. Ніхто не робив обидва в одному поданні.
Злите подання: календар подій згори, гамма-експозиція знизу.
Панель давала трейдерам фірми календар звітностей, що виводив тикери з чотирьох незалежних джерел даних — дедубльовані, звірені, з індикаторами часу дзвінка — поверх живого графіка гамма-експозиції для SPX. Дві поверхні були синхронізовані: наведіть курсор на дату звітності, і графік гамми перемикався на картину експозиції для цієї сесії.
Графік гамми показував сукупну дилерську експозицію за страйком, дельта-скориговану, з лінією чистої гамми, що робила перехід плюс/мінус помітним з першого погляду. Нульова гамма — точка, де дилерське хеджування перемикається зі стабілізувального на підсилювальне — була позначена як константа. Денне відкриття й попереднє закриття були опорними лініями. Кластери страйків, де концентрувалася експозиція, з'являлися як піки.
Календар звітностей показував підтверджені й попередні події з індикаторами часу дзвінка (BMO, AMC, непідтверджено). Фільтрування на рівні тикера, селектор діапазону дат і подання списку відстеження дозволяли трейдеру звузити календар до свого портфеля, не торкаючись шару гамми. Два подання поділяли одну вісь дат. У цьому й була вся суть.
Фронтенд працював на Next.js 16 і React 19, D3.js відповідав за рендеринг гамми, а Zustand керував спільним станом календаря/графіка. Vercel Analytics було підключено з першого тижня продакшну.
- F · 01Багатоджерельний календар звітностей
- Чотири незалежні джерела, дедубльовані за тикером, датою й часом дзвінка. Обробка повторних спроб і backoff на рівні джерела плавно деградує календар, коли джерело ламається — подання трейдера ніколи не гасне.
- F · 02Живий графік гамма-експозиції SPX
- Гамма за Блеком-Шоулзом обчислюється свіжою на повному опціонному ланцюзі SPX кожні п'ятнадцять хвилин у ринкові години. Агрегована за страйком, дельта-скоригована. Нульова гамма й ключові опорні лінії позначені.
- F · 03Синхронізована вісь дат
- Наведіть курсор на подію звітності, і графік гамми перемикається на картину експозиції для цієї сесії. Злиття — у взаємодії, а не лише в макеті.
- F · 04Гаряче сховище лише на Redis
- Жодної холодної бази даних. Обидва конвеєри пишуть у Redis JSON, з ключем за датою й тикером. Субмілісекундне читання на фронтенді. Патерну доступу ніколи не було потрібно нічого більшого.
- F · 05Телеметрія на рівні джерела
- Кожен парсер звітностей виставляє свій стан повторних спроб як метрику. Зламаний парсер з'являється в моніторинговій панелі протягом одного циклу оновлення — раніше, ніж це помітить будь-хто, хто торгує проти цих даних.
- F · 06Двомовний бекенд
- Node/Express для скрейпінгу й дедублікації. Python/FastAPI для кількісного шару — scipy.stats, numpy, pandas. Кожна мова володіє проблемою, якій вона пасує. Один кеш зберігає результат.
Один кеш, два конвеєри, дві мови.
Проблема злиття даних під панеллю була архітектурним вибором із чіткою відповіддю: два незалежні конвеєри, жоден не блокує інший, що пишуть в єдине гаряче сховище.
Конвеєр звітностей працював на Node і Express. Сервіс-скрейпер витягував дані з чотирьох незалежних публічних календарів кожні три години. Кожне джерело мало власний парсер — ізольований, з обробкою повторних спроб і backoff, щоб тимчасовий збій на одному джерелі не псував інших. Дедублікація проводилася за тикером плюс датою плюс часом дзвінка; тикер, що з'являвся у трьох джерелах із незначними відмінностями у форматуванні дати, зводився до одного канонічного запису. Телеметрія на рівні джерела означала, що зламаний парсер було видно в моніторинговій панелі ще до того, як трейдер помічав щось не так.
Конвеєр гамми працював на Python. FastAPI обслуговував кількісний шар: свіжий опціонний ланцюг SPX, витягнутий із CSV-фіду біржі кожні п'ятнадцять хвилин у ринкові години, проведений через обчислення гамми за Блеком-Шоулзом із використанням scipy.stats і numpy, агрегований за страйком і дельта-скоригований. Python був тут правильним інструментом — scipy.stats, наукова екосистема Python і те, як кількісна команда вже мислила про обчислення. Переписування цього на TypeScript додало б тертя без жодної вигоди.
Щойно не стало холодної бази даних, на яку можна відкотитися, кожен конвеєр мусив бути коректним на момент запису. Цей тиск дав чистіші парсери й чіткішу історію on-call.
Обидва конвеєри писали в Redis. Жодної холодної бази даних — за задумом, а не через недогляд. Телеметрія з перших тижнів продакшну підтвердила те, що припускала архітектура: патерн доступу завжди був «як картина виглядає прямо зараз», ніколи «як вона виглядала шість тижнів тому». Redis JSON, з ключем за датою й тикером, давав фронтенду субмілісекундне читання поточного стану. Кеш був усім сховищем.
Чотири роки у продакшні. Досі оновлюється кожні п'ятнадцять хвилин.
Панель випустила своє перше продакшн-розгортання у 2022 році й відтоді працює безперервно. Архітектура з двох конвеєрів пережила три роки змін селекторів на джерелах звітностей, зміни формату біржового фіду з боку гамми й повну перебудову фронтенду, коли відбулася міграція на React 19.
Покриття звітностей сягає 5 000+ тикерів із чотирьох незалежних джерел. Гамма-рушій опрацьовує 3 000+ страйків SPX за цикл експірації. Каданси оновлення не змінилися: звітності кожні три години, гамма кожні п'ятнадцять хвилин протягом касової сесії.
Телеметрія circuit-breaker на рівні джерела виявилася найкориснішою з операційного погляду функцією. Парсери джерел ламаються тихо — редизайн календаря чи зміна API без оголошення. Телеметрія позначає деградоване джерело протягом одного циклу оновлення. Подання календаря в трейдера деградує плавно; воно не гасне.
Кодова база фронтенду досягла 325 комітів до кінця 2024 року. Бекенд перетнув 541. Гамма-рушій, що змінився найменше, стоїть на 59. Це співвідношення — чесна карта того, де насправді була робота: не в кількісній математиці — вона усталилася рано — а в безперервному обслуговуванні шару даних під нею.
Ми перестали вести окрему таблицю для цього протягом тижня після запуску. Воно робить звірку, яку ми робили вручну, і знає, як виглядає ринок, коли це робить.
