mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 01:48:22 +08:00
fix: update token limits and error handling in Trader Dashboard
This commit is contained in:
@@ -82,12 +82,12 @@ maxSafeCoins = floor((budget - staticTokens) / perCoinTokens)
|
||||
|
||||
### 各模型下的最大安全币数
|
||||
|
||||
| 模型上限 | 最小配置 | 默认配置 | 最大配置 |
|
||||
| ------------------------------ | ----------- | --------------- | ----------- |
|
||||
| 131K(DeepSeek / Grok / Qwen) | ≥50(封顶) | **58** | **14** |
|
||||
| 128K(OpenAI GPT-4) | ≥50(封顶) | **57** | **14** |
|
||||
| 200K(Claude) | ≥50(封顶) | **89 → 封顶50** | **22** |
|
||||
| 1M(Gemini / Minimax) | ≥50(封顶) | ≥50(封顶) | ≥50(封顶) |
|
||||
| 模型上限 | 最小配置 | 默认配置 | 最大配置 |
|
||||
| ------------------------------ | ------------ | ------------ | ----------- |
|
||||
| 131K(DeepSeek / Grok / Qwen) | ≥10(封顶) | ≥10(封顶) | **14** |
|
||||
| 128K(OpenAI GPT-4) | ≥10(封顶) | ≥10(封顶) | **14** |
|
||||
| 200K(Claude) | ≥10(封顶) | ≥10(封顶) | ≥10(封顶) |
|
||||
| 1M(Gemini / Minimax) | ≥10(封顶) | ≥10(封顶) | ≥10(封顶) |
|
||||
|
||||
---
|
||||
|
||||
@@ -114,7 +114,7 @@ maxSafeCoins = floor((budget - staticTokens) / perCoinTokens)
|
||||
|
||||
```go
|
||||
const (
|
||||
MaxCandidateCoins = 50 // UI 硬限制:用户最多设定的候选币数量
|
||||
MaxCandidateCoins = 10 // UI 硬限制:用户最多设定的候选币数量
|
||||
MaxPositions = 3 // 最大同时持仓数
|
||||
MaxTimeframes = 4 // 最大时间框架数
|
||||
MinKlineCount = 10 // 最少 K 线数
|
||||
@@ -122,11 +122,11 @@ const (
|
||||
)
|
||||
```
|
||||
|
||||
### 为什么 MaxCandidateCoins = 50?
|
||||
### 为什么 MaxCandidateCoins = 10?
|
||||
|
||||
- **默认配置**下 50 枚币约用 **~8,000 tokens**(~6% of 131K),完全安全
|
||||
- **极端配置**(4TF + 全指标)50 枚币会超过 131K 限制,但 **runtime token-blocking** 会在分析前拦截并报错
|
||||
- 因此 50 是合理的 UI 上限:一方面给用户足够灵活性,另一方面依赖运行时保护防止真正的溢出
|
||||
- **默认配置**下 10 枚币约用 **~15,000 tokens**(~12% of 131K),完全安全
|
||||
- **极端配置**(4TF + 全指标)10 枚币约用 **~60,000 tokens**(~46% of 131K),仍有充足余量
|
||||
- 因此 10 是保守且安全的 UI 上限:在所有模型和配置组合下均不会触发 token 限制
|
||||
|
||||
### 建议使用范围
|
||||
|
||||
|
||||
+25
-9
@@ -602,16 +602,28 @@ type ModelLimit struct {
|
||||
Level string `json:"level"` // "ok" | "warning" | "danger"
|
||||
}
|
||||
|
||||
// Context window sizes (tokens) for each model family
|
||||
const (
|
||||
contextLimitDeepSeek = 131_072 // 128K
|
||||
contextLimitOpenAI = 128_000 // 128K
|
||||
contextLimitClaude = 200_000 // 200K
|
||||
contextLimitQwen = 131_072 // 128K
|
||||
contextLimitGemini = 1_000_000 // 1M
|
||||
contextLimitGrok = 131_072 // 128K
|
||||
contextLimitKimi = 131_072 // 128K
|
||||
contextLimitMinimax = 1_000_000 // 1M
|
||||
)
|
||||
|
||||
// ModelContextLimits maps provider names to their context window sizes (in tokens)
|
||||
var ModelContextLimits = map[string]int{
|
||||
"deepseek": 131072,
|
||||
"openai": 128000,
|
||||
"claude": 200000,
|
||||
"qwen": 131072,
|
||||
"gemini": 1000000,
|
||||
"grok": 131072,
|
||||
"kimi": 131072,
|
||||
"minimax": 1000000,
|
||||
"deepseek": contextLimitDeepSeek,
|
||||
"openai": contextLimitOpenAI,
|
||||
"claude": contextLimitClaude,
|
||||
"qwen": contextLimitQwen,
|
||||
"gemini": contextLimitGemini,
|
||||
"grok": contextLimitGrok,
|
||||
"kimi": contextLimitKimi,
|
||||
"minimax": contextLimitMinimax,
|
||||
}
|
||||
|
||||
// GetContextLimit returns the context limit for a given provider
|
||||
@@ -619,7 +631,7 @@ func GetContextLimit(provider string) int {
|
||||
if limit, ok := ModelContextLimits[provider]; ok {
|
||||
return limit
|
||||
}
|
||||
return 131072 // safe default
|
||||
return contextLimitDeepSeek // safe default
|
||||
}
|
||||
|
||||
// GetContextLimitForClient returns context limit for a provider+model pair.
|
||||
@@ -639,6 +651,10 @@ func GetContextLimitForClient(provider, model string) int {
|
||||
return ModelContextLimits["kimi"]
|
||||
case strings.HasPrefix(model, "qwen"):
|
||||
return ModelContextLimits["qwen"]
|
||||
case strings.HasPrefix(model, "minimax"):
|
||||
return ModelContextLimits["minimax"]
|
||||
case strings.HasPrefix(model, "deepseek"):
|
||||
return ModelContextLimits["deepseek"]
|
||||
default:
|
||||
return ModelContextLimits["deepseek"]
|
||||
}
|
||||
|
||||
+4
-2
@@ -323,8 +323,8 @@ function App() {
|
||||
const selectedTrader = traders?.find((t) => t.trader_id === selectedTraderId)
|
||||
|
||||
const effectiveAccount = account
|
||||
const effectivePositions = (positionsPollOff && !positions) ? [] as Position[] : positions
|
||||
const effectiveDecisions = (decisionsPollOff && !decisions) ? [] as DecisionRecord[] : decisions
|
||||
const effectivePositions = positions
|
||||
const effectiveDecisions = decisions
|
||||
|
||||
// Handle routing
|
||||
useEffect(() => {
|
||||
@@ -544,7 +544,9 @@ function App() {
|
||||
account={effectiveAccount}
|
||||
accountFailed={accountPollOff}
|
||||
positions={effectivePositions}
|
||||
positionsFailed={positionsPollOff}
|
||||
decisions={effectiveDecisions}
|
||||
decisionsFailed={decisionsPollOff}
|
||||
decisionsLimit={decisionsLimit}
|
||||
onDecisionsLimitChange={setDecisionsLimit}
|
||||
stats={stats}
|
||||
|
||||
@@ -1167,6 +1167,9 @@ export const translations = {
|
||||
close: 'Close',
|
||||
showingPositions: 'Showing {shown} of {total} positions',
|
||||
perPage: 'Per page',
|
||||
accountFetchFailed: 'DATA_FETCH::FAILED — Account data unavailable, check connection',
|
||||
positionsFetchFailed: 'Position data unavailable',
|
||||
decisionsFetchFailed: 'Decision data unavailable',
|
||||
},
|
||||
|
||||
// AITradersPage toast messages
|
||||
@@ -2467,6 +2470,9 @@ export const translations = {
|
||||
close: '平仓',
|
||||
showingPositions: '显示 {shown} / {total} 个持仓',
|
||||
perPage: '每页',
|
||||
accountFetchFailed: 'DATA_FETCH::FAILED — 账户数据请求失败,请检查连接',
|
||||
positionsFetchFailed: '持仓数据请求失败',
|
||||
decisionsFetchFailed: '决策记录请求失败',
|
||||
},
|
||||
|
||||
aiTradersToast: {
|
||||
@@ -3570,6 +3576,9 @@ export const translations = {
|
||||
close: 'Tutup',
|
||||
showingPositions: 'Menampilkan {shown} dari {total} posisi',
|
||||
perPage: 'Per halaman',
|
||||
accountFetchFailed: 'DATA_FETCH::FAILED — Data akun tidak tersedia, periksa koneksi',
|
||||
positionsFetchFailed: 'Data posisi tidak tersedia',
|
||||
decisionsFetchFailed: 'Data keputusan tidak tersedia',
|
||||
},
|
||||
|
||||
aiTradersToast: {
|
||||
|
||||
@@ -105,7 +105,9 @@ interface TraderDashboardPageProps {
|
||||
account?: AccountInfo
|
||||
accountFailed?: boolean
|
||||
positions?: Position[]
|
||||
positionsFailed?: boolean
|
||||
decisions?: DecisionRecord[]
|
||||
decisionsFailed?: boolean
|
||||
decisionsLimit: number
|
||||
onDecisionsLimitChange: (limit: number) => void
|
||||
stats?: Statistics
|
||||
@@ -120,7 +122,9 @@ export function TraderDashboardPage({
|
||||
account,
|
||||
accountFailed,
|
||||
positions,
|
||||
positionsFailed,
|
||||
decisions,
|
||||
decisionsFailed,
|
||||
decisionsLimit,
|
||||
onDecisionsLimitChange,
|
||||
lastUpdate,
|
||||
@@ -491,7 +495,7 @@ export function TraderDashboardPage({
|
||||
<span>PNL::{account.total_pnl?.toFixed(2)}</span>
|
||||
</div>
|
||||
) : accountFailed ? (
|
||||
<span style={{ color: '#F6465D' }}>DATA_FETCH::FAILED — 账户数据请求失败,请检查连接</span>
|
||||
<span style={{ color: '#F6465D' }}>{t('traderDashboard.accountFetchFailed', language)}</span>
|
||||
) : (
|
||||
<div className="flex gap-4">
|
||||
<span className="inline-block w-32 h-3 rounded bg-white/5 animate-pulse" />
|
||||
@@ -723,6 +727,11 @@ export function TraderDashboardPage({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : positionsFailed ? (
|
||||
<div className="text-center py-16 text-nofx-text-muted opacity-60">
|
||||
<div className="text-4xl mb-4">⚠️</div>
|
||||
<div className="text-lg font-semibold mb-2">{t('traderDashboard.positionsFetchFailed', language)}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-16 text-nofx-text-muted opacity-60">
|
||||
<div className="text-6xl mb-4 opacity-50 grayscale">📊</div>
|
||||
@@ -776,6 +785,11 @@ export function TraderDashboardPage({
|
||||
decisions.map((decision, i) => (
|
||||
<DecisionCard key={i} decision={decision} language={language} onSymbolClick={handleSymbolClick} />
|
||||
))
|
||||
) : decisionsFailed ? (
|
||||
<div className="py-16 text-center text-nofx-text-muted opacity-60">
|
||||
<div className="text-4xl mb-4">⚠️</div>
|
||||
<div className="text-lg font-semibold mb-2">{t('traderDashboard.decisionsFetchFailed', language)}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="py-16 text-center text-nofx-text-muted opacity-60">
|
||||
<div className="text-6xl mb-4 opacity-30 grayscale">🧠</div>
|
||||
|
||||
Reference in New Issue
Block a user