123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- /**
- * Unit tests for HedgingManager
- */
- import { HedgingManager } from '../../src/core/HedgingManager';
- import { HedgingSessionRequest, HedgingSession } from '../../src/types/hedging';
- import { Logger } from '../../src/utils/Logger';
- // Mock dependencies
- jest.mock('../../src/utils/Logger');
- jest.mock('../../src/core/RiskManager');
- jest.mock('../../src/core/OrderCoordinator');
- jest.mock('../../src/core/HedgingStrategyEngine');
- jest.mock('../../src/core/HedgingConfigManager');
- describe('HedgingManager', () => {
- let hedgingManager: HedgingManager;
- let mockConfig: any;
- beforeEach(() => {
- mockConfig = {
- maxConcurrentSessions: 5,
- sessionTimeout: 60000,
- riskCheckInterval: 1000
- };
- hedgingManager = new HedgingManager(mockConfig);
- });
- afterEach(() => {
- jest.clearAllMocks();
- });
- describe('Initialization', () => {
- it('should initialize with correct configuration', async () => {
- await hedgingManager.initialize();
-
- const status = hedgingManager.getStatus();
- expect(status.isRunning).toBe(false);
- expect(status.totalSessions).toBe(0);
- });
- it('should throw error if already initialized', async () => {
- await hedgingManager.initialize();
-
- await expect(hedgingManager.initialize()).rejects.toThrow('HedgingManager is already initialized');
- });
- });
- describe('Session Management', () => {
- beforeEach(async () => {
- await hedgingManager.initialize();
- });
- it('should create a new hedging session', async () => {
- const sessionRequest: HedgingSessionRequest = {
- strategy: {
- symbol: 'ETH/USD',
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: ['account1', 'account2'],
- duration: 300000
- };
- const session = await hedgingManager.createSession(sessionRequest);
-
- expect(session).toBeDefined();
- expect(session.id).toBeDefined();
- expect(session.status).toBe('created');
- expect(session.strategy.symbol).toBe('ETH/USD');
- expect(session.accounts).toEqual(['account1', 'account2']);
- });
- it('should throw error when creating session with invalid request', async () => {
- const invalidRequest = {
- strategy: {
- symbol: '', // Invalid empty symbol
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: [],
- duration: 300000
- } as HedgingSessionRequest;
- await expect(hedgingManager.createSession(invalidRequest)).rejects.toThrow();
- });
- it('should start a hedging session', async () => {
- const sessionRequest: HedgingSessionRequest = {
- strategy: {
- symbol: 'ETH/USD',
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: ['account1', 'account2'],
- duration: 300000
- };
- const session = await hedgingManager.createSession(sessionRequest);
- await hedgingManager.startSession(session.id);
-
- const updatedSession = hedgingManager.getSession(session.id);
- expect(updatedSession?.status).toBe('running');
- });
- it('should pause a running session', async () => {
- const sessionRequest: HedgingSessionRequest = {
- strategy: {
- symbol: 'ETH/USD',
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: ['account1', 'account2'],
- duration: 300000
- };
- const session = await hedgingManager.createSession(sessionRequest);
- await hedgingManager.startSession(session.id);
- await hedgingManager.pauseSession(session.id);
-
- const updatedSession = hedgingManager.getSession(session.id);
- expect(updatedSession?.status).toBe('paused');
- });
- it('should resume a paused session', async () => {
- const sessionRequest: HedgingSessionRequest = {
- strategy: {
- symbol: 'ETH/USD',
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: ['account1', 'account2'],
- duration: 300000
- };
- const session = await hedgingManager.createSession(sessionRequest);
- await hedgingManager.startSession(session.id);
- await hedgingManager.pauseSession(session.id);
- await hedgingManager.resumeSession(session.id);
-
- const updatedSession = hedgingManager.getSession(session.id);
- expect(updatedSession?.status).toBe('running');
- });
- it('should stop a session', async () => {
- const sessionRequest: HedgingSessionRequest = {
- strategy: {
- symbol: 'ETH/USD',
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: ['account1', 'account2'],
- duration: 300000
- };
- const session = await hedgingManager.createSession(sessionRequest);
- await hedgingManager.startSession(session.id);
- await hedgingManager.stopSession(session.id);
-
- const updatedSession = hedgingManager.getSession(session.id);
- expect(updatedSession?.status).toBe('stopped');
- });
- it('should throw error when operating on non-existent session', async () => {
- await expect(hedgingManager.startSession('non-existent')).rejects.toThrow();
- await expect(hedgingManager.pauseSession('non-existent')).rejects.toThrow();
- await expect(hedgingManager.resumeSession('non-existent')).rejects.toThrow();
- await expect(hedgingManager.stopSession('non-existent')).rejects.toThrow();
- });
- it('should throw error when starting already running session', async () => {
- const sessionRequest: HedgingSessionRequest = {
- strategy: {
- symbol: 'ETH/USD',
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: ['account1', 'account2'],
- duration: 300000
- };
- const session = await hedgingManager.createSession(sessionRequest);
- await hedgingManager.startSession(session.id);
-
- await expect(hedgingManager.startSession(session.id)).rejects.toThrow();
- });
- });
- describe('Session Retrieval', () => {
- beforeEach(async () => {
- await hedgingManager.initialize();
- });
- it('should get all sessions', async () => {
- const sessionRequest: HedgingSessionRequest = {
- strategy: {
- symbol: 'ETH/USD',
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: ['account1', 'account2'],
- duration: 300000
- };
- const session1 = await hedgingManager.createSession(sessionRequest);
- const session2 = await hedgingManager.createSession(sessionRequest);
-
- const allSessions = hedgingManager.getAllSessions();
- expect(allSessions).toHaveLength(2);
- expect(allSessions.map(s => s.id)).toContain(session1.id);
- expect(allSessions.map(s => s.id)).toContain(session2.id);
- });
- it('should get session by ID', async () => {
- const sessionRequest: HedgingSessionRequest = {
- strategy: {
- symbol: 'ETH/USD',
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: ['account1', 'account2'],
- duration: 300000
- };
- const session = await hedgingManager.createSession(sessionRequest);
- const retrievedSession = hedgingManager.getSession(session.id);
-
- expect(retrievedSession).toBeDefined();
- expect(retrievedSession?.id).toBe(session.id);
- });
- it('should return null for non-existent session', () => {
- const session = hedgingManager.getSession('non-existent');
- expect(session).toBeNull();
- });
- });
- describe('Order Management', () => {
- beforeEach(async () => {
- await hedgingManager.initialize();
- });
- it('should get session orders', async () => {
- const sessionRequest: HedgingSessionRequest = {
- strategy: {
- symbol: 'ETH/USD',
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: ['account1', 'account2'],
- duration: 300000
- };
- const session = await hedgingManager.createSession(sessionRequest);
- const orders = hedgingManager.getSessionOrders(session.id);
-
- expect(orders).toBeDefined();
- expect(Array.isArray(orders)).toBe(true);
- });
- it('should return empty array for non-existent session orders', () => {
- const orders = hedgingManager.getSessionOrders('non-existent');
- expect(orders).toEqual([]);
- });
- });
- describe('Risk Management', () => {
- beforeEach(async () => {
- await hedgingManager.initialize();
- });
- it('should get session risk status', async () => {
- const sessionRequest: HedgingSessionRequest = {
- strategy: {
- symbol: 'ETH/USD',
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: ['account1', 'account2'],
- duration: 300000
- };
- const session = await hedgingManager.createSession(sessionRequest);
- const riskStatus = hedgingManager.getSessionRiskStatus(session.id);
-
- expect(riskStatus).toBeDefined();
- expect(riskStatus.overallRisk).toBeDefined();
- expect(riskStatus.activeBreaches).toBeDefined();
- expect(riskStatus.acknowledgedBreaches).toBeDefined();
- });
- it('should get session risk breaches', async () => {
- const sessionRequest: HedgingSessionRequest = {
- strategy: {
- symbol: 'ETH/USD',
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: ['account1', 'account2'],
- duration: 300000
- };
- const session = await hedgingManager.createSession(sessionRequest);
- const breaches = hedgingManager.getSessionRiskBreaches(session.id);
-
- expect(breaches).toBeDefined();
- expect(Array.isArray(breaches)).toBe(true);
- });
- it('should acknowledge risk breach', async () => {
- const sessionRequest: HedgingSessionRequest = {
- strategy: {
- symbol: 'ETH/USD',
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: ['account1', 'account2'],
- duration: 300000
- };
- const session = await hedgingManager.createSession(sessionRequest);
- const breaches = hedgingManager.getSessionRiskBreaches(session.id);
-
- if (breaches.length > 0) {
- const breach = breaches[0];
- await hedgingManager.acknowledgeRiskBreach(session.id, breach.id);
-
- const updatedBreaches = hedgingManager.getSessionRiskBreaches(session.id);
- const updatedBreach = updatedBreaches.find(b => b.id === breach.id);
- expect(updatedBreach?.acknowledged).toBe(true);
- }
- });
- it('should throw error when acknowledging non-existent breach', async () => {
- const sessionRequest: HedgingSessionRequest = {
- strategy: {
- symbol: 'ETH/USD',
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: ['account1', 'account2'],
- duration: 300000
- };
- const session = await hedgingManager.createSession(sessionRequest);
-
- await expect(hedgingManager.acknowledgeRiskBreach(session.id, 'non-existent')).rejects.toThrow();
- });
- });
- describe('Lifecycle Management', () => {
- it('should start and stop the manager', async () => {
- await hedgingManager.initialize();
-
- await hedgingManager.start();
- const status = hedgingManager.getStatus();
- expect(status.isRunning).toBe(true);
-
- await hedgingManager.stop();
- const stoppedStatus = hedgingManager.getStatus();
- expect(stoppedStatus.isRunning).toBe(false);
- });
- it('should throw error when starting already running manager', async () => {
- await hedgingManager.initialize();
- await hedgingManager.start();
-
- await expect(hedgingManager.start()).rejects.toThrow();
- });
- it('should throw error when stopping non-running manager', async () => {
- await hedgingManager.initialize();
-
- await expect(hedgingManager.stop()).rejects.toThrow();
- });
- });
- describe('Status and Statistics', () => {
- beforeEach(async () => {
- await hedgingManager.initialize();
- });
- it('should return correct status', () => {
- const status = hedgingManager.getStatus();
-
- expect(status).toBeDefined();
- expect(typeof status.isRunning).toBe('boolean');
- expect(typeof status.totalSessions).toBe('number');
- expect(typeof status.activeSessions).toBe('number');
- expect(typeof status.pausedSessions).toBe('number');
- expect(typeof status.stoppedSessions).toBe('number');
- });
- it('should return correct statistics', () => {
- const stats = hedgingManager.getStatistics();
-
- expect(stats).toBeDefined();
- expect(typeof stats.totalSessions).toBe('number');
- expect(typeof stats.totalOrders).toBe('number');
- expect(typeof stats.totalVolume).toBe('number');
- expect(typeof stats.averageExecutionTime).toBe('number');
- expect(typeof stats.successRate).toBe('number');
- });
- });
- describe('Error Handling', () => {
- it('should handle initialization errors gracefully', async () => {
- const invalidConfig = {
- maxConcurrentSessions: -1, // Invalid negative value
- sessionTimeout: 60000,
- riskCheckInterval: 1000
- };
- const invalidManager = new HedgingManager(invalidConfig);
-
- await expect(invalidManager.initialize()).rejects.toThrow();
- });
- it('should handle session creation errors gracefully', async () => {
- await hedgingManager.initialize();
-
- const invalidRequest = {
- strategy: null, // Invalid null strategy
- accounts: ['account1'],
- duration: 300000
- } as any;
- await expect(hedgingManager.createSession(invalidRequest)).rejects.toThrow();
- });
- });
- describe('Event Emission', () => {
- beforeEach(async () => {
- await hedgingManager.initialize();
- });
- it('should emit session created event', async () => {
- const sessionRequest: HedgingSessionRequest = {
- strategy: {
- symbol: 'ETH/USD',
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: ['account1', 'account2'],
- duration: 300000
- };
- const eventSpy = jest.fn();
- hedgingManager.on('sessionCreated', eventSpy);
-
- await hedgingManager.createSession(sessionRequest);
-
- expect(eventSpy).toHaveBeenCalled();
- });
- it('should emit session started event', async () => {
- const sessionRequest: HedgingSessionRequest = {
- strategy: {
- symbol: 'ETH/USD',
- volumeDistribution: 'equal',
- 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', fallback: 'market' }
- },
- accounts: ['account1', 'account2'],
- duration: 300000
- };
- const session = await hedgingManager.createSession(sessionRequest);
-
- const eventSpy = jest.fn();
- hedgingManager.on('sessionStarted', eventSpy);
-
- await hedgingManager.startSession(session.id);
-
- expect(eventSpy).toHaveBeenCalled();
- });
- });
- });
|