import time import uuid import datetime from tqdm import tqdm from loguru import logger from bitmart.api_contract import APIContract from bitmart.lib.cloud_exceptions import APIException from 交易.tools import send_dingtalk_message class BitmartFuturesTransaction: def __init__(self): self.api_key = "a0fb7b98464fd9bcce67e7c519d58ec10d0c38a8" self.secret_key = "4eaeba78e77aeaab1c2027f846a276d164f264a44c2c1bb1c5f3be50c8de1ca5" self.memo = "合约交易" self.contract_symbol = "ETHUSDT" self.contractAPI = APIContract(self.api_key, self.secret_key, self.memo, timeout=(5, 15)) self.start = 0 # 持仓状态: -1 空, 0 无, 1 多 self.direction = None self.pbar = tqdm(total=30, desc="等待K线", ncols=80) self.last_kline_time = None self.leverage = "100" # 高杠杆(全仓模式下可开更大仓位) self.open_type = "cross" # 全仓模式(你的“成本开仓”需求) self.risk_percent = 0.01 # 每次开仓使用可用余额的 1% self.open_avg_price = None # 开仓价格 self.current_amount = None # 持仓量 def is_bullish(self, c): return float(c['close']) > float(c['open']) def is_bearish(self, c): return float(c['close']) < float(c['open']) def is_trending(self, klines): """判断是否为单边行情,通过布林带或RSI""" close_prices = [kline['close'] for kline in klines] rsi_value = self.calculate_rsi(close_prices, 14) # 使用14期的RSI if rsi_value > 70 or rsi_value < 30: return True # 单边行情 return False # 震荡行情 def calculate_rsi(self, prices, period=14): """计算RSI指标""" deltas = [prices[i] - prices[i - 1] for i in range(1, len(prices))] gains = [delta if delta > 0 else 0 for delta in deltas] losses = [-delta if delta < 0 else 0 for delta in deltas] avg_gain = sum(gains[:period]) / period avg_loss = sum(losses[:period]) / period rs = avg_gain / avg_loss if avg_loss != 0 else 0 rsi = 100 - (100 / (1 + rs)) return rsi def get_klines(self): """获取最近3根30分钟K线(step=30)""" try: end_time = int(time.time()) # 获取足够多的条目确保有最新3根 response = self.contractAPI.get_kline( contract_symbol=self.contract_symbol, step=30, # 30分钟 start_time=end_time - 3600 * 10, # 取最近10小时 end_time=end_time )[0]["data"] # 每根: [timestamp, open, high, low, close, volume] formatted = [] for k in response: formatted.append({ 'id': int(k["timestamp"]), 'open': float(k["open_price"]), 'high': float(k["high_price"]), 'low': float(k["low_price"]), 'close': float(k["close_price"]) }) formatted.sort(key=lambda x: x['id']) return formatted[-3:] # 最近3根: kline_1 (最老), kline_2, kline_3 (最新) except Exception as e: logger.error(f"获取K线异常: {e}") self.ding(error=True, msg="获取K线异常") return None def get_current_price(self): """获取当前最新价格,用于计算张数""" try: end_time = int(time.time()) response = self.contractAPI.get_kline( contract_symbol=self.contract_symbol, step=1, # 1分钟 start_time=end_time - 3600 * 10, # 取最近10小时 end_time=end_time )[0] if response['code'] == 1000: return float(response['data'][0]["close_price"]) return None except Exception as e: logger.error(f"获取价格异常: {e}") return None def get_available_balance(self): """获取合约账户可用USDT余额""" try: response = self.contractAPI.get_assets_detail()[0] if response['code'] == 1000: data = response['data'] if isinstance(data, dict): return float(data.get('available_balance', 0)) elif isinstance(data, list): for asset in data: if asset.get('currency') == 'USDT': return float(asset.get('available_balance', 0)) return None except Exception as e: logger.error(f"余额查询异常: {e}") return None def get_position_status(self): """获取当前持仓方向""" try: response = self.contractAPI.get_position(contract_symbol=self.contract_symbol)[0] if response['code'] == 1000: positions = response['data'] if not positions: self.start = 0 return True self.start = 1 if positions[0]['position_type'] == 1 else -1 self.open_avg_price = positions[0]['open_avg_price'] self.current_amount = positions[0]['current_amount'] self.position_cross = positions[0]["position_cross"] return True else: return False except Exception as e: logger.error(f"持仓查询异常: {e}") return False def calculate_size(self): """计算开仓张数:使用可用余额的1%作为保证金""" balance = self.get_available_balance() self.balance = balance if not balance or balance < 10: logger.warning("余额不足,无法开仓") return 0 price = self.get_current_price() if not price: price = 3000 # 保守估计,避免size过大 leverage = int(self.leverage) margin = balance * self.risk_percent # 使用1%余额 # ETHUSDT 1张 ≈ 0.001 ETH size = int((margin * leverage) / (price * 0.001)) size = max(1, size) logger.info(f"余额 {balance:.2f} USDT → 使用 {margin:.2f} USDT (1%) → 开仓 {size} 张 (价格≈{price})") return size def place_market_order(self, side: int, size: int): if size <= 0: return False client_order_id = f"auto_{int(time.time())}_{uuid.uuid4().hex[:8]}" try: response = self.contractAPI.post_submit_order( contract_symbol=self.contract_symbol, client_order_id=client_order_id, side=side, mode=1, type='market', leverage=self.leverage, open_type=self.open_type, size=size )[0] if response['code'] == 1000: logger.success( f"下单成功: {'开多' if side in [1] else '开空' if side in [4] else '平多' if side in [3] else '平空'} {size}张") return True else: logger.error(f"下单失败: {response}") return False except APIException as e: logger.error(f"API下单异常: {e}") return False def check_signal(self, prev, curr): """简化英戈尔夫形态""" if self.is_bullish(curr) and self.is_bearish(prev) and float(curr['close']) >= float(prev['open']): return "long" if self.is_bearish(curr) and self.is_bullish(prev) and float(curr['close']) <= float(prev['open']): return "short" return None def execute_trade(self): """执行交易逻辑,根据市场状态切换策略""" klines = self.get_klines() if not klines or len(klines) < 3: return if self.is_trending(klines): # 单边行情 self.direction = self.check_signal(klines[1], klines[2]) if self.direction: logger.success(f"检测到{self.direction}信号,准备开仓(用余额1%)") self.execute_trade() # 执行趋势跟随交易 else: # 震荡行情 self.execute_grid_trade() # 执行网格交易策略 def execute_grid_trade(self): """网格交易策略""" logger.info("开始网格交易") # 获取当前价格 current_price = self.get_current_price() if not current_price: logger.error("无法获取当前价格,网格交易无法执行") return # 假设的网格区间(可以根据需要调整) grid_step = 10 # 每次10USDT为一个网格 grid_size = 1 # 每次开仓数量,单位ETH # 计算上网格和下网格价格 lower_price = current_price - grid_step # 下网格价格 upper_price = current_price + grid_step # 上网格价格 # 生成买卖网格订单 try: # 设置买单 buy_order = self.place_market_order(side=1, size=grid_size) # 开多 if buy_order: logger.info(f"已成功设置买单,买入价格:{lower_price},数量:{grid_size} ETH") # 设置卖单 sell_order = self.place_market_order(side=4, size=grid_size) # 开空 if sell_order: logger.info(f"已成功设置卖单,卖出价格:{upper_price},数量:{grid_size} ETH") except Exception as e: logger.error(f"网格交易下单失败: {e}") def set_leverage(self): """程序启动时设置全仓 + 高杠杆""" try: response = self.contractAPI.post_submit_leverage( contract_symbol=self.contract_symbol, leverage=self.leverage, open_type=self.open_type )[0] if response['code'] == 1000: logger.success(f"全仓模式 + {self.leverage}x 杠杆设置成功") return True else: logger.error(f"杠杆设置失败: {response}") return False except Exception as e: logger.error(f"设置杠杆异常: {e}") return False def action(self): # 启动时设置全仓高杠杆 if not self.set_leverage(): logger.error("杠杆设置失败,程序继续运行但可能下单失败") return while True: current_minute = datetime.datetime.now().minute if current_minute < 30: self.pbar.n = current_minute else: self.pbar.n = current_minute - 30 self.pbar.refresh() self.execute_trade() time.sleep(2) # 高频交易,减少等待时间 if __name__ == '__main__': BitmartFuturesTransaction().action()