Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions docs/guides/RELIABILITY_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Reliability & RAG Failure Checklist for Multi-AI Trading

Trading with multiple AI models (GPT, Claude, Gemini, DeepSeek) through a RAG (Retrieval-Augmented Generation) pipeline requires rigorous safety checks to prevent "hallucinated trades" or "data blindness."

This checklist is designed for NOFX users and developers to ensure their AI trading assistants remain reliable under high market volatility.

## 1. Data Retrieval Integrity (The RAG Pillar)
* [ ] **Recency Check:** Is the market data retrieved within the last 5 minutes? (Preventing trading on stale candles).
* [ ] **Source Divergence:** Does the RAG pipeline fetch data from at least 2 independent sources (e.g., Binance + Bybit)?
* [ ] **Noise Filtering:** Are outlier wicks or flash crashes pre-filtered before feeding data to the LLM?
* [ ] **Token Limits:** Does the retrieved context (Market sentiment + Indicators) fit within the model's context window without truncation?

## 2. Reasoning & Logic Safety (The AI Pillar)
* [ ] **CoT (Chain of Thought):** Is the AI forced to explain *why* it wants to open a position before executing? (Mandatory for debugging).
* [ ] **Negative Prompting:** Does the prompt explicitly forbid trading during high-impact news (e.g., CPI/FOMC) unless specified?
* [ ] **Temperature Control:** Is the LLM's `temperature` set to `< 0.2` for deterministic, logic-based trading decisions?
* [ ] **Hallucination Detection:** Does the system check if the "recommended coin" actually exists in the provided market data?

## 3. Execution & Risk Safeguards (The Wallet Pillar)
* [ ] **Max Drawdown:** Is there a hard-coded close-out if the PnL drops below a specific threshold (e.g., -5%)?
* [ ] **Liquidity Check:** Is the order size `< 1%` of the 24h volume for the specific pair?
* [ ] **Fee Awareness:** Does the AI factor in taker fees and slippage when calculating "Expected Profit"?
* [ ] **API Failover:** If a primary model (e.g., Claude) fails, is there an automated switch to a fallback (e.g., GPT-4o)?

## 4. RAG Failure Recovery Mode
* **Symptom:** AI keeps reporting "Insufficient Data."
* *Fix:* Check the `top_k` retrieval parameter and vector store connectivity.
* **Symptom:** AI ignores recent price action.
* *Fix:* Increase the weight of "Latest Candles" in the RAG ranking algorithm.
* **Symptom:** Conflicting advice from different models.
* *Fix:* Use a "Consensus Engine" (Majority vote or Weighted averaging based on model accuracy).

---
*Authored by: [chulinhcql-art](https://github.com/chulinhcql-art) - NOFX Contributor*
43 changes: 28 additions & 15 deletions kernel/formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,20 +325,26 @@ func formatKlineDataZH(symbol string, tfData map[string]*market.TimeframeSeriesD
sb.WriteString("```\n")
sb.WriteString("时间(UTC) 开盘 最高 最低 收盘 成交量\n")

// Only show the latest 30 klines
// Optimization: Only show the latest 10 klines to save tokens (99% waste fix)
startIdx := 0
if len(data.Klines) > 30 {
startIdx = len(data.Klines) - 30
if len(data.Klines) > 10 {
startIdx = len(data.Klines) - 10
}

// Add a brief summary of the last 30 periods for context
if len(data.Klines) >= 30 {
p30 := data.Klines[len(data.Klines)-30].Close
pnow := data.Klines[len(data.Klines)-1].Close
change := ((pnow - p30) / p30) * 100
sb.WriteString(fmt.Sprintf("Last 30 periods change: %+.2f%%\n", change))
}

for i := startIdx; i < len(data.Klines); i++ {
k := data.Klines[i]
t := time.UnixMilli(k.Time).UTC()
sb.WriteString(fmt.Sprintf("%s %.4f %.4f %.4f %.4f %.2f\n",
t.Format("01-02 15:04"),
k.Open,
k.High,
k.Low,
// Compact format: Time | Close | Vol (AI is smart enough to derive trend from Close)
sb.WriteString(fmt.Sprintf("%s %.4f %.2f\n",
t.Format("15:04"),
k.Close,
k.Volume,
))
Expand Down Expand Up @@ -592,19 +598,26 @@ func formatKlineDataEN(symbol string, tfData map[string]*market.TimeframeSeriesD
sb.WriteString("```\n")
sb.WriteString("Time(UTC) Open High Low Close Volume\n")

// Optimization: Only show the latest 10 klines to save tokens (99% waste fix)
startIdx := 0
if len(data.Klines) > 30 {
startIdx = len(data.Klines) - 30
if len(data.Klines) > 10 {
startIdx = len(data.Klines) - 10
}

// Add a brief summary of the last 30 periods for context
if len(data.Klines) >= 30 {
p30 := data.Klines[len(data.Klines)-30].Close
pnow := data.Klines[len(data.Klines)-1].Close
change := ((pnow - p30) / p30) * 100
sb.WriteString(fmt.Sprintf("Last 30 periods change: %+.2f%%\n", change))
}

for i := startIdx; i < len(data.Klines); i++ {
k := data.Klines[i]
t := time.UnixMilli(k.Time).UTC()
sb.WriteString(fmt.Sprintf("%s %.4f %.4f %.4f %.4f %.2f\n",
t.Format("01-02 15:04"),
k.Open,
k.High,
k.Low,
// Compact format: Time | Close | Vol (AI is smart enough to derive trend from Close)
sb.WriteString(fmt.Sprintf("%s %.4f %.2f\n",
t.Format("15:04"),
k.Close,
k.Volume,
))
Expand Down
7 changes: 4 additions & 3 deletions store/telegram_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package store
import (
"errors"
"fmt"
"nofx/crypto"
"sync"
"time"

Expand All @@ -11,9 +12,9 @@ import (

// TelegramConfig stores the Telegram bot binding (single row, always ID=1)
type TelegramConfig struct {
ID uint `gorm:"primaryKey"`
BotToken string `gorm:"column:bot_token"`
ChatID int64 `gorm:"column:chat_id"`
ID uint `gorm:"primaryKey"`
BotToken crypto.EncryptedString `gorm:"column:bot_token"`
ChatID int64 `gorm:"column:chat_id"`
Username string `gorm:"column:username"` // @username for display
BoundAt time.Time `gorm:"column:bound_at"`
ModelID string `gorm:"column:model_id;default:''"` // AI model used for Telegram replies
Expand Down
7 changes: 7 additions & 0 deletions trader/auto_trader_risk.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ func (at *AutoTrader) checkPositionDrawdown() {
entryPrice := pos["entryPrice"].(float64)
markPrice := pos["markPrice"].(float64)
quantity := pos["positionAmt"].(float64)

// ISO-2026 Protection: Guard against division by zero from exchange API errors
if entryPrice <= 0 {
logger.Infof("⚠️ Skipping drawdown check for %s: entryPrice is zero", symbol)
continue
}

if quantity < 0 {
quantity = -quantity // Short position quantity is negative, convert to positive
}
Expand Down