Powrót do bloga
Vibe Sense

To twoja platforma obserwująca samą siebie.

Lata temu porzuciłem bota tradingowego. Jego architektura stała się najciekawszą częścią next-vibe. Każdy węzeł w grafie jest endpointem - wywoływalnym z CLI, wykrywalnym przez AI, wbudowanym w twoją platformę.

14 min czytania
Architektura

Bot tradingowy, który porzuciłem

Kilka lat temu sforkałem OctoBot i zbudowałem coś, co nazwałem Octane. Backend Python, frontend React. Można było przeciągać wskaźniki techniczne na płótno, łączyć ewaluatory, konfigurować reguły wykonania, ustawiać alerty, wystawiać zlecenia. Miał pełny wizualny kreator strategii. Nadal używam go do własnego portfela.

Porzuciłem go jako bazę kodu. Python był rozrośnięty, architektura nagromadziła wystarczająco dużo długu technicznego, że każda zmiana była kosztowna.

Ale wciąż o tym myślałem. Konkretnie o tym, co sprawiało, że działał jako system.

Bot tradingowy miał tę architekturę właściwą. To, co zrobiłem źle, to zbudowanie go w izolacji.

Octane (fork OctoBot)

Wizualny kreator strategii, wskaźniki drag-and-drop, backend Python. Porzucony jako baza kodu.

Odkrycie

Ta struktura nie opisuje tradingu. Opisuje każdy proces biznesowy z danymi w czasie.

Przebudowany w next-vibe

Właściwie typowany. Każdy węzeł to standardowy endpoint. Dostępny wszędzie, gdzie jest platforma.

Każdy biznes jest szeregiem czasowym

W bocie tradingowym części są proste. Źródło danych: dane cenowe, wolumen, cokolwiek czytasz. Wskaźnik: średnia krocząca, RSI, MACD - przyjmuje surowe dane, produkuje pochodny sygnał. Ewaluator: czy szybka MA jest ponad wolną MA? Warunek logiczny. Akcja: kiedy ewaluator strzela, zrób coś.

Ta struktura nie opisuje tradingu. Opisuje każdy proces biznesowy, w którym masz dane w czasie, warunki które cię interesują, i akcje, które chcesz podjąć, gdy te warunki są spełnione.

Wzrost użytkowników

To szereg czasowy. Czy spada? To jest ewaluator. Wyślij kampanię win-back. To jest akcja.

Zdrowie kampanii e-mail

Wskaźniki otwarć, wskaźniki odrzuceń, wskaźniki wypisów. Wszystko to szeregi czasowe. Wszystkie warunki do ewaluacji. Wszystkie wyzwalalne.

Ekonomia kredytów

Prędkość wydatków. Wskaźnik spalania vs. wskaźnik zakupów. Wszystko.

Anomalie przychodów

Wskaźnik zwrotów przekracza 20% w ciągu dnia - Thea jest powiadamiana przed zobaczeniem tego w dashboardzie.

Cztery typy węzłów

Trzy typy węzłów, które trzeba zrozumieć, aby czytać dowolny graf. Plus czwarty, który zamyka krąg.

DataSource

Endpoint należący do domeny, który odpytuje bazę danych i zwraca { timestamp, value }[] dla danego zakresu czasu i rozdzielczości. Żyje ze swoją domeną. Zna własny schemat.

Indicator

Czysty, wielokrotnego użytku endpoint obliczeniowy - EMA, RSI, MACD, Wstęgi Bollingera, clamp, delta, średnia okienna. Brak SQL. Brak wiedzy domenowej. Działa na dowolnym źródle danych.

Evaluator

Próg lub warunek. Przyjmuje serię i zadaje pytanie. Czy ta wartość jest poniżej 0,7? Czy ten stosunek przekroczył 20%? Zwraca sygnał - wystrzelony lub nie.

Action

Gdy poprzedzający ewaluator strzeli, wywoływany jest konkretny endpoint. In-process. Brak HTTP. Taka sama walidacja, taka sama autoryzacja, taki sam typ odpowiedzi jak każde inne wywołanie w systemie.

DataSource

Endpoint należący do domeny, który odpytuje bazę danych i zwraca { timestamp, value }[] dla danego zakresu czasu i rozdzielczości. Żyje ze swoją domeną. Zna własny schemat.

Indicator

Czysty, wielokrotnego użytku endpoint obliczeniowy - EMA, RSI, MACD, Wstęgi Bollingera, clamp, delta, średnia okienna. Brak SQL. Brak wiedzy domenowej. Działa na dowolnym źródle danych.

Evaluator

Próg lub warunek. Przyjmuje serię i zadaje pytanie. Czy ta wartość jest poniżej 0,7? Czy ten stosunek przekroczył 20%? Zwraca sygnał - wystrzelony lub nie.

Action

Gdy poprzedzający ewaluator strzeli, wywoływany jest konkretny endpoint. In-process. Brak HTTP. Taka sama walidacja, taka sama autoryzacja, taki sam typ odpowiedzi jak każde inne wywołanie w systemie.

Każdy węzeł jest endpointem

To jest najważniejsza rzecz, którą chcę teraz wyjaśnić, zanim pójdziemy dalej.

Stare podejście (Octane)

EMA istnieje tylko jako węzeł w grafie. Nie można go wywołać z CLI. Nie pojawia się jako narzędzie AI. Jest prywatnym szczegółem implementacyjnym.

Podejście next-vibe

Każdy węzeł Vibe Sense to standardowy endpoint, zdefiniowany za pomocą createEndpoint(), zarejestrowany w tym samym rejestrze endpointów co wszystko inne na platformie.

bash
1$ vibe vibe-sense-ema --period=7
2
3  ┌─────────────────────────────────────────┐
4  │  analytics/indicators/ema               │
5  │  EMA (Exponential Moving Average)       │
6  ├─────────────────────────────────────────┤
7  │  period   7                             │
8  │  points   365 input → 365 output        │
9  │  result   [ { timestamp, value }, ... ] │
10  └─────────────────────────────────────────┘

Ten sam endpoint EMA, który działał jako węzeł w grafie lejka leadów - ta sama definicja, ta sama walidacja, ta sama autoryzacja - wywoływalny samodzielnie z CLI.

TEN SAM endpoint, który jest węzłem w grafie lejka leadów, jest też samodzielnym narzędziem na 13 platformach.

Pipeline to tylko endpointy wywołujące endpointy.

Ale akcje to nie są transakcje

Gdy sygnał strzeli, silnik wywołuje dowolny endpoint. In-process. Bez rundy HTTP. Alert. Wyzwalacz kampanii. Eskalacja AI z wypełnionym wstępnie kontekstem. Thea jest powiadamiana. Sekwencja win-back startuje. Cokolwiek jest podłączone do tego ewaluatora.

Brak webhooka.
Brak własnego serwisu alertowania.
Brak Zapiera.

Platforma wywołuje samą siebie.

Alert

Wywołaj complete-task - Thea odbiera to natychmiast.

Kampania

Wyzwól sekwencję konwersji, gdy prędkość leadów przekroczy próg.

Eskalacja AI

Uruchom przetwarzanie AI z wypełnionym wstępnie kontekstem o tym, który sygnał je wyzwolił.

Przechodzenie przez graf lejka leadów

To jest Lead Acquisition Funnel. Działa co sześć godzin. Prześledźmy go od góry do dołu.

Kolumna 1: Źródła danych

Prawdziwe endpointy. Każdy żyje pod leads/data-sources/. Przyjmują zakres czasu i rozdzielczość, wykonują zapytanie SQL i zwracają { timestamp, value }[].

leads.created

Zapytanie o leady według created_at. Sparse - godziny bez nowych leadów nie produkują punktu danych.

leads.converted

Pogrupowane według converted_at, zlicza leady, które osiągnęły status SIGNED_UP.

leads.bounced

Leady z odrzuconym e-mailem na przedział czasowy.

leads.active

Wskaźnik migawkowy przy rozdzielczości ONE_DAY. Zlicza wszystkie leady nie będące w stanach końcowych.

Kolumna 2: Wskaźniki

Czyste obliczenia. Endpoint EMA żyje pod analytics/indicators/ema. Jego konfiguracja w grafie to tylko { type: "indicator", indicatorId: "ema", params: { period: 7 } }.

leads_created_ema7

Wskaźnik EMA, period=7. Automatycznie rozszerza zakres pobierania danych upstream dla rozgrzewki.

conversion_rate

Transformer: dzieli leads.converted przez leads.created dla każdego przedziału czasowego. Ograniczony do 0–1.

Kolumna 3: Ewaluatory

Warunki progowe. Każdy zwraca sygnał - wystrzelony lub nie.

eval_lead_drop

EMA(7) < 0,7 przy rozdzielczości ONE_WEEK. Prędkość tworzenia leadów, wygładzona przez 7 okresów, spada poniżej 70%.

eval_zero_leads

leads.created < 1/dzień. Cały dzień mija bez żadnych nowych leadów.

eval_low_conversion

conversion_rate < 5%/tydzień. Konwersja lejka spada poniżej 5%.

Każdy węzeł jest endpointem

Ten sam endpoint EMA, który działał jako węzeł w grafie lejka leadów - ta sama definicja, ta sama walidacja, ta sama autoryzacja - wywoływalny samodzielnie z CLI.

typescript
1// analytics/indicators/ema/definition.ts
2const { POST } = createEndpoint({
3  scopedTranslation,
4  aliases: [EMA_ALIAS],
5  method: Methods.POST,
6  path: ["system", "unified-interface", "vibe-sense", "indicators", "ema"],
7  title: "post.title",
8  description: "post.description",
9  icon: "activity",
10  category: "endpointCategories.analyticsIndicators",
11  tags: ["tags.vibeSense"],
12  allowedRoles: [UserRole.ADMIN],
13  fields: objectField(scopedTranslation, {
14    usage: { request: "data", response: true },
15    children: {
16      source: timeSeriesRequestField(scopedTranslation, { ... }),
17      resolution: resolutionRequestField(scopedTranslation, { ... }),
18      range: rangeRequestField(scopedTranslation, { ... }),
19      lookback: lookbackRequestField(scopedTranslation, { ... }),
20      period: requestField(scopedTranslation, {
21        fieldType: FieldDataType.NUMBER,
22        schema: z.number().int().min(1).max(500),
23        label: "post.fields.period.label",
24        description: "post.fields.period.description",
25      }),
26      result: timeSeriesResponseField(scopedTranslation, { ... }),
27      meta: nodeMetaResponseField(scopedTranslation, { ... }),
28    },
29  }),
30  errorTypes: { /* all 9 required */ },
31  successTypes: { title: "post.success.title", ... },
32  examples: { requests: { default: { source: [...], period: 3 } }, ... },
33});

computeEma()

TEN SAM endpoint, który jest węzłem w grafie lejka leadów, jest też samodzielnym narzędziem na 13 platformach.

typescript
1// analytics/indicators/ema/repository.ts
2// Pure computation. No DB access.
3export class EmaIndicatorRepository {
4  static computeEma(points: TimeSeries, period: number): TimeSeries {
5    if (points.length === 0) return [];
6    const k = 2 / (period + 1);
7    const result: TimeSeries = [];
8    let ema: number | undefined;
9    for (const p of points) {
10      ema = ema === undefined ? p.value : p.value * k + ema * (1 - k);
11      result.push({ timestamp: p.timestamp, value: ema });
12    }
13    return result;
14  }
15}
16
17// route.ts wires it:
18// handler: ({ data }) => {
19//   const result = EmaIndicatorRepository.computeEma(data.source, data.period);
20//   return success({ result, meta: { ... } });
21// }

Źródła danych należące do domeny

Jedna z decyzji architektonicznych, z których jestem najbardziej zadowolony: źródła danych żyją ze swoją domeną, nie w jakimś centralnym katalogu vibe-sense/.

Domena leadów
leads/data-sources/
leads-created/
constants.ts
definition.ts
i18n/
repository.ts
route.ts
leads-converted/
leads-bounced/
leads-active/
leads-campaign-running/
leads-email-opens/
leads-email-clicks/
leads-engagements/
leads-form-submits/
leads-website-visits/
... 15 źródeł danych łącznie
Domena kredytów
credits/data-sources/
credits-spent-total/
constants.ts
definition.ts
i18n/
repository.ts
route.ts
credits-purchased/
credits-earned/
credits-refunded/
credits-balance-total/
credits-subscription-revenue/
credits-transactions-count/
credits-wallets-total/
... 17 źródeł danych łącznie

leads/data-sources/leads-created zna tabelę leads. Importuje z leads/db. Używa LeadStatus z leads/enum. Jeśli usuniesz moduł leads, źródła danych idą razem z nim. Nic nie pozostaje osierocone.

Wskaźniki pod analytics/indicators/

Czyste obliczenia - EMA, RSI, MACD, Wstęgi Bollingera, clamp, delta, średnia okienna. Brak wiedzy domenowej. Działa na dowolnym źródle danych.

Przy starcie rejestr wskaźników automatycznie odkrywa oba. Endpointy źródeł danych rejestrują się jako definicje węzłów. Dodajesz nową domenę, dodajesz endpointy data-sources/, eksportujesz graphSeeds. Pojawiają się.

Domena posiada własną obserwowalność.

Wersjonowanie, backtest, trwałość

Trzy rzeczy, które sprawiają, że Vibe Sense jest bezpieczny do uruchomienia na produkcji.

Wersjonowanie

Grafy są wersjonowane. Gdy edytujesz graf, tworzysz nową wersję - nigdy nie mutuj aktywnej. Nowa wersja jest wersją roboczą. Promujesz ją jawnie. Rollback jest trywialny.

Backtest

Przed promowaniem możesz przetestować wstecznie na historycznym zakresie czasu. Warunki są ewaluowane. Sygnały są rejestrowane. Endpointy nigdy nie strzelają. Brama zamknięta.

Tryby trwałości

always

Każdy obliczony punkt danych jest zapisywany do magazynu punktów danych. Dla wskaźników opartych na zdarzeniach: utworzone leady na minutę, wydane kredyty na minutę.

snapshot

Obliczane na żądanie, buforowane, ale nie zapisywane do głównej tabeli. Dzienne sumy, skumulowane zliczenia.

never

Zawsze obliczane na żywo z danych wejściowych. Wyjścia EMA, wskaźniki - brak kosztów przechowywania. Lookback automatycznie rozszerzone dla rozgrzewki.

Co jest dziś dostępne vs. co nadchodzi

Dziś gotowe na produkcję

Pełny silnik: endpointy źródeł danych, endpointy wskaźników (EMA, RSI, MACD, Bollinger, clamp, delta, okno), ewaluatory progowe, węzły transformer, węzły akcji endpoint.

Topologiczne wykonanie przez graph walker. Obsługa wielu rozdzielczości z automatycznym skalowaniem. Rozszerzenie zakresu uwzględniające lookback.

Wersjonowanie, tryb backtest z pełną historią uruchomień, trwałość sygnałów jako ślad audytu.

Dostęp CLI - vibe vibe-sense-ema, vibe vibe-sense-rsi, dowolny endpoint wskaźnika, wywoływalny samodzielnie.

Rejestracja MCP - endpointy wskaźników pojawiają się na liście narzędzi. Thea może bezpośrednio wywoływać wskaźniki.

Grafy seed: 29 grafów w 9 domenach - leady, kredyty, użytkownicy, subskrypcje, polecenia, newslettery, płatności, messenger, czat AI i zdrowie systemu. Wszystkie działają od razu na vibe dev.

Co nadchodzi

Wizualny kreator grafów drag-and-drop. Silnik jest w pełni zbudowany. Edytor kanwy to następny rozdział.

Endpointy tradingowe. Endpointy źródeł danych cenowych, endpointy API giełd, wykonywanie zleceń jako węzły endpoint. Graf tradingowy to po prostu kolejny graf.

Rynek strategii. Gdy można budować grafy wizualnie, można je udostępniać. Zaimportuj gotową strategię monitorowania leadów. Sforkuj ją, zmodyfikuj.

Czym to naprawdę jest

Każdy proces biznesowy, który można opisać jako: mając te dane, gdy te warunki są spełnione, zrób to - to jest graf Vibe Sense. Monitoring, tak. Alertowanie, tak. Ale też: automatyczna kwalifikacja leadów, wykrywanie anomalii przychodów, równoważenie ekonomii kredytów, automatyzacja marketingu.

Bot tradingowy miał tę architekturę właściwą. Wskaźniki, ewaluatory, akcje, tryb backtest. To, co zrobiłem źle, to zbudowanie go w izolacji. W Octane EMA było zamknięte w pipeline. W next-vibe EMA jest endpointem pierwszej klasy.

Nie budujesz systemu monitorowania. Budujesz swoją platformę. System monitorowania jest już tam.

Quick start
bash
1git clone https://github.com/techfreaque/next-vibe
2cd next-vibe
3cp .env.example .env
4bun install
5vibe dev

vibe dev uruchamia PostgreSQL w Dockerze, wykonuje migracje, seeduje dane, seeduje grafy Vibe Sense, uzupełnia 365 dni danych historycznych i uruchamia serwer deweloperski. Otwórz localhost:3000. Grafy działają.

Zdefiniuj raz. Istnieje wszędzie. Pipeline to tylko endpointy wywołujące endpointy.