123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- import { describe, it, expect, beforeEach, afterEach } from '@jest/globals'
- import { RiskMonitoringService } from '../../src/services/RiskMonitoringService.js'
- import { MonitoringEventManager } from '../../src/models/MonitoringEvent.js'
- /**
- * RiskMonitoringService 单元测试
- * 测试风险监控服务的核心功能
- */
- describe('RiskMonitoringService Unit Tests', () => {
- let riskService: RiskMonitoringService
- let mockEventManager: jest.Mocked<MonitoringEventManager>
- beforeEach(() => {
- // 创建模拟的事件管理器
- mockEventManager = {
- addEvent: jest.fn(),
- getRecentEvents: jest.fn().mockReturnValue([]),
- getEventsByType: jest.fn().mockReturnValue([]),
- clearOldEvents: jest.fn(),
- } as any
- riskService = new RiskMonitoringService(mockEventManager)
- })
- afterEach(async () => {
- await riskService.stop()
- })
- describe('Service Lifecycle', () => {
- it('should initialize with stopped status', () => {
- expect(riskService.getStatus()).toBe('stopped')
- })
- it('should start monitoring successfully', async () => {
- await riskService.start()
- expect(riskService.getStatus()).toBe('running')
- })
- it('should stop monitoring successfully', async () => {
- await riskService.start()
- await riskService.stop()
- expect(riskService.getStatus()).toBe('stopped')
- })
- })
- describe('Risk Metrics Calculation', () => {
- beforeEach(async () => {
- await riskService.start()
- })
- it('should return null for non-existent account', async () => {
- const metrics = await riskService.getRiskMetrics('non-existent-account')
- expect(metrics).toBeNull()
- })
- it('should calculate risk metrics for valid account state', async () => {
- // 更新账户状态
- await riskService.updateAccountRisk('test-account', {
- netPosition: 0.5, // 0.5 BTC净仓位
- unrealizedPnl: 100, // $100 未实现盈亏
- leverage: 2, // 2x杠杆
- entryPrice: 50000, // $50,000入场价
- currentPrice: 51000, // $51,000当前价格
- })
- const metrics = await riskService.getRiskMetrics('test-account')
- expect(metrics).not.toBeNull()
- expect(metrics!.accountId).toBe('test-account')
- expect(metrics!.deltaDeviation).toBe(0.5) // 净仓位偏差
- expect(metrics!.utilizationRate).toBeGreaterThan(0)
- expect(metrics!.leverageRatio).toBe(2)
- expect(metrics!.unrealizedPnl).toBe(100)
- })
- it('should track maximum drawdown correctly', async () => {
- await riskService.updateAccountRisk('test-account', {
- netPosition: 1.0,
- unrealizedPnl: -500, // 负的P&L
- leverage: 3,
- })
- const metrics = await riskService.getRiskMetrics('test-account')
- expect(metrics!.maxDrawdown).toBe(500) // 最大回撤为正值
- })
- })
- describe('Risk Alert Generation', () => {
- beforeEach(async () => {
- await riskService.start()
- })
- it('should generate delta alert for high position deviation', async () => {
- await riskService.updateAccountRisk('test-account', {
- netPosition: 2.5, // 高净仓位偏差
- unrealizedPnl: 0,
- leverage: 1,
- })
- const alerts = riskService.getActiveAlerts('test-account')
- const deltaAlert = alerts.find(alert => alert.type === 'delta-alert')
- expect(deltaAlert).toBeDefined()
- expect(deltaAlert!.severity).toBe('WARN')
- })
- it('should generate utilization alert for high usage', async () => {
- await riskService.updateAccountRisk('test-account', {
- netPosition: 0.1,
- unrealizedPnl: 0,
- leverage: 1,
- utilizationRate: 0.85, // 85% 高利用率
- })
- const alerts = riskService.getActiveAlerts('test-account')
- const utilizationAlert = alerts.find(alert => alert.type === 'utilization-alert')
- expect(utilizationAlert).toBeDefined()
- expect(utilizationAlert!.severity).toBe('WARN')
- })
- it('should generate critical alert for excessive drawdown', async () => {
- await riskService.updateAccountRisk('test-account', {
- netPosition: 0.5,
- unrealizedPnl: -2500, // 高回撤
- leverage: 5,
- })
- const alerts = riskService.getActiveAlerts('test-account')
- const criticalAlert = alerts.find(alert => alert.severity === 'CRITICAL')
- expect(criticalAlert).toBeDefined()
- })
- })
- describe('Historical Risk Tracking', () => {
- beforeEach(async () => {
- await riskService.start()
- })
- it('should maintain risk metrics history', async () => {
- // 添加第一个风险指标
- await riskService.updateAccountRisk('test-account', {
- netPosition: 0.5,
- unrealizedPnl: 100,
- leverage: 2,
- })
- // 等待一小段时间
- await new Promise(resolve => setTimeout(resolve, 10))
- // 添加第二个风险指标
- await riskService.updateAccountRisk('test-account', {
- netPosition: 0.3,
- unrealizedPnl: 150,
- leverage: 2,
- })
- const history = riskService.getRiskHistory('test-account')
- expect(history.length).toBeGreaterThanOrEqual(2)
- expect(history[0].deltaDeviation).toBe(0.5)
- expect(history[history.length - 1].deltaDeviation).toBe(0.3)
- })
- it('should limit risk history to maximum size', async () => {
- // 添加大量风险指标 (超过限制)
- for (let i = 0; i < 1100; i++) {
- await riskService.updateAccountRisk('test-account', {
- netPosition: i * 0.001,
- unrealizedPnl: i,
- leverage: 1,
- })
- }
- const history = riskService.getRiskHistory('test-account')
- expect(history.length).toBeLessThanOrEqual(1000) // 最大1000条历史记录
- })
- })
- describe('Event Integration', () => {
- beforeEach(async () => {
- await riskService.start()
- })
- it('should log risk events to monitoring system', async () => {
- await riskService.updateAccountRisk('test-account', {
- netPosition: 2.0, // 触发风险警报
- unrealizedPnl: -1000,
- leverage: 5,
- })
- // 验证事件被记录
- expect(mockEventManager.addEvent).toHaveBeenCalled()
- const eventCall = mockEventManager.addEvent.mock.calls[0][0]
- expect(eventCall.type).toBe('delta-alert')
- expect(eventCall.accountId).toBe('test-account')
- })
- it('should include detailed risk data in events', async () => {
- await riskService.updateAccountRisk('test-account', {
- netPosition: 1.5,
- unrealizedPnl: -800,
- leverage: 4,
- entryPrice: 50000,
- currentPrice: 49000,
- })
- expect(mockEventManager.addEvent).toHaveBeenCalled()
- const eventCall = mockEventManager.addEvent.mock.calls[0][0]
- expect(eventCall.data).toMatchObject({
- deltaDeviation: 1.5,
- unrealizedPnl: -800,
- leverageRatio: 4,
- maxDrawdown: 800,
- })
- })
- })
- describe('Multi-Account Risk Management', () => {
- beforeEach(async () => {
- await riskService.start()
- })
- it('should handle multiple accounts independently', async () => {
- await riskService.updateAccountRisk('account-1', {
- netPosition: 0.5,
- unrealizedPnl: 100,
- leverage: 2,
- })
- await riskService.updateAccountRisk('account-2', {
- netPosition: -0.3,
- unrealizedPnl: -50,
- leverage: 1,
- })
- const metrics1 = await riskService.getRiskMetrics('account-1')
- const metrics2 = await riskService.getRiskMetrics('account-2')
- expect(metrics1!.deltaDeviation).toBe(0.5)
- expect(metrics2!.deltaDeviation).toBe(0.3)
- expect(metrics1!.unrealizedPnl).toBe(100)
- expect(metrics2!.unrealizedPnl).toBe(-50)
- })
- it('should return all active alerts across accounts', async () => {
- await riskService.updateAccountRisk('account-1', {
- netPosition: 2.0, // 触发警报
- unrealizedPnl: 0,
- leverage: 1,
- })
- await riskService.updateAccountRisk('account-2', {
- netPosition: 1.8, // 触发警报
- unrealizedPnl: 0,
- leverage: 1,
- })
- const allAlerts = riskService.getActiveAlerts()
- expect(allAlerts.length).toBeGreaterThanOrEqual(2)
- const account1Alerts = riskService.getActiveAlerts('account-1')
- const account2Alerts = riskService.getActiveAlerts('account-2')
- expect(account1Alerts.length).toBeGreaterThan(0)
- expect(account2Alerts.length).toBeGreaterThan(0)
- })
- })
- describe('Performance and Cleanup', () => {
- beforeEach(async () => {
- await riskService.start()
- })
- it('should clean up old alerts automatically', async () => {
- // 创建过期警报
- await riskService.updateAccountRisk('test-account', {
- netPosition: 2.0, // 触发警报
- unrealizedPnl: 0,
- leverage: 1,
- })
- // 获取初始警报数量
- const initialAlerts = riskService.getActiveAlerts()
- expect(initialAlerts.length).toBeGreaterThan(0)
- // 手动触发清理 (模拟时间流逝)
- await new Promise(resolve => setTimeout(resolve, 100))
- // 验证服务正在运行清理过程
- expect(riskService.getStatus()).toBe('running')
- })
- it('should handle concurrent risk updates safely', async () => {
- const promises = []
- // 并发更新同一账户的风险状态
- for (let i = 0; i < 10; i++) {
- promises.push(
- riskService.updateAccountRisk('test-account', {
- netPosition: i * 0.1,
- unrealizedPnl: i * 10,
- leverage: 1,
- })
- )
- }
- await Promise.all(promises)
- const metrics = await riskService.getRiskMetrics('test-account')
- expect(metrics).not.toBeNull()
- expect(metrics!.accountId).toBe('test-account')
- })
- })
- })
|