diff --git a/docs/减少亏损_配置操作步骤.md b/docs/减少亏损_配置操作步骤.md new file mode 100644 index 0000000..c2a9ccc --- /dev/null +++ b/docs/减少亏损_配置操作步骤.md @@ -0,0 +1,98 @@ +# 减少亏损的配置操作步骤 + +基于「最近一天亏损分析」的结论,按下面步骤改配置即可生效(无需改代码,改完保存后配置会热更新,交易进程下一轮扫描即用新值)。 + +--- + +## 一、在哪里改 + +- **前端**:登录系统 → 左侧 **「配置」** 或 **「全局配置」**(管理员用全局配置可改所有账号默认值)。 +- **按账号**:配置页顶部选好要改的**账号**,再改下面项,保存后只对该账号生效。 + +--- + +## 二、建议先改的几项(优先顺序) + +### 1. 确保 4H 上涨时禁止开空(减少空单在反弹里被扫) + +| 配置项 | 建议值 | 说明 | +|--------|--------|------| +| **BLOCK_SHORT_WHEN_4H_UP** | **true** | 4H 趋势为上涨时禁止开空,避免像 WLD/XLM/PENGU/SIREN 那样做空后被反弹打止损。 | + +若当前是 `false`,改成 `true` 并保存。 + +--- + +### 2. 市场方案(牛/熊/正常) + +| 配置项 | 建议值 | 说明 | +|--------|--------|------| +| **MARKET_SCHEME** | **normal** 或 **bull** | 若你认为近期偏多,可设为 **bull**:牛市时系统会禁止开空,进一步减少逆势空单。 | + +- 想自动识别牛熊:把 **AUTO_MARKET_SCHEME_ENABLED** 设为 **true**,并保证服务器上定时跑 `scripts/update_market_scheme.py --apply`(见该脚本说明)。 +- 不自动的话就手动把 **MARKET_SCHEME** 设为 **bull** / **bear** / **normal**。 + +--- + +### 3. 放宽止损,减少「约 3% 被洗」再反向 + +亏损单多在约 2.8%~3.2% 出场,可把止损略放宽,给波动留空间(仍会止损,只是不那么紧)。 + +| 配置项 | 原常见值 | 建议值 | 说明 | +|--------|----------|--------|------| +| **STOP_LOSS_PERCENT** | 0.05~0.08(5%~8%) | **0.08~0.10**(8%~10%) | 保证金比例止损。8%~10% 仍能控单笔风险,但比 5% 不易被正常波动扫掉。 | +| **ATR_STOP_LOSS_MULTIPLIER** | 2.0 | **2.5~3.0** | 用 ATR 动态止损时,倍数大一点 = 止损距离稍远,适合波动大的山寨。 | + +若 **USE_ATR_STOP_LOSS** 为 true,优先调 **ATR_STOP_LOSS_MULTIPLIER**;否则主要调 **STOP_LOSS_PERCENT**。 + +--- + +### 4. 提高信号门槛(少做弱信号单) + +| 配置项 | 建议值 | 说明 | +|--------|--------|------| +| **MIN_SIGNAL_STRENGTH** | **6~8** | 最小信号强度(0-10)。提高到 6~8 会少很多单,但单笔质量更好,适合当前胜率偏低时。 | + +--- + +### 5. 周日 / 晚间(可选) + +若亏损多发生在周日或晚间 22~23 点、9~10 点,可加强限制: + +| 配置项 | 建议值 | 说明 | +|--------|--------|------| +| **SUNDAY_MAX_OPENS** | **2~3** | 周日最多开仓次数,0=不限制。2~3 可压周日亏损。 | +| **SUNDAY_MIN_SIGNAL_STRENGTH** | **8~9** | 周日只有信号强度 ≥ 此值才开仓,0=不提高。 | +| **NIGHT_HOURS_NO_OPEN_ENABLED** | **true** | 晚间禁止开新仓(配合下面起止小时)。 | +| **NIGHT_HOURS_START** / **NIGHT_HOURS_END** | 如 **21** / **6** | 北京 21:00~06:00 不开新仓;**NIGHT_HOURS_ONLY_SUNDAY** = true 则仅周六晚~周日早。 | + +--- + +## 三、操作清单(复制自查) + +- [ ] **BLOCK_SHORT_WHEN_4H_UP** = true +- [ ] **MARKET_SCHEME** = normal 或 bull(若偏多选 bull) +- [ ] **STOP_LOSS_PERCENT** = 0.08~0.10,或 **ATR_STOP_LOSS_MULTIPLIER** = 2.5~3.0 +- [ ] **MIN_SIGNAL_STRENGTH** = 6~8 +- [ ] (可选)**SUNDAY_MAX_OPENS** = 2~3,**SUNDAY_MIN_SIGNAL_STRENGTH** = 8 +- [ ] (可选)**NIGHT_HOURS_NO_OPEN_ENABLED** = true + +改完后在配置页点**保存**;配置会写入数据库/Redis,交易进程下一次读配置即生效,一般**无需重启**(若你部署方式有缓存,可按需重启一次交易服务)。 + +--- + +## 四、想更保守时 + +- **MAX_OPEN_POSITIONS** 调小(如 15 → 10),减少同时持仓。 +- **MAX_DAILY_ENTRIES** 调小(如 15 → 8~10),降低频率。 +- **AUTO_TRADE_ALLOW_4H_NEUTRAL** = **false**:4H 震荡时不再自动开仓,只做趋势更明确的单。 + +--- + +## 五、改完如何验证 + +- 看**交易记录**:新开仓是否变少、空单是否明显减少。 +- 看**日志**:是否有「4H 趋势上涨,禁止开空」「持仓数量已达上限」「今日开仓次数已达上限」等,说明限制在生效。 +- 跑一段时间后再导出一份**币安成交**,对比同样天数的盈亏与胜率。 + +以上均为配置层面的操作,无需改代码;若你希望「只对做空单独提高信号门槛」等更细的规则,可以再在策略里加逻辑(需改代码)。 diff --git a/trading_system/position_manager.py b/trading_system/position_manager.py index 1cb1baa..d10d7a6 100644 --- a/trading_system/position_manager.py +++ b/trading_system/position_manager.py @@ -1143,7 +1143,35 @@ class PositionManager: (p for p in positions if p['symbol'] == symbol), None ) - + + # 缓存未命中时 REST 复核:_get_open_positions 可能来自缓存,若缓存无该 symbol 会误判为无持仓,但币安实际有仓(如对冲模式、多账号缓存隔离不全、未刷新) + if not position: + live_amt = await self._get_live_position_amt(symbol, position_side=None) + if live_amt is not None and abs(live_amt) > 0: + try: + raw = await self.client.client.futures_position_information(symbol=symbol, recvWindow=20000) + for p in (raw or []): + if not isinstance(p, dict): + continue + try: + amt = float(p.get("positionAmt", 0)) + except (TypeError, ValueError): + continue + if abs(amt) <= 0: + continue + position = { + 'symbol': symbol, + 'positionAmt': amt, + 'entryPrice': float(p.get('entryPrice', 0)), + 'positionSide': (p.get('positionSide') or '').strip().upper() or ('LONG' if amt > 0 else 'SHORT'), + } + logger.info( + f"{symbol} [平仓] 缓存未命中该 symbol,已用 REST 拉取到持仓: amt={amt}, positionSide={position['positionSide']}" + ) + break + except Exception as e: + logger.debug(f"{symbol} [平仓] REST 拉取持仓失败: {e}") + if not position: logger.warning(f"{symbol} [平仓] 币安账户中没有持仓,可能已被平仓") # 即使币安没有持仓,也要更新数据库状态 @@ -1213,11 +1241,12 @@ class PositionManager: # 如果更新了数据库,返回成功;否则返回失败 return updated - # 确定平仓方向(与开仓相反) + # 确定平仓方向(与开仓相反);对冲模式下使用 REST 返回的 positionSide(LONG/SHORT),BOTH 或缺失时按 positionAmt 推导 position_amt = position['positionAmt'] side = 'SELL' if position_amt > 0 else 'BUY' quantity = abs(position_amt) - position_side = 'LONG' if position_amt > 0 else 'SHORT' + ps_raw = (position.get('positionSide') or '').strip().upper() + position_side = ps_raw if ps_raw in ('LONG', 'SHORT') else ('LONG' if position_amt > 0 else 'SHORT') # 二次校验:用币安实时持仓数量兜底,避免 reduceOnly 被拒绝(-2022) live_amt = await self._get_live_position_amt(symbol, position_side=position_side)