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 @@