Live HL data → in-browser TA → 4-agent committee → LONG/SHORT/SKIP verdict. Terminal grid command bar + interactive agent-workflow graph. Paper-trading worker reuses the browser brain (dry-run + Telegram). Forgejo static deploy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
21 KiB
$QUANT — техническая документация (как это реализовано)
Описание реальной реализации системы: стек, поток данных, модули, алгоритмы, формулы. Документ описывает то, что есть в коде. Термины называются как есть (rule-based, детерминированно, paper-trading) — на этой фактуре можно строить тексты без додумывания.
1. Обзор
$QUANT — мульти-агентная система анализа бессрочных фьючерсов (перпов) на бирже Hyperliquid. Поток обработки:
рыночные данные (Hyperliquid API)
│
▼
теханализ (RSI, MACD, EMA, ATR, Bollinger)
│
▼
4 стратегии-агента голосуют LONG / SHORT / SKIP
│
▼
агрегация → вердикт (направление + уверенность + размер + TP/SL)
│
├──► веб-терминал (живая отрисовка, опрос раз в ~12 c)
└──► воркер: paper-trading на живых ценах + Telegram-уведомления
Стек. Фронтенд: React 19, Vite 8, TypeScript, Tailwind v4, Recharts, React Router 7,
anime.js, Lenis. Воркер: Node.js + tsx (TypeScript-рантайм). Менеджер пакетов: pnpm.
Ключевое свойство — детерминированность. Одно и то же состояние рынка даёт один и тот же вердикт. В торговом контуре нет генератора случайных чисел и нет LLM/ML: решение рассчитывается формулами по индикаторам.
2. Слой L1 — данные (src/lib/hyperliquid.ts)
Источник — публичный info-API Hyperliquid: POST https://api.hyperliquid.xyz/info,
без авторизации, CORS открыт (можно дёргать из браузера). Все ответы — JSON.
Три запроса:
| Функция | Тело запроса | Что возвращает |
|---|---|---|
getMarketStats(coins) |
{ "type": "metaAndAssetCtxs" } |
mark/mid цена, ставка фандинга, OI, объём по каждой монете |
getCandles(coin, interval, lookback) |
{ "type": "candleSnapshot", "req": { coin, interval, startTime, endTime } } |
массив свечей OHLCV |
getAllMids() |
{ "type": "allMids" } |
средние цены по всем тикерам |
Что извлекается в MarketStat: mark (маркировочная цена), mid, prevDayPx →
из неё changePct (изменение за 24 ч), funding (часовая ставка фандинга),
openInterest (в базовом активе) и oiUsd = openInterest × mark, dayVolumeUsd.
Свечи (Candle = { t, o, h, l, c, v }): по умолчанию интервал 1h, глубина
lookback = 120 свечей. Поддерживаемые интервалы: 1m / 5m / 15m / 1h / 4h / 1d.
Торгуемая вселенная по умолчанию: BTC, ETH, SOL (константа SYMBOLS).
Веб-терминал дополнительно показывает вкладки BNB, AVAX.
Сетевой слой: таймаут 12 c через AbortController, при ошибке — throw, который выше по
стеку обрабатывается (показывается последнее удачное значение / seed).
3. Слой L2 — технический анализ (src/lib/ta.ts)
Считается из реальных свечей. Реализованные индикаторы:
| Индикатор | Параметры | Реализация |
|---|---|---|
| EMA | периоды 9, 21, 50 | экспоненциальное сглаживание, k = 2/(period+1) |
| RSI | период 14 | по Уайлдеру (сглаженные средние gain/loss) |
| MACD | 12 / 26 / 9 | гистограмма = MACD-линия − сигнальная |
| ATR | период 14 | EMA от True Range (волатильность) |
| Bollinger %B | 20, 2σ | позиция цены в полосах: 0 = низ, 1 = верх |
| Funding-Z | — | нормировка ставки фандинга: clamp(funding / 0.00005, −4, 4) (иллюстративная шкала, не статистический z-score по истории) |
Функция summarizeTA(candles, funding) сворачивает серию в снимок TASnapshot:
{ price, rsi, ema9, ema21, ema50,
emaDiffPct, // (ema9 − ema21) / price × 100 — наклон тренда
macdHist, atr, atrPct,
pctB, // Bollinger %B
changePct, // изменение от первой свечи окна
fundingZ, closes }
Этот снимок — единственный вход для слоя сигналов. Один и тот же TASnapshot всегда даёт
один и тот же набор голосов.
4. Слой L3 — сигнальные агенты (src/lib/signals.ts)
Четыре независимых rule-based стратегии. Каждая возвращает голос
Vote = { agent, layer, direction, score, reason }, где direction ∈ {LONG, SHORT, SKIP},
score ∈ 0..1 — «уверенность» (конвикция), reason — человекочитаемое объяснение.
TrendAgent — следование за трендом
- LONG, если
ema9 > ema21иrsi > 50; SHORT, еслиema9 < ema21иrsi < 50; иначе SKIP. score = clamp(|emaDiffPct| × 14 + |rsi − 50| / 60, 0, 1).- Пример reason:
«EMA9 above EMA21, RSI 56».
BreakoutAgent — пробой полос Боллинджера
- LONG, если
%B > 0.92; SHORT, если%B < 0.08; иначе SKIP. score = clamp(|%B − 0.5| × 1.7, 0, 1).
MeanReversionAgent — возврат к среднему (фейд крайностей)
- LONG, если
rsi < 32и%B < 0.15; SHORT, еслиrsi > 68и%B > 0.85; иначе SKIP. scoreрастёт по мере удаления RSI от порога.
FundingAgent — фейд перекошенного фандинга
- SHORT, если
funding-Z > 1.5(лонги перегреты); LONG, еслиfunding-Z < −1.5; иначе SKIP. score = clamp(|funding-Z| / 3, 0, 1).
Для голосов SKIP score дополнительно занижается (×0.3–0.4) — пропуск тоже взвешивается.
5. Слой L4 — агрегация и вердикт (src/lib/signals.ts, aggregate())
Голоса сводятся в итог Verdict:
{ decision, // LONG / SHORT / SKIP
confidence, // 0..1
sizePct, // рекомендуемый размер позиции, %
tpPct, slPct, // take-profit / stop-loss, %
reasoning, // текстовое объяснение
net } // чистый счёт голосов
Алгоритм:
net = Σ (±1 × score)по всем не-SKIP голосам (LONG = +, SHORT = −).agreement= доля голосов, совпавших со знакомnet.- Порог
thr = 0.5:net > 0.5 → LONG,net < −0.5 → SHORT, иначеSKIP. - Уверенность:
- для сделки:
confidence = clamp(0.45 + |net|×0.18 + agreement×0.18, 0, 0.97); - для SKIP:
clamp(0.3 + |net|×0.2, 0.18, 0.5).
- для сделки:
- Параметры риска (масштабируются от волатильности
vol = clamp(atrPct, 0.4, 6)):tpPct = clamp(vol × 1.6, 0.8, 6);slPct = clamp(vol × 0.9, 0.6, 3.5);sizePct = clamp(confidence × 3.2, 0.5, 3.2).
reasoningсобирается из числа согласных агентов и причины лидирующего голоса, например:«1/1 agents align LONG. EMA9 above EMA21, RSI 56. RR 1.9»(RR = TP/SL).
Примечание по терминологии: в UI этот слой назван «LLMScreener / ConfidenceGate», но в текущей реализации это детерминированная агрегация формулами, а не вызов LLM. Порог уверенности (роль «ConfidenceGate») в воркере вынесен в
confFloor(см. §6).
6. Слой L5 — paper-trading движок (worker/paper.ts)
Превращает вердикт в бумажную позицию и ведёт её по реальной цене Hyperliquid.
Режим жёстко dry-run: ордера на биржу не подписываются и не отправляются — это
симуляция исполнения на живых рыночных данных, считающая реальный PnL.
Открытие — maybeOpen(coin, price, verdict):
- пропуск, если
decision = SKIP, илиconfidence < confFloor(по умолчанию 0.55), или по монете уже есть открытая позиция (maxOpenPerCoin = 1); entry = price(текущая mark-цена);tp = entry × (1 ± tpPct/100),sl = entry × (1 ∓ slPct/100)(знак по направлению);notional = equity × sizePct/100 × leverage(по умолчанию leverage = 5);- сохраняется
Position { id, coin, dir, entry, notional, tp, sl, tpPct, slPct, confidence, reason, openedAt }.
Ведение — markToMarket(coin, price): на каждом тике, если цена пересекла TP или SL,
позиция закрывается по уровню (исполнение по TP/SL предполагается точным).
Закрытие — расчёт PnL:
move = (exit − entry) / entry
pnlPct = (dir = LONG ? move : −move) × 100
pnlUsd = notional × pnlPct / 100
equity = equity + pnlUsd // капитал компаундируется
Закрытая сделка пишется в Trade (добавляются exit, closedAt, pnlPct, pnlUsd, outcome ('tp'|'sl'), holdMin).
Статистика — stats() (источник команды /pnl): число сделок, открытые позиции,
винрейт, суммарный PnL ($), ROI (%), profit factor, средний выигрыш/проигрыш, лучшая и
худшая сделка, текущий капитал.
7. Слой L6 — Telegram (worker/telegram.ts, worker/loop.ts)
Реализован на «голом» Bot API (без библиотек), через fetch.
Исходящие — tgSend(): POST .../sendMessage с parse_mode: HTML. Сообщения шлются
при открытии и закрытии позиции. Если токен/чат не заданы — текст печатается в
stdout (воркер работает и без Telegram).
Пример сообщения об открытии (реальный формат):
🟢 LONG BTC-PERP @ $62,135
size 2.6% · ×5 → $1,300 notional
TP +1.30% ($62,943) · SL -0.70% ($61,700)
confidence 81% · 1/1 agents align LONG. EMA9 above EMA21, RSI 56. RR 1.9.
Пример закрытия:
✅ TP BTC-PERP LONG
$62,135 → $62,943 (+1.30%, +$16.90)
held 2.4h · equity $10,017
Входящие — tgPoll(): long-polling getUpdates (offset-based), раз в ~3 c.
Поддерживаемые команды: /status (капитал, открытые позиции, винрейт), /positions
(детально по открытым), /pnl (он же /learn — сводная статистика по закрытым сделкам),
/help, /start.
Терминология: команда
/learnв текущей реализации возвращает статистическую сводку по закрытым сделкам, а не дообучение модели.
8. Воркер: рантайм (worker/loop.ts)
Постоянный фоновый цикл — работает независимо от того, открыт сайт или нет.
Тик (tick()) — по умолчанию раз в pollMs = 60 000 мс:
- один запрос
getMarketStats(coins)на все монеты; - по каждой монете (
tickCoin):getCandles→summarizeTA→screen(голоса + вердикт) →price = mark || ta.price→markToMarket(закрыть, если задело TP/SL, + уведомление) →maybeOpen(открыть, если есть сигнал, + уведомление) → запись в лог сигналов при смене решения по монете; save()— сохранение состояния на диск.
Параллельно: commandLoop() раз в 3 c (если задан Telegram-токен).
Запуск:
pnpm worker # бесконечный цикл
pnpm worker:once # один тик и выход (смоук-тест)
9. Хранение состояния (worker/store.ts)
Один JSON-файл worker/data/state.json (в .gitignore), запись атомарная
(во временный файл + rename). Переживает перезапуск. Структура State:
{ startedAt, mode,
equity, // текущий капитал
positions: Position[], // открытые
closed: Trade[], // закрытые (последние 500)
signals: SignalLog[], // лог решений при смене (последние 200)
equityCurve: { t, equity }[] } // кривая капитала (последние 1000 точек)
Это ровно тот формат, который будущий HTTP-эндпоинт (/api/equity, /api/signals) сможет
отдавать на сайт. (SQLite заменяема без изменения вызывающего кода — сейчас используется
JSON.)
10. Фронтенд (src/)
Две страницы (React Router, src/App.tsx):
/— лендинг (src/pages/Landing.tsx): секции Hero, Thesis, Layers (6-слойный конвейер), SignalDesk, AIBrief (ресёрч-агент), AgentRoster, Performance, Tokenomics, TelegramModes, Roadmap, FAQ, CTA./terminal— живой терминал (src/pages/Terminal.tsx): опрашивает Hyperliquid раз в ~12 c (useScreen), показывает: цену/изменение/фандинг/OI/объём/ATR, сетку индикаторов, карточку вердикта, голоса агентов с барами уверенности, граф «оркестра» агентов.
Seed-фолбэк (src/data/seedMarket.ts): пока грузятся живые данные, мгновенно
показываются детерминированные значения (помечены как не-live), чтобы UI не «висел»; затем
заменяются реальными.
Ресёрч-агент (FactsAI) (src/lib/factsai.ts, AIBrief.tsx): интеграция с deep-research
API (AI-бриф рынка с цитатами источников). Требует прокси (CORS + ключ на сервере);
секция сама скрывается, если фид недоступен.
11. Статус реализации (по слоям)
Технический срез: что считается на живых данных, что симулируется, чего ещё нет.
| Компонент | Статус |
|---|---|
| L1: свечи, mark/mid, фандинг, OI, объём | ✅ Реально (Hyperliquid live) |
| L1: стакан, лента ликвидаций | ⚪ Не реализовано (только в описании ростера) |
| L2: RSI / MACD / EMA / ATR / Bollinger / funding-Z | ✅ Реально |
| L3: 4 агента (Trend / Breakout / MeanReversion / Funding) | ✅ Реально |
| L4: агрегация → вердикт + риск-параметры | ✅ Реально (формулы, не LLM) |
| L5: исполнение | 🟡 Paper / dry-run (реальный PnL, без ордеров на бирже) |
| L6: Telegram (уведомления + команды) | ✅ Реально |
| Персистентность | ✅ Реально (JSON; в копирайте сайта упомянут SQLite) |
| Ростер «18 агентов» | ⚪ Метаданные UI; реально голосуют 4 стратегии |
| Кривая капитала / «последние сделки» на сайте | 🟡 Seed-заглушка (live-данные даёт воркер, на сайт ещё не выведены) |
| FactsAI ресёрч-бриф | 🟡 Код есть, выключен (нужен прокси; upstream отдаёт ошибку) |
| Коннект кошелька / hold-to-access | ⚪ Косметика (после TGE) |
| Токен $QUANT (цена/контракт/холдеры) | ⚪ pre-TGE (ещё не выпущен) |
В разработке (roadmap): реальное исполнение ордеров на Hyperliquid (live-режим за
флагом); LLM-слой (обоснование вердиктов и осмысленный /learn через Claude); вывод
реального трек-рекорда воркера на сайт через /api/*.
12. Конфигурация (env, всё опционально — есть дефолты)
Читается воркером из .env.local (worker/config.ts):
| Переменная | Дефолт | Назначение |
|---|---|---|
QUANT_COINS |
BTC,ETH,SOL |
торгуемая вселенная |
QUANT_INTERVAL |
1h |
таймфрейм свечей |
QUANT_POLL_MS |
60000 |
период тика, мс |
QUANT_EQUITY_START |
10000 |
стартовый бумажный капитал, $ |
QUANT_LEVERAGE |
5 |
плечо для расчёта notional |
QUANT_CONF_FLOOR |
0.55 |
минимальная уверенность для входа |
QUANT_MAX_OPEN_PER_COIN |
1 |
одновременных позиций на монету |
TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID |
— | Telegram (без них — вывод в stdout) |
13. Глоссарий
- Perp (перп) — бессрочный фьючерс, контракт без даты экспирации.
- Hyperliquid — децентрализованная биржа перпов с открытым публичным API данных.
- LONG / SHORT / SKIP — ставка на рост / на падение / нет сигнала.
- Funding (фандинг) — периодический платёж между лонгами и шортами; перекос показывает «перегретость» толпы. Положительная ставка → лонги платят шортам.
- OI (Open Interest) — открытый интерес, сумма открытых контрактов.
- Mark price — справедливая цена для расчётов (а не цена последней сделки).
- RSI — осциллятор 0–100: >70 перекуплен, <30 перепродан.
- MACD — индикатор моментума по разнице скользящих средних.
- EMA — экспоненциальная скользящая средняя (тренд).
- ATR — мера волатильности (средний диапазон свечи).
- Bollinger %B — положение цены внутри полос Боллинджера (0 = низ, 1 = верх).
- TP / SL — take-profit / stop-loss.
- RR (risk/reward) — отношение потенциальной прибыли к риску (TP/SL).
- Notional — номинальный размер позиции в $ (капитал × размер% × плечо).
- PnL — прибыль/убыток. Equity — капитал. Profit factor — сумма прибылей / сумма убытков.
- Paper-trading / dry-run — «бумажная» торговля: расчёт как в реале, но без ордеров и денег.
- Детерминированный — при одинаковом входе всегда одинаковый результат (нет случайности).
- pre-TGE — до выпуска токена (Token Generation Event).