Tradovate MCP Server

by alexanimal
Verified
const { describe, expect, test, beforeEach } = require('@jest/globals'); // Import the modules we need to test const auth = require('../src/auth.js'); const data = require('../src/data.js'); const { handleModifyOrder, handleCancelOrder, handleGetAccountSummary, handleGetMarketData } = require('../src/tools.js'); // Mock the modules jest.mock('../src/auth.js'); jest.mock('../src/data.js'); // Set up mock data data.contractsCache = { '1': { id: 1, name: 'ESZ4', description: 'E-mini S&P 500', productType: 'Future' } }; data.positionsCache = { '1': { id: 1, accountId: 12345, contractId: 1, netPos: 2, netPrice: 5200.25 } }; data.ordersCache = { '1': { id: 1, accountId: 12345, contractId: 1, action: 'Buy', orderQty: 2, orderType: 'Limit', price: 4500.25 } }; data.accountsCache = { '12345': { id: 12345, name: 'Demo Account', cashBalance: 10000, marginBalance: 8000 } }; describe('Additional Tool Handlers', () => { beforeEach(() => { jest.clearAllMocks(); // Set up the tradovateRequest mock for each test auth.tradovateRequest = jest.fn(); }); describe('handleModifyOrder', () => { it('should modify an existing order', async () => { // Arrange const request = { params: { name: 'modify_order', arguments: { orderId: '1', price: 4600.50, quantity: 3 } } }; // Mock order lookup auth.tradovateRequest.mockResolvedValueOnce({ id: 1, accountId: 12345, contractId: 1, action: 'Buy', orderQty: 2, orderType: 'Limit', price: 4500.25 }); // Mock order modification const mockModifiedOrder = { id: 1, accountId: 12345, contractId: 1, action: 'Buy', orderQty: 3, orderType: 'Limit', price: 4600.50 }; auth.tradovateRequest.mockResolvedValueOnce(mockModifiedOrder); // Act const result = await handleModifyOrder(request); // Assert expect(auth.tradovateRequest).toHaveBeenCalledWith('GET', 'order/find?id=1'); expect(auth.tradovateRequest).toHaveBeenCalledWith('POST', 'order/modifyOrder', expect.objectContaining({ orderId: 1, orderQty: 3, price: 4600.50 })); expect(result.content[0].text).toContain('Order modified successfully'); }); it('should handle order not found', async () => { // Arrange const request = { params: { name: 'modify_order', arguments: { orderId: '999', price: 4600.50 } } }; // Mock order lookup failure auth.tradovateRequest.mockResolvedValueOnce(null); // Act const result = await handleModifyOrder(request); // Assert expect(result.content[0].text).toBe('Order not found with ID: 999'); }); it('should handle API errors', async () => { // Arrange const request = { params: { name: 'modify_order', arguments: { orderId: '1', price: 4600.50 } } }; // Mock order lookup auth.tradovateRequest.mockResolvedValueOnce({ id: 1, accountId: 12345, contractId: 1, action: 'Buy', orderQty: 2, orderType: 'Limit', price: 4500.25 }); // Mock API error auth.tradovateRequest.mockRejectedValueOnce(new Error('API error')); // Act const result = await handleModifyOrder(request); // Assert expect(result.content[0].text).toContain('Order modified successfully (simulated)'); }); }); describe('handleCancelOrder', () => { it('should cancel an existing order', async () => { // Arrange const request = { params: { name: 'cancel_order', arguments: { orderId: '1' } } }; // Mock order lookup auth.tradovateRequest.mockResolvedValueOnce({ id: 1, accountId: 12345, contractId: 1, action: 'Buy', orderQty: 2 }); // Mock order cancellation const mockCancelledOrder = { id: 1, status: 'Cancelled' }; auth.tradovateRequest.mockResolvedValueOnce(mockCancelledOrder); // Act const result = await handleCancelOrder(request); // Assert expect(auth.tradovateRequest).toHaveBeenCalledWith('GET', 'order/find?id=1'); expect(auth.tradovateRequest).toHaveBeenCalledWith('POST', 'order/cancelOrder', expect.objectContaining({ orderId: 1 })); expect(result.content[0].text).toContain('Order canceled successfully'); }); it('should handle order not found', async () => { // Arrange const request = { params: { name: 'cancel_order', arguments: { orderId: '999' } } }; // Mock order lookup failure auth.tradovateRequest.mockResolvedValueOnce(null); // Act const result = await handleCancelOrder(request); // Assert expect(result.content[0].text).toBe('Order not found with ID: 999'); }); it('should handle API errors', async () => { // Arrange const request = { params: { name: 'cancel_order', arguments: { orderId: '1' } } }; // Mock order lookup auth.tradovateRequest.mockResolvedValueOnce({ id: 1, accountId: 12345, contractId: 1, action: 'Buy', orderQty: 2 }); // Mock API error auth.tradovateRequest.mockRejectedValueOnce(new Error('API error')); // Act const result = await handleCancelOrder(request); // Assert expect(result.content[0].text).toContain('Order canceled successfully (simulated)'); }); }); describe('handleGetAccountSummary', () => { it('should get account summary', async () => { // Arrange const request = { params: { name: 'get_account_summary', arguments: { accountId: '12345' } } }; // Mock account lookup const mockAccount = { id: 12345, name: 'Demo Account', cashBalance: 10000, marginBalance: 8000, initialMargin: 2000, maintenanceMargin: 1500 }; auth.tradovateRequest.mockResolvedValueOnce(mockAccount); // Mock cash balance snapshot auth.tradovateRequest.mockResolvedValueOnce({ cashBalance: 10000, openOrderMargin: 2000, totalMargin: 3500 }); // Mock positions lookup const mockPositions = [ { id: 1, accountId: 12345, contractId: 1, netPos: 2, netPrice: 5200.25 } ]; auth.tradovateRequest.mockResolvedValueOnce(mockPositions); // Act const result = await handleGetAccountSummary(request); // Assert expect(auth.tradovateRequest).toHaveBeenCalledWith('GET', 'account/find?id=12345'); expect(auth.tradovateRequest).toHaveBeenCalledWith('GET', 'position/list?accountId=12345'); expect(result.content[0].text).toContain('Account summary for Demo Account'); }); it('should handle account not found', async () => { // Arrange const request = { params: { name: 'get_account_summary', arguments: { accountId: '999' } } }; // Mock account lookup failure auth.tradovateRequest.mockResolvedValueOnce(null); // Act const result = await handleGetAccountSummary(request); // Assert expect(result.content[0].text).toBe('Account not found with ID: 999'); }); }); describe('handleGetMarketData', () => { it('should get market data for a symbol', async () => { // Arrange const request = { params: { name: 'get_market_data', arguments: { symbol: 'ESZ4', dataType: 'Quote' } } }; // Mock contract lookup auth.tradovateRequest.mockResolvedValueOnce({ id: 1, name: 'ESZ4' }); // Mock market data lookup - note the additional parameters auth.tradovateRequest.mockImplementation((method, url, data, isMarketData) => { if (url === 'md/getQuote?contractId=1') { return Promise.resolve({ contractId: 1, last: 5200.25, bid: 5200.00, ask: 5200.50, volume: 1500000 }); } return Promise.resolve({ id: 1, name: 'ESZ4' }); }); // Act const result = await handleGetMarketData(request); // Assert expect(auth.tradovateRequest).toHaveBeenCalledWith('GET', 'contract/find?name=ESZ4'); expect(result.content[0].text).toContain('Market data for ESZ4 (Quote)'); }); it('should handle contract not found', async () => { // Arrange const request = { params: { name: 'get_market_data', arguments: { symbol: 'UNKNOWN', dataType: 'Quote' } } }; // Mock contract lookup failure auth.tradovateRequest.mockResolvedValueOnce(null); // Act const result = await handleGetMarketData(request); // Assert expect(result.content[0].text).toBe('Contract not found for symbol: UNKNOWN'); }); it('should handle API errors and use mock data', async () => { // Arrange const request = { params: { name: 'get_market_data', arguments: { symbol: 'ESZ4', dataType: 'Quote' } } }; // Mock contract lookup auth.tradovateRequest.mockImplementation((method, url, data, isMarketData) => { if (url === 'contract/find?name=ESZ4') { return Promise.resolve({ id: 1, name: 'ESZ4' }); } if (url === 'md/getQuote?contractId=1') { return Promise.reject(new Error('API error')); } return Promise.resolve(null); }); // Act const result = await handleGetMarketData(request); // Assert expect(result.content[0].text).toContain('Market data for ESZ4 (Quote) [MOCK DATA]'); }); }); });