From 0127edbc97e18a8809282a6cd1346891641ed270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89?= Date: Sun, 1 Mar 2026 11:25:03 +0800 Subject: [PATCH] feat(config): Add new risk management configurations for Sunday and night trading hours Introduced new configuration options to manage trading activities on Sundays and during night hours. This includes limits on the number of trades on Sundays, minimum signal strength requirements for Sunday trades, and restrictions on opening new positions during specified night hours. Updated relevant backend and frontend components to reflect these changes, enhancing risk control and user awareness of trading conditions. --- backend/api/routes/config.py | 33 ++++ backend/config_manager.py | 11 ++ docs/trade_analysis_2026-03-01.md | 67 +++++++ docs/trade_analysis_2026-03-01_losing_day.md | 187 +++++++++++++++++++ frontend/src/components/GlobalConfig.jsx | 14 +- trading_system/binance_client.py | 5 +- trading_system/config.py | 8 + trading_system/main.py | 13 ++ trading_system/position_manager.py | 4 +- trading_system/risk_manager.py | 41 +++- trading_system/strategy.py | 14 +- 11 files changed, 389 insertions(+), 8 deletions(-) create mode 100644 docs/trade_analysis_2026-03-01.md create mode 100644 docs/trade_analysis_2026-03-01_losing_day.md 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(