123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- #!/usr/bin/env tsx
- /**
- * 日志分析工具 - 用于快速复盘和检查
- *
- * 使用方法:
- * tsx scripts/analyze-logs.ts [date]
- * 例如: tsx scripts/analyze-logs.ts 2025-10-02
- *
- * 功能:
- * 1. 交易概览(总订单数、成功率、失败原因)
- * 2. 账户状态变化(余额、仓位)
- * 3. Delta 历史记录
- * 4. 保证金警告历史
- * 5. 性能指标(周期耗时)
- */
- import { readFileSync, existsSync, readdirSync } from 'fs';
- import { join } from 'path';
- interface LogEntry {
- timestamp: string;
- level: string;
- message: string;
- [key: string]: any;
- }
- class LogAnalyzer {
- private logs: LogEntry[] = [];
- private auditLogs: LogEntry[] = [];
- constructor(private date: string) {}
- public async analyze(): Promise<void> {
- console.log(`\n╔════════════════════════════════════════════════╗`);
- console.log(`║ 日志分析报告 - ${this.date} `);
- console.log(`╚════════════════════════════════════════════════╝\n`);
- // 加载日志文件
- this.loadLogs();
- if (this.logs.length === 0 && this.auditLogs.length === 0) {
- console.log(`❌ 未找到 ${this.date} 的日志文件`);
- return;
- }
- // 各项分析
- this.analyzeOverview();
- this.analyzeOrders();
- this.analyzeBalance();
- this.analyzePositions();
- this.analyzeDelta();
- this.analyzeMarginWarnings();
- this.analyzePerformance();
- this.analyzeErrors();
- }
- private loadLogs(): void {
- const logsDir = 'logs';
- const auditDir = 'logs/audit';
- // 主日志
- const mainLogFile = join(logsDir, `trading-${this.date}.log`);
- if (existsSync(mainLogFile)) {
- const content = readFileSync(mainLogFile, 'utf-8');
- this.logs = content
- .split('\n')
- .filter(line => line.trim())
- .map(line => {
- try {
- return JSON.parse(line);
- } catch {
- return null;
- }
- })
- .filter(Boolean) as LogEntry[];
- console.log(`✅ 加载主日志: ${this.logs.length} 条`);
- }
- // 审计日志
- const auditLogFile = join(auditDir, `audit-${this.date}.log`);
- if (existsSync(auditLogFile)) {
- const content = readFileSync(auditLogFile, 'utf-8');
- this.auditLogs = content
- .split('\n')
- .filter(line => line.trim())
- .map(line => {
- try {
- return JSON.parse(line);
- } catch {
- return null;
- }
- })
- .filter(Boolean) as LogEntry[];
- console.log(`✅ 加载审计日志: ${this.auditLogs.length} 条\n`);
- }
- }
- private analyzeOverview(): void {
- console.log(`\n📊 === 交易概览 ===`);
- const sessionStarts = this.auditLogs.filter(l => l.message === 'SESSION_START');
- const sessionEnds = this.auditLogs.filter(l => l.message === 'SESSION_END');
- const cycleStarts = this.auditLogs.filter(l => l.message === 'CYCLE_START');
- const cycleEnds = this.auditLogs.filter(l => l.message === 'CYCLE_END');
- console.log(` 会话数: ${sessionStarts.length}`);
- console.log(` 交易周期数: ${cycleStarts.length}`);
- console.log(` 完成周期数: ${cycleEnds.length}`);
- if (sessionStarts.length > 0) {
- const firstSession = sessionStarts[0];
- console.log(` 首次启动: ${firstSession.timestamp}`);
- }
- if (sessionEnds.length > 0) {
- const lastSession = sessionEnds[sessionEnds.length - 1];
- console.log(` 最后停止: ${lastSession.timestamp}`);
- if (lastSession.totalCycles) {
- console.log(` 总周期数: ${lastSession.totalCycles}`);
- }
- }
- }
- private analyzeOrders(): void {
- console.log(`\n📋 === 订单分析 ===`);
- const orderSubmits = this.auditLogs.filter(l => l.message === 'ORDER_SUBMIT');
- const orderSuccess = this.auditLogs.filter(l => l.message === 'ORDER_SUCCESS');
- const orderFailed = this.auditLogs.filter(l => l.message === 'ORDER_FAILED');
- const orderCancelled = this.auditLogs.filter(l => l.message === 'ORDER_CANCELLED');
- const orderFilled = this.auditLogs.filter(l => l.message === 'ORDER_FILLED');
- console.log(` 提交订单: ${orderSubmits.length}`);
- console.log(` 成功订单: ${orderSuccess.length}`);
- console.log(` 失败订单: ${orderFailed.length}`);
- console.log(` 取消订单: ${orderCancelled.length}`);
- console.log(` 成交订单: ${orderFilled.length}`);
- if (orderSubmits.length > 0) {
- const successRate = ((orderSuccess.length / orderSubmits.length) * 100).toFixed(2);
- console.log(` 成功率: ${successRate}%`);
- }
- // 按账户分组
- const ordersByAccount = this.groupBy(orderSubmits, 'accountId');
- console.log(`\n 按账户分布:`);
- for (const [accountId, orders] of Object.entries(ordersByAccount)) {
- console.log(` ${accountId}: ${orders.length} 单`);
- }
- // 失败原因分析
- if (orderFailed.length > 0) {
- console.log(`\n ❌ 失败原因分析:`);
- const errorGroups = this.groupBy(orderFailed, 'error');
- for (const [error, orders] of Object.entries(errorGroups)) {
- console.log(` ${error}: ${orders.length} 次`);
- }
- }
- // 取消原因分析
- if (orderCancelled.length > 0) {
- console.log(`\n 🚫 取消原因分析:`);
- const reasonGroups = this.groupBy(orderCancelled, 'reason');
- for (const [reason, orders] of Object.entries(reasonGroups)) {
- console.log(` ${reason || '超时自动取消'}: ${orders.length} 次`);
- }
- }
- }
- private analyzeBalance(): void {
- console.log(`\n💰 === 账户余额变化 ===`);
- const balanceUpdates = this.auditLogs.filter(l => l.message === 'BALANCE_UPDATE');
- if (balanceUpdates.length === 0) {
- console.log(` 无余额更新记录`);
- return;
- }
- const byAccount = this.groupBy(balanceUpdates, 'accountId');
- for (const [accountId, updates] of Object.entries(byAccount)) {
- if (updates.length === 0) continue;
- const first = updates[0];
- const last = updates[updates.length - 1];
- console.log(`\n 账户: ${accountId}`);
- console.log(` 起始余额: ${first.total?.toFixed(2) || 'N/A'} (可用: ${first.available?.toFixed(2) || 'N/A'})`);
- console.log(` 结束余额: ${last.total?.toFixed(2) || 'N/A'} (可用: ${last.available?.toFixed(2) || 'N/A'})`);
- if (first.total && last.total) {
- const change = last.total - first.total;
- const changePercent = ((change / first.total) * 100).toFixed(2);
- const changeStr = change >= 0 ? `+${change.toFixed(2)}` : change.toFixed(2);
- console.log(` 变化: ${changeStr} (${changePercent}%)`);
- }
- console.log(` 更新次数: ${updates.length}`);
- }
- }
- private analyzePositions(): void {
- console.log(`\n📈 === 仓位变化 ===`);
- const positionUpdates = this.auditLogs.filter(l => l.message === 'POSITION_UPDATE');
- if (positionUpdates.length === 0) {
- console.log(` 无仓位更新记录`);
- return;
- }
- const byAccount = this.groupBy(positionUpdates, 'accountId');
- for (const [accountId, updates] of Object.entries(byAccount)) {
- if (updates.length === 0) continue;
- const last = updates[updates.length - 1];
- console.log(`\n 账户: ${accountId}`);
- console.log(` 交易对: ${last.symbol}`);
- console.log(` 仓位: ${last.size}`);
- console.log(` 入场价: ${last.entryPrice?.toFixed(2) || 'N/A'}`);
- console.log(` 标记价: ${last.markPrice?.toFixed(2) || 'N/A'}`);
- console.log(` 盈亏: ${last.pnl?.toFixed(2) || 'N/A'}`);
- console.log(` 更新次数: ${updates.length}`);
- }
- }
- private analyzeDelta(): void {
- console.log(`\n⚖️ === Delta 分析 ===`);
- const deltaAssessments = this.auditLogs.filter(l => l.message === 'DELTA_ASSESSMENT');
- if (deltaAssessments.length === 0) {
- console.log(` 无 Delta 评估记录`);
- return;
- }
- console.log(` 评估次数: ${deltaAssessments.length}`);
- // 统计超过容忍度的次数
- const exceedCount = deltaAssessments.filter(d => d.exceedsTolerance).length;
- console.log(` 超过容忍度: ${exceedCount} 次`);
- // Delta 范围
- const deltas = deltaAssessments.map(d => d.weightedDelta).filter(Boolean);
- if (deltas.length > 0) {
- const minDelta = Math.min(...deltas);
- const maxDelta = Math.max(...deltas);
- const avgDelta = deltas.reduce((a, b) => a + b, 0) / deltas.length;
- console.log(` Delta 范围: [${minDelta.toFixed(6)}, ${maxDelta.toFixed(6)}]`);
- console.log(` Delta 平均: ${avgDelta.toFixed(6)}`);
- }
- // 最近的 Delta 评估
- const recent = deltaAssessments.slice(-5);
- console.log(`\n 最近 5 次评估:`);
- recent.forEach((d, i) => {
- const exceed = d.exceedsTolerance ? '⚠️ ' : '✅';
- console.log(` ${exceed} ${d.timestamp}: Delta=${d.weightedDelta?.toFixed(6)}, ${d.recommendation}`);
- });
- }
- private analyzeMarginWarnings(): void {
- console.log(`\n🚨 === 保证金警告 ===`);
- const warnings = this.auditLogs.filter(l => l.message === 'MARGIN_WARNING');
- const reduceSignals = this.auditLogs.filter(l => l.message === 'REDUCE_SIGNAL');
- if (warnings.length === 0 && reduceSignals.length === 0) {
- console.log(` ✅ 无保证金警告`);
- return;
- }
- if (warnings.length > 0) {
- console.log(` 警告次数: ${warnings.length}`);
- const byLevel = this.groupBy(warnings, 'level');
- for (const [level, items] of Object.entries(byLevel)) {
- console.log(` ${level}: ${items.length} 次`);
- }
- }
- if (reduceSignals.length > 0) {
- console.log(`\n 减仓信号: ${reduceSignals.length} 次`);
- console.log(`\n 减仓详情:`);
- reduceSignals.forEach(signal => {
- console.log(` [${signal.timestamp}] ${signal.accountId}`);
- console.log(` 等级: ${signal.level}`);
- console.log(` 利用率: ${(signal.currentUtilization * 100).toFixed(2)}%`);
- console.log(` 减仓比例: ${(signal.reducePercent * 100).toFixed(0)}%`);
- console.log(` 取消订单: ${signal.ordersToCancel}`);
- console.log(` 平仓数: ${signal.positionsToReduce}`);
- });
- }
- }
- private analyzePerformance(): void {
- console.log(`\n⚡ === 性能分析 ===`);
- const cycleEnds = this.auditLogs.filter(l => l.message === 'CYCLE_END');
- if (cycleEnds.length === 0) {
- console.log(` 无性能数据`);
- return;
- }
- const durations = cycleEnds.map(c => c.duration).filter(Boolean);
- if (durations.length > 0) {
- const min = Math.min(...durations);
- const max = Math.max(...durations);
- const avg = durations.reduce((a, b) => a + b, 0) / durations.length;
- console.log(` 周期耗时:`);
- console.log(` 最快: ${min}ms`);
- console.log(` 最慢: ${max}ms`);
- console.log(` 平均: ${avg.toFixed(0)}ms`);
- // 检查慢周期
- const slowCycles = cycleEnds.filter(c => c.duration > 10000);
- if (slowCycles.length > 0) {
- console.log(`\n ⚠️ 慢周期 (>10s): ${slowCycles.length} 次`);
- }
- }
- }
- private analyzeErrors(): void {
- console.log(`\n❌ === 错误分析 ===`);
- const errors = this.logs.filter(l => l.level === 'error');
- const exceptions = this.logs.filter(l => l.message === 'EXCEPTION');
- if (errors.length === 0 && exceptions.length === 0) {
- console.log(` ✅ 无错误记录`);
- return;
- }
- console.log(` 错误数: ${errors.length}`);
- console.log(` 异常数: ${exceptions.length}`);
- if (errors.length > 0) {
- console.log(`\n 错误分布:`);
- const errorMessages = errors.map(e => e.message).slice(0, 10);
- const grouped = errorMessages.reduce((acc, msg) => {
- acc[msg] = (acc[msg] || 0) + 1;
- return acc;
- }, {} as Record<string, number>);
- Object.entries(grouped)
- .sort((a, b) => b[1] - a[1])
- .slice(0, 5)
- .forEach(([msg, count]) => {
- console.log(` ${msg}: ${count} 次`);
- });
- }
- }
- private groupBy<T extends Record<string, any>>(
- items: T[],
- key: string
- ): Record<string, T[]> {
- return items.reduce((acc, item) => {
- const group = item[key] || 'unknown';
- if (!acc[group]) acc[group] = [];
- acc[group].push(item);
- return acc;
- }, {} as Record<string, T[]>);
- }
- }
- // 主函数
- async function main() {
- const date = process.argv[2] || new Date().toISOString().split('T')[0];
- const analyzer = new LogAnalyzer(date);
- await analyzer.analyze();
- console.log(`\n✅ 分析完成\n`);
- }
- main().catch(console.error);
|