123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- /**
- * 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');
- }
- });
- });
- });
|