All checks were successful
deploy / deploy (push) Successful in 7s
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>
348 lines
21 KiB
Markdown
348 lines
21 KiB
Markdown
# $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`:
|
||
|
||
```ts
|
||
{ 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`:
|
||
|
||
```ts
|
||
{ decision, // LONG / SHORT / SKIP
|
||
confidence, // 0..1
|
||
sizePct, // рекомендуемый размер позиции, %
|
||
tpPct, slPct, // take-profit / stop-loss, %
|
||
reasoning, // текстовое объяснение
|
||
net } // чистый счёт голосов
|
||
```
|
||
|
||
**Алгоритм:**
|
||
1. `net = Σ (±1 × score)` по всем не-SKIP голосам (LONG = +, SHORT = −).
|
||
2. `agreement` = доля голосов, совпавших со знаком `net`.
|
||
3. Порог `thr = 0.5`: `net > 0.5 → LONG`, `net < −0.5 → SHORT`, иначе `SKIP`.
|
||
4. **Уверенность:**
|
||
- для сделки: `confidence = clamp(0.45 + |net|×0.18 + agreement×0.18, 0, 0.97)`;
|
||
- для SKIP: `clamp(0.3 + |net|×0.2, 0.18, 0.5)`.
|
||
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)`.
|
||
6. `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` мс:
|
||
1. один запрос `getMarketStats(coins)` на все монеты;
|
||
2. по каждой монете (`tickCoin`):
|
||
`getCandles` → `summarizeTA` → `screen` (голоса + вердикт) → `price = mark || ta.price`
|
||
→ `markToMarket` (закрыть, если задело TP/SL, + уведомление) → `maybeOpen` (открыть, если
|
||
есть сигнал, + уведомление) → запись в лог сигналов **при смене решения** по монете;
|
||
3. `save()` — сохранение состояния на диск.
|
||
|
||
Параллельно: `commandLoop()` раз в 3 c (если задан Telegram-токен).
|
||
|
||
**Запуск:**
|
||
```bash
|
||
pnpm worker # бесконечный цикл
|
||
pnpm worker:once # один тик и выход (смоук-тест)
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Хранение состояния (`worker/store.ts`)
|
||
|
||
Один JSON-файл `worker/data/state.json` (в `.gitignore`), запись **атомарная**
|
||
(во временный файл + `rename`). Переживает перезапуск. Структура `State`:
|
||
|
||
```ts
|
||
{ 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).
|
||
```
|