import { describe, it, expect, beforeEach, afterEach } from '@jest/globals' import { DeltaNeutralController } from '../../src/controllers/DeltaNeutralController.js' import { AccountManager } from '../../src/modules/account/AccountManager.js' import { HedgeExecutionService } from '../../src/services/HedgeExecutionService.js' import { RiskMonitoringService } from '../../src/services/RiskMonitoringService.js' /** * DeltaNeutralController 单元测试 * 测试Delta中性控制器的核心功能 */ describe('DeltaNeutralController Unit Tests', () => { let controller: DeltaNeutralController let mockAccountManager: jest.Mocked let mockHedgeService: jest.Mocked let mockRiskService: jest.Mocked beforeEach(() => { // 创建模拟依赖 mockAccountManager = { getAccounts: jest.fn().mockReturnValue(new Map([ ['account-1', { netPosition: 0.5, unrealizedPnl: 100 }], ['account-2', { netPosition: -0.3, unrealizedPnl: -50 }] ])), getAccountState: jest.fn(), updateAccountState: jest.fn(), } as any mockHedgeService = { executeHedge: jest.fn().mockResolvedValue({ success: true, executedOrders: [ { accountId: 'account-1', symbol: 'BTC-USD', amount: 0.2, side: 'sell' } ], totalCost: 1000, executionTime: 150, netDeltaChange: -0.2 }), getStatus: jest.fn().mockReturnValue('running'), } as any mockRiskService = { getRiskMetrics: jest.fn().mockResolvedValue({ accountId: 'test-account', deltaDeviation: 0.5, utilizationRate: 0.7, leverageRatio: 2, unrealizedPnl: 100, maxDrawdown: 200, timestamp: Date.now() }), updateAccountRisk: jest.fn(), getActiveAlerts: jest.fn().mockReturnValue([]), getStatus: jest.fn().mockReturnValue('running'), } as any controller = new DeltaNeutralController( mockAccountManager, mockHedgeService, mockRiskService ) }) afterEach(async () => { await controller.stop() }) describe('Service Lifecycle', () => { it('should initialize with stopped status', () => { expect(controller.getStatus()).toBe('stopped') }) it('should start successfully', async () => { await controller.start() expect(controller.getStatus()).toBe('running') }) it('should stop successfully', async () => { await controller.start() await controller.stop() expect(controller.getStatus()).toBe('stopped') }) it('should handle multiple start calls gracefully', async () => { await controller.start() await controller.start() // 第二次调用应该被忽略 expect(controller.getStatus()).toBe('running') }) }) describe('Delta Control Execution', () => { beforeEach(async () => { await controller.start() }) it('should execute delta control with valid request', async () => { const request = { targetDelta: 0.0, accounts: ['account-1', 'account-2'], maxDeviation: 0.1, symbol: 'BTC-USD' } const response = await controller.executeDeltaControl(request) expect(response.success).toBe(true) expect(response.accountsProcessed).toEqual(['account-1', 'account-2']) expect(response.totalAdjustments).toBeGreaterThan(0) expect(response.finalDelta).toBeDefined() expect(mockHedgeService.executeHedge).toHaveBeenCalled() }) it('should calculate net delta correctly', async () => { const request = { targetDelta: 0.0, accounts: ['account-1', 'account-2'], maxDeviation: 0.1, symbol: 'BTC-USD' } const response = await controller.executeDeltaControl(request) // 净Delta = 0.5 + (-0.3) = 0.2 expect(response.initialDelta).toBe(0.2) expect(response.deltaDeviation).toBe(0.2) // 偏离目标0.0 }) it('should skip adjustment when delta is within tolerance', async () => { // 设置小的净仓位偏差 mockAccountManager.getAccounts.mockReturnValue(new Map([ ['account-1', { netPosition: 0.05, unrealizedPnl: 10 }], ['account-2', { netPosition: -0.03, unrealizedPnl: -5 }] ])) const request = { targetDelta: 0.0, accounts: ['account-1', 'account-2'], maxDeviation: 0.1, // 10% 容差 symbol: 'BTC-USD' } const response = await controller.executeDeltaControl(request) expect(response.success).toBe(true) expect(response.adjustmentNeeded).toBe(false) expect(response.reason).toContain('在容差范围内') expect(mockHedgeService.executeHedge).not.toHaveBeenCalled() }) it('should handle hedge execution failure gracefully', async () => { mockHedgeService.executeHedge.mockResolvedValue({ success: false, error: 'Insufficient balance', executedOrders: [], totalCost: 0, executionTime: 0, netDeltaChange: 0 }) const request = { targetDelta: 0.0, accounts: ['account-1', 'account-2'], maxDeviation: 0.1, symbol: 'BTC-USD' } const response = await controller.executeDeltaControl(request) expect(response.success).toBe(false) expect(response.error).toContain('对冲执行失败') }) }) describe('Utilization Rebalancing', () => { beforeEach(async () => { await controller.start() }) it('should execute utilization rebalancing successfully', async () => { const request = { targetUtilization: 0.75, // 75% 目标利用率 accounts: ['account-1', 'account-2'], symbol: 'BTC-USD' } const response = await controller.executeUtilizationRebalance(request) expect(response.success).toBe(true) expect(response.accountsProcessed).toEqual(['account-1', 'account-2']) expect(response.rebalanceActions).toBeDefined() }) it('should calculate utilization rates correctly', async () => { // 模拟风险服务返回特定利用率 mockRiskService.getRiskMetrics .mockResolvedValueOnce({ accountId: 'account-1', deltaDeviation: 0.5, utilizationRate: 0.85, // 85% 高利用率 leverageRatio: 2, unrealizedPnl: 100, maxDrawdown: 200, timestamp: Date.now() }) .mockResolvedValueOnce({ accountId: 'account-2', deltaDeviation: 0.3, utilizationRate: 0.65, // 65% 低利用率 leverageRatio: 1, unrealizedPnl: -50, maxDrawdown: 100, timestamp: Date.now() }) const request = { targetUtilization: 0.75, accounts: ['account-1', 'account-2'], symbol: 'BTC-USD' } const response = await controller.executeUtilizationRebalance(request) expect(response.utilizationMetrics).toHaveLength(2) expect(response.utilizationMetrics[0].currentUtilization).toBe(0.85) expect(response.utilizationMetrics[1].currentUtilization).toBe(0.65) expect(response.averageUtilization).toBe(0.75) // (0.85 + 0.65) / 2 }) it('should identify accounts needing rebalancing', async () => { mockRiskService.getRiskMetrics .mockResolvedValueOnce({ accountId: 'account-1', deltaDeviation: 0.5, utilizationRate: 0.95, // 需要降低 leverageRatio: 3, unrealizedPnl: 200, maxDrawdown: 300, timestamp: Date.now() }) .mockResolvedValueOnce({ accountId: 'account-2', deltaDeviation: 0.2, utilizationRate: 0.45, // 需要提高 leverageRatio: 1, unrealizedPnl: 50, maxDrawdown: 50, timestamp: Date.now() }) const request = { targetUtilization: 0.75, accounts: ['account-1', 'account-2'], symbol: 'BTC-USD', tolerancePercent: 0.1 // 10% 容差 } const response = await controller.executeUtilizationRebalance(request) expect(response.rebalanceActions).toHaveLength(2) const account1Action = response.rebalanceActions.find(a => a.accountId === 'account-1') const account2Action = response.rebalanceActions.find(a => a.accountId === 'account-2') expect(account1Action?.action).toBe('reduce') // 降低利用率 expect(account2Action?.action).toBe('increase') // 提高利用率 }) it('should skip rebalancing when utilization is optimal', async () => { mockRiskService.getRiskMetrics.mockResolvedValue({ accountId: 'test-account', deltaDeviation: 0.1, utilizationRate: 0.75, // 正好是目标利用率 leverageRatio: 2, unrealizedPnl: 100, maxDrawdown: 150, timestamp: Date.now() }) const request = { targetUtilization: 0.75, accounts: ['account-1', 'account-2'], symbol: 'BTC-USD', tolerancePercent: 0.05 // 5% 容差 } const response = await controller.executeUtilizationRebalance(request) expect(response.success).toBe(true) expect(response.rebalanceNeeded).toBe(false) expect(response.reason).toContain('在目标范围内') }) }) describe('Risk Integration', () => { beforeEach(async () => { await controller.start() }) it('should check risk metrics before executing operations', async () => { const request = { targetDelta: 0.0, accounts: ['account-1'], maxDeviation: 0.1, symbol: 'BTC-USD' } await controller.executeDeltaControl(request) expect(mockRiskService.getRiskMetrics).toHaveBeenCalledWith('account-1') }) it('should abort operations when risk alerts are present', async () => { // 模拟活跃的风险警报 mockRiskService.getActiveAlerts.mockReturnValue([ { id: 'alert-1', type: 'delta-alert', severity: 'CRITICAL', accountId: 'account-1', message: '严重Delta偏差', timestamp: Date.now(), data: { deltaDeviation: 5.0 } } ]) const request = { targetDelta: 0.0, accounts: ['account-1'], maxDeviation: 0.1, symbol: 'BTC-USD' } const response = await controller.executeDeltaControl(request) expect(response.success).toBe(false) expect(response.error).toContain('存在CRITICAL级别风险警报') expect(mockHedgeService.executeHedge).not.toHaveBeenCalled() }) it('should update risk monitoring after successful operations', async () => { const request = { targetDelta: 0.0, accounts: ['account-1', 'account-2'], maxDeviation: 0.1, symbol: 'BTC-USD' } await controller.executeDeltaControl(request) expect(mockRiskService.updateAccountRisk).toHaveBeenCalled() }) }) describe('Error Handling and Edge Cases', () => { beforeEach(async () => { await controller.start() }) it('should handle empty account list', async () => { const request = { targetDelta: 0.0, accounts: [], maxDeviation: 0.1, symbol: 'BTC-USD' } const response = await controller.executeDeltaControl(request) expect(response.success).toBe(false) expect(response.error).toContain('账户列表为空') }) it('should handle invalid target delta values', async () => { const request = { targetDelta: NaN, accounts: ['account-1'], maxDeviation: 0.1, symbol: 'BTC-USD' } const response = await controller.executeDeltaControl(request) expect(response.success).toBe(false) expect(response.error).toContain('无效的目标Delta值') }) it('should handle account manager failures', async () => { mockAccountManager.getAccounts.mockImplementation(() => { throw new Error('AccountManager connection failed') }) const request = { targetDelta: 0.0, accounts: ['account-1'], maxDeviation: 0.1, symbol: 'BTC-USD' } const response = await controller.executeDeltaControl(request) expect(response.success).toBe(false) expect(response.error).toContain('获取账户状态失败') }) it('should handle risk service unavailability', async () => { mockRiskService.getRiskMetrics.mockRejectedValue(new Error('Risk service unavailable')) const request = { targetUtilization: 0.75, accounts: ['account-1'], symbol: 'BTC-USD' } const response = await controller.executeUtilizationRebalance(request) expect(response.success).toBe(false) expect(response.error).toContain('风险评估失败') }) }) describe('Performance and Monitoring', () => { beforeEach(async () => { await controller.start() }) it('should track operation execution time', async () => { const request = { targetDelta: 0.0, accounts: ['account-1', 'account-2'], maxDeviation: 0.1, symbol: 'BTC-USD' } const response = await controller.executeDeltaControl(request) expect(response.executionTime).toBeGreaterThan(0) expect(response.executionTime).toBeLessThan(10000) // 应该在10秒内完成 }) it('should provide detailed operation metrics', async () => { const request = { targetDelta: 0.0, accounts: ['account-1', 'account-2'], maxDeviation: 0.1, symbol: 'BTC-USD' } const response = await controller.executeDeltaControl(request) expect(response.operationId).toBeDefined() expect(response.timestamp).toBeDefined() expect(response.accountsProcessed).toBeDefined() expect(response.totalAdjustments).toBeDefined() }) it('should handle concurrent operation requests', async () => { const request1 = { targetDelta: 0.0, accounts: ['account-1'], maxDeviation: 0.1, symbol: 'BTC-USD' } const request2 = { targetUtilization: 0.75, accounts: ['account-2'], symbol: 'BTC-USD' } const [response1, response2] = await Promise.all([ controller.executeDeltaControl(request1), controller.executeUtilizationRebalance(request2) ]) expect(response1.success).toBe(true) expect(response2.success).toBe(true) expect(response1.operationId).not.toBe(response2.operationId) }) }) })