From 4a69b423925bb558a67cb545d9384051963cf826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89?= Date: Sat, 14 Feb 2026 12:14:54 +0800 Subject: [PATCH] 1 --- BINANCE_IP_BAN_1003.md | 39 ++++++++++++++++++++++++ frontend/src/components/GlobalConfig.jsx | 19 ++++++------ 2 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 BINANCE_IP_BAN_1003.md diff --git a/BINANCE_IP_BAN_1003.md b/BINANCE_IP_BAN_1003.md new file mode 100644 index 0000000..930dc82 --- /dev/null +++ b/BINANCE_IP_BAN_1003.md @@ -0,0 +1,39 @@ +# 币安 API -1003 限频/封禁说明 + +## 错误含义 + +- **APIError(code=-1003)**: `Way too many requests; IP ... banned until 1771041059726` +- 表示当前 IP 请求过于频繁,被**临时封禁**,直到指定时间戳(毫秒)后自动解除。 + +## 如何查看「还要等多久」 + +时间戳 `1771041059726` 是**毫秒**,表示封禁**解除时间**(Unix 时间戳,毫秒)。 + +在项目根目录执行: + +```bash +python3 -c " +from datetime import datetime, timezone +ts_ms = 1771041059726 # 替换成你日志里的时间戳 +utc = datetime.fromtimestamp(ts_ms/1000, tz=timezone.utc) +now = datetime.now(timezone.utc) +delta = utc - now +print('解封时间(UTC):', utc.strftime('%Y-%m-%d %H:%M:%S')) +print('当前时间(UTC):', now.strftime('%Y-%m-%d %H:%M:%S')) +if delta.total_seconds() > 0: + print('还需等待:', delta.days, '天', delta.seconds//3600, '小时', (delta.seconds%3600)//60, '分钟') +else: + print('已过解封时间,可重试;若仍报错可等几分钟或换网络。') +" +``` + +把上面脚本里的 `1771041059726` 换成你实际日志中的 `banned until` 后面的数字即可。 + +## 如何减少再次被限/封禁 + +1. **拉大扫描间隔**:全局配置里把 `SCAN_INTERVAL` 调大(如 900 → 1200 或 1800),降低整体请求频率。 +2. **缩小扫描范围**:适当减小 `MAX_SCAN_SYMBOLS`、`TOP_N_SYMBOLS`,减少单次扫描的 API 调用量。 +3. **并发已做限制**:`market_scanner` 已用信号量限制并发(如 3),避免同时打爆;若仍触限,可再减小并发或增加批次间延迟。 +4. **错误提示**:日志里「分析超时(10秒)」多是因为当时已被限频/封禁导致请求挂起或失败,解封后一般会恢复。 + +解封后若仍偶发 -1003,可先等 1~2 分钟再跑,或临时增大 `SCAN_INTERVAL` 再观察。 diff --git a/frontend/src/components/GlobalConfig.jsx b/frontend/src/components/GlobalConfig.jsx index 9c74bfc..9899def 100644 --- a/frontend/src/components/GlobalConfig.jsx +++ b/frontend/src/components/GlobalConfig.jsx @@ -896,11 +896,10 @@ const GlobalConfig = () => { // 管理员全局配置页面:不依赖任何 account,直接管理全局配置表 const isGlobalStrategyAccount = isAdmin - // 简单计算:当前预设(直接在 render 时计算,不使用 useMemo) + // 简单计算:当前预设(与后端返回值同单位比较:比例即 0.65,不做 *100) let currentPreset = null if (configs && Object.keys(configs).length > 0 && presets) { try { - // 直接内联检测逻辑,避免函数调用 for (const [presetKey, preset] of Object.entries(presets)) { let match = true for (const [key, expectedValue] of Object.entries(preset.configs)) { @@ -909,20 +908,20 @@ const GlobalConfig = () => { match = false break } - let currentValue = currentConfig.value + let cur = currentConfig.value + let exp = expectedValue + // 后端返回的百分比类配置多为比例(0.65),预设里也是比例;只有少数如 MAX_CHANGE_PERCENT_FOR_LONG 为 25 表示 25% if (key.includes('PERCENT') || key.includes('PCT')) { - if (PCT_LIKE_KEYS.has(key)) { - currentValue = currentValue <= 0.05 ? currentValue * 100 : currentValue - } else { - currentValue = currentValue * 100 + if (typeof exp === 'number' && exp > 1) { + exp = exp / 100 } } - if (typeof expectedValue === 'number' && typeof currentValue === 'number') { - if (Math.abs(currentValue - expectedValue) > 0.01) { + if (typeof exp === 'number' && typeof cur === 'number') { + if (Math.abs(cur - exp) > 0.01) { match = false break } - } else if (currentValue !== expectedValue) { + } else if (cur !== exp) { match = false break }