/** * Integration test for risk breach handling * Tests the complete flow of detecting, managing, and resolving risk breaches */ import { describe, it, expect, beforeAll, afterAll, beforeEach } from '@jest/globals'; import { HedgingManager } from '../../src/core/HedgingManager'; import { RiskManager } from '../../src/core/RiskManager'; import { RiskBreach, BreachType, BreachSeverity } from '../../src/types/hedging'; describe('Risk Breach Handling Integration', () => { let hedgingManager: HedgingManager; let riskManager: RiskManager; let testSessionId: string; beforeAll(async () => { // Initialize hedging manager hedgingManager = new HedgingManager({ accounts: './config/accounts.json', hedging: './config/hedging-config.json', marketData: './config/market-data-config.json' }); // Initialize risk manager riskManager = new RiskManager({ riskCheckInterval: 1000, alertThresholds: { positionSize: 0.08, portfolioLoss: 0.03, slippage: 0.015 }, maxConcurrentBreaches: 10 }); await hedgingManager.initialize(); await riskManager.initialize(); }); afterAll(async () => { if (hedgingManager) { await hedgingManager.shutdown(); } if (riskManager) { await riskManager.shutdown(); } }); beforeEach(async () => { // Create a test session for each test const sessionRequest = { name: 'Risk Breach Test Session', accountIds: ['account-1', 'account-2'], volumeTarget: 10000, strategy: { symbol: 'ETH/USD', volumeDistribution: 'equal' as const, priceRange: { min: 0.001, max: 0.01 }, timing: { minInterval: 30, maxInterval: 120, orderSize: { min: 100, max: 500 } }, riskLimits: { maxPositionSize: 0.1, stopLossThreshold: 0.05, maxSlippage: 0.02 }, orderTypes: { primary: 'limit' as const, fallback: 'market' as const } } }; try { const session = await hedgingManager.createSession(sessionRequest); testSessionId = session.id; } catch (error) { testSessionId = 'mock-session-id'; } }); describe('Risk Breach Detection', () => { it('should detect position size breaches', async () => { try { // Mock position size breach scenario const breach = await riskManager.checkPositionSizeRisk(testSessionId, 'account-1', 0.12); expect(breach).toBeDefined(); expect(breach.breachType).toBe('position_size_exceeded'); expect(breach.severity).toBe('critical'); expect(breach.sessionId).toBe(testSessionId); expect(breach.accountId).toBe('account-1'); expect(breach.details.currentValue).toBe(0.12); expect(breach.details.limitValue).toBe(0.1); expect(breach.details.percentage).toBe(120); expect(breach.resolved).toBe(false); expect(breach.timestamp).toBeInstanceOf(Date); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); it('should detect portfolio loss breaches', async () => { try { // Mock portfolio loss breach scenario const breach = await riskManager.checkPortfolioLossRisk(testSessionId, -0.06); expect(breach).toBeDefined(); expect(breach.breachType).toBe('portfolio_loss_exceeded'); expect(breach.severity).toBe('critical'); expect(breach.sessionId).toBe(testSessionId); expect(breach.accountId).toBeUndefined(); // Portfolio-level breach expect(breach.details.currentValue).toBe(-0.06); expect(breach.details.limitValue).toBe(-0.05); expect(breach.details.percentage).toBe(120); expect(breach.resolved).toBe(false); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); it('should detect slippage breaches', async () => { try { // Mock slippage breach scenario const breach = await riskManager.checkSlippageRisk(testSessionId, 'account-1', 0.025); expect(breach).toBeDefined(); expect(breach.breachType).toBe('slippage_exceeded'); expect(breach.severity).toBe('warning'); expect(breach.sessionId).toBe(testSessionId); expect(breach.accountId).toBe('account-1'); expect(breach.details.currentValue).toBe(0.025); expect(breach.details.limitValue).toBe(0.02); expect(breach.details.percentage).toBe(125); expect(breach.resolved).toBe(false); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); it('should detect margin insufficient breaches', async () => { try { // Mock margin insufficient breach scenario const breach = await riskManager.checkMarginRisk(testSessionId, 'account-1', 0.05); expect(breach).toBeDefined(); expect(breach.breachType).toBe('margin_insufficient'); expect(breach.severity).toBe('critical'); expect(breach.sessionId).toBe(testSessionId); expect(breach.accountId).toBe('account-1'); expect(breach.details.currentValue).toBe(0.05); expect(breach.details.limitValue).toBe(0.1); expect(breach.details.percentage).toBe(50); expect(breach.resolved).toBe(false); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); it('should detect account liquidation risk', async () => { try { // Mock liquidation risk breach scenario const breach = await riskManager.checkLiquidationRisk(testSessionId, 'account-1', 0.02); expect(breach).toBeDefined(); expect(breach.breachType).toBe('account_liquidation_risk'); expect(breach.severity).toBe('critical'); expect(breach.sessionId).toBe(testSessionId); expect(breach.accountId).toBe('account-1'); expect(breach.details.currentValue).toBe(0.02); expect(breach.details.limitValue).toBe(0.05); expect(breach.details.percentage).toBe(40); expect(breach.resolved).toBe(false); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); }); describe('Risk Breach Management', () => { let testBreachId: string; beforeEach(async () => { try { // Create a test breach const breach = await riskManager.checkPositionSizeRisk(testSessionId, 'account-1', 0.12); testBreachId = breach.id; } catch (error) { testBreachId = 'mock-breach-id'; } }); it('should store and retrieve risk breaches', async () => { try { const breach = await riskManager.getBreach(testBreachId); expect(breach).toBeDefined(); expect(breach.id).toBe(testBreachId); expect(breach.sessionId).toBe(testSessionId); expect(breach.breachType).toBe('position_size_exceeded'); expect(breach.severity).toBe('critical'); expect(breach.resolved).toBe(false); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); it('should list active breaches for a session', async () => { try { const breaches = await riskManager.getActiveBreaches(testSessionId); expect(Array.isArray(breaches)).toBe(true); expect(breaches.length).toBeGreaterThan(0); const testBreach = breaches.find(b => b.id === testBreachId); expect(testBreach).toBeDefined(); expect(testBreach.resolved).toBe(false); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); it('should filter breaches by type', async () => { try { const positionBreaches = await riskManager.getBreachesByType(testSessionId, 'position_size_exceeded'); expect(Array.isArray(positionBreaches)).toBe(true); positionBreaches.forEach(breach => { expect(breach.breachType).toBe('position_size_exceeded'); }); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); it('should filter breaches by severity', async () => { try { const criticalBreaches = await riskManager.getBreachesBySeverity(testSessionId, 'critical'); expect(Array.isArray(criticalBreaches)).toBe(true); criticalBreaches.forEach(breach => { expect(breach.severity).toBe('critical'); }); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); }); describe('Risk Breach Resolution', () => { let testBreachId: string; beforeEach(async () => { try { // Create a test breach const breach = await riskManager.checkPositionSizeRisk(testSessionId, 'account-1', 0.12); testBreachId = breach.id; } catch (error) { testBreachId = 'mock-breach-id'; } }); it('should resolve a risk breach', async () => { try { const resolvedBreach = await riskManager.resolveBreach(testBreachId, 'Position reduced below limit'); expect(resolvedBreach).toBeDefined(); expect(resolvedBreach.id).toBe(testBreachId); expect(resolvedBreach.resolved).toBe(true); expect(resolvedBreach.resolvedAt).toBeDefined(); expect(resolvedBreach.resolvedAt).toBeInstanceOf(Date); expect(resolvedBreach.action).toBe('Position reduced below limit'); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); it('should acknowledge a risk breach without resolving', async () => { try { const acknowledgedBreach = await riskManager.acknowledgeBreach(testBreachId, 'ignore', 'Monitoring situation'); expect(acknowledgedBreach).toBeDefined(); expect(acknowledgedBreach.id).toBe(testBreachId); expect(acknowledgedBreach.resolved).toBe(false); expect(acknowledgedBreach.action).toBe('ignore'); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); it('should prevent operations on resolved breaches', async () => { try { // First resolve the breach await riskManager.resolveBreach(testBreachId, 'Position reduced'); // Try to resolve again await riskManager.resolveBreach(testBreachId, 'Another action'); fail('Should have rejected resolving an already resolved breach'); } catch (error) { expect(error.message).toContain('already resolved'); } }); }); describe('Risk Monitoring', () => { it('should continuously monitor risk metrics', async () => { const riskEvents: string[] = []; try { riskManager.on('breachDetected', (breach) => { riskEvents.push('breachDetected'); expect(breach.sessionId).toBe(testSessionId); }); riskManager.on('breachResolved', (breach) => { riskEvents.push('breachResolved'); expect(breach.sessionId).toBe(testSessionId); }); // Start monitoring await riskManager.startMonitoring(testSessionId); // Wait for monitoring to detect risks await new Promise(resolve => setTimeout(resolve, 2000)); // Stop monitoring await riskManager.stopMonitoring(testSessionId); expect(riskEvents.length).toBeGreaterThan(0); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); it('should calculate overall risk level', async () => { try { const riskLevel = await riskManager.calculateOverallRisk(testSessionId); expect(riskLevel).toBeDefined(); expect(['low', 'medium', 'high']).toContain(riskLevel); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); it('should generate risk status report', async () => { try { const riskStatus = await riskManager.getRiskStatus(testSessionId); expect(riskStatus).toBeDefined(); expect(riskStatus.sessionId).toBe(testSessionId); expect(riskStatus.overallRisk).toBeDefined(); expect(riskStatus.accountRisks).toBeDefined(); expect(Array.isArray(riskStatus.accountRisks)).toBe(true); expect(riskStatus.portfolioRisk).toBeDefined(); expect(riskStatus.activeBreaches).toBeDefined(); expect(Array.isArray(riskStatus.activeBreaches)).toBe(true); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); }); describe('Risk Breach Notifications', () => { it('should emit breach detection events', async () => { const events: string[] = []; try { riskManager.on('breachDetected', (breach) => { events.push('breachDetected'); expect(breach).toBeDefined(); expect(breach.id).toBeDefined(); expect(breach.sessionId).toBe(testSessionId); }); // Trigger a breach await riskManager.checkPositionSizeRisk(testSessionId, 'account-1', 0.12); expect(events).toContain('breachDetected'); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); it('should emit breach resolution events', async () => { const events: string[] = []; let testBreachId: string; try { riskManager.on('breachResolved', (breach) => { events.push('breachResolved'); expect(breach).toBeDefined(); expect(breach.id).toBe(testBreachId); }); // Create and resolve a breach const breach = await riskManager.checkPositionSizeRisk(testSessionId, 'account-1', 0.12); testBreachId = breach.id; await riskManager.resolveBreach(testBreachId, 'Position reduced'); expect(events).toContain('breachResolved'); } catch (error) { // This test should fail initially since RiskManager doesn't exist yet expect(error.message).toContain('RiskManager'); } }); }); describe('Error Handling', () => { it('should handle non-existent breach operations gracefully', async () => { const nonExistentBreachId = 'non-existent-breach-id'; try { await riskManager.getBreach(nonExistentBreachId); fail('Should have thrown error for non-existent breach'); } catch (error) { expect(error.message).toContain('not found'); } }); it('should handle invalid breach resolution attempts', async () => { const nonExistentBreachId = 'non-existent-breach-id'; try { await riskManager.resolveBreach(nonExistentBreachId, 'Test resolution'); fail('Should have thrown error for non-existent breach'); } catch (error) { expect(error.message).toContain('not found'); } }); it('should handle monitoring errors gracefully', async () => { try { await riskManager.startMonitoring('non-existent-session'); fail('Should have thrown error for non-existent session'); } catch (error) { expect(error.message).toContain('session'); } }); }); });