Browse Source

feat(web_dashboard): 添加多数据库文件支持并增强统计功能

- 新增数据库文件选择功能,支持从多个路径加载数据库
- 改进统计数据显示,包括最新价格和价差信息
- 优化API查询性能,添加时间范围参数
- 删除过时的测试文件和数据库
skyfffire 5 days ago
parent
commit
52f70913ae
4 changed files with 355 additions and 279 deletions
  1. 108 18
      src/leadlag/templates/dashboard.html
  2. 247 135
      src/leadlag/web_dashboard.py
  3. 0 126
      test_database.py
  4. BIN
      test_trading_data.db

+ 108 - 18
src/leadlag/templates/dashboard.html

@@ -225,6 +225,13 @@
     
     <div class="container">
         <div class="controls">
+            <div class="control-group">
+                <label for="databaseSelect">数据库文件</label>
+                <select id="databaseSelect">
+                    <option value="">正在加载...</option>
+                </select>
+            </div>
+            
             <div class="control-group">
                 <label for="timeRange">时间范围</label>
                 <select id="timeRange">
@@ -265,22 +272,81 @@
 
     <script>
         let priceChart = null;
+        let currentDbPath = '';
         
         // 初始化页面
         document.addEventListener('DOMContentLoaded', function() {
-            refreshData();
+            loadDatabases();
             // 每30秒自动刷新
             setInterval(refreshData, 30000);
+            
+            // 添加数据库选择变化事件监听
+            document.getElementById('databaseSelect').addEventListener('change', function() {
+                currentDbPath = this.value;
+                refreshData();
+            });
         });
         
+        // 加载可用数据库列表
+        async function loadDatabases() {
+            try {
+                const response = await fetch('/api/databases');
+                const result = await response.json();
+                
+                if (result.success) {
+                    displayDatabaseOptions(result.data);
+                    // 如果有数据库文件,自动选择第一个(最新的)
+                    if (result.data.length > 0) {
+                        currentDbPath = result.data[0].path;
+                        document.getElementById('databaseSelect').value = currentDbPath;
+                        refreshData();
+                    }
+                } else {
+                    throw new Error(result.error);
+                }
+            } catch (error) {
+                console.error('加载数据库列表失败:', error);
+                showError('数据库列表加载失败: ' + error.message);
+            }
+        }
+        
+        // 显示数据库选项
+        function displayDatabaseOptions(databases) {
+            const select = document.getElementById('databaseSelect');
+            
+            if (databases.length === 0) {
+                select.innerHTML = '<option value="">未找到数据库文件</option>';
+                return;
+            }
+            
+            select.innerHTML = databases.map(db => {
+                const displayName = `${db.name} (${formatFileSize(db.size)}, ${db.modified_time})`;
+                return `<option value="${db.path}">${displayName}</option>`;
+            }).join('');
+        }
+        
+        // 格式化文件大小
+        function formatFileSize(bytes) {
+            if (bytes === 0) return '0 B';
+            const k = 1024;
+            const sizes = ['B', 'KB', 'MB', 'GB'];
+            const i = Math.floor(Math.log(bytes) / Math.log(k));
+            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
+        }
+        
         // 刷新数据
         async function refreshData() {
+            if (!currentDbPath) {
+                console.log('没有选择数据库文件');
+                return;
+            }
+            
             const timeRange = document.getElementById('timeRange').value;
             const symbol = document.getElementById('symbolFilter').value.trim();
             
             try {
                 await Promise.all([
-                    loadStats(),
+                    loadStats(timeRange, symbol),
                     loadPriceData(timeRange, symbol),
                     loadTradingEvents(timeRange, symbol)
                 ]);
@@ -291,9 +357,13 @@
         }
         
         // 加载统计数据
-        async function loadStats() {
+        async function loadStats(hours, symbol) {
             try {
-                const response = await fetch('/api/stats');
+                const params = new URLSearchParams({ hours: hours || 24 });
+                if (symbol) params.append('symbol', symbol);
+                if (currentDbPath) params.append('db_path', currentDbPath);
+                
+                const response = await fetch(`/api/statistics?${params}`);
                 const result = await response.json();
                 
                 if (result.success) {
@@ -310,26 +380,33 @@
         function displayStats(stats) {
             const statsGrid = document.getElementById('statsGrid');
             
+            // 计算交易事件总数
+            const totalEvents = Object.values(stats.trading_events || {}).reduce((sum, count) => sum + count, 0);
+            
             const cards = [
                 {
                     title: '价格数据记录',
-                    value: stats.price_data.total_records.toLocaleString(),
+                    value: (stats.price_data_count || 0).toLocaleString(),
                     icon: '📊'
                 },
                 {
-                    title: '成功交易',
-                    value: stats.success_rate.successful,
-                    icon: ''
+                    title: '交易事件总数',
+                    value: totalEvents.toLocaleString(),
+                    icon: '🔄'
                 },
                 {
-                    title: '成功率',
-                    value: stats.success_rate.rate.toFixed(1) + '%',
-                    icon: '🎯'
+                    title: '最新价格',
+                    value: stats.latest_price ? 
+                        `$${(stats.latest_price.binance_price || 0).toFixed(4)}` : 
+                        '无数据',
+                    icon: '💰'
                 },
                 {
-                    title: '总交易次数',
-                    value: stats.success_rate.total,
-                    icon: '🔄'
+                    title: '价差 (bps)',
+                    value: stats.latest_price ? 
+                        `${(stats.latest_price.spread_bps || 0).toFixed(2)}` : 
+                        '无数据',
+                    icon: '📈'
                 }
             ];
             
@@ -346,6 +423,7 @@
             try {
                 const params = new URLSearchParams({ hours });
                 if (symbol) params.append('symbol', symbol);
+                if (currentDbPath) params.append('db_path', currentDbPath);
                 
                 const response = await fetch(`/api/price_data?${params}`);
                 const result = await response.json();
@@ -453,6 +531,7 @@
             try {
                 const params = new URLSearchParams({ hours });
                 if (symbol) params.append('symbol', symbol);
+                if (currentDbPath) params.append('db_path', currentDbPath);
                 
                 const response = await fetch(`/api/trading_events?${params}`);
                 const result = await response.json();
@@ -487,7 +566,8 @@
                             <th>价格</th>
                             <th>数量</th>
                             <th>方向</th>
-                            <th>状态</th>
+                            <th>策略状态</th>
+                            <th>价差(bps)</th>
                             <th>交易哈希</th>
                         </tr>
                     </thead>
@@ -495,14 +575,15 @@
                         ${events.map(event => `
                             <tr>
                                 <td>${new Date(event.timestamp).toLocaleString()}</td>
-                                <td>${event.symbol}</td>
+                                <td>${event.symbol || '-'}</td>
                                 <td class="${getEventClass(event.event_type)}">${formatEventType(event.event_type)}</td>
                                 <td>${event.price ? event.price.toFixed(6) : '-'}</td>
                                 <td>${event.quantity || '-'}</td>
                                 <td>${event.side || '-'}</td>
-                                <td class="${event.success ? 'event-success' : 'event-failed'}">
-                                    ${event.success ? '✅ 成功' : '❌ 失败'}
+                                <td class="${getStrategyStateClass(event.strategy_state)}">
+                                    ${event.strategy_state || '-'}
                                 </td>
+                                <td>${event.spread_bps ? event.spread_bps.toFixed(2) : '-'}</td>
                                 <td>
                                     ${event.tx_hash ? 
                                         `<a href="#" title="${event.tx_hash}">${event.tx_hash.substring(0, 10)}...</a>` : 
@@ -525,6 +606,15 @@
             return '';
         }
         
+        // 获取策略状态的CSS类
+        function getStrategyStateClass(state) {
+            if (!state) return '';
+            if (state.includes('success') || state.includes('成功')) return 'event-success';
+            if (state.includes('failed') || state.includes('失败') || state.includes('error')) return 'event-failed';
+            if (state.includes('waiting') || state.includes('等待')) return 'event-waiting';
+            return '';
+        }
+        
         // 格式化事件类型
         function formatEventType(eventType) {
             const typeMap = {

+ 247 - 135
src/leadlag/web_dashboard.py

@@ -8,28 +8,89 @@ import sqlite3
 import json
 from datetime import datetime, timedelta
 import os
+import glob
 from typing import Dict, List, Any
 
 class TradingDashboard:
-    def __init__(self, db_path: str = "trading_data.db"):
+    def __init__(self, db_path: str = "trading_data.db", data_dir: str = None):
         self.app = Flask(__name__)
         self.db_path = db_path
+        # 设置数据目录,默认为项目根目录和data目录
+        self.data_dirs = []
+        if data_dir:
+            self.data_dirs.append(data_dir)
+        
+        # 添加默认搜索路径
+        current_dir = os.path.dirname(os.path.abspath(__file__))
+        project_root = os.path.dirname(os.path.dirname(current_dir))
+        self.data_dirs.extend([
+            project_root,  # 项目根目录
+            os.path.join(project_root, 'data'),  # data目录
+            os.path.join(project_root, 'src', 'leadlag')  # leadlag目录
+        ])
+        
         self.setup_routes()
     
+    def get_available_databases(self) -> List[Dict[str, Any]]:
+        """获取所有可用的数据库文件"""
+        databases = []
+        
+        for data_dir in self.data_dirs:
+            if not os.path.exists(data_dir):
+                continue
+                
+            # 搜索数据库文件
+            db_pattern = os.path.join(data_dir, "*.db")
+            db_files = glob.glob(db_pattern)
+            
+            for db_file in db_files:
+                if os.path.getsize(db_file) > 0:  # 只包含非空文件
+                    file_name = os.path.basename(db_file)
+                    file_size = os.path.getsize(db_file)
+                    file_mtime = os.path.getmtime(db_file)
+                    
+                    databases.append({
+                        'path': db_file,
+                        'name': file_name,
+                        'size': file_size,
+                        'modified_time': datetime.fromtimestamp(file_mtime).strftime('%Y-%m-%d %H:%M:%S'),
+                        'modified_timestamp': file_mtime
+                    })
+        
+        # 按修改时间排序,最新的在前面
+        databases.sort(key=lambda x: x['modified_timestamp'], reverse=True)
+        return databases
+    
     def setup_routes(self):
         """设置路由"""
         @self.app.route('/')
         def index():
             return render_template('dashboard.html')
         
+        @self.app.route('/api/databases')
+        def get_databases():
+            """获取可用数据库列表API"""
+            try:
+                databases = self.get_available_databases()
+                return jsonify({
+                    'success': True,
+                    'data': databases
+                })
+            except Exception as e:
+                return jsonify({
+                    'success': False,
+                    'error': str(e)
+                }), 500
+        
         @self.app.route('/api/price_data')
         def get_price_data():
             """获取价格数据API"""
             hours = request.args.get('hours', 24, type=int)
             symbol = request.args.get('symbol', '')
+            db_path = request.args.get('db_path', self.db_path)
             
             try:
-                data = self.get_price_data(hours, symbol)
+                data = self.get_price_data(hours, symbol, db_path)
                 return jsonify({
                     'success': True,
                     'data': data
@@ -45,9 +106,10 @@ class TradingDashboard:
             """获取交易事件API"""
             hours = request.args.get('hours', 24, type=int)
             symbol = request.args.get('symbol', '')
+            db_path = request.args.get('db_path', self.db_path)
             
             try:
-                events = self.get_trading_events(hours, symbol)
+                events = self.get_trading_events(hours, symbol, db_path)
                 return jsonify({
                     'success': True,
                     'data': events
@@ -58,11 +120,15 @@ class TradingDashboard:
                     'error': str(e)
                 }), 500
         
-        @self.app.route('/api/stats')
-        def get_stats():
+        @self.app.route('/api/statistics')
+        def get_statistics():
             """获取统计数据API"""
+            hours = request.args.get('hours', 24, type=int)
+            symbol = request.args.get('symbol', '')
+            db_path = request.args.get('db_path', self.db_path)
+            
             try:
-                stats = self.get_statistics()
+                stats = self.get_statistics(hours, symbol, db_path)
                 return jsonify({
                     'success': True,
                     'data': stats
@@ -73,152 +139,198 @@ class TradingDashboard:
                     'error': str(e)
                 }), 500
     
-    def get_price_data(self, hours: int = 24, symbol: str = '') -> List[Dict[str, Any]]:
+    def get_price_data(self, hours: int = 24, symbol: str = '', db_path: str = None) -> List[Dict[str, Any]]:
         """获取价格数据"""
-        conn = sqlite3.connect(self.db_path)
-        conn.row_factory = sqlite3.Row
-        
-        try:
-            query = """
-                SELECT timestamp, symbol, binance_price, lighter_price, 
-                       lighter_bid, lighter_ask, spread_bps
-                FROM price_data 
-                WHERE timestamp > datetime('now', '-{} hours')
-            """.format(hours)
+        if db_path is None:
+            db_path = self.db_path
             
-            if symbol:
-                query += " AND symbol = ?"
-                cursor = conn.execute(query, (symbol,))
-            else:
-                cursor = conn.execute(query)
+        if not os.path.exists(db_path):
+            return []
             
-            rows = cursor.fetchall()
-            
-            data = []
-            for row in rows:
-                data.append({
-                    'timestamp': row['timestamp'],
-                    'symbol': row['symbol'],
-                    'binance_price': row['binance_price'],
-                    'lighter_price': row['lighter_price'],
-                    'lighter_bid': row['lighter_bid'],
-                    'lighter_ask': row['lighter_ask'],
-                    'spread_bps': row['spread_bps']
-                })
+        conn = sqlite3.connect(db_path)
+        cursor = conn.cursor()
+        
+        # 计算时间范围
+        end_time = datetime.now()
+        start_time = end_time - timedelta(hours=hours)
+        
+        # 构建查询
+        query = """
+        SELECT timestamp, symbol, binance_price, lighter_price, spread_bps
+        FROM price_data 
+        WHERE timestamp >= ? AND timestamp <= ?
+        """
+        params = [start_time.isoformat(), end_time.isoformat()]
+        
+        if symbol:
+            query += " AND symbol = ?"
+            params.append(symbol)
             
-            return data
+        query += " ORDER BY timestamp ASC"
+        
+        cursor.execute(query, params)
+        rows = cursor.fetchall()
+        conn.close()
         
-        finally:
-            conn.close()
+        # 转换为字典格式
+        data = []
+        for row in rows:
+            data.append({
+                'timestamp': row[0],
+                'symbol': row[1],
+                'binance_price': float(row[2]) if row[2] else None,
+                'lighter_price': float(row[3]) if row[3] else None,
+                'spread_bps': float(row[4]) if row[4] else None
+            })
+        
+        return data
     
-    def get_trading_events(self, hours: int = 24, symbol: str = '') -> List[Dict[str, Any]]:
+    def get_trading_events(self, hours: int = 24, symbol: str = '', db_path: str = None) -> List[Dict[str, Any]]:
         """获取交易事件"""
-        conn = sqlite3.connect(self.db_path)
-        conn.row_factory = sqlite3.Row
-        
-        try:
-            query = """
-                SELECT timestamp, symbol, event_type, price, quantity, 
-                       side, success, tx_hash, error_message, metadata
-                FROM trading_events 
-                WHERE timestamp > datetime('now', '-{} hours')
-                ORDER BY timestamp DESC
-            """.format(hours)
-            
-            if symbol:
-                query = query.replace("ORDER BY", "AND symbol = ? ORDER BY")
-                cursor = conn.execute(query, (symbol,))
-            else:
-                cursor = conn.execute(query)
+        if db_path is None:
+            db_path = self.db_path
             
-            rows = cursor.fetchall()
+        if not os.path.exists(db_path):
+            return []
             
-            events = []
-            for row in rows:
-                metadata = json.loads(row['metadata']) if row['metadata'] else {}
-                events.append({
-                    'timestamp': row['timestamp'],
-                    'symbol': row['symbol'],
-                    'event_type': row['event_type'],
-                    'price': row['price'],
-                    'quantity': row['quantity'],
-                    'side': row['side'],
-                    'success': bool(row['success']),
-                    'tx_hash': row['tx_hash'],
-                    'error_message': row['error_message'],
-                    'metadata': metadata
-                })
+        conn = sqlite3.connect(db_path)
+        cursor = conn.cursor()
+        
+        # 计算时间范围
+        end_time = datetime.now()
+        start_time = end_time - timedelta(hours=hours)
+        
+        # 构建查询
+        query = """
+        SELECT timestamp, symbol, event_type, price, quantity, side, 
+               strategy_state, spread_bps, lighter_price, binance_price, 
+               tx_hash, error_message, metadata
+        FROM trading_events 
+        WHERE timestamp >= ? AND timestamp <= ?
+        """
+        params = [start_time.isoformat(), end_time.isoformat()]
+        
+        if symbol:
+            query += " AND symbol = ?"
+            params.append(symbol)
             
-            return events
+        query += " ORDER BY timestamp DESC"
         
-        finally:
-            conn.close()
+        cursor.execute(query, params)
+        rows = cursor.fetchall()
+        conn.close()
+        
+        # 转换为字典格式
+        events = []
+        for row in rows:
+            metadata = {}
+            if row[12]:  # metadata字段
+                try:
+                    metadata = json.loads(row[12])
+                except:
+                    pass
+                    
+            events.append({
+                'timestamp': row[0],
+                'symbol': row[1],
+                'event_type': row[2],
+                'price': float(row[3]) if row[3] else None,
+                'quantity': float(row[4]) if row[4] else None,
+                'side': row[5],
+                'strategy_state': row[6],
+                'spread_bps': float(row[7]) if row[7] else None,
+                'lighter_price': float(row[8]) if row[8] else None,
+                'binance_price': float(row[9]) if row[9] else None,
+                'tx_hash': row[10],
+                'error_message': row[11],
+                'metadata': metadata
+            })
+        
+        return events
     
-    def get_statistics(self) -> Dict[str, Any]:
+    def get_statistics(self, hours: int = 24, symbol: str = '', db_path: str = None) -> Dict[str, Any]:
         """获取统计数据"""
-        conn = sqlite3.connect(self.db_path)
-        conn.row_factory = sqlite3.Row
-        
-        try:
-            # 获取基本统计
-            stats = {}
+        if db_path is None:
+            db_path = self.db_path
             
-            # 价格数据统计
-            cursor = conn.execute("""
-                SELECT COUNT(*) as total_records,
-                       MIN(timestamp) as first_record,
-                       MAX(timestamp) as last_record
-                FROM price_data
-            """)
-            price_stats = cursor.fetchone()
+        if not os.path.exists(db_path):
+            return {}
             
-            # 交易事件统计
-            cursor = conn.execute("""
-                SELECT event_type, COUNT(*) as count
-                FROM trading_events
-                GROUP BY event_type
-            """)
-            event_stats = cursor.fetchall()
-            
-            # 成功率统计
-            cursor = conn.execute("""
-                SELECT 
-                    SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as successful,
-                    COUNT(*) as total
-                FROM trading_events
-                WHERE event_type LIKE '%_success' OR event_type LIKE '%_failed'
-            """)
-            success_stats = cursor.fetchone()
-            
-            stats = {
-                'price_data': {
-                    'total_records': price_stats['total_records'],
-                    'first_record': price_stats['first_record'],
-                    'last_record': price_stats['last_record']
-                },
-                'trading_events': {
-                    event['event_type']: event['count'] 
-                    for event in event_stats
-                },
-                'success_rate': {
-                    'successful': success_stats['successful'] or 0,
-                    'total': success_stats['total'] or 0,
-                    'rate': (success_stats['successful'] / success_stats['total'] * 100) 
-                           if success_stats['total'] > 0 else 0
-                }
+        conn = sqlite3.connect(db_path)
+        cursor = conn.cursor()
+        
+        # 计算时间范围
+        end_time = datetime.now()
+        start_time = end_time - timedelta(hours=hours)
+        
+        stats = {}
+        
+        # 价格数据统计
+        query = "SELECT COUNT(*) FROM price_data WHERE timestamp >= ? AND timestamp <= ?"
+        params = [start_time.isoformat(), end_time.isoformat()]
+        if symbol:
+            query += " AND symbol = ?"
+            params.append(symbol)
+        
+        cursor.execute(query, params)
+        stats['price_data_count'] = cursor.fetchone()[0]
+        
+        # 交易事件统计
+        query = "SELECT event_type, COUNT(*) FROM trading_events WHERE timestamp >= ? AND timestamp <= ?"
+        params = [start_time.isoformat(), end_time.isoformat()]
+        if symbol:
+            query += " AND symbol = ?"
+            params.append(symbol)
+        query += " GROUP BY event_type"
+        
+        cursor.execute(query, params)
+        event_stats = {}
+        for row in cursor.fetchall():
+            event_stats[row[0]] = row[1]
+        
+        stats['trading_events'] = event_stats
+        
+        # 最新价格信息
+        query = """
+        SELECT symbol, binance_price, lighter_price, spread_bps, timestamp
+        FROM price_data 
+        WHERE timestamp >= ? AND timestamp <= ?
+        """
+        params = [start_time.isoformat(), end_time.isoformat()]
+        if symbol:
+            query += " AND symbol = ?"
+            params.append(symbol)
+        query += " ORDER BY timestamp DESC LIMIT 1"
+        
+        cursor.execute(query, params)
+        latest_price = cursor.fetchone()
+        if latest_price:
+            stats['latest_price'] = {
+                'symbol': latest_price[0],
+                'binance_price': float(latest_price[1]) if latest_price[1] else None,
+                'lighter_price': float(latest_price[2]) if latest_price[2] else None,
+                'spread_bps': float(latest_price[3]) if latest_price[3] else None,
+                'timestamp': latest_price[4]
             }
-            
-            return stats
         
-        finally:
-            conn.close()
+        conn.close()
+        return stats
     
     def run(self, host='127.0.0.1', port=5000, debug=True):
         """启动Web服务器"""
-        print(f"启动交易策略监控面板: http://{host}:{port}")
-        self.app.run(host=host, port=port, debug=debug)
-
-if __name__ == '__main__':
-    # 创建并启动dashboard
-    dashboard = TradingDashboard()
-    dashboard.run()
+        print(f"🚀 启动交易策略Web面板...")
+        print(f"📊 访问地址: http://{host}:{port}")
+        print(f"📁 数据库路径: {os.path.abspath(self.db_path)}")
+        
+        # 显示可用的数据库文件
+        databases = self.get_available_databases()
+        if databases:
+            print(f"📋 发现 {len(databases)} 个数据库文件:")
+            for db in databases[:5]:  # 只显示前5个
+                print(f"   - {db['name']} ({db['size']} bytes, {db['modified_time']})")
+            if len(databases) > 5:
+                print(f"   ... 还有 {len(databases) - 5} 个文件")
+        else:
+            print("⚠️  未发现任何数据库文件")
+        
+        self.app.run(host=host, port=port, debug=debug)

+ 0 - 126
test_database.py

@@ -1,126 +0,0 @@
-#!/usr/bin/env python3
-"""
-测试数据库功能
-"""
-
-import sys
-import time
-import random
-from pathlib import Path
-
-# 添加项目路径到Python路径
-project_root = Path(__file__).parent
-sys.path.insert(0, str(project_root / "src"))
-
-from leadlag.database import TradingDatabase
-
-def test_database():
-    """测试数据库功能"""
-    print("🧪 开始测试数据库功能...")
-    
-    # 创建测试数据库
-    db_path = "test_trading_data.db"
-    db = TradingDatabase(db_path)
-    
-    print(f"✅ 数据库创建成功: {db_path}")
-    
-    # 测试价格数据记录
-    print("\n📊 测试价格数据记录...")
-    
-    base_price = 50000.0
-    for i in range(10):
-        # 模拟价格波动
-        binance_price = base_price + random.uniform(-100, 100)
-        lighter_price = binance_price + random.uniform(-5, 5)
-        spread_bps = (lighter_price - binance_price) / binance_price * 10000
-        
-        db.record_price_data(
-            symbol="BTCUSDT",
-            binance_price=binance_price,
-            lighter_price=lighter_price,
-            lighter_bid=lighter_price - 0.5,
-            lighter_ask=lighter_price + 0.5,
-            lighter_bid_size=random.uniform(0.1, 1.0),
-            lighter_ask_size=random.uniform(0.1, 1.0),
-            spread_bps=spread_bps,
-            raw_data={"test": True, "index": i}
-        )
-        
-        print(f"  记录 {i+1}: Binance={binance_price:.2f}, Lighter={lighter_price:.2f}, Spread={spread_bps:.2f}bps")
-        time.sleep(0.1)  # 模拟时间间隔
-    
-    # 测试交易事件记录
-    print("\n🔄 测试交易事件记录...")
-    
-    events = [
-        ("open_long", 49950.0, 0.1, "long", True),
-        ("open_long_success", 49950.0, 0.1, "long", True),
-        ("close_long", 50050.0, 0.1, "long", True),
-        ("close_long_success", 50050.0, 0.1, "long", True),
-        ("open_short", 50100.0, 0.1, "short", False),
-        ("open_short_failed", 50100.0, 0.1, "short", False)
-    ]
-    
-    for event_type, price, quantity, side, success in events:
-        db.record_trading_event(
-            symbol="BTCUSDT",
-            event_type=event_type,
-            price=price,
-            quantity=quantity,
-            side=side,
-            strategy_state="EXECUTING_OPEN" if "open" in event_type else "EXECUTING_CLOSE",
-            spread_bps=random.uniform(-10, 10),
-            lighter_price=price + random.uniform(-1, 1),
-            binance_price=price + random.uniform(-2, 2),
-            success=success,
-            tx_hash="0x123456789abcdef" if success else None,
-            error_message="Test error" if not success else None,
-            metadata={"test": True, "event": event_type}
-        )
-        
-        status = "✅ 成功" if success else "❌ 失败"
-        print(f"  事件: {event_type} - 价格={price:.2f}, 数量={quantity}, {status}")
-    
-    # 测试数据查询
-    print("\n📈 测试数据查询...")
-    
-    # 查询价格数据 (最近1小时)
-    current_time = time.time()
-    one_hour_ago = current_time - 3600
-    
-    price_data = db.get_price_data(start_time=one_hour_ago, limit=100)
-    print(f"  价格数据记录数: {len(price_data)}")
-    
-    # 查询交易事件
-    trading_events = db.get_trading_events(start_time=one_hour_ago, limit=100)
-    print(f"  交易事件记录数: {len(trading_events)}")
-    
-    # 查询统计信息
-    stats = db.get_statistics()
-    print(f"  统计信息: {stats}")
-    
-    print("\n🎉 数据库功能测试完成!")
-    print(f"📁 测试数据库文件: {Path(db_path).absolute()}")
-    
-    return db_path
-
-def main():
-    """主函数"""
-    try:
-        test_db_path = test_database()
-        
-        print("\n" + "="*60)
-        print("🚀 现在可以启动Web Dashboard查看测试数据:")
-        print(f"   python run_dashboard.py")
-        print("   或者修改 run_dashboard.py 中的数据库路径为:")
-        print(f"   {Path(test_db_path).absolute()}")
-        print("="*60)
-        
-    except Exception as e:
-        print(f"❌ 测试失败: {e}")
-        import traceback
-        traceback.print_exc()
-        sys.exit(1)
-
-if __name__ == '__main__':
-    main()

BIN
test_trading_data.db