浏览代码

feat(strategy): 实现交易策略状态机逻辑

添加交易策略状态机的完整实现,包括:
- 空闲监控状态下的价差检测和开仓逻辑
- 执行交易状态下的持仓检查
- 等待收敛状态下的平仓条件判断
- 平仓状态下的持仓验证
新增相关字段用于跟踪交易状态和方向

demo
skyfffire 1 周之前
父节点
当前提交
8611fa9a8f
共有 1 个文件被更改,包括 186 次插入12 次删除
  1. 186 12
      src/record/strategy.py

+ 186 - 12
src/record/strategy.py

@@ -41,10 +41,13 @@ class TradingStrategy:
         """初始化策略"""
         self.state = StrategyState.WAITING_INIT
         self.current_position = None    # 当前持仓信息
-        self.entry_price_bps = 5        # 入场时的价差
+        self.entry_price_bps = 5        # 入场时的价差(单位:bps)
         self.target_symbol = "DOGE"     # 目标交易对
+        self.trade_quantity = 1         # 交易数量(买卖数量)
         self.account_info = None        # 存储账户信息
         self.last_account_update_time = 0  # 上次更新账户信息的时间戳
+        self.last_trade_time = 0        # 上次交易时间戳(开仓或平仓)
+        self.position_side = None       # 持仓方向:'long' 或 'short'
 
         self.account_index = 318163
         self.api_key_index = 0
@@ -153,23 +156,194 @@ class TradingStrategy:
     
     async def _handle_idle_monitoring(self, market_data):
         """处理空闲监听状态 - 监控价差"""
-        # TODO: 实现价差监控逻辑
-        pass
+        symbol = market_data.get('symbol')
+        if symbol != self.target_symbol:
+            return
+            
+        binance_price = market_data.get('binance_price')
+        lighter_price = market_data.get('lighter_price')
+        orderbook = market_data.get('orderbook')
+        
+        if not binance_price or not lighter_price or not orderbook:
+            return
+        
+        # 计算价差(单位:bps)
+        binance_price_float = float(binance_price) if isinstance(binance_price, str) else binance_price
+        lighter_price_float = float(lighter_price) if isinstance(lighter_price, str) else lighter_price
+        price_diff_bps = (lighter_price_float - binance_price_float) / binance_price_float * 10000
+        
+        # 检查是否触发开仓条件
+        if price_diff_bps > self.entry_price_bps:
+            # 做空:价差过大,lighter价格高于binance,卖出lighter
+            logger.info(f"触发做空条件:价差={price_diff_bps:.2f}bps > {self.entry_price_bps}bps")
+            await self._open_position(orderbook, binance_price_float, is_ask=True, side='short')
+        elif price_diff_bps < -self.entry_price_bps:
+            # 做多:价差过小(负值),lighter价格低于binance,买入lighter
+            logger.info(f"触发做多条件:价差={price_diff_bps:.2f}bps < -{self.entry_price_bps}bps")
+            await self._open_position(orderbook, binance_price_float, is_ask=False, side='long')
+    
+    async def _open_position(self, orderbook, price, is_ask, side):
+        """开仓"""
+        logger.info(f"开始开仓:方向={'做空' if side == 'short' else '做多'},数量={self.trade_quantity},价格={price}")
+        
+        tx_hash, error = await self.create_order_and_send_tx(
+            orderbook=orderbook,
+            quantity=self.trade_quantity,
+            price=price,
+            is_ask=is_ask,
+            reduce_only=False
+        )
+        
+        if error:
+            logger.error(f"开仓失败: {error}")
+            return
+        
+        # 记录开仓时间和持仓方向
+        self.last_trade_time = time.time()
+        self.position_side = side
+        
+        # 转换状态到执行交易
+        self.state = StrategyState.EXECUTING_TRADE
+        logger.info(f"状态转换: IDLE_MONITORING -> EXECUTING_TRADE,交易哈希={tx_hash}")
+        logger.info(f"等待1秒后检查持仓...")
     
     async def _handle_executing_trade(self, market_data):
-        """处理执行交易状态"""
-        # TODO: 实现交易执行逻辑
-        pass
+        """处理执行交易状态 - 等待1秒后检查持仓"""
+        # 检查是否已经等待了至少1秒
+        if time.time() - self.last_trade_time < 1.0:
+            return
+        
+        # 检查持仓
+        symbol = market_data.get('symbol')
+        if symbol != self.target_symbol:
+            return
+        
+        if not self.account_info or not self.account_info.accounts:
+            logger.warning("账户信息不可用,无法检查持仓")
+            return
+        
+        # 查找目标交易对的持仓
+        position = None
+        for pos in self.account_info.accounts[0].positions:
+            if pos.symbol == self.target_symbol:
+                position = pos
+                break
+        
+        if position and int(position.position) != 0:
+            # 有持仓,转换到等待价差回归状态
+            self.current_position = position
+            self.state = StrategyState.WAITING_CONVERGENCE
+            logger.info(f"检测到持仓:方向={'做多' if position.sign == 1 else '做空'},数量={position.position}")
+            logger.info(f"状态转换: EXECUTING_TRADE -> WAITING_CONVERGENCE")
+        else:
+            # 没有持仓,回到空闲状态
+            logger.warning(f"开仓后未检测到持仓,回到空闲状态")
+            self.state = StrategyState.IDLE_MONITORING
+            self.position_side = None
+            logger.info(f"状态转换: EXECUTING_TRADE -> IDLE_MONITORING")
     
     async def _handle_waiting_convergence(self, market_data):
-        """处理等待收敛状态"""
-        # TODO: 实现等待价差收敛逻辑
-        pass
+        """处理等待收敛状态 - 等待价差回归到0轴"""
+        symbol = market_data.get('symbol')
+        if symbol != self.target_symbol:
+            return
+        
+        binance_price = market_data.get('binance_price')
+        lighter_price = market_data.get('lighter_price')
+        orderbook = market_data.get('orderbook')
+        
+        if not binance_price or not lighter_price or not orderbook:
+            return
+        
+        # 计算价差(单位:bps)
+        binance_price_float = float(binance_price) if isinstance(binance_price, str) else binance_price
+        lighter_price_float = float(lighter_price) if isinstance(lighter_price, str) else lighter_price
+        price_diff_bps = (lighter_price_float - binance_price_float) / binance_price_float * 10000
+        
+        # 检查是否触发平仓条件
+        should_close = False
+        if self.position_side == 'long':
+            # 做多:价差需要往上回归(从负值回到0或正值)
+            if price_diff_bps >= 0:
+                should_close = True
+                logger.info(f"做多平仓条件触发:价差={price_diff_bps:.2f}bps >= 0")
+        elif self.position_side == 'short':
+            # 做空:价差需要往下回归(从正值回到0或负值)
+            if price_diff_bps <= 0:
+                should_close = True
+                logger.info(f"做空平仓条件触发:价差={price_diff_bps:.2f}bps <= 0")
+        
+        if should_close:
+            await self._close_position(orderbook, binance_price_float, lighter_price_float)
+    
+    async def _close_position(self, orderbook, binance_price, lighter_price):
+        """平仓"""
+        # 确定平仓价格:使用不利方向的价格
+        if self.position_side == 'short':
+            # 做空平仓(买入):取两者较高的价格
+            close_price = max(binance_price, lighter_price)
+            is_ask = False  # 买入
+        else:  # long
+            # 做多平仓(卖出):取两者较低的价格
+            close_price = min(binance_price, lighter_price)
+            is_ask = True  # 卖出
+        
+        logger.info(f"开始平仓:方向={'做空' if self.position_side == 'short' else '做多'},价格={close_price}")
+        
+        tx_hash, error = await self.create_order_and_send_tx(
+            orderbook=orderbook,
+            quantity=self.trade_quantity,
+            price=close_price,
+            is_ask=is_ask,
+            reduce_only=True
+        )
+        
+        if error:
+            logger.error(f"平仓失败: {error}")
+            return
+        
+        # 记录平仓时间
+        self.last_trade_time = time.time()
+        
+        # 转换状态到平仓中
+        self.state = StrategyState.CLOSING_POSITION
+        logger.info(f"状态转换: WAITING_CONVERGENCE -> CLOSING_POSITION,交易哈希={tx_hash}")
+        logger.info(f"等待1秒后检查平仓是否生效...")
     
     async def _handle_closing_position(self, market_data):
-        """处理平仓状态"""
-        # TODO: 实现平仓逻辑
-        pass
+        """处理平仓中状态 - 等待1秒后检查持仓是否为0"""
+        # 检查是否已经等待了至少1秒
+        if time.time() - self.last_trade_time < 1.0:
+            return
+        
+        # 检查持仓
+        symbol = market_data.get('symbol')
+        if symbol != self.target_symbol:
+            return
+        
+        if not self.account_info or not self.account_info.accounts:
+            logger.warning("账户信息不可用,无法检查平仓状态")
+            return
+        
+        # 查找目标交易对的持仓
+        position = None
+        for pos in self.account_info.accounts[0].positions:
+            if pos.symbol == self.target_symbol:
+                position = pos
+                break
+        
+        if not position or int(position.position) == 0:
+            # 平仓成功,回到空闲状态
+            logger.info(f"平仓成功,当前持仓为0")
+            self.state = StrategyState.IDLE_MONITORING
+            self.position_side = None
+            self.current_position = None
+            logger.info(f"状态转换: CLOSING_POSITION -> IDLE_MONITORING")
+        else:
+            # 平仓未生效,回到执行交易状态重新检查
+            logger.warning(f"平仓未生效,当前持仓={position.position},重新检查持仓状态")
+            self.state = StrategyState.EXECUTING_TRADE
+            logger.info(f"状态转换: CLOSING_POSITION -> EXECUTING_TRADE")
     
     async def _handle_position_closed(self):
         """处理平仓完成状态"""