diff --git a/docs/token-estimation.zh-CN.md b/docs/token-estimation.zh-CN.md index 8f77ed63..b5bb5a56 100644 --- a/docs/token-estimation.zh-CN.md +++ b/docs/token-estimation.zh-CN.md @@ -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 限制 ### 建议使用范围 diff --git a/store/strategy.go b/store/strategy.go index 7888ce1f..19bc9526 100644 --- a/store/strategy.go +++ b/store/strategy.go @@ -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"] } diff --git a/web/src/App.tsx b/web/src/App.tsx index 412d8f16..3173fb97 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -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} diff --git a/web/src/i18n/translations.ts b/web/src/i18n/translations.ts index 1db615d2..12d85412 100644 --- a/web/src/i18n/translations.ts +++ b/web/src/i18n/translations.ts @@ -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: { diff --git a/web/src/pages/TraderDashboardPage.tsx b/web/src/pages/TraderDashboardPage.tsx index c8db2549..53f545d8 100644 --- a/web/src/pages/TraderDashboardPage.tsx +++ b/web/src/pages/TraderDashboardPage.tsx @@ -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({ PNL::{account.total_pnl?.toFixed(2)} ) : accountFailed ? ( - DATA_FETCH::FAILED — 账户数据请求失败,请检查连接 + {t('traderDashboard.accountFetchFailed', language)} ) : (
@@ -723,6 +727,11 @@ export function TraderDashboardPage({
)} + ) : positionsFailed ? ( +
+
⚠️
+
{t('traderDashboard.positionsFetchFailed', language)}
+
) : (
📊
@@ -776,6 +785,11 @@ export function TraderDashboardPage({ decisions.map((decision, i) => ( )) + ) : decisionsFailed ? ( +
+
⚠️
+
{t('traderDashboard.decisionsFetchFailed', language)}
+
) : (
🧠