diff --git a/README.ja.md b/README.ja.md index bc7cfdab..0999860a 100644 --- a/README.ja.md +++ b/README.ja.md @@ -103,6 +103,43 @@ Binance互換の分散型無期限先物取引所! --- +## 対応取引所 + +### CEX(中央集権型取引所) + +| 取引所 | ステータス | 登録(手数料割引) | +|:-------|:----------:|:-------------------| +| **Binance** | ✅ | [登録](https://www.binance.com/join?ref=NOFXENG) | +| **Bybit** | ✅ | [登録](https://partner.bybit.com/b/83856) | +| **OKX** | ✅ | [登録](https://www.okx.com/join/1865360) | +| **Bitget** | ✅ | [登録](https://www.bitget.com/referral/register?from=referral&clacCode=c8a43172) | +| **KuCoin** | ✅ | [登録](https://www.kucoin.com/r/broker/CXEV7XKK) | +| **Gate** | ✅ | [登録](https://www.gatenode.xyz/share/VQBGUAxY) | + +### Perp-DEX(分散型無期限取引所) + +| 取引所 | ステータス | 登録(手数料割引) | +|:-------|:----------:|:-------------------| +| **Hyperliquid** | ✅ | [登録](https://app.hyperliquid.xyz/join/AITRADING) | +| **Aster DEX** | ✅ | [登録](https://www.asterdex.com/en/referral/fdfc0e) | +| **Lighter** | ✅ | [登録](https://app.lighter.xyz/?referral=68151432) | + +--- + +## 対応AIモデル + +| AIモデル | ステータス | APIキー取得 | +|:---------|:----------:|:------------| +| **DeepSeek** | ✅ | [APIキー取得](https://platform.deepseek.com) | +| **Qwen** | ✅ | [APIキー取得](https://dashscope.console.aliyun.com) | +| **OpenAI (GPT)** | ✅ | [APIキー取得](https://platform.openai.com) | +| **Claude** | ✅ | [APIキー取得](https://console.anthropic.com) | +| **Gemini** | ✅ | [APIキー取得](https://aistudio.google.com) | +| **Grok** | ✅ | [APIキー取得](https://console.x.ai) | +| **Kimi** | ✅ | [APIキー取得](https://platform.moonshot.cn) | + +--- + ## 📸 スクリーンショット ### 🏆 競争モード - リアルタイムAIバトル diff --git a/README.md b/README.md index b34297c5..95b2a449 100644 --- a/README.md +++ b/README.md @@ -78,35 +78,35 @@ To use NOFX, you'll need: ### CEX (Centralized Exchanges) | Exchange | Status | Register (Fee Discount) | -|----------|--------|-------------------------| -| **Binance** | ✅ Supported | [Register](https://www.binance.com/join?ref=NOFXENG) | -| **Bybit** | ✅ Supported | [Register](https://partner.bybit.com/b/83856) | -| **OKX** | ✅ Supported | [Register](https://www.okx.com/join/1865360) | -| **Bitget** | ✅ Supported | [Register](https://www.bitget.com/referral/register?from=referral&clacCode=c8a43172) | -| **KuCoin** | ✅ Supported | [Register](https://www.kucoin.com/r/broker/CXEV7XKK) | -| **Gate** | ✅ Supported | [Register](https://www.gatenode.xyz/share/VQBGUAxY) | +|:---------|:------:|:------------------------| +| **Binance** | ✅ | [Register](https://www.binance.com/join?ref=NOFXENG) | +| **Bybit** | ✅ | [Register](https://partner.bybit.com/b/83856) | +| **OKX** | ✅ | [Register](https://www.okx.com/join/1865360) | +| **Bitget** | ✅ | [Register](https://www.bitget.com/referral/register?from=referral&clacCode=c8a43172) | +| **KuCoin** | ✅ | [Register](https://www.kucoin.com/r/broker/CXEV7XKK) | +| **Gate** | ✅ | [Register](https://www.gatenode.xyz/share/VQBGUAxY) | ### Perp-DEX (Decentralized Perpetual Exchanges) | Exchange | Status | Register (Fee Discount) | -|----------|--------|-------------------------| -| **Hyperliquid** | ✅ Supported | [Register](https://app.hyperliquid.xyz/join/AITRADING) | -| **Aster DEX** | ✅ Supported | [Register](https://www.asterdex.com/en/referral/fdfc0e) | -| **Lighter** | ✅ Supported | [Register](https://app.lighter.xyz/?referral=68151432) | +|:---------|:------:|:------------------------| +| **Hyperliquid** | ✅ | [Register](https://app.hyperliquid.xyz/join/AITRADING) | +| **Aster DEX** | ✅ | [Register](https://www.asterdex.com/en/referral/fdfc0e) | +| **Lighter** | ✅ | [Register](https://app.lighter.xyz/?referral=68151432) | --- ## Supported AI Models | AI Model | Status | Get API Key | -|----------|--------|-------------| -| **DeepSeek** | ✅ Supported | [Get API Key](https://platform.deepseek.com) | -| **Qwen** | ✅ Supported | [Get API Key](https://dashscope.console.aliyun.com) | -| **OpenAI (GPT)** | ✅ Supported | [Get API Key](https://platform.openai.com) | -| **Claude** | ✅ Supported | [Get API Key](https://console.anthropic.com) | -| **Gemini** | ✅ Supported | [Get API Key](https://aistudio.google.com) | -| **Grok** | ✅ Supported | [Get API Key](https://console.x.ai) | -| **Kimi** | ✅ Supported | [Get API Key](https://platform.moonshot.cn) | +|:---------|:------:|:------------| +| **DeepSeek** | ✅ | [Get API Key](https://platform.deepseek.com) | +| **Qwen** | ✅ | [Get API Key](https://dashscope.console.aliyun.com) | +| **OpenAI (GPT)** | ✅ | [Get API Key](https://platform.openai.com) | +| **Claude** | ✅ | [Get API Key](https://console.anthropic.com) | +| **Gemini** | ✅ | [Get API Key](https://aistudio.google.com) | +| **Grok** | ✅ | [Get API Key](https://console.x.ai) | +| **Kimi** | ✅ | [Get API Key](https://platform.moonshot.cn) | --- diff --git a/provider/nofxos/ai500.go b/provider/nofxos/ai500.go index a62e2d3d..5e1c6450 100644 --- a/provider/nofxos/ai500.go +++ b/provider/nofxos/ai500.go @@ -105,7 +105,8 @@ func (c *Client) GetTopRatedCoins(limit int) ([]string, error) { } if len(availableCoins) == 0 { - return nil, fmt.Errorf("no available coins") + // Empty list is normal - just return empty slice, not an error + return []string{}, nil } // Sort by Score descending (bubble sort) @@ -147,10 +148,7 @@ func (c *Client) GetAvailableCoins() ([]string, error) { } } - if len(symbols) == 0 { - return nil, fmt.Errorf("no available coins") - } - + // Empty list is normal - just return empty slice, not an error return symbols, nil } diff --git a/trader/auto_trader.go b/trader/auto_trader.go index db1fcae8..f1b3565b 100644 --- a/trader/auto_trader.go +++ b/trader/auto_trader.go @@ -578,9 +578,19 @@ func (at *AutoTrader) runCycle() error { // NOTE: Must be called BEFORE candidate coins check to ensure equity is always recorded at.saveEquitySnapshot(ctx) - // 如果没有候选币种,友好提示并跳过本周期 + // 如果没有候选币种,记录但不报错 if len(ctx.CandidateCoins) == 0 { logger.Infof("ℹ️ No candidate coins available, skipping this cycle") + record.Success = true // 不是错误,只是没有候选币 + record.ExecutionLog = append(record.ExecutionLog, "No candidate coins available, cycle skipped") + record.AccountState = store.AccountSnapshot{ + TotalBalance: ctx.Account.TotalEquity, + AvailableBalance: ctx.Account.AvailableBalance, + TotalUnrealizedProfit: ctx.Account.UnrealizedPnL, + PositionCount: ctx.Account.PositionCount, + InitialBalance: at.initialBalance, + } + at.saveDecision(record) return nil } diff --git a/trader/kucoin/trader.go b/trader/kucoin/trader.go index 525acf7c..d012526a 100644 --- a/trader/kucoin/trader.go +++ b/trader/kucoin/trader.go @@ -50,6 +50,10 @@ type KuCoinTrader struct { // HTTP client httpClient *http.Client + // Server time offset (local - server) in milliseconds + serverTimeOffset int64 + serverTimeMutex sync.RWMutex + // Balance cache cachedBalance map[string]interface{} balanceCacheTime time.Time @@ -107,10 +111,64 @@ func NewKuCoinTrader(apiKey, secretKey, passphrase string) *KuCoinTrader { contractsCache: make(map[string]*KuCoinContract), } + // Sync server time on initialization + if err := trader.syncServerTime(); err != nil { + logger.Warnf("⚠️ Failed to sync KuCoin server time: %v (will retry on first request)", err) + } + logger.Infof("✓ KuCoin Futures trader initialized") return trader } +// syncServerTime fetches KuCoin server time and calculates offset +func (t *KuCoinTrader) syncServerTime() error { + resp, err := t.httpClient.Get(kucoinBaseURL + "/api/v1/timestamp") + if err != nil { + return fmt.Errorf("failed to get server time: %w", err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("failed to read response: %w", err) + } + + var result struct { + Code string `json:"code"` + Data int64 `json:"data"` + } + + if err := json.Unmarshal(body, &result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) + } + + if result.Code != "200000" { + return fmt.Errorf("server time API error: %s", result.Code) + } + + serverTime := result.Data + localTime := time.Now().UnixMilli() + offset := localTime - serverTime + + t.serverTimeMutex.Lock() + t.serverTimeOffset = offset + t.serverTimeMutex.Unlock() + + logger.Infof("✓ KuCoin time synced: offset=%dms (local %d - server %d)", offset, localTime, serverTime) + return nil +} + +// getTimestamp returns the current timestamp adjusted for server time offset +func (t *KuCoinTrader) getTimestamp() string { + t.serverTimeMutex.RLock() + offset := t.serverTimeOffset + t.serverTimeMutex.RUnlock() + + // Subtract offset to get server time from local time + timestamp := time.Now().UnixMilli() - offset + return strconv.FormatInt(timestamp, 10) +} + // sign generates KuCoin API signature func (t *KuCoinTrader) sign(timestamp, method, requestPath, body string) string { // KuCoin signature: base64(HMAC-SHA256(timestamp + method + endpoint + body, secretKey)) @@ -147,7 +205,7 @@ func (t *KuCoinTrader) doRequest(method, path string, body interface{}) ([]byte, } } - timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10) + timestamp := t.getTimestamp() signature := t.sign(timestamp, method, path, string(bodyBytes)) signedPassphrase := t.signPassphrase(t.passphrase) @@ -186,6 +244,13 @@ func (t *KuCoinTrader) doRequest(method, path string, body interface{}) ([]byte, } if kcResp.Code != "200000" { + // If timestamp error, try to re-sync server time + if kcResp.Code == "400002" || strings.Contains(kcResp.Msg, "TIMESTAMP") { + logger.Warnf("⚠️ KuCoin timestamp error, re-syncing server time...") + if err := t.syncServerTime(); err != nil { + logger.Warnf("⚠️ Failed to re-sync server time: %v", err) + } + } return nil, fmt.Errorf("KuCoin API error: code=%s, msg=%s", kcResp.Code, kcResp.Msg) } diff --git a/web/public/icons/claude.svg b/web/public/icons/claude.svg index 89cc9316..e11d42d7 100644 --- a/web/public/icons/claude.svg +++ b/web/public/icons/claude.svg @@ -1,4 +1,4 @@ Claude - +