浏览代码

feat: 在lighter断线重连后清空order_book_cache,并添加order_book有效性检查

- 当WebSocket连接断开时,清空order_book_cache中的所有数据,防止使用过期数据
- 在trigger_strategy_update中添加order_book有效性检查,只有当order_book有买一卖一价时才调用do_strategy
- 这确保了断线重连后,直到买一卖一价准备好才继续执行策略
skyfffire 17 小时之前
父节点
当前提交
e009b73093
共有 2 个文件被更改,包括 165 次插入4 次删除
  1. 16 4
      src/leadlag/main.py
  2. 149 0
      src/leadlag/test_order_book_validation.py

+ 16 - 4
src/leadlag/main.py

@@ -449,10 +449,16 @@ async def handle_order_book_websocket(config):
         except websockets.exceptions.ConnectionClosed:
             logger.warning("WebSocket连接已关闭,尝试重新连接...")
             lighter_websocket = None
+            # 清空order_book_cache,防止使用过期数据
+            order_book_cache.clear()
+            logger.info("已清空order_book_cache中的所有数据")
             await asyncio.sleep(5)
         except Exception as e:
             logger.error(f"WebSocket连接出错: {str(e)}")
             lighter_websocket = None
+            # 清空order_book_cache,防止使用过期数据
+            order_book_cache.clear()
+            logger.info("已清空order_book_cache中的所有数据")
             await asyncio.sleep(5)
 
 
@@ -461,22 +467,28 @@ async def trigger_strategy_update():
     触发策略更新,将最新的市场数据传递给策略
     """
     global trading_strategy
-    
+
     if trading_strategy is None:
         return
-    
+
     # 遍历所有匹配的交易对,调用策略
     for market_id, market_info in market_id_to_market_info.items():
         if market_id not in order_book_cache:
             continue
-        
+
         symbol = market_info.get('symbol')
         binance_symbol = f"{symbol}USDT"
         if binance_symbol not in binance_data_cache['latest_prices']:
             continue
-        
+
         # 获取OrderBook实例,提取最优买卖价
         order_book_instance = order_book_cache[market_id]
+
+        # 检查order_book是否有效(必须有买一卖一价)
+        if not order_book_instance.is_valid():
+            # logger.debug(f"订单簿数据无效(缺少买一卖一价),跳过策略执行: {symbol}")
+            continue
+
         best_bid = order_book_instance.get_best_bid()
         best_ask = order_book_instance.get_best_ask()
 

+ 149 - 0
src/leadlag/test_order_book_validation.py

@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+测试order_book有效性检查和清空功能
+"""
+
+import sys
+import os
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+
+from main import OrderBook
+from decimal import Decimal
+
+def test_order_book_validity():
+    """测试order_book有效性检查"""
+    print("=" * 60)
+    print("测试1: OrderBook有效性检查")
+    print("=" * 60)
+    
+    # 创建一个OrderBook实例
+    ob = OrderBook(market_index=1)
+    
+    # 测试1.1: 空的order_book应该无效
+    print("\n测试1.1: 空的order_book")
+    is_valid = ob.is_valid()
+    print(f"  is_valid() = {is_valid}")
+    assert not is_valid, "空的order_book应该无效"
+    print("  ✓ 通过:空order_book被正确识别为无效")
+    
+    # 测试1.2: 只有买单的order_book应该无效
+    print("\n测试1.2: 只有买单的order_book")
+    ob.bids[Decimal('100')] = Decimal('10')
+    is_valid = ob.is_valid()
+    print(f"  is_valid() = {is_valid}")
+    assert not is_valid, "只有买单的order_book应该无效"
+    print("  ✓ 通过:只有买单的order_book被正确识别为无效")
+    
+    # 测试1.3: 只有卖单的order_book应该无效
+    print("\n测试1.3: 只有卖单的order_book")
+    ob.bids.clear()
+    ob.asks[Decimal('101')] = Decimal('10')
+    is_valid = ob.is_valid()
+    print(f"  is_valid() = {is_valid}")
+    assert not is_valid, "只有卖单的order_book应该无效"
+    print("  ✓ 通过:只有卖单的order_book被正确识别为无效")
+    
+    # 测试1.4: 有买卖单但买价>=卖价的order_book应该无效
+    print("\n测试1.4: 买价>=卖价的order_book")
+    ob.bids[Decimal('101')] = Decimal('10')
+    ob.asks[Decimal('101')] = Decimal('10')
+    is_valid = ob.is_valid()
+    print(f"  is_valid() = {is_valid}")
+    assert not is_valid, "买价>=卖价的order_book应该无效"
+    print("  ✓ 通过:买价>=卖价的order_book被正确识别为无效")
+    
+    # 测试1.5: 正常的order_book应该有效
+    print("\n测试1.5: 正常的order_book(买价<卖价)")
+    ob.bids.clear()
+    ob.asks.clear()
+    ob.bids[Decimal('100')] = Decimal('10')
+    ob.asks[Decimal('101')] = Decimal('10')
+    is_valid = ob.is_valid()
+    print(f"  is_valid() = {is_valid}")
+    assert is_valid, "正常的order_book应该有效"
+    print("  ✓ 通过:正常的order_book被正确识别为有效")
+    
+    # 测试1.6: 多个档位的order_book应该有效
+    print("\n测试1.6: 多个档位的order_book")
+    ob.bids[Decimal('99')] = Decimal('20')
+    ob.bids[Decimal('98')] = Decimal('30')
+    ob.asks[Decimal('102')] = Decimal('20')
+    ob.asks[Decimal('103')] = Decimal('30')
+    is_valid = ob.is_valid()
+    print(f"  is_valid() = {is_valid}")
+    assert is_valid, "多个档位的order_book应该有效"
+    print("  ✓ 通过:多个档位的order_book被正确识别为有效")
+    
+    print("\n" + "=" * 60)
+    print("所有有效性检查测试通过!✓")
+    print("=" * 60)
+
+
+def test_order_book_update():
+    """测试order_book更新功能"""
+    print("\n" + "=" * 60)
+    print("测试2: OrderBook更新功能")
+    print("=" * 60)
+    
+    ob = OrderBook(market_index=1)
+    
+    # 测试2.1: 初始更新
+    print("\n测试2.1: 初始更新")
+    order_book_data = {
+        'bids': [
+            {'price': '100', 'size': '10'},
+            {'price': '99', 'size': '20'}
+        ],
+        'asks': [
+            {'price': '101', 'size': '10'},
+            {'price': '102', 'size': '20'}
+        ]
+    }
+    ob.update(order_book_data, offset=1)
+    print(f"  更新后的统计信息: {ob.get_stats()}")
+    assert ob.is_valid(), "更新后的order_book应该有效"
+    print("  ✓ 通过:初始更新成功")
+    
+    # 测试2.2: 增量更新(删除档位)
+    print("\n测试2.2: 增量更新(删除档位)")
+    order_book_data = {
+        'bids': [
+            {'price': '99', 'size': '0'}  # 删除99的买单
+        ]
+    }
+    ob.update(order_book_data, offset=2)
+    print(f"  更新后的统计信息: {ob.get_stats()}")
+    assert ob.is_valid(), "删除档位后的order_book应该有效"
+    assert Decimal('99') not in ob.bids, "99的买单应该被删除"
+    print("  ✓ 通过:增量更新成功")
+    
+    # 测试2.3: 清空order_book
+    print("\n测试2.3: 清空order_book")
+    ob.bids.clear()
+    ob.asks.clear()
+    print(f"  清空后的统计信息: {ob.get_stats()}")
+    assert not ob.is_valid(), "清空后的order_book应该无效"
+    print("  ✓ 通过:清空成功")
+    
+    print("\n" + "=" * 60)
+    print("所有更新功能测试通过!✓")
+    print("=" * 60)
+
+
+if __name__ == '__main__':
+    try:
+        test_order_book_validity()
+        test_order_book_update()
+        print("\n" + "=" * 60)
+        print("所有测试通过!✓✓✓")
+        print("=" * 60)
+    except AssertionError as e:
+        print(f"\n✗ 测试失败: {e}")
+        sys.exit(1)
+    except Exception as e:
+        print(f"\n✗ 测试出错: {e}")
+        import traceback
+        traceback.print_exc()
+        sys.exit(1)
+