From c58f020e9e86d36e72b1bbe4c3fc69247c44ecfd Mon Sep 17 00:00:00 2001 From: ddrwode <34234@3来 34> Date: Fri, 6 Feb 2026 14:32:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=89=8D=E6=94=B9=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../四分之一,五分钟,反手条件充足_保守模式.py | 237 +++++++++++++++++- 1 file changed, 231 insertions(+), 6 deletions(-) diff --git a/bitmart/保守模式参数优化/四分之一,五分钟,反手条件充足_保守模式.py b/bitmart/保守模式参数优化/四分之一,五分钟,反手条件充足_保守模式.py index b877420..f1b4967 100644 --- a/bitmart/保守模式参数优化/四分之一,五分钟,反手条件充足_保守模式.py +++ b/bitmart/保守模式参数优化/四分之一,五分钟,反手条件充足_保守模式.py @@ -149,6 +149,35 @@ class BitmartFuturesTransactionConservative: self.auto_optimize_script_path = Path(__file__).with_name("optimize_params.py") self.auto_optimized_this_run = False + # 方案B:智能动态模式 + self.enable_smart_mode = os.getenv("BITMART_SMART_MODE_ENABLED", "1").strip().lower() not in {"0", "false", "off", "no"} + # 波动率分档阈值(单位:百分比) + self.vol_regime_low_threshold_pct = float(os.getenv("BITMART_VOL_REGIME_LOW_PCT", "0.22")) + self.vol_regime_high_threshold_pct = float(os.getenv("BITMART_VOL_REGIME_HIGH_PCT", "0.45")) + self.mid_vol_order_size_multiplier = float(os.getenv("BITMART_MID_VOL_SIZE_MULT", "0.7")) + self.high_vol_order_size_multiplier = float(os.getenv("BITMART_HIGH_VOL_SIZE_MULT", "0.3")) + self.high_vol_pause_trading = os.getenv("BITMART_HIGH_VOL_PAUSE", "1").strip().lower() not in {"0", "false", "off", "no"} + self.mid_vol_stop_loss_multiplier = float(os.getenv("BITMART_MID_VOL_SL_MULT", "0.85")) + self.high_vol_stop_loss_multiplier = float(os.getenv("BITMART_HIGH_VOL_SL_MULT", "0.70")) + # 时间过滤:亚洲时段 + 跳过换线小时 + self.trade_asia_session_only = os.getenv("BITMART_ASIA_SESSION_ONLY", "1").strip().lower() not in {"0", "false", "off", "no"} + self.asia_session_start_hour_utc = int(os.getenv("BITMART_ASIA_START_HOUR_UTC", "1")) + self.asia_session_end_hour_utc = int(os.getenv("BITMART_ASIA_END_HOUR_UTC", "10")) + self.blocked_utc_hours_utc = self._parse_int_set_csv(os.getenv("BITMART_BLOCKED_UTC_HOURS", "0,8,16"), {0, 8, 16}) + # 日内盈亏管理 + self.daily_profit_reduce_size_usd = float(os.getenv("BITMART_DAILY_PROFIT_REDUCE_SIZE_USD", "8")) + self.daily_reduced_size_multiplier = float(os.getenv("BITMART_DAILY_REDUCED_SIZE_MULT", "0.5")) + self.daily_max_loss_usd = float(os.getenv("BITMART_DAILY_MAX_LOSS_USD", "-10")) + self.daily_halt_on_loss = os.getenv("BITMART_DAILY_HALT_ON_LOSS", "1").strip().lower() not in {"0", "false", "off", "no"} + self.daily_balance_refresh_seconds = float(os.getenv("BITMART_DAILY_BALANCE_REFRESH_SECONDS", "15")) + self.daily_stat_day = None + self.daily_start_balance = None + self.daily_current_balance = None + self.daily_pnl_usd = 0.0 + self.daily_halt = False + self.last_daily_balance_refresh_time = 0.0 + self.last_smart_status_log_time = 0.0 + # 启动时尝试读取动态参数(可由优化脚本自动生成) self.load_runtime_params() self.capture_base_risk_params() @@ -203,6 +232,22 @@ class BitmartFuturesTransactionConservative: "dynamic_vol_target_pct", "dynamic_scale_min", "dynamic_scale_max", + "enable_smart_mode", + "vol_regime_low_threshold_pct", + "vol_regime_high_threshold_pct", + "mid_vol_order_size_multiplier", + "high_vol_order_size_multiplier", + "high_vol_pause_trading", + "mid_vol_stop_loss_multiplier", + "high_vol_stop_loss_multiplier", + "trade_asia_session_only", + "asia_session_start_hour_utc", + "asia_session_end_hour_utc", + "blocked_utc_hours_utc", + "daily_profit_reduce_size_usd", + "daily_reduced_size_multiplier", + "daily_max_loss_usd", + "daily_halt_on_loss", } try: @@ -218,8 +263,23 @@ class BitmartFuturesTransactionConservative: continue if key == "leverage": setattr(self, key, str(value)) - elif key in {"dynamic_risk_enabled", "enable_shadow_reverse", "enable_trend_filter", "enable_ai_filter"}: + elif key in { + "dynamic_risk_enabled", + "enable_shadow_reverse", + "enable_trend_filter", + "enable_ai_filter", + "enable_smart_mode", + "high_vol_pause_trading", + "trade_asia_session_only", + "daily_halt_on_loss", + }: setattr(self, key, self._to_bool(value)) + elif key == "blocked_utc_hours_utc": + if isinstance(value, (list, tuple, set)): + parsed = {int(v) for v in value if str(v).strip().isdigit()} + setattr(self, key, {h for h in parsed if 0 <= h <= 23} or {0, 8, 16}) + else: + setattr(self, key, self._parse_int_set_csv(str(value), {0, 8, 16})) else: setattr(self, key, value) @@ -232,7 +292,8 @@ class BitmartFuturesTransactionConservative: f"BreakoutBuf={self.open_breakout_buffer_pct}%, " f"OpenCD={self.open_cooldown_seconds}s, ReverseCD={self.reverse_cooldown_seconds}s, " f"ReverseMove={self.reverse_min_move_pct}%, " - f"TrendFilter={self.enable_trend_filter}, AIFilter={self.enable_ai_filter}" + f"TrendFilter={self.enable_trend_filter}, AIFilter={self.enable_ai_filter}, " + f"SmartMode={self.enable_smart_mode}" ) except Exception as e: logger.error(f"加载动态参数文件失败: {e} | path={params_path}") @@ -470,9 +531,155 @@ class BitmartFuturesTransactionConservative: return value.strip().lower() in {"1", "true", "yes", "y", "on"} return False + def _parse_int_set_csv(self, text, default): + if not text: + return set(default) + result = set() + for item in str(text).split(","): + token = item.strip() + if not token: + continue + try: + value = int(token) + except Exception: + continue + if 0 <= value <= 23: + result.add(value) + return result or set(default) + def _clip(self, value, low, high): return max(low, min(high, value)) + def _normalize_hour(self, hour): + return int(hour) % 24 + + def _utc_hour(self, ts=None): + return int(time.strftime("%H", time.gmtime(ts if ts is not None else time.time()))) + + def _utc_day(self, ts=None): + return time.strftime("%Y-%m-%d", time.gmtime(ts if ts is not None else time.time())) + + def _is_hour_in_range(self, hour, start, end): + hour = self._normalize_hour(hour) + start = self._normalize_hour(start) + end = self._normalize_hour(end) + if start <= end: + return start <= hour <= end + return hour >= start or hour <= end + + def infer_volatility_regime(self): + vol_pct = self.get_recent_volatility_pct_from_cache(20) + if vol_pct is None: + return "unknown", None + + low_th = max(0.0, float(self.vol_regime_low_threshold_pct)) + high_th = max(low_th, float(self.vol_regime_high_threshold_pct)) + if vol_pct >= high_th: + return "high", vol_pct + if vol_pct <= low_th: + return "low", vol_pct + return "mid", vol_pct + + def refresh_daily_pnl_state(self, force=False): + now = time.time() + day = self._utc_day(now) + + if self.daily_stat_day != day: + self.daily_stat_day = day + self.daily_start_balance = None + self.daily_current_balance = None + self.daily_pnl_usd = 0.0 + self.daily_halt = False + self.last_daily_balance_refresh_time = 0.0 + logger.info(f"日内风控重置: day={day}") + + if not force and (now - self.last_daily_balance_refresh_time) < self.daily_balance_refresh_seconds: + return + + balance = self.get_available_balance() + self.last_daily_balance_refresh_time = now + if balance is None: + return + + self.daily_current_balance = float(balance) + if self.daily_start_balance is None: + self.daily_start_balance = float(balance) + logger.info(f"日内风控基准余额: {self.daily_start_balance:.4f} USDT") + + self.daily_pnl_usd = float(self.daily_current_balance - self.daily_start_balance) + if self.daily_halt_on_loss and self.daily_pnl_usd <= float(self.daily_max_loss_usd): + if not self.daily_halt: + logger.warning( + f"触发日内亏损熔断: DailyPnL={self.daily_pnl_usd:.2f} <= {self.daily_max_loss_usd:.2f}" + ) + self.daily_halt = True + + def get_smart_trade_controls(self): + controls = { + "vol_regime": "unknown", + "vol_pct": None, + "size_multiplier": 1.0, + "stop_loss_multiplier": 1.0, + "allow_new_trade": True, + "block_reason": "", + "daily_pnl_usd": self.daily_pnl_usd, + "daily_halt": self.daily_halt, + } + if not self.enable_smart_mode: + return controls + + # 1) 波动率分档 + vol_regime, vol_pct = self.infer_volatility_regime() + controls["vol_regime"] = vol_regime + controls["vol_pct"] = vol_pct + if vol_regime == "mid": + controls["size_multiplier"] *= max(0.05, float(self.mid_vol_order_size_multiplier)) + controls["stop_loss_multiplier"] *= max(0.05, float(self.mid_vol_stop_loss_multiplier)) + elif vol_regime == "high": + controls["size_multiplier"] *= max(0.05, float(self.high_vol_order_size_multiplier)) + controls["stop_loss_multiplier"] *= max(0.05, float(self.high_vol_stop_loss_multiplier)) + if self.high_vol_pause_trading: + controls["allow_new_trade"] = False + controls["block_reason"] = "高波动暂停开新仓" + + # 2) 时间段过滤 + hour = self._utc_hour() + if hour in self.blocked_utc_hours_utc: + controls["allow_new_trade"] = False + controls["block_reason"] = f"UTC {hour}:00 属于换线禁交易时段" + elif self.trade_asia_session_only: + if not self._is_hour_in_range(hour, self.asia_session_start_hour_utc, self.asia_session_end_hour_utc): + controls["allow_new_trade"] = False + controls["block_reason"] = ( + f"非亚洲交易时段(UTC {hour}:00,不在 {self.asia_session_start_hour_utc}-{self.asia_session_end_hour_utc})" + ) + + # 3) 日内盈亏管理 + self.refresh_daily_pnl_state(force=False) + controls["daily_pnl_usd"] = self.daily_pnl_usd + controls["daily_halt"] = self.daily_halt + if self.daily_pnl_usd >= float(self.daily_profit_reduce_size_usd): + controls["size_multiplier"] *= max(0.05, float(self.daily_reduced_size_multiplier)) + if self.daily_halt: + controls["allow_new_trade"] = False + controls["block_reason"] = f"日内亏损达到阈值 {self.daily_max_loss_usd:.2f},暂停开新仓" + + return controls + + def log_smart_trade_controls(self, controls): + now = time.time() + if now - self.last_smart_status_log_time < 30: + return + self.last_smart_status_log_time = now + vol_text = "n/a" if controls["vol_pct"] is None else f"{controls['vol_pct']:.4f}%" + logger.info( + "智能模式状态: " + f"regime={controls['vol_regime']} vol={vol_text} " + f"sizeMult={controls['size_multiplier']:.3f} slMult={controls['stop_loss_multiplier']:.3f} " + f"dailyPnL={controls['daily_pnl_usd']:.2f} allowNew={controls['allow_new_trade']} " + f"reason={controls['block_reason'] or '-'}" + ) + def _ema_series(self, closes, period): period = max(1, int(period)) if not closes: @@ -1339,6 +1546,9 @@ class BitmartFuturesTransactionConservative: logger.error("杠杆设置失败,程序继续运行但可能下单失败") return + # 初始化日内风控状态(方案B) + self.refresh_daily_pnl_state(force=True) + if self.start_price_stream(): logger.success("实时价格流已启动(WebSocket 优先)") else: @@ -1395,14 +1605,20 @@ class BitmartFuturesTransactionConservative: continue logger.debug(f"当前持仓状态: {self.start} (0=无, 1=多, -1=空)") + smart_controls = self.get_smart_trade_controls() + self.log_smart_trade_controls(smart_controls) + effective_stop_loss = self.stop_loss_usd + sl_mult = max(0.05, float(smart_controls.get("stop_loss_multiplier", 1.0))) + if effective_stop_loss < 0: + effective_stop_loss = -abs(float(effective_stop_loss)) * sl_mult # 3.5 止损/止盈/保本锁盈/移动止损 if self.start != 0: pnl_usd = self.get_unrealized_pnl_usd() if pnl_usd is not None: # 固定止损:亏损达到 stop_loss_usd 平仓 - if pnl_usd <= self.stop_loss_usd: - logger.info(f"仓位亏损 {pnl_usd:.2f} 美元 <= 止损 {self.stop_loss_usd} 美元,执行止损平仓") + if pnl_usd <= effective_stop_loss: + logger.info(f"仓位亏损 {pnl_usd:.2f} 美元 <= 止损 {effective_stop_loss:.2f} 美元,执行止损平仓") self.平仓() self.max_unrealized_pnl_seen = None time.sleep(3) @@ -1441,6 +1657,11 @@ class BitmartFuturesTransactionConservative: # 4. 检查信号 signal = self.check_signal(current_price, prev_kline, current_kline) + # 4.5 智能模式拦截:高波动暂停/时间过滤/日内亏损熔断 + if signal and not smart_controls["allow_new_trade"]: + logger.info(f"智能模式阻止交易: {signal[0]} | {smart_controls['block_reason']}") + signal = None + # 5. 反手过滤:冷却时间 + 最小价差 if signal and signal[0].startswith('reverse_'): if not self.can_reverse(current_price, signal[1]): @@ -1455,9 +1676,13 @@ class BitmartFuturesTransactionConservative: # 6. 有信号则执行交易 if signal: - trade_success = self.execute_trade(signal) + dynamic_size = max(1.0, float(self.default_order_size) * float(smart_controls["size_multiplier"])) + dynamic_size = round(dynamic_size, 4) + trade_success = self.execute_trade(signal, size=dynamic_size) if trade_success: - logger.success(f"交易执行完成: {signal[0]}, 当前持仓状态: {self.start}") + logger.success( + f"交易执行完成: {signal[0]}, 下单数量={dynamic_size}, 当前持仓状态: {self.start}" + ) page_start = True else: logger.warning(f"交易执行失败或被阻止: {signal[0]}")