diff --git a/backend/api/routes/config.py b/backend/api/routes/config.py index 7261af8..4d124d4 100644 --- a/backend/api/routes/config.py +++ b/backend/api/routes/config.py @@ -40,6 +40,12 @@ USER_RISK_KNOBS = { "AUTO_TRADE_ENABLED", # 总开关:关闭则只生成推荐不自动下单 "MAX_OPEN_POSITIONS", # 同时持仓数量上限 "MAX_DAILY_ENTRIES", # 每日最多开仓次数 + "SUNDAY_MAX_OPENS", # 周日开仓上限(0=不限制) + "SUNDAY_MIN_SIGNAL_STRENGTH", # 周日最低信号强度(0=不提高) + "NIGHT_HOURS_NO_OPEN_ENABLED", + "NIGHT_HOURS_START", + "NIGHT_HOURS_END", + "NIGHT_HOURS_ONLY_SUNDAY", # 策略筛选(允许用户调整) "TOP_N_SYMBOLS", "MIN_SIGNAL_STRENGTH", @@ -61,6 +67,12 @@ USER_VISIBLE_DEFAULTS = { "AUTO_TRADE_ENABLED": {"value": True, "type": "boolean", "category": "risk", "description": "自动交易总开关:关闭后仅生成推荐,不会自动下单"}, "MAX_OPEN_POSITIONS": {"value": 3, "type": "number", "category": "position", "description": "同时持仓数量上限"}, "MAX_DAILY_ENTRIES": {"value": 8, "type": "number", "category": "risk", "description": "每日最多开仓次数"}, + "SUNDAY_MAX_OPENS": {"value": 3, "type": "number", "category": "risk", "description": "周日最多开仓次数,0=不限制(与每日一致)"}, + "SUNDAY_MIN_SIGNAL_STRENGTH": {"value": 8, "type": "number", "category": "risk", "description": "周日最低信号强度(0-10),0=不提高门槛"}, + "NIGHT_HOURS_NO_OPEN_ENABLED": {"value": True, "type": "boolean", "category": "risk", "description": "晚间/凌晨(21:00~06:00北京)禁止开仓"}, + "NIGHT_HOURS_START": {"value": 21, "type": "number", "category": "risk", "description": "禁止开仓开始小时(含)"}, + "NIGHT_HOURS_END": {"value": 6, "type": "number", "category": "risk", "description": "禁止开仓结束小时(不含)"}, + "NIGHT_HOURS_ONLY_SUNDAY": {"value": True, "type": "boolean", "category": "risk", "description": "True=仅周六21:00~周日06:00;False=每天"}, "TOP_N_SYMBOLS": {"value": 8, "type": "number", "category": "scan", "description": "每次扫描后优先处理的交易对数量"}, "MIN_SIGNAL_STRENGTH": {"value": 8, "type": "number", "category": "scan", "description": "最小信号强度(0-10)"}, "MIN_VOLUME_24H": {"value": 30_000_000, "type": "number", "category": "scan", "description": "24h 成交量下限(USD)"}, @@ -90,6 +102,27 @@ RISK_KNOBS_DEFAULTS = { "category": "risk", "description": "每日最多开仓次数(防止高频下单/过度交易)。建议 3-15。", }, + "SUNDAY_MAX_OPENS": { + "value": 3, + "type": "number", + "category": "risk", + "description": "周日最多开仓次数。0=不限制(与每日一致);设为 2-3 可降低周日亏损又不完全停单。", + }, + "SUNDAY_MIN_SIGNAL_STRENGTH": { + "value": 8, + "type": "number", + "category": "risk", + "description": "周日最低信号强度(0-10)。仅当信号强度>=此值才开仓,0=不提高(与平日一致)。建议 8~9。", + }, + "NIGHT_HOURS_NO_OPEN_ENABLED": { + "value": True, + "type": "boolean", + "category": "risk", + "description": "晚间/凌晨禁止开新仓。开启后 21:00~06:00(北京时间)不开新仓,亏损多从昨晚21点后开始。", + }, + "NIGHT_HOURS_START": {"value": 21, "type": "number", "category": "risk", "description": "禁止开仓开始小时(北京),含"}, + "NIGHT_HOURS_END": {"value": 6, "type": "number", "category": "risk", "description": "禁止开仓结束小时(北京),不含"}, + "NIGHT_HOURS_ONLY_SUNDAY": {"value": True, "type": "boolean", "category": "risk", "description": "仅周日夜间。True=周六21:00~周日06:00;False=每天。"}, } # API key/secret 脱敏 diff --git a/backend/config_manager.py b/backend/config_manager.py index 7c3fc27..3011ef3 100644 --- a/backend/config_manager.py +++ b/backend/config_manager.py @@ -63,6 +63,11 @@ RISK_KNOBS_KEYS = { "AUTO_TRADE_ENABLED", "MAX_OPEN_POSITIONS", "MAX_DAILY_ENTRIES", + "SUNDAY_MAX_OPENS", + "SUNDAY_MIN_SIGNAL_STRENGTH", + "NIGHT_HOURS_NO_OPEN_ENABLED", + "NIGHT_HOURS_START", + "NIGHT_HOURS_END", # 2026-02-06 Added for Altcoin Strategy presets "TOP_N_SYMBOLS", "MIN_SIGNAL_STRENGTH", @@ -825,6 +830,12 @@ class ConfigManager: 'AUTO_TRADE_ENABLED': eff_get('AUTO_TRADE_ENABLED', True), 'MAX_OPEN_POSITIONS': eff_get('MAX_OPEN_POSITIONS', 3), 'MAX_DAILY_ENTRIES': eff_get('MAX_DAILY_ENTRIES', max_daily_default), + 'SUNDAY_MAX_OPENS': eff_get('SUNDAY_MAX_OPENS', 3), # 周日开仓上限,0=不限制 + 'SUNDAY_MIN_SIGNAL_STRENGTH': eff_get('SUNDAY_MIN_SIGNAL_STRENGTH', 8), # 周日最低信号强度,0=不提高 + 'NIGHT_HOURS_NO_OPEN_ENABLED': eff_get('NIGHT_HOURS_NO_OPEN_ENABLED', True), + 'NIGHT_HOURS_START': eff_get('NIGHT_HOURS_START', 21), + 'NIGHT_HOURS_END': eff_get('NIGHT_HOURS_END', 6), + 'NIGHT_HOURS_ONLY_SUNDAY': eff_get('NIGHT_HOURS_ONLY_SUNDAY', True), # 同步/系统单标识(全局配置,账号可覆盖) 'ONLY_AUTO_TRADE_CREATES_RECORDS': eff_get('ONLY_AUTO_TRADE_CREATES_RECORDS', True), # True=不补建「仅币安有仓」;False 时配合 SYNC_RECOVER 可补建 diff --git a/docs/trade_analysis_2026-03-01.md b/docs/trade_analysis_2026-03-01.md new file mode 100644 index 0000000..6a36735 --- /dev/null +++ b/docs/trade_analysis_2026-03-01.md @@ -0,0 +1,67 @@ +# 交易导出分析:binance_trades_2_2026-03-01(近一日) + +## 一、数据统计在算什么 + +| 指标 | 数值 | 说明 | +|------|------|------| +| 总成交笔数 | 88 | 含开仓+平仓的所有成交 | +| 有 realizedPnl 的笔数 | 52 | 即「平仓」产生的盈亏 | +| 总已实现盈亏 | **+11.29 USDT** | 所有**已平仓**交易的 realizedPnl 之和 | +| 总手续费 | 0.92 USDT | | +| **净盈亏(本文件统计)** | **+10.37 USDT** | 已实现盈亏 − 手续费 | + +结论:**数据统计的「盈利」= 仅针对「已平仓」的已实现盈亏**,不包含: +- 当时仍持仓的**浮盈/浮亏**(未平仓不算进去) +- 你看到的「币安账户权益」在某一刻到过 +15U、后来又下来的那种**过程曲线** + +所以:**统计上 +10.37U 是准确的(已实现部分),和你「先赚 15U 又亏回去一半」的体感不冲突**——体感来自账户权益的峰值与回撤,统计只来自最终平仓结果。 + +--- + +## 二、为什么会有「先赚 15U 又亏回一半」的体感 + +### 1. 按日期看:周六大赚、周日回吐 + +| 日期 | 已实现盈亏 | +|------|------------| +| 2026-02-28(周六) | **+21.69 USDT** | +| 2026-03-01(周日) | **-10.39 USDT** | + +也就是说:**周六一天平仓就赚了约 21.7U,周日平仓又亏回去约 10.4U**,两日合计仍净赚约 11.3U(和上面的 11.29 一致)。 + +### 2. 按「平仓顺序」累加已实现盈亏 + +若按时间顺序,把每一笔平仓的 realizedPnl 累加: + +- **累加峰值**:约 **25.60 USDT**(到 2月28日 20 时 BCHUSDT 那笔平仓为止) +- **最终累加**:11.29 USDT + +即:仅看**已平仓**的流水,也曾「账面累计赚到过约 25.6U」,之后又有多笔平仓亏损,把累计拉回到 11.29。这和「先上去、再回撤一半」的体验一致。 + +### 3. 你记得的「赚 15U」可能来自哪 + +- **可能是「某一刻账户权益 +15U」**:那时 = 已实现盈亏 + **当时未平仓的浮盈**。后来行情回落或止损/止盈触发,浮盈变少或变成已实现亏损,所以「又亏回去一半」。 +- **也可能是「周六当天赚了一截」的粗略印象**:周六已实现就 +21.69,若再叠加当时持仓浮盈,某一刻到 +15U 很正常。 + +无论哪种,都和导出里的**已实现合计 +10.37 USDT** 不矛盾:统计只算「已经平仓落袋」的部分。 + +--- + +## 三、大额单笔(已实现) + +- **单笔盈利 >2U**:1000SHIBUSDT(+6.72)、WIFUSDT(+4.97)、1000PEPEUSDT(+4.16 / +2.36)、FILUSDT(+2.42)、RIVERUSDT(+2.00) 等。 +- **单笔亏损 >2U**:ASTERUSDT(-3.99)、HBARUSDT(-3.47)、ICPUSDT(-2.13) 等。 + +周日回吐里,ASTER、ICP 等有贡献。 + +--- + +## 四、小结与建议 + +1. **数据统计是盈利的**:已实现净 +10.37 USDT,只反映「已平仓」部分,**没问题**。 +2. **「先赚 15U 又亏回一半」**:和导出数据一致——按日/按累加都能看到「先上去、再回撤」;若 15U 是当时账户权益峰值(含浮盈),后来回撤或平仓后变少,就会是现在这种体感。 +3. **若想以后更贴近「体感」**:可以额外做两件事: + - 按小时/按日看**已实现盈亏曲线**(本分析里的「按时间累加」),用来对照「哪天、哪段在赚/在亏」; + - 若有账户权益/余额快照(或未实现盈亏日志),可以算**从峰值的回撤**(例如「从 +15U 回撤到 +7.5U」),单独记一笔,和「已实现净 +10.37」一起看,会更完整。 + +当前这份导出**没有包含未平仓持仓**,所以无法从文件里还原「某一刻浮盈到 15U」的精确数字,但时间线(周六大赚、周日回吐、累加曾到 25.6 再回落到 11.29)已经能支撑「数据统计盈利 + 实际先赚后亏回去一截」的结论。 diff --git a/docs/trade_analysis_2026-03-01_losing_day.md b/docs/trade_analysis_2026-03-01_losing_day.md new file mode 100644 index 0000000..c043cb4 --- /dev/null +++ b/docs/trade_analysis_2026-03-01_losing_day.md @@ -0,0 +1,187 @@ +# 当日亏损分析:2026-03-01(周日)及优化建议 + +基于 `binance_trades_2_2026-03-01 (2).json` 导出;**星期几结论** 见下方「七、最近 7 天按星期几统计」,数据来自 `binance_trades_2_2026-03-01 (3).json`(最近 7 天)。 + +--- + +## 一、整体与「当天」口径 + +| 口径 | 已实现盈亏 | 说明 | +|------|------------|------| +| **全导出(2/28+3/1)** | +11.29 USDT(净 +10.37) | 两天合计仍盈利 | +| **2026-03-01(周日)** | **-10.39 USDT** | 当天 1 盈 12 亏,明显亏损 | +| **2026-02-28(周六)** | +21.69 USDT | 19 盈 20 亏,大赚 | + +结论:**「当天亏损」= 周日 3 月 1 日**,把周六赚的约一半回吐;统计没问题,问题在周日当天的表现。 + +--- + +## 二、周日亏损结构 + +### 1. 按交易对(周日当天) + +| 交易对 | 已实现盈亏(USDT) | +|--------|------------------| +| ASTERUSDT | -3.99 | +| WLFIUSDT | -1.74 | +| FILUSDT | -1.51 | +| WIFUSDT | -1.18 | +| ICPUSDT | -1.12 | +| WLDUSDT | -0.89 | +| ENAUSDT | -0.68 | +| AAVEUSDT | -0.64 | +| HUSDT | -0.46 | +| 1000LUNCUSDT | +1.80(周日唯一一笔盈利) | + +周日共 13 笔平仓,12 笔亏、1 笔盈,且亏损集中在凌晨 0–6 时和个别品种。 + +### 2. 按小时(全导出,可看出「危险时段」) + +| 时段 | 已实现盈亏 | 备注 | +|------|------------|------| +| 0 时 | -0.46 | 亏 | +| 2 时 | -2.83 | 亏 | +| 3 时 | +0.68 | 盈 | +| **4 时** | **-6.61** | **单小时亏损最大** | +| 6 时 | -1.18 | 亏 | +| 10 时 | -1.22 | 亏 | +| 12 时 | +2.00 | 盈 | +| 14 时 | +11.50 | 盈 | +| 15 时 | +1.93 | 盈 | +| 17 时 | +0.70 | 盈 | +| 18 时 | -2.19 | 亏 | +| 19 时 | +8.17 | 盈 | +| 20 时 | +4.70 | 盈 | +| **21 时** | **-4.38** | 亏 | +| 22 时 | +1.61 | 盈 | +| 23 时 | -1.15 | 亏 | + +亏损集中:**0、2、4、6、10、18、21、23 时**;盈利集中:**12–15、19–20、22 时**。周日当天亏损主要发生在**凌晨 0–6 时**(含 4 时 -6.61)和晚间 21 时等。实盘反馈:亏损实际从**昨晚 21:00 之后**开始(周六晚 21 点 → 周日凌晨),危险时段 **21:00~次日 06:00**,已加此时段禁止开新仓(NIGHT_HOURS_NO_OPEN)。 + +### 3. 全周期亏损交易对(可做黑名单/降权参考) + +| 交易对 | 已实现盈亏 | 胜/负 | +|--------|------------|-------| +| ASTERUSDT | -3.99 | 0/1 | +| HBARUSDT | -3.47 | 0/1 | +| ICPUSDT | -3.25 | 0/3 | +| ZROUSDT | -2.37 | 0/2 | +| TAOUSDT | -2.31 | 0/2 | +| WLFIUSDT | -1.74 | 0/2 | +| FARTCOINUSDT | -1.67 | 0/2 | +| WLDUSDT | -0.89 | 0/1 | +| HYPEUSDT | -0.83 | 0/1 | +| PUMPUSDT | -0.73 | 0/1 | +| ENAUSDT | -0.68 | 0/1 | +| SIGNUSDT | -0.52 | 0/4 | + +这些品种在本周期内**全部或多数为亏**,适合做统计过滤(黑名单/降权)。 + +--- + +## 三、问题归纳 + +1. **周日凌晨 0–6 时**:多笔止损/亏损平仓(ASTER、WLFI、WLD、WIF、ICP、ENA、AAVE、HUSDT 等),单 4 时就 -6.61。 +2. **胜率偏低**:全周期 38.5%(20 盈 / 32 亏),周日更极端(1 盈 / 12 亏)。 +3. **同一交易对反复亏**:如 ICPUSDT 3 笔全亏、SIGNUSDT 4 笔全亏、ZRO/TAO/WLFI/FARTCOIN 等 2 笔全亏。 +4. **时段与星期效应**:凌晨 + 周日 明显偏亏;午后到晚上(12–15、19–20、22 时)偏盈。 + +--- + +## 四、优化建议 + +### 1. 时段过滤(优先) + +- **凌晨 0–6 时**:在「按小时统计」或策略里对该时段**降权或禁止开新仓**(至少周日或周末可更严)。 +- **21 时、23 时**:本周期也为亏,可列为「谨慎时段」或小幅降权。 +- 实现方式:用现有 `trade_stats` 的 by_hour,对亏损集中时段提高门槛或禁止开仓(如 `STOP_OPEN_HOURS` 或按小时 bucket 权重)。 + +### 2. 周日/周末规则(建议) + +- 周日当天 1 盈 12 亏,可单独对待: + - 选项 A:**周日禁止开新仓**(只平仓、不新开)。 + - 选项 B:周日**提高信号门槛**(如 MIN_SIGNAL_STRENGTH+1)或**降低仓位/次数**。 +- 若统计上「周六赚、周日亏」持续出现,可加配置如 `WEEKEND_REDUCE_OPEN` 或 `SUNDAY_NO_OPEN`。 + +### 3. 交易对黑名单/降权 + +- **硬黑名单(禁止开仓)**:ASTER、HBAR、ICP、ZRO、TAO、WLFI、FARTCOIN、WLD、HYPE、PUMP、ENA、SIGNUSDT 等在本周期内 0 胜多负,可先纳入统计黑名单或硬黑名单,观察一段时间再放开。 +- **软黑名单(降权)**:同周期内胜率极低的品种,用现有「按 symbol 统计 + 黑名单 mode」做降权,减少仓位或提高开仓门槛。 + +### 4. 冷却与频控 + +- 同一交易对在**同一天或 24 小时内**多次亏损(如 ICP 3 笔、SIGN 4 笔): + - 拉长 **SYMBOL_LOSS_COOLDOWN_SEC**(如 2–4 小时)。 + - 或增加「**同一 symbol 每日最大开仓次数**」(如 2 次),避免连续试错。 + +### 5. 入场质量 + +- 周日 12 笔亏损多为**止损出场**,说明入场质量在凌晨/周日偏差: + - 在**亏损时段**(0–6、21、23 时)或**周日**提高 **MIN_SIGNAL_STRENGTH**(如 +1)。 + - 或在这些时段/星期**关闭智能追价、只做限价**,减少被动成交。 + +### 6. 大盘与风控 + +- 若周日凌晨 BTC/大盘明显下跌,可加强 **BETA_FILTER** 或**周日降低多单仓位**,避免逆势加码。 + +--- + +## 五、建议落地顺序 + +| 优先级 | 动作 | +|--------|------| +| P0 | 凌晨 0–6 时禁止或大幅降权开新仓(尤其 4 时);21/23 时酌情降权。 | +| P0 | 周日:禁止开新仓或提高信号门槛 + 降低仓位。 | +| P1 | 对 ASTER、ICP、ZRO、TAO、WLFI、FARTCOIN、SIGN 等做黑名单或降权。 | +| P1 | 拉长 SYMBOL_LOSS_COOLDOWN_SEC,或加「同 symbol 每日开仓上限」。 | +| P2 | 在亏损时段/周日提高 MIN_SIGNAL_STRENGTH 或关闭追价。 | + +--- + +## 六、最近 7 天按星期几统计(是否加星期几规则) + +数据来源:`binance_trades_2_2026-03-01 (3).json`(约 7 天、422 笔成交,64 盈 / 141 亏,总已实现 +39.60 U)。 + +### 按星期几汇总 + +| 星期 | 已实现盈亏(U) | 盈/亏笔数 | 胜率(%) | 涉及日期 | +|------|----------------|-----------|---------|----------| +| 周一 | -5.67 | 0 / 2 | 0 | 2026-02-23 | +| 周二 | +2.57 | 1 / 0 | 100 | 2026-02-24 | +| 周三 | +8.99 | 6 / 15 | 28.6 | 2026-02-25 | +| 周四 | **+37.03** | 28 / 32 | 46.7 | 2026-02-26 | +| 周五 | -7.69 | 5 / 38 | **11.6** | 2026-02-27 | +| 周六 | +22.00 | 22 / 31 | 41.5 | 2026-02-28 | +| **周日** | **-17.63** | **2 / 23** | **8.0** | 2026-02-22、2026-03-01 | + +### 按日期(辅助) + +| 日期 | 星期 | 已实现(U) | 盈/亏 | +|------|------|-----------|-------| +| 2026-02-22 | 周日 | -7.24 | 1 / 11 | +| 2026-02-23 | 周一 | -5.67 | 0 / 2 | +| 2026-02-24 | 周二 | +2.57 | 1 / 0 | +| 2026-02-25 | 周三 | +8.99 | 6 / 15 | +| 2026-02-26 | 周四 | +37.03 | 28 / 32 | +| 2026-02-27 | 周五 | -7.69 | 5 / 38 | +| 2026-02-28 | 周六 | +22.00 | 22 / 31 | +| 2026-03-01 | 周日 | -10.39 | 1 / 12 | + +### 结论(星期几是否有关) + +- **周日**:7 天内有 **2 个周日**(2/22、3/1),**两日都亏**(-7.24、-10.39),合计 -17.63 U,胜率仅 8%(2 盈 23 亏)。**不是单日偶然,支持加「周日」限制。** +- **周五**:样本内 1 天(2/27),-7.69 U、胜率 11.6%,也偏差,可作次要观察。 +- **周四、周六**:表现最好(+37、+22),周三、周二为正。 + +**建议**:在策略里增加**星期几规则**有数据支撑——至少对**周日**禁止开新仓或明显降权;若后续多周周五持续偏亏,再考虑周五降权。 + +**已实现**:增加配置 **`SUNDAY_MAX_OPENS`**(周日最多开仓次数)。设为 **0** = 不限制(与平日一致);设为 **2~3** = 周日最多开 2~3 单,既控制周日亏损又不完全停单。后端/前端配置页可调。 + +--- + +## 七、小结 + +- **「当天亏损」= 2026-03-01(周日)**,已实现 -10.39 USDT,1 盈 12 亏。 +- **主要来源**:凌晨 0–6 时(尤其 4 时)多笔止损 + 若干持续亏损的交易对(ASTER、WLFI、FIL、WIF、ICP、WLD、ENA、AAVE、HUSDT)。 +- **星期几**:最近 7 天内两个周日都亏、胜率极低,**支持加周日限制**;周五样本内也差,可后续再观察。 +- **优化方向**:**时段过滤(凌晨/晚间)+ 周日规则(推荐)+ 亏损品种黑名单/降权 + 冷却与频控**,可显著减少类似「周六赚、周日一把回吐」的重复。 diff --git a/frontend/src/components/GlobalConfig.jsx b/frontend/src/components/GlobalConfig.jsx index 3428b6c..2633777 100644 --- a/frontend/src/components/GlobalConfig.jsx +++ b/frontend/src/components/GlobalConfig.jsx @@ -97,6 +97,12 @@ const CONFIG_GUIDE_DETAILS = { AUTO_TRADE_ENABLED: '自动交易总开关。关闭后仅生成推荐,不会自动下单,适合先观察/体验。', MAX_OPEN_POSITIONS: '同时持仓数量上限(防止仓位过多/难管理)。建议 1-5。', MAX_DAILY_ENTRIES: '每日最多开仓次数(防止高频下单/过度交易)。建议 3-15。', + SUNDAY_MAX_OPENS: '周日最多开仓次数。0=不限制(与每日一致);2-3 可降低周日亏损又不完全停单。', + SUNDAY_MIN_SIGNAL_STRENGTH: '周日最低信号强度(0-10)。仅当>=此值才开仓,0=不提高。建议 8-9。', + NIGHT_HOURS_NO_OPEN_ENABLED: '晚间/凌晨禁止开新仓。开启后 21:00~06:00(北京)不开新仓。', + NIGHT_HOURS_START: '禁止开仓开始小时(北京),含', + NIGHT_HOURS_END: '禁止开仓结束小时(北京),不含', + NIGHT_HOURS_ONLY_SUNDAY: 'True=仅周六21:00~周日06:00禁止;False=每天21:00~06:00禁止', BINANCE_API_KEY: '币安API密钥。用于访问币安账户的凭证,需要启用"合约交易"权限。请妥善保管,不要泄露。', BINANCE_API_SECRET: '币安API密钥Secret。与API Key配对使用,用于签名验证。请妥善保管,不要泄露。', USE_TESTNET: '是否使用币安测试网。true表示模拟交易(无真实资金),false表示真实交易。', @@ -343,6 +349,12 @@ const GlobalConfig = () => { MAX_TOTAL_POSITION_PERCENT: 0.65, MIN_POSITION_PERCENT: 0.02, MAX_DAILY_ENTRIES: 15, + SUNDAY_MAX_OPENS: 3, + SUNDAY_MIN_SIGNAL_STRENGTH: 8, + NIGHT_HOURS_NO_OPEN_ENABLED: true, + NIGHT_HOURS_START: 21, + NIGHT_HOURS_END: 6, + NIGHT_HOURS_ONLY_SUNDAY: true, MAX_OPEN_POSITIONS: 4, LEVERAGE: 4, MAX_LEVERAGE: 12, @@ -1913,7 +1925,7 @@ const GlobalConfig = () => { // 1. 基础过滤(排除非对象、风险旋钮、API Key) if (!config || typeof config !== 'object') return false const RISK_KNOBS_KEYS = ['MIN_MARGIN_USDT', 'MIN_POSITION_PERCENT', 'MAX_POSITION_PERCENT', - 'MAX_TOTAL_POSITION_PERCENT', 'AUTO_TRADE_ENABLED', 'MAX_OPEN_POSITIONS', 'MAX_DAILY_ENTRIES'] + 'MAX_TOTAL_POSITION_PERCENT', 'AUTO_TRADE_ENABLED', 'MAX_OPEN_POSITIONS', 'MAX_DAILY_ENTRIES', 'SUNDAY_MAX_OPENS', 'SUNDAY_MIN_SIGNAL_STRENGTH', 'NIGHT_HOURS_NO_OPEN_ENABLED', 'NIGHT_HOURS_START', 'NIGHT_HOURS_END', 'NIGHT_HOURS_ONLY_SUNDAY'] if (RISK_KNOBS_KEYS.includes(key)) return false if (key === 'BINANCE_API_KEY' || key === 'BINANCE_API_SECRET' || key === 'USE_TESTNET') return false diff --git a/trading_system/binance_client.py b/trading_system/binance_client.py index d75a547..5ac1a47 100644 --- a/trading_system/binance_client.py +++ b/trading_system/binance_client.py @@ -2290,7 +2290,7 @@ class BinanceClient: logger.error(f" 错误代码: {error_code}") logger.error(f" 参数: {params}") if error_code == -4014: - logger.error(f" 原因: 价格步长错误,triggerPrice 需要调整到 tickSize 的倍数") + logger.error(f" 原因: 触发价步长错误(-4014),triggerPrice 须为该交易对 PRICE_FILTER.tickSize 的整数倍,请检查 exchangeInfo 或重试") elif error_code == -4164: logger.error(f" 原因: 订单名义价值不足(至少需要 5 USDT)") elif error_code == -2022: @@ -2697,7 +2697,8 @@ class BinanceClient: return fallback except (TimeoutError, asyncio.TimeoutError, BinanceAPIException): continue - logger.error(f"设置杠杆最终失败: {symbol} (目标: {target_leverage}x)") + err_code = getattr(e, "code", None) + logger.error(f"设置杠杆最终失败: {symbol} (目标: {target_leverage}x) 错误码={err_code} 详情: {e}") return 0 def get_realtime_price(self, symbol: str) -> Optional[float]: diff --git a/trading_system/config.py b/trading_system/config.py index 6c2dfe2..a6fc674 100644 --- a/trading_system/config.py +++ b/trading_system/config.py @@ -186,6 +186,14 @@ DEFAULT_TRADING_CONFIG = { 'AUTO_TRADE_ENABLED': True, # 自动交易总开关 'MAX_OPEN_POSITIONS': 4, # 同时持仓数量上限(总仓位12% / 单笔1.5% = 最多4个) 'MAX_DAILY_ENTRIES': 15, # 每日最多15笔(快速验证模式:提高上限以快速验证策略) + # 周日开仓上限:周日最多允许开仓次数,0=不限制(与 MAX_DAILY_ENTRIES 一致)。设为 2~3 可降低周日亏损又不完全停单 + 'SUNDAY_MAX_OPENS': 3, + # 周日信号门槛:周日仅当信号强度>=此值才开仓,0=不提高。建议 8~9 + 'SUNDAY_MIN_SIGNAL_STRENGTH': 8, + 'NIGHT_HOURS_NO_OPEN_ENABLED': True, + 'NIGHT_HOURS_START': 21, + 'NIGHT_HOURS_END': 6, + 'NIGHT_HOURS_ONLY_SUNDAY': True, 'ONE_WAY_POSITION_ONLY': True, # 账号固定为单向持仓模式,不传 positionSide,不检测对冲模式(避免 -4061) 'MAX_POSITION_PERCENT': 0.20, # 单笔仓位上限20%(作为风控熔断,实际仓位由固定风险模型决定) diff --git a/trading_system/main.py b/trading_system/main.py index b8364dc..9aa2ec5 100644 --- a/trading_system/main.py +++ b/trading_system/main.py @@ -496,6 +496,19 @@ async def main(): logger.info(f" 最小仓位: {config.TRADING_CONFIG.get('MIN_POSITION_PERCENT', 0.01)*100:.2f}%") logger.info(f" 最大持仓数: {config.TRADING_CONFIG.get('MAX_OPEN_POSITIONS', 10)} 个") logger.info(f" 每日最大开仓: {config.TRADING_CONFIG.get('MAX_DAILY_ENTRIES', 20)} 笔") + sunday_max = config.TRADING_CONFIG.get('SUNDAY_MAX_OPENS', 0) + if sunday_max and int(sunday_max) > 0: + logger.info(f" 周日开仓上限: {int(sunday_max)} 笔") + sunday_min_sig = config.TRADING_CONFIG.get('SUNDAY_MIN_SIGNAL_STRENGTH', 0) + if sunday_min_sig and int(sunday_min_sig) > 0: + logger.info(f" 周日信号门槛: >={int(sunday_min_sig)}") + if config.TRADING_CONFIG.get('NIGHT_HOURS_NO_OPEN_ENABLED', False): + only_sun = config.TRADING_CONFIG.get('NIGHT_HOURS_ONLY_SUNDAY', True) + logger.info( + f" 晚间禁止开仓: {config.TRADING_CONFIG.get('NIGHT_HOURS_START', 21)}:00~次日" + f"{config.TRADING_CONFIG.get('NIGHT_HOURS_END', 6)}:00(北京)" + + (",仅周六晚~周日晨" if only_sun else ",每日") + ) logger.info("") logger.info("【杠杆配置】") logger.info(f" 基础杠杆: {config.TRADING_CONFIG.get('LEVERAGE', 10)}x") diff --git a/trading_system/position_manager.py b/trading_system/position_manager.py index 5026b30..84cf683 100644 --- a/trading_system/position_manager.py +++ b/trading_system/position_manager.py @@ -1820,7 +1820,7 @@ class PositionManager: logger.error(f"{symbol} ⚠️ 止损单会立即触发(-2021),视为已触发止损,立即执行市价平仓") await self.close_position(symbol, reason='stop_loss') return - logger.error(f"{symbol} 挂止损单失败: {e}") + logger.error(f"{symbol} 挂止损单失败(API/网络): {e}") sl_order = None if sl_order and entry_order_id: @@ -1850,7 +1850,7 @@ class PositionManager: logger.error(f" 当前价格: {current_price if current_price else '无(已尝试 WS bookTicker/ticker24h、持仓、REST)'}") logger.error(f" 持仓方向: {side}") logger.error(f" ⚠️ 警告: 没有交易所级别的止损保护,如果系统崩溃或网络中断,可能无法及时止损!") - logger.error(f" 💡 建议: 检查止损价格计算是否正确,或手动在币安界面设置止损") + logger.error(f" 💡 建议: 检查上方或下方日志中的 API 错误码(如 -4014 触发价步长不符、-4164 名义价值不足、-2021 会立即触发)或手动在币安界面设置止损") # ⚠️ 关键修复:止损单挂单失败后,立即检查当前价格是否已触发止损 # 如果已触发,立即执行市价平仓,避免亏损扩大 diff --git a/trading_system/risk_manager.py b/trading_system/risk_manager.py index e0d3099..9e3f57a 100644 --- a/trading_system/risk_manager.py +++ b/trading_system/risk_manager.py @@ -897,15 +897,52 @@ class RiskManager: logger.info(f"{symbol} 已有持仓,跳过") return False + try: + if bool(config.TRADING_CONFIG.get("NIGHT_HOURS_NO_OPEN_ENABLED", False)): + bj = timezone(timedelta(hours=8)) + now_bj = datetime.now(bj) + h = now_bj.hour + wd = now_bj.weekday() + start_h = int(config.TRADING_CONFIG.get("NIGHT_HOURS_START", 21) or 21) + end_h = int(config.TRADING_CONFIG.get("NIGHT_HOURS_END", 6) or 6) + only_sunday = bool(config.TRADING_CONFIG.get("NIGHT_HOURS_ONLY_SUNDAY", True)) + if start_h > end_h: + in_night = (h >= start_h) or (h < end_h) + else: + in_night = (h >= start_h and h < end_h) + if in_night: + if only_sunday: + apply_block = (wd == 5 and h >= start_h) or (wd == 6 and h < end_h) + else: + apply_block = True + if apply_block: + logger.info( + f"{symbol} 晚间禁止开仓({'仅周六晚~周日晨' if only_sunday else '每日'},北京{h}时),跳过" + ) + return False + except Exception: + pass + # 每日开仓次数限制(Redis 计数;无 Redis 时降级为内存计数) try: max_daily = int(config.TRADING_CONFIG.get("MAX_DAILY_ENTRIES", 0) or 0) + bj = timezone(timedelta(hours=8)) + today_bj = datetime.now(bj) + is_sunday = (today_bj.weekday() == 6) # 0=Mon, 6=Sun + sunday_max = int(config.TRADING_CONFIG.get("SUNDAY_MAX_OPENS", 0) or 0) + effective_max = max_daily + if is_sunday and sunday_max > 0: + effective_max = min(max_daily, sunday_max) except Exception: max_daily = 0 + effective_max = 0 + is_sunday = False + sunday_max = 0 if max_daily > 0: c = await self._get_daily_entries_count() - if c >= max_daily: - logger.info(f"{symbol} 今日开仓次数已达上限:{c}/{max_daily},跳过") + if c >= effective_max: + reason = "(周日限制)" if (is_sunday and sunday_max > 0) else "" + logger.info(f"{symbol} 今日开仓次数已达上限{reason}:{c}/{effective_max},跳过") return False # ⚠️ 2026-01-29新增:检查同一交易对连续亏损情况,避免连续亏损后继续交易 diff --git a/trading_system/strategy.py b/trading_system/strategy.py index eea4670..efa31bb 100644 --- a/trading_system/strategy.py +++ b/trading_system/strategy.py @@ -4,6 +4,7 @@ import asyncio import logging import time +from datetime import datetime, timezone, timedelta from typing import List, Dict, Optional try: from .binance_client import BinanceClient @@ -242,7 +243,18 @@ class TradingStrategy: f"原因: {entry_reason} | " f"信号强度: {signal_strength}/10" ) - + # 周日提高信号门槛:只做高信号单 + try: + bj = timezone(timedelta(hours=8)) + is_sunday = (datetime.now(bj).weekday() == 6) + sunday_min = int(config.TRADING_CONFIG.get("SUNDAY_MIN_SIGNAL_STRENGTH", 0) or 0) + if is_sunday and sunday_min > 0 and signal_strength < sunday_min: + logger.info( + f"{symbol} 周日信号门槛未达 {sunday_min}(当前 {signal_strength}/10),跳过开仓" + ) + continue + except Exception: + pass # 根据信号强度计算动态杠杆(高质量信号使用更高杠杆) # ⚠️ 优化:同时检查交易对支持的最大杠杆限制和小众币限制 dynamic_leverage = await self.risk_manager.calculate_dynamic_leverage(