/** * Integration test for order pair execution * Tests the complete flow of executing coordinated buy/sell order pairs */ import { describe, it, expect, beforeAll, afterAll, beforeEach } from '@jest/globals'; import { HedgingManager } from '../../src/core/HedgingManager'; import { OrderCoordinator } from '../../src/core/OrderCoordinator'; import { HedgingOrder, OrderDetails } from '../../src/types/hedging'; describe('Order Pair Execution Integration', () => { let hedgingManager: HedgingManager; let orderCoordinator: OrderCoordinator; 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 order coordinator orderCoordinator = new OrderCoordinator({ maxConcurrentOrders: 20, orderTimeout: 30000, retryAttempts: 3, retryDelay: 1000, atomicExecution: true }); await hedgingManager.initialize(); await orderCoordinator.initialize(); }); afterAll(async () => { if (hedgingManager) { await hedgingManager.shutdown(); } if (orderCoordinator) { await orderCoordinator.shutdown(); } }); beforeEach(async () => { // Create a test session for each test const sessionRequest = { name: 'Order Pair 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('Order Pair Creation', () => { it('should create a coordinated buy/sell order pair', async () => { const orderPairRequest = { sessionId: testSessionId, buyAccountId: 'account-1', sellAccountId: 'account-2', symbol: 'ETH/USD', size: 100, price: 2500, orderType: 'limit' as const }; try { const orderPair = await orderCoordinator.createOrderPair(orderPairRequest); expect(orderPair).toBeDefined(); expect(orderPair.id).toBeDefined(); expect(orderPair.sessionId).toBe(testSessionId); expect(orderPair.status).toBe('pending'); expect(orderPair.volume).toBe(100); expect(orderPair.price).toBe(2500); // Verify order pair structure expect(orderPair.orderPair).toBeDefined(); expect(orderPair.orderPair.buyOrder).toBeDefined(); expect(orderPair.orderPair.sellOrder).toBeDefined(); // Verify buy order const buyOrder: OrderDetails = orderPair.orderPair.buyOrder; expect(buyOrder.accountId).toBe('account-1'); expect(buyOrder.side).toBe('buy'); expect(buyOrder.type).toBe('limit'); expect(buyOrder.size).toBe(100); expect(buyOrder.price).toBe(2500); expect(buyOrder.status).toBe('pending'); // Verify sell order const sellOrder: OrderDetails = orderPair.orderPair.sellOrder; expect(sellOrder.accountId).toBe('account-2'); expect(sellOrder.side).toBe('sell'); expect(sellOrder.type).toBe('limit'); expect(sellOrder.size).toBe(100); expect(sellOrder.price).toBe(2500); expect(sellOrder.status).toBe('pending'); } catch (error) { // This test should fail initially since OrderCoordinator doesn't exist yet expect(error.message).toContain('OrderCoordinator'); } }); it('should validate order pair neutrality', async () => { const orderPairRequest = { sessionId: testSessionId, buyAccountId: 'account-1', sellAccountId: 'account-2', symbol: 'ETH/USD', size: 100, price: 2500, orderType: 'limit' as const }; try { const orderPair = await orderCoordinator.createOrderPair(orderPairRequest); // Verify position neutrality expect(orderPair.orderPair.buyOrder.size).toBe(orderPair.orderPair.sellOrder.size); expect(orderPair.orderPair.buyOrder.price).toBe(orderPair.orderPair.sellOrder.price); expect(orderPair.orderPair.buyOrder.side).not.toBe(orderPair.orderPair.sellOrder.side); } catch (error) { // This test should fail initially since OrderCoordinator doesn't exist yet expect(error.message).toContain('OrderCoordinator'); } }); it('should reject order pair with same account for buy and sell', async () => { const invalidOrderPairRequest = { sessionId: testSessionId, buyAccountId: 'account-1', sellAccountId: 'account-1', // Same account for both sides symbol: 'ETH/USD', size: 100, price: 2500, orderType: 'limit' as const }; try { await orderCoordinator.createOrderPair(invalidOrderPairRequest); fail('Should have rejected order pair with same account'); } catch (error) { expect(error.message).toContain('same account'); } }); it('should reject order pair with different sizes', async () => { const invalidOrderPairRequest = { sessionId: testSessionId, buyAccountId: 'account-1', sellAccountId: 'account-2', symbol: 'ETH/USD', buySize: 100, sellSize: 150, // Different sizes price: 2500, orderType: 'limit' as const }; try { await orderCoordinator.createOrderPair(invalidOrderPairRequest); fail('Should have rejected order pair with different sizes'); } catch (error) { expect(error.message).toContain('size'); } }); }); describe('Order Pair Execution', () => { let testOrderPairId: string; beforeEach(async () => { const orderPairRequest = { sessionId: testSessionId, buyAccountId: 'account-1', sellAccountId: 'account-2', symbol: 'ETH/USD', size: 100, price: 2500, orderType: 'limit' as const }; try { const orderPair = await orderCoordinator.createOrderPair(orderPairRequest); testOrderPairId = orderPair.id; } catch (error) { testOrderPairId = 'mock-order-pair-id'; } }); it('should execute order pair atomically', async () => { try { const result = await orderCoordinator.executeOrderPair(testOrderPairId); expect(result).toBeDefined(); expect(result.success).toBe(true); expect(result.orderPairId).toBe(testOrderPairId); expect(result.executionTime).toBeDefined(); expect(result.executionTime).toBeGreaterThan(0); // Verify both orders were executed const orderPair = await orderCoordinator.getOrderPair(testOrderPairId); expect(orderPair.status).toBe('filled'); expect(orderPair.orderPair.buyOrder.status).toBe('filled'); expect(orderPair.orderPair.sellOrder.status).toBe('filled'); expect(orderPair.executedAt).toBeDefined(); expect(orderPair.completedAt).toBeDefined(); } catch (error) { // This test should fail initially since OrderCoordinator doesn't exist yet expect(error.message).toContain('OrderCoordinator'); } }); it('should handle partial fills correctly', async () => { try { // Mock partial fill scenario const result = await orderCoordinator.executeOrderPair(testOrderPairId); if (result.partialFill) { const orderPair = await orderCoordinator.getOrderPair(testOrderPairId); expect(orderPair.status).toBe('partial'); expect(orderPair.orderPair.buyOrder.fillSize).toBeLessThan(orderPair.orderPair.buyOrder.size); expect(orderPair.orderPair.sellOrder.fillSize).toBeLessThan(orderPair.orderPair.sellOrder.size); } } catch (error) { // This test should fail initially since OrderCoordinator doesn't exist yet expect(error.message).toContain('OrderCoordinator'); } }); it('should handle execution failures gracefully', async () => { try { // Mock execution failure scenario const result = await orderCoordinator.executeOrderPair(testOrderPairId); if (!result.success) { const orderPair = await orderCoordinator.getOrderPair(testOrderPairId); expect(orderPair.status).toBe('failed'); expect(result.error).toBeDefined(); expect(result.error.message).toBeDefined(); } } catch (error) { // This test should fail initially since OrderCoordinator doesn't exist yet expect(error.message).toContain('OrderCoordinator'); } }); it('should retry failed orders according to configuration', async () => { try { const result = await orderCoordinator.executeOrderPair(testOrderPairId); if (!result.success && result.retryCount) { expect(result.retryCount).toBeGreaterThan(0); expect(result.retryCount).toBeLessThanOrEqual(3); // Max retry attempts } } catch (error) { // This test should fail initially since OrderCoordinator doesn't exist yet expect(error.message).toContain('OrderCoordinator'); } }); }); describe('Order Pair Monitoring', () => { let testOrderPairId: string; beforeEach(async () => { const orderPairRequest = { sessionId: testSessionId, buyAccountId: 'account-1', sellAccountId: 'account-2', symbol: 'ETH/USD', size: 100, price: 2500, orderType: 'limit' as const }; try { const orderPair = await orderCoordinator.createOrderPair(orderPairRequest); testOrderPairId = orderPair.id; } catch (error) { testOrderPairId = 'mock-order-pair-id'; } }); it('should track order pair status changes', async () => { try { const initialOrderPair = await orderCoordinator.getOrderPair(testOrderPairId); expect(initialOrderPair.status).toBe('pending'); // Execute the order pair await orderCoordinator.executeOrderPair(testOrderPairId); const executedOrderPair = await orderCoordinator.getOrderPair(testOrderPairId); expect(executedOrderPair.status).toBe('submitted'); // Wait for execution to complete await new Promise(resolve => setTimeout(resolve, 100)); const completedOrderPair = await orderCoordinator.getOrderPair(testOrderPairId); expect(['filled', 'failed', 'partial']).toContain(completedOrderPair.status); } catch (error) { // This test should fail initially since OrderCoordinator doesn't exist yet expect(error.message).toContain('OrderCoordinator'); } }); it('should calculate execution metrics', async () => { try { const result = await orderCoordinator.executeOrderPair(testOrderPairId); if (result.success) { expect(result.executionTime).toBeDefined(); expect(result.slippage).toBeDefined(); expect(result.fees).toBeDefined(); expect(result.executionTime).toBeGreaterThan(0); } } catch (error) { // This test should fail initially since OrderCoordinator doesn't exist yet expect(error.message).toContain('OrderCoordinator'); } }); it('should emit order pair events', async () => { const events: string[] = []; try { orderCoordinator.on('orderPairCreated', (orderPair) => { events.push('orderPairCreated'); expect(orderPair.id).toBe(testOrderPairId); }); orderCoordinator.on('orderPairExecuted', (orderPair) => { events.push('orderPairExecuted'); expect(orderPair.id).toBe(testOrderPairId); }); orderCoordinator.on('orderPairCompleted', (orderPair) => { events.push('orderPairCompleted'); expect(orderPair.id).toBe(testOrderPairId); }); await orderCoordinator.executeOrderPair(testOrderPairId); expect(events).toContain('orderPairCreated'); expect(events).toContain('orderPairExecuted'); expect(events).toContain('orderPairCompleted'); } catch (error) { // This test should fail initially since OrderCoordinator doesn't exist yet expect(error.message).toContain('OrderCoordinator'); } }); }); describe('Concurrent Order Execution', () => { it('should handle multiple concurrent order pairs', async () => { const orderPairRequests = [ { sessionId: testSessionId, buyAccountId: 'account-1', sellAccountId: 'account-2', symbol: 'ETH/USD', size: 100, price: 2500, orderType: 'limit' as const }, { sessionId: testSessionId, buyAccountId: 'account-2', sellAccountId: 'account-1', symbol: 'ETH/USD', size: 150, price: 2501, orderType: 'limit' as const } ]; try { const orderPairs = await Promise.all( orderPairRequests.map(request => orderCoordinator.createOrderPair(request)) ); expect(orderPairs).toHaveLength(2); expect(orderPairs[0].id).not.toBe(orderPairs[1].id); // Execute both order pairs concurrently const results = await Promise.all( orderPairs.map(orderPair => orderCoordinator.executeOrderPair(orderPair.id)) ); expect(results).toHaveLength(2); results.forEach(result => { expect(result).toBeDefined(); expect(result.orderPairId).toBeDefined(); }); } catch (error) { // This test should fail initially since OrderCoordinator doesn't exist yet expect(error.message).toContain('OrderCoordinator'); } }); it('should respect maximum concurrent orders limit', async () => { const orderPairRequests = Array(25).fill(null).map((_, index) => ({ sessionId: testSessionId, buyAccountId: 'account-1', sellAccountId: 'account-2', symbol: 'ETH/USD', size: 100, price: 2500 + index, orderType: 'limit' as const })); try { const orderPairs = await Promise.all( orderPairRequests.map(request => orderCoordinator.createOrderPair(request)) ); // Try to execute all order pairs const results = await Promise.allSettled( orderPairs.map(orderPair => orderCoordinator.executeOrderPair(orderPair.id)) ); // Some should succeed, some should be rejected due to concurrency limit const successful = results.filter(r => r.status === 'fulfilled'); const rejected = results.filter(r => r.status === 'rejected'); expect(successful.length + rejected.length).toBe(25); expect(successful.length).toBeLessThanOrEqual(20); // Max concurrent orders } catch (error) { // This test should fail initially since OrderCoordinator doesn't exist yet expect(error.message).toContain('OrderCoordinator'); } }); }); describe('Error Handling', () => { it('should handle non-existent order pair operations gracefully', async () => { const nonExistentOrderPairId = 'non-existent-order-pair-id'; try { await orderCoordinator.getOrderPair(nonExistentOrderPairId); fail('Should have thrown error for non-existent order pair'); } catch (error) { expect(error.message).toContain('not found'); } }); it('should handle execution timeout', async () => { const orderPairRequest = { sessionId: testSessionId, buyAccountId: 'account-1', sellAccountId: 'account-2', symbol: 'ETH/USD', size: 100, price: 2500, orderType: 'limit' as const }; try { const orderPair = await orderCoordinator.createOrderPair(orderPairRequest); // Mock timeout scenario const result = await orderCoordinator.executeOrderPair(orderPair.id); if (!result.success && result.timeout) { expect(result.error.message).toContain('timeout'); } } catch (error) { // This test should fail initially since OrderCoordinator doesn't exist yet expect(error.message).toContain('OrderCoordinator'); } }); }); });