加入一个回测,
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
基于回测策略 bb_backtest_march_2026.py
|
||||
使用框架的API查询 + 浏览器自动化交易
|
||||
"""
|
||||
import json
|
||||
import time
|
||||
import numpy as np
|
||||
from datetime import datetime, timezone
|
||||
@@ -37,7 +38,8 @@ class BBDelayReversalConfig:
|
||||
LEVERAGE = "50"
|
||||
OPEN_TYPE = "isolated" # 逐仓模式
|
||||
MARGIN_PCT = 0.01 # 首次开仓1%
|
||||
|
||||
STOP_LOSS_RATIO = 0.5 # 浮亏达到总保证金50%止损
|
||||
|
||||
# 运行参数
|
||||
POLL_INTERVAL = 5
|
||||
KLINE_STEP = 5
|
||||
@@ -84,15 +86,69 @@ class BBDelayReversalTrader:
|
||||
# 交易控制
|
||||
self.last_trade_time = 0.0
|
||||
self.last_kline_id = None
|
||||
self.last_closed_kline_id = None
|
||||
self.cooldown_seconds = 10 # 交易冷却时间
|
||||
|
||||
|
||||
# 日志
|
||||
self.log_dir = Path(__file__).resolve().parent
|
||||
self.state_file = self.log_dir / "bb_delay_reversal_state.json"
|
||||
logger.add(
|
||||
self.log_dir / "bb_delay_trade_{time:YYYY-MM-DD}.log",
|
||||
rotation="1 day", retention="30 days",
|
||||
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}"
|
||||
)
|
||||
self.load_state()
|
||||
|
||||
def load_state(self):
|
||||
"""加载本地策略状态,便于重启后延续运行。"""
|
||||
if not self.state_file.exists():
|
||||
return
|
||||
|
||||
try:
|
||||
data = json.loads(self.state_file.read_text(encoding="utf-8"))
|
||||
except Exception as e:
|
||||
logger.warning(f"读取本地状态失败,忽略旧状态: {e}")
|
||||
return
|
||||
|
||||
self.position_count = int(data.get("position_count", self.position_count))
|
||||
self.total_margin = float(data.get("total_margin", self.total_margin))
|
||||
self.mid_closed_half = bool(data.get("mid_closed_half", self.mid_closed_half))
|
||||
self.delay_reverse_price = data.get("delay_reverse_price")
|
||||
self.delay_reverse_type = data.get("delay_reverse_type")
|
||||
self.delay_reverse_kline_id = data.get("delay_reverse_kline_id")
|
||||
self.last_kline_id = data.get("last_kline_id")
|
||||
self.last_closed_kline_id = data.get("last_closed_kline_id")
|
||||
|
||||
if self.delay_reverse_price is not None:
|
||||
self.delay_reverse_price = float(self.delay_reverse_price)
|
||||
if self.delay_reverse_kline_id is not None:
|
||||
self.delay_reverse_kline_id = int(self.delay_reverse_kline_id)
|
||||
if self.last_kline_id is not None:
|
||||
self.last_kline_id = int(self.last_kline_id)
|
||||
if self.last_closed_kline_id is not None:
|
||||
self.last_closed_kline_id = int(self.last_closed_kline_id)
|
||||
|
||||
logger.info("已加载本地策略状态")
|
||||
|
||||
def save_state(self):
|
||||
"""保存关键状态,降低重启造成的状态丢失。"""
|
||||
data = {
|
||||
"position_count": self.position_count,
|
||||
"total_margin": self.total_margin,
|
||||
"mid_closed_half": self.mid_closed_half,
|
||||
"delay_reverse_price": self.delay_reverse_price,
|
||||
"delay_reverse_type": self.delay_reverse_type,
|
||||
"delay_reverse_kline_id": self.delay_reverse_kline_id,
|
||||
"last_kline_id": self.last_kline_id,
|
||||
"last_closed_kline_id": self.last_closed_kline_id,
|
||||
}
|
||||
try:
|
||||
self.state_file.write_text(
|
||||
json.dumps(data, ensure_ascii=False, indent=2),
|
||||
encoding="utf-8",
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"写入本地状态失败: {e}")
|
||||
|
||||
# ========== API查询方法 ==========
|
||||
|
||||
@@ -160,6 +216,18 @@ class BBDelayReversalTrader:
|
||||
def get_position_status(self) -> bool:
|
||||
"""查询持仓状态(使用框架方法)"""
|
||||
try:
|
||||
old_state = (
|
||||
self.position,
|
||||
self.position_count,
|
||||
self.entry_price,
|
||||
self.current_amount,
|
||||
self.total_margin,
|
||||
self.mid_closed_half,
|
||||
self.delay_reverse_price,
|
||||
self.delay_reverse_type,
|
||||
self.delay_reverse_kline_id,
|
||||
)
|
||||
old_position = self.position
|
||||
response = self.contractAPI.get_position(contract_symbol=self.cfg.CONTRACT_SYMBOL)[0]
|
||||
if response['code'] == 1000:
|
||||
positions = response['data']
|
||||
@@ -168,12 +236,55 @@ class BBDelayReversalTrader:
|
||||
self.position_count = 0
|
||||
self.entry_price = 0
|
||||
self.current_amount = 0
|
||||
self.total_margin = 0
|
||||
self.mid_closed_half = False
|
||||
self.delay_reverse_price = None
|
||||
self.delay_reverse_type = None
|
||||
self.delay_reverse_kline_id = None
|
||||
if (
|
||||
self.position,
|
||||
self.position_count,
|
||||
self.entry_price,
|
||||
self.current_amount,
|
||||
self.total_margin,
|
||||
self.mid_closed_half,
|
||||
self.delay_reverse_price,
|
||||
self.delay_reverse_type,
|
||||
self.delay_reverse_kline_id,
|
||||
) != old_state:
|
||||
self.save_state()
|
||||
return True
|
||||
pos = positions[0]
|
||||
self.position = 1 if pos['position_type'] == 1 else -1
|
||||
self.entry_price = float(pos['open_avg_price'])
|
||||
self.current_amount = float(pos['current_amount'])
|
||||
|
||||
if old_position not in (0, self.position):
|
||||
logger.warning("检测到持仓方向变化,重置本地策略状态")
|
||||
self.position_count = 1
|
||||
self.mid_closed_half = False
|
||||
self.delay_reverse_price = None
|
||||
self.delay_reverse_type = None
|
||||
self.delay_reverse_kline_id = None
|
||||
elif self.position_count == 0:
|
||||
self.position_count = 1
|
||||
|
||||
if self.total_margin <= 0 and self.current_amount > 0:
|
||||
leverage = float(self.cfg.LEVERAGE)
|
||||
self.total_margin = self.current_amount * self.entry_price / leverage
|
||||
|
||||
if (
|
||||
self.position,
|
||||
self.position_count,
|
||||
self.entry_price,
|
||||
self.current_amount,
|
||||
self.total_margin,
|
||||
self.mid_closed_half,
|
||||
self.delay_reverse_price,
|
||||
self.delay_reverse_type,
|
||||
self.delay_reverse_kline_id,
|
||||
) != old_state:
|
||||
self.save_state()
|
||||
logger.debug(f"持仓: {'多' if self.position > 0 else '空'} | "
|
||||
f"价格={self.entry_price:.2f} | 数量={self.current_amount:.4f}")
|
||||
return True
|
||||
@@ -213,6 +324,17 @@ class BBDelayReversalTrader:
|
||||
upper = mid + self.cfg.BB_STD * std
|
||||
lower = mid - self.cfg.BB_STD * std
|
||||
return mid, upper, lower
|
||||
|
||||
def calc_shifted_bollinger_for_index(self, closed_klines: list, target_index: int):
|
||||
"""计算某根已收盘K线对应的右移一根布林带。"""
|
||||
if target_index < self.cfg.BB_PERIOD:
|
||||
return None
|
||||
|
||||
closes = [
|
||||
k['close']
|
||||
for k in closed_klines[target_index - self.cfg.BB_PERIOD:target_index]
|
||||
]
|
||||
return self.calc_bollinger(closes)
|
||||
|
||||
# ========== 浏览器自动化 ==========
|
||||
|
||||
@@ -346,8 +468,8 @@ class BBDelayReversalTrader:
|
||||
logger.error(f"全平仓确认超时,当前仓位={self.position}")
|
||||
return False
|
||||
|
||||
def open_with_balance_and_confirm(self, direction: str) -> bool:
|
||||
"""按余额比例开仓并确认方向正确"""
|
||||
def open_with_balance_and_confirm(self, direction: str, previous_amount: float = 0.0) -> bool:
|
||||
"""按余额比例开仓/加仓,并确认方向与数量变化正确。"""
|
||||
balance = self.get_balance()
|
||||
if not balance:
|
||||
logger.error("余额获取失败,无法开仓")
|
||||
@@ -372,8 +494,57 @@ class BBDelayReversalTrader:
|
||||
logger.error(f"开仓结果不一致: 期望={expected_pos}, 实际={self.position}")
|
||||
return False
|
||||
|
||||
if previous_amount > 0:
|
||||
min_increase = max(previous_amount * 0.05, 1e-8)
|
||||
if self.current_amount <= previous_amount + min_increase:
|
||||
logger.error(
|
||||
f"开仓后数量未明显增加: 之前={previous_amount:.4f}, 当前={self.current_amount:.4f}"
|
||||
)
|
||||
return False
|
||||
self.total_margin += usdt_amount
|
||||
else:
|
||||
self.total_margin = usdt_amount
|
||||
|
||||
self.save_state()
|
||||
logger.success(f"✓ 开{'多' if direction == 'long' else '空'}成功")
|
||||
return True
|
||||
|
||||
def close_partial_and_confirm(
|
||||
self,
|
||||
ratio: float,
|
||||
previous_amount: float,
|
||||
timeout_seconds: int = 15,
|
||||
poll_seconds: float = 1.0,
|
||||
) -> bool:
|
||||
"""执行部分平仓,并确认仓位数量明显下降。"""
|
||||
if self.position == 0 or previous_amount <= 0:
|
||||
return False
|
||||
|
||||
expected_pos = self.position
|
||||
if not self.browser_close_position(ratio):
|
||||
logger.error("部分平仓指令发送失败")
|
||||
return False
|
||||
|
||||
deadline = time.time() + timeout_seconds
|
||||
while time.time() < deadline:
|
||||
time.sleep(poll_seconds)
|
||||
if not self.get_position_status():
|
||||
continue
|
||||
if self.position != expected_pos:
|
||||
continue
|
||||
|
||||
if self.current_amount <= previous_amount * 0.75:
|
||||
self.total_margin *= (1 - ratio)
|
||||
self.save_state()
|
||||
logger.success(
|
||||
f"✓ 平仓{int(ratio*100)}%确认完成 | 之前={previous_amount:.4f}, 当前={self.current_amount:.4f}"
|
||||
)
|
||||
return True
|
||||
|
||||
logger.error(
|
||||
f"平仓{int(ratio*100)}%确认超时,数量未明显下降 | 之前={previous_amount:.4f}, 当前={self.current_amount:.4f}"
|
||||
)
|
||||
return False
|
||||
|
||||
# ========== 延迟反转逻辑 ==========
|
||||
|
||||
@@ -382,13 +553,34 @@ class BBDelayReversalTrader:
|
||||
self.delay_reverse_type = reverse_type
|
||||
self.delay_reverse_price = trigger_price
|
||||
self.delay_reverse_kline_id = kline_id
|
||||
self.save_state()
|
||||
logger.warning(f"⚠️ 延迟反转触发: {reverse_type} @ {trigger_price:.2f} | K线ID: {kline_id}")
|
||||
|
||||
def clear_delay_reversal(self):
|
||||
"""清除延迟反转状态"""
|
||||
had_state = (
|
||||
self.delay_reverse_price is not None
|
||||
or self.delay_reverse_type is not None
|
||||
or self.delay_reverse_kline_id is not None
|
||||
)
|
||||
self.delay_reverse_price = None
|
||||
self.delay_reverse_type = None
|
||||
self.delay_reverse_kline_id = None
|
||||
if had_state:
|
||||
self.save_state()
|
||||
|
||||
def check_stop_loss(self, high: float, low: float) -> bool:
|
||||
"""检查是否达到总保证金50%的止损阈值。"""
|
||||
if self.position == 0 or self.current_amount <= 0 or self.total_margin <= 0:
|
||||
return False
|
||||
|
||||
stop_price = low if self.position > 0 else high
|
||||
if self.position > 0:
|
||||
unrealized_pnl = self.current_amount * (stop_price - self.entry_price)
|
||||
else:
|
||||
unrealized_pnl = self.current_amount * (self.entry_price - stop_price)
|
||||
|
||||
return unrealized_pnl <= -self.total_margin * self.cfg.STOP_LOSS_RATIO
|
||||
|
||||
def check_delay_reversal(self, current_kline, prev_kline, prev_upper, prev_lower) -> tuple | None:
|
||||
"""
|
||||
@@ -496,9 +688,6 @@ class BBDelayReversalTrader:
|
||||
logger.info(f"初始持仓: {self.position}")
|
||||
|
||||
page_start = True
|
||||
kline_history = [] # 保存历史K线
|
||||
prev_bb_upper = None # 保存上一根K线的上轨
|
||||
prev_bb_lower = None # 保存上一根K线的下轨
|
||||
|
||||
while True:
|
||||
try:
|
||||
@@ -540,7 +729,6 @@ class BBDelayReversalTrader:
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
|
||||
# 使用已收盘K线计算BB
|
||||
closed_klines = klines[:-1]
|
||||
current_kline = klines[-1]
|
||||
|
||||
@@ -557,35 +745,128 @@ class BBDelayReversalTrader:
|
||||
continue
|
||||
|
||||
bb_mid, bb_upper, bb_lower = bb
|
||||
signal_kline = closed_klines[-1]
|
||||
signal_bb = self.calc_shifted_bollinger_for_index(
|
||||
closed_klines,
|
||||
len(closed_klines) - 1,
|
||||
)
|
||||
|
||||
# 当前价优先使用当前5m实时K线close,避免1m收盘价滞后导致观感偏差
|
||||
current_price = float(current_kline['close'])
|
||||
|
||||
cur_high = current_kline['high']
|
||||
cur_low = current_kline['low']
|
||||
kline_id = current_kline['id']
|
||||
|
||||
# 触轨判断
|
||||
touched_upper = cur_high >= bb_upper
|
||||
touched_lower = cur_low <= bb_lower
|
||||
touched_middle = cur_low <= bb_mid <= cur_high
|
||||
|
||||
# 延迟反转状态显示
|
||||
delay_status = ""
|
||||
if self.delay_reverse_price is not None:
|
||||
delay_status = f" | 🔄延迟反转中: {self.delay_reverse_type} @ {self.delay_reverse_price:.2f}"
|
||||
|
||||
signal_status = ""
|
||||
if signal_bb is not None:
|
||||
signal_mid, signal_upper, signal_lower = signal_bb
|
||||
signal_status = (
|
||||
f" | 收盘K H/L={signal_kline['high']:.2f}/{signal_kline['low']:.2f}"
|
||||
f" BB={signal_lower:.2f}/{signal_mid:.2f}/{signal_upper:.2f}"
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"价格={current_price:.2f} | "
|
||||
f"BB: {bb_lower:.2f}/{bb_mid:.2f}/{bb_upper:.2f} | "
|
||||
f"触上={touched_upper} 触下={touched_lower} 触中={touched_middle} | "
|
||||
f"仓位={self.position}{delay_status}"
|
||||
f"仓位={self.position}{delay_status}{signal_status}"
|
||||
)
|
||||
|
||||
# 同步持仓
|
||||
if not self.get_position_status():
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
|
||||
# ===== 收盘K确认逻辑:止损 / 中轨平半 / 回开仓价反手 =====
|
||||
if signal_bb is not None and signal_kline['id'] != self.last_closed_kline_id:
|
||||
signal_mid, _, _ = signal_bb
|
||||
signal_high = signal_kline['high']
|
||||
signal_low = signal_kline['low']
|
||||
signal_ts = datetime.fromtimestamp(signal_kline['id'] / 1000).strftime('%Y-%m-%d %H:%M')
|
||||
closed_processed = True
|
||||
|
||||
if self.check_stop_loss(signal_high, signal_low):
|
||||
logger.warning(f"🛑 收盘K触发止损: {signal_ts}")
|
||||
if self.close_all_and_confirm():
|
||||
self.last_closed_kline_id = signal_kline['id']
|
||||
self.last_kline_id = kline_id
|
||||
self.update_trade_time()
|
||||
self.save_state()
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
closed_processed = False
|
||||
|
||||
elif self.position != 0 and self.delay_reverse_price is None:
|
||||
had_mid_closed_half = self.mid_closed_half
|
||||
|
||||
if self.position > 0:
|
||||
if had_mid_closed_half and signal_low <= self.entry_price:
|
||||
logger.info(f"💰 收盘K回到开仓价 {self.entry_price:.2f},全平并反手开空")
|
||||
if self.close_all_and_confirm() and self.open_with_balance_and_confirm('short'):
|
||||
self.position_count = 1
|
||||
self.mid_closed_half = False
|
||||
self.last_closed_kline_id = signal_kline['id']
|
||||
self.last_kline_id = kline_id
|
||||
self.update_trade_time()
|
||||
self.save_state()
|
||||
logger.success("✓ 收盘K反手完成")
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
closed_processed = False
|
||||
elif not had_mid_closed_half and signal_low <= signal_mid <= signal_high:
|
||||
logger.info(f"📊 收盘K触中轨 {signal_mid:.2f},平50%")
|
||||
previous_amount = self.current_amount
|
||||
if self.close_partial_and_confirm(0.5, previous_amount):
|
||||
self.mid_closed_half = True
|
||||
self.last_closed_kline_id = signal_kline['id']
|
||||
self.last_kline_id = kline_id
|
||||
self.update_trade_time()
|
||||
self.save_state()
|
||||
logger.success("✓ 收盘K平半完成")
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
closed_processed = False
|
||||
|
||||
else:
|
||||
if had_mid_closed_half and signal_high >= self.entry_price:
|
||||
logger.info(f"💰 收盘K回到开仓价 {self.entry_price:.2f},全平并反手开多")
|
||||
if self.close_all_and_confirm() and self.open_with_balance_and_confirm('long'):
|
||||
self.position_count = 1
|
||||
self.mid_closed_half = False
|
||||
self.last_closed_kline_id = signal_kline['id']
|
||||
self.last_kline_id = kline_id
|
||||
self.update_trade_time()
|
||||
self.save_state()
|
||||
logger.success("✓ 收盘K反手完成")
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
closed_processed = False
|
||||
elif not had_mid_closed_half and signal_low <= signal_mid <= signal_high:
|
||||
logger.info(f"📊 收盘K触中轨 {signal_mid:.2f},平50%")
|
||||
previous_amount = self.current_amount
|
||||
if self.close_partial_and_confirm(0.5, previous_amount):
|
||||
self.mid_closed_half = True
|
||||
self.last_closed_kline_id = signal_kline['id']
|
||||
self.last_kline_id = kline_id
|
||||
self.update_trade_time()
|
||||
self.save_state()
|
||||
logger.success("✓ 收盘K平半完成")
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
closed_processed = False
|
||||
|
||||
if not closed_processed:
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
|
||||
self.last_closed_kline_id = signal_kline['id']
|
||||
self.save_state()
|
||||
|
||||
# 避免同一K线重复触发
|
||||
if kline_id == self.last_kline_id:
|
||||
@@ -594,9 +875,11 @@ class BBDelayReversalTrader:
|
||||
|
||||
# ===== 延迟反转确认(优先级最高)=====
|
||||
if self.delay_reverse_price is not None:
|
||||
prev_kline = kline_history[-1] if len(kline_history) > 0 else None
|
||||
prev_kline = signal_kline if signal_bb is not None else None
|
||||
prev_upper = signal_bb[1] if signal_bb is not None else None
|
||||
prev_lower = signal_bb[2] if signal_bb is not None else None
|
||||
reversal = self.check_delay_reversal(
|
||||
current_kline, prev_kline, prev_bb_upper, prev_bb_lower
|
||||
current_kline, prev_kline, prev_upper, prev_lower
|
||||
)
|
||||
|
||||
if reversal:
|
||||
@@ -612,102 +895,42 @@ class BBDelayReversalTrader:
|
||||
self.clear_delay_reversal()
|
||||
self.last_kline_id = kline_id
|
||||
self.update_trade_time()
|
||||
self.save_state()
|
||||
logger.success("✓ 延迟反转完成!")
|
||||
else:
|
||||
logger.error("延迟反转取消:全平仓未确认")
|
||||
|
||||
# 更新历史
|
||||
kline_history.append(current_kline)
|
||||
prev_bb_upper = bb_upper
|
||||
prev_bb_lower = bb_lower
|
||||
continue
|
||||
|
||||
# ===== 中轨平仓(延迟反转期间不执行)=====
|
||||
if self.position != 0 and touched_middle and self.delay_reverse_price is None:
|
||||
if not self.mid_closed_half:
|
||||
logger.info("📊 触中轨,平50%")
|
||||
if not self.can_trade():
|
||||
continue
|
||||
self.browser_close_position(0.5)
|
||||
time.sleep(3)
|
||||
self.get_position_status()
|
||||
self.mid_closed_half = True
|
||||
self.last_kline_id = kline_id
|
||||
self.update_trade_time()
|
||||
logger.success(f"✓ 平半成功")
|
||||
# 更新历史
|
||||
kline_history.append(current_kline)
|
||||
prev_bb_upper = bb_upper
|
||||
prev_bb_lower = bb_lower
|
||||
continue
|
||||
|
||||
# ===== 回到开仓价全平+反手(仅在已平半的情况下)=====
|
||||
if self.position != 0 and self.mid_closed_half and self.delay_reverse_price is None:
|
||||
should_close = False
|
||||
if self.position > 0 and cur_low <= self.entry_price:
|
||||
should_close = True
|
||||
elif self.position < 0 and cur_high >= self.entry_price:
|
||||
should_close = True
|
||||
|
||||
if should_close:
|
||||
logger.info(f"💰 回到开仓价 {self.entry_price:.2f},全平+反手")
|
||||
if not self.can_trade():
|
||||
continue
|
||||
|
||||
old_direction = 'long' if self.position > 0 else 'short'
|
||||
new_direction = 'short' if old_direction == 'long' else 'long'
|
||||
|
||||
if self.close_all_and_confirm():
|
||||
if self.open_with_balance_and_confirm(new_direction):
|
||||
self.position_count = 1
|
||||
self.mid_closed_half = False
|
||||
self.last_kline_id = kline_id
|
||||
self.update_trade_time()
|
||||
logger.success("✓ 反手完成!")
|
||||
else:
|
||||
logger.error("反手取消:全平仓未确认")
|
||||
|
||||
# 更新历史
|
||||
kline_history.append(current_kline)
|
||||
prev_bb_upper = bb_upper
|
||||
prev_bb_lower = bb_lower
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
|
||||
# ===== 开仓(空仓时)=====
|
||||
if self.position == 0:
|
||||
self.clear_delay_reversal()
|
||||
|
||||
balance = self.get_balance()
|
||||
if not balance:
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
|
||||
usdt_amount = round(balance * self.cfg.MARGIN_PCT, 2)
|
||||
if self.delay_reverse_price is not None:
|
||||
self.clear_delay_reversal()
|
||||
|
||||
if touched_upper:
|
||||
logger.info(f"🔴 空仓触上轨,开空 {usdt_amount}U")
|
||||
logger.info("🔴 空仓触上轨,开空")
|
||||
if not self.can_trade():
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
self.browser_open_position('short', usdt_amount)
|
||||
time.sleep(3)
|
||||
self.get_position_status()
|
||||
if self.position == -1:
|
||||
if self.open_with_balance_and_confirm('short'):
|
||||
self.position_count = 1
|
||||
self.mid_closed_half = False
|
||||
self.last_kline_id = kline_id
|
||||
self.update_trade_time()
|
||||
self.save_state()
|
||||
logger.success(f"✓ 开空成功")
|
||||
|
||||
elif touched_lower:
|
||||
logger.info(f"🟢 空仓触下轨,开多 {usdt_amount}U")
|
||||
logger.info("🟢 空仓触下轨,开多")
|
||||
if not self.can_trade():
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
self.browser_open_position('long', usdt_amount)
|
||||
time.sleep(3)
|
||||
self.get_position_status()
|
||||
if self.position == 1:
|
||||
if self.open_with_balance_and_confirm('long'):
|
||||
self.position_count = 1
|
||||
self.mid_closed_half = False
|
||||
self.last_kline_id = kline_id
|
||||
self.update_trade_time()
|
||||
self.save_state()
|
||||
logger.success(f"✓ 开多成功")
|
||||
|
||||
# ===== 延迟反转触发(有持仓且未在延迟反转中)=====
|
||||
@@ -715,51 +938,41 @@ class BBDelayReversalTrader:
|
||||
logger.warning("⚠️ 多仓触上轨,标记延迟反转")
|
||||
self.mark_delay_reversal('long_to_short', bb_upper, kline_id)
|
||||
self.last_kline_id = kline_id
|
||||
self.save_state()
|
||||
|
||||
elif self.position < 0 and touched_lower and self.delay_reverse_price is None:
|
||||
logger.warning("⚠️ 空仓触下轨,标记延迟反转")
|
||||
self.mark_delay_reversal('short_to_long', bb_lower, kline_id)
|
||||
self.last_kline_id = kline_id
|
||||
self.save_state()
|
||||
|
||||
# ===== 加仓(仅首次开仓后,且未在延迟反转中)=====
|
||||
elif self.position_count == 1 and self.delay_reverse_price is None:
|
||||
balance = self.get_balance()
|
||||
if balance:
|
||||
usdt_amount = round(balance * self.cfg.MARGIN_PCT, 2)
|
||||
|
||||
if self.position > 0 and touched_lower:
|
||||
logger.info(f"➕ 多仓触下轨,加仓 {usdt_amount}U")
|
||||
if not self.can_trade():
|
||||
continue
|
||||
self.browser_open_position('long', usdt_amount)
|
||||
time.sleep(3)
|
||||
self.get_position_status()
|
||||
if self.position == 1:
|
||||
self.position_count = 2
|
||||
self.last_kline_id = kline_id
|
||||
self.update_trade_time()
|
||||
logger.success(f"✓ 加仓成功")
|
||||
|
||||
elif self.position < 0 and touched_upper:
|
||||
logger.info(f"➕ 空仓触上轨,加仓 {usdt_amount}U")
|
||||
if not self.can_trade():
|
||||
continue
|
||||
self.browser_open_position('short', usdt_amount)
|
||||
time.sleep(3)
|
||||
self.get_position_status()
|
||||
if self.position == -1:
|
||||
self.position_count = 2
|
||||
self.last_kline_id = kline_id
|
||||
self.update_trade_time()
|
||||
logger.success(f"✓ 加仓成功")
|
||||
|
||||
# 更新K线历史和布林带历史
|
||||
kline_history.append(current_kline)
|
||||
if len(kline_history) > 100:
|
||||
kline_history = kline_history[-100:]
|
||||
|
||||
prev_bb_upper = bb_upper
|
||||
prev_bb_lower = bb_lower
|
||||
previous_amount = self.current_amount
|
||||
|
||||
if self.position > 0 and touched_lower:
|
||||
logger.info("➕ 多仓触下轨,加仓")
|
||||
if not self.can_trade():
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
if self.open_with_balance_and_confirm('long', previous_amount=previous_amount):
|
||||
self.position_count = 2
|
||||
self.last_kline_id = kline_id
|
||||
self.update_trade_time()
|
||||
self.save_state()
|
||||
logger.success(f"✓ 加仓成功")
|
||||
|
||||
elif self.position < 0 and touched_upper:
|
||||
logger.info("➕ 空仓触上轨,加仓")
|
||||
if not self.can_trade():
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
if self.open_with_balance_and_confirm('short', previous_amount=previous_amount):
|
||||
self.position_count = 2
|
||||
self.last_kline_id = kline_id
|
||||
self.update_trade_time()
|
||||
self.save_state()
|
||||
logger.success(f"✓ 加仓成功")
|
||||
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
|
||||
|
||||
106551
bb_sweep_results.csv
106551
bb_sweep_results.csv
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user