Przejdź do głównej zawartości

📊 Logi handlowe — logTrade()

HornX™ zapisuje szczegółowy log każdego trade'u do pliku executor_tradelog.json (per użytkownik, max 500 wpisów). Od sesji 30-03-2026 każdy wpis zawiera pełne dane do analizy i uczenia AI.


Gdzie są logi?

data/users/{userId}/executor_tradelog.json
  • Max 500 wpisów na użytkownika (najnowsze na górze)
  • Automatyczny backup .bak przy każdym zapisie
  • Dostępne przez dashboard → zakładka Historia

Struktura wpisu

Każdy wpis zawiera pola bazowe (od zawsze) + 16 nowych pól dodanych w FIX-TRADELOG (commit 19f513c):

{
"symbol": "BTCUSDT",
"side": "long",
"entry": 82450.0,
"exit": 83200.0,
"sl": 81900.0,
"tp": 83500.0,
"result": "tp1_hit",
"pnl": 18.40,
"size": 0.024,
"mode": "paper",
"orderId": "4521893712",
"aqs": 87,
"signalScore": 74,

"tradeId": "4521893712",
"strategy": "TrendFollow",
"direction": "LONG",
"timeframe": "15m",
"leverage": 10,
"positionUsdt": 1980.0,
"tp2": 84100.0,
"tp3": 85000.0,

"exitPrice": 83200.0,
"exitTime": "2026-03-30T14:22:11.000Z",
"exitReason": "TP1_HIT",

"feesUsdt": 2.38,
"netPnlUsdt": 16.02,
"pnlPct": 0.81,

"aiDecision": "CONFIRM",
"aiProvider": "groq",
"aiConfidence": 82,
"aiReason": "Strong trend continuation, volume above avg, RSI not overbought",

"timestamp": "2026-03-30T14:22:11.123Z"
}

Opis 16 nowych pól

Identyfikacja i kontekst strategii

PoleTypOpis
tradeIdstringorderId z giełdy lub wewnętrzny klucz trade'u
strategystringNazwa strategii ATE (np. TrendFollow, BreakOut, DipHunter)
directionstringLONG lub SHORT
timeframestringTimeframe sygnału: 1m, 5m, 15m, 1H, 4H

Ekspozycja na ryzyko

PoleTypOpis
leveragenumberEfektywna dźwignia użyta przy wejściu
positionUsdtnumberWartość pozycji w USDT (size * entry)
tp2numberDrugi target zysku (jeśli ustawiony)
tp3numberTrzeci target zysku (jeśli ustawiony)

Dane zamknięcia

PoleTypOpis
exitPricenumberCena zamknięcia pozycji
exitTimestringISO 8601 timestamp zamknięcia
exitReasonstringPowód zamknięcia (patrz tabela niżej)

Możliwe wartości exitReason:

WartośćKiedy
TP1_HITZamknięcie na pierwszym targecie
SL_HITStop loss trafiony
EMERGENCYSL placement failed — pozycja zamknięta awaryjnie
KILL_SWITCHWyłącznik kill-switch aktywowany
ACCOUNT_RESETReset konta papierowego
CLOSED_ON_EXCHANGEPozycja zamknięta na giełdzie podczas przestoju bota

Wynik finansowy (auto-obliczane)

PoleTypWzórOpis
feesUsdtnumberpositionUsdt × 0.0006 × 2Szacowane opłaty (WEEX taker 0.06%, wejście + wyjście)
netPnlUsdtnumberpnl - feesUsdtWynik netto po opłatach
pnlPctnumber(pnl / positionUsdt) × 100Procent zysku/straty od wartości pozycji
notatka

Wartości feesUsdt, netPnlUsdt, pnlPct są szacowane. Faktyczne opłaty zależą od poziomu VIP i slippage.

Werdykt AI

PoleTypOpis
aiDecisionstringCONFIRM, REJECT, lub SKIP (brak AI)
aiProviderstringModel który podjął decyzję: groq, deepseek, gemini
aiConfidencenumberPewność AI w skali 0–100
aiReasonstringUzasadnienie AI (max 500 znaków)

Pipeline: Sygnał → logTrade

ATE generuje sygnał (ate-signal-gen.js)

ate-controller.js: AI walidacja → _lastAiVerdict
↓ formatSignal(sig) → execSignal
↓ execSignal.strategy = sig.strategy
↓ execSignal.aiDecision = _lastAiVerdict.decision
↓ execSignal.aiProvider / aiConfidence / aiReason

weex-executor.js: processSignal(execSignal)
↓ handleEntrySignal({ ..., strategy, aiDecision, ... })

openTrades.set(key, {
...,
strategy, direction, timeframe,
leverage, positionUsdt,
aiDecision, aiProvider, aiConfidence, aiReason
})

_handleCloseInternal() → logTrade(entry, {
strategy: trade.strategy,
exitPrice, exitTime, exitReason,
feesUsdt, ← auto-computed
netPnlUsdt, ← auto-computed
pnlPct, ← auto-computed
aiDecision: trade.aiDecision,
...
})

executor_tradelog.json

Jak używać logów do analizy?

Filtrowanie po strategii

const logs = JSON.parse(fs.readFileSync('data/users/1/executor_tradelog.json'));
const trendFollow = logs.filter(t => t.strategy === 'TrendFollow');

Skuteczność AI

const aiTrades = logs.filter(t => t.aiDecision === 'CONFIRM');
const aiWins = aiTrades.filter(t => t.pnl > 0).length;
const aiWinRate = (aiWins / aiTrades.length * 100).toFixed(1);

Najgorszy provider

const byProvider = {};
logs.forEach(t => {
if (!t.aiProvider) return;
byProvider[t.aiProvider] = byProvider[t.aiProvider] || { pnl: 0, count: 0 };
byProvider[t.aiProvider].pnl += t.netPnlUsdt || 0;
byProvider[t.aiProvider].count++;
});

Powiązane