Tradovate MCP Server
by alexanimal
Verified
- tradovate-mcp-server
- tests
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 {
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, realizedPnl: 500, openPnl: 100 },
'2': { id: 2, accountId: 12345, contractId: 2, netPos: -1, netPrice: 4800.50, realizedPnl: -200, openPnl: -50 }
};
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('Complete Coverage Tool Handlers', () => {
beforeEach(() => {
jest.clearAllMocks();
// Set up the tradovateRequest mock for each test
auth.tradovateRequest = jest.fn();
});
describe('handleGetAccountSummary', () => {
it('should get account summary with multiple positions', async () => {
// Arrange
const request = {
params: {
name: 'get_account_summary',
arguments: {
accountId: '12345'
}
}
};
// Mock account lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 12345,
name: 'Demo Account',
cashBalance: 10000
});
// Mock cash balance
auth.tradovateRequest.mockResolvedValueOnce({
cashBalance: 10000,
initialMargin: 2000,
totalMargin: 3500
});
// Mock positions
auth.tradovateRequest.mockResolvedValueOnce([
{
id: 1,
accountId: 12345,
contractId: 1,
netPos: 2,
netPrice: 5200.25,
realizedPnl: 500,
openPnl: 100
},
{
id: 2,
accountId: 12345,
contractId: 2,
netPos: -1,
netPrice: 4800.50,
realizedPnl: -200,
openPnl: -50
}
]);
// Act
const result = await handleGetAccountSummary(request);
// Assert
expect(auth.tradovateRequest).toHaveBeenCalledWith('GET', 'account/find?id=12345');
expect(auth.tradovateRequest).toHaveBeenCalledWith('POST', 'cashBalance/getCashBalanceSnapshot', { accountId: 12345 });
expect(auth.tradovateRequest).toHaveBeenCalledWith('GET', 'position/list?accountId=12345');
expect(result.content[0].text).toContain('Account summary for Demo Account:');
expect(result.content[0].text).toContain('"openPnl": 50');
expect(result.content[0].text).toContain('"positionCount": 2');
});
it('should get account summary with no positions', async () => {
// Arrange
const request = {
params: {
name: 'get_account_summary',
arguments: {
accountId: '12345'
}
}
};
// Mock account lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 12345,
name: 'Demo Account',
cashBalance: 10000
});
// Mock cash balance
auth.tradovateRequest.mockResolvedValueOnce({
cashBalance: 10000,
initialMargin: 0,
totalMargin: 0
});
// Mock empty positions
auth.tradovateRequest.mockResolvedValueOnce([]);
// Act
const result = await handleGetAccountSummary(request);
// Assert
expect(result.content[0].text).toContain('Account summary for Demo Account:');
expect(result.content[0].text).toContain('"openPnl": 0');
expect(result.content[0].text).toContain('"positionCount": 0');
});
it('should get account summary without specifying accountId', async () => {
// Arrange
const request = {
params: {
name: 'get_account_summary',
arguments: {}
}
};
// Mock accounts list
auth.tradovateRequest.mockResolvedValueOnce([
{
id: 12345,
name: 'Demo Account',
cashBalance: 10000
}
]);
// Mock cash balance
auth.tradovateRequest.mockResolvedValueOnce({
cashBalance: 10000,
initialMargin: 2000,
totalMargin: 3500
});
// Mock positions
auth.tradovateRequest.mockResolvedValueOnce([
{
id: 1,
accountId: 12345,
contractId: 1,
netPos: 2,
netPrice: 5200.25,
realizedPnl: 500,
openPnl: 100
}
]);
// Act
const result = await handleGetAccountSummary(request);
// Assert
expect(auth.tradovateRequest).toHaveBeenCalledWith('GET', 'account/list');
expect(result.content[0].text).toContain('Account summary for Demo Account:');
});
it('should handle no accounts found', async () => {
// Arrange
const request = {
params: {
name: 'get_account_summary',
arguments: {}
}
};
// Mock empty accounts list
auth.tradovateRequest.mockResolvedValueOnce([]);
// Act
const result = await handleGetAccountSummary(request);
// Assert
expect(result.content[0].text).toBe('No accounts found');
});
it('should handle API error with cached account', async () => {
// Arrange
const request = {
params: {
name: 'get_account_summary',
arguments: {
accountId: '12345'
}
}
};
// Add account to cache
data.accountsCache['12345'] = {
id: '12345',
name: 'Demo Account (simulated)',
balance: 10000
};
// Mock cash balance cache
data.cashBalanceCache = {
'12345': {
accountId: '12345',
cashBalance: 10000,
initialMargin: 2000
}
};
// Mock positions cache
data.positionsCache = {
'pos1': {
accountId: '12345',
contractId: 'ESZ4',
netPos: 1,
openPnl: 500,
realizedPnl: 200
}
};
// Mock API error for first attempt
auth.tradovateRequest.mockRejectedValueOnce(new Error('API error'));
// Mock API error for retry attempt
auth.tradovateRequest.mockRejectedValueOnce(new Error('API error on retry'));
// Act
const result = await handleGetAccountSummary(request);
// Assert
expect(result.content[0].text).toContain('Error getting account summary:');
});
it('should handle API error with no cached account', async () => {
// Arrange
const request = {
params: {
name: 'get_account_summary',
arguments: {
accountId: '999'
}
}
};
// Mock API error for first attempt
auth.tradovateRequest.mockRejectedValueOnce(new Error('API error'));
// Mock API error for retry attempt
auth.tradovateRequest.mockRejectedValueOnce(new Error('API error on retry'));
// Act
const result = await handleGetAccountSummary(request);
// Assert
expect(result.content[0].text).toBe('Error getting account summary: API error on retry');
});
it('should handle API error with no cached accounts', async () => {
// Arrange
const request = {
params: {
name: 'get_account_summary',
arguments: {}
}
};
// Mock API error for first attempt
auth.tradovateRequest.mockRejectedValueOnce(new Error('API error'));
// Mock API error for retry attempt
auth.tradovateRequest.mockRejectedValueOnce(new Error('API error on retry'));
// Act
const result = await handleGetAccountSummary(request);
// Assert
expect(result.content[0].text).toBe('Error getting account summary: API error on retry');
});
});
describe('handleGetMarketData', () => {
it('should get DOM data for a symbol', async () => {
// Arrange
const request = {
params: {
name: 'get_market_data',
arguments: {
symbol: 'ESZ4',
dataType: 'DOM'
}
}
};
// Mock contract lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 1,
name: 'ESZ4',
description: 'E-mini S&P 500'
});
// Mock DOM data
const mockDOM = {
bids: [
{ price: 5275.25, size: 250 },
{ price: 5275.00, size: 175 }
],
asks: [
{ price: 5275.50, size: 180 },
{ price: 5275.75, size: 220 }
]
};
auth.tradovateRequest.mockResolvedValueOnce(mockDOM);
// Act
const result = await handleGetMarketData(request);
// Assert
expect(auth.tradovateRequest).toHaveBeenCalledWith('GET', 'contract/find?name=ESZ4');
expect(auth.tradovateRequest).toHaveBeenCalledWith('GET', 'md/getDOM?contractId=1', undefined, true);
expect(result.content[0].text).toContain('Market data for ESZ4 (DOM)');
expect(result.content[0].text).toContain('"bids":');
expect(result.content[0].text).toContain('"asks":');
});
it('should get chart data with 5min timeframe', async () => {
// Arrange
const request = {
params: {
name: 'get_market_data',
arguments: {
symbol: 'ESZ4',
dataType: 'Chart',
chartTimeframe: '5min'
}
}
};
// Mock contract lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 1,
name: 'ESZ4',
description: 'E-mini S&P 500'
});
// Mock chart data
const mockChart = {
bars: [
{ timestamp: '2023-01-01T12:00:00Z', open: 5250, high: 5255, low: 5245, close: 5252 }
]
};
auth.tradovateRequest.mockResolvedValueOnce(mockChart);
// Act
const result = await handleGetMarketData(request);
// Assert
expect(auth.tradovateRequest).toHaveBeenCalledWith('GET', 'contract/find?name=ESZ4');
expect(auth.tradovateRequest).toHaveBeenCalledWith('GET', 'md/getChart?contractId=1&chartDescription=5m&timeRange=3600', undefined, true);
expect(result.content[0].text).toContain('Market data for ESZ4 (Chart)');
});
it('should get chart data with 15min timeframe', async () => {
// Arrange
const request = {
params: {
name: 'get_market_data',
arguments: {
symbol: 'ESZ4',
dataType: 'Chart',
chartTimeframe: '15min'
}
}
};
// Mock contract lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 1,
name: 'ESZ4',
description: 'E-mini S&P 500'
});
// Mock chart data
const mockChart = {
bars: [
{ timestamp: '2023-01-01T12:00:00Z', open: 5250, high: 5255, low: 5245, close: 5252 }
]
};
auth.tradovateRequest.mockResolvedValueOnce(mockChart);
// Act
const result = await handleGetMarketData(request);
// Assert
expect(auth.tradovateRequest).toHaveBeenCalledWith('GET', 'md/getChart?contractId=1&chartDescription=15m&timeRange=3600', undefined, true);
});
it('should get chart data with 30min timeframe', async () => {
// Arrange
const request = {
params: {
name: 'get_market_data',
arguments: {
symbol: 'ESZ4',
dataType: 'Chart',
chartTimeframe: '30min'
}
}
};
// Mock contract lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 1,
name: 'ESZ4',
description: 'E-mini S&P 500'
});
// Mock chart data
const mockChart = {
bars: [
{ timestamp: '2023-01-01T12:00:00Z', open: 5250, high: 5255, low: 5245, close: 5252 }
]
};
auth.tradovateRequest.mockResolvedValueOnce(mockChart);
// Act
const result = await handleGetMarketData(request);
// Assert
expect(auth.tradovateRequest).toHaveBeenCalledWith('GET', 'md/getChart?contractId=1&chartDescription=30m&timeRange=3600', undefined, true);
});
it('should get chart data with 4hour timeframe', async () => {
// Arrange
const request = {
params: {
name: 'get_market_data',
arguments: {
symbol: 'ESZ4',
dataType: 'Chart',
chartTimeframe: '4hour'
}
}
};
// Mock contract lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 1,
name: 'ESZ4',
description: 'E-mini S&P 500'
});
// Mock chart data
const mockChart = {
bars: [
{ timestamp: '2023-01-01T12:00:00Z', open: 5250, high: 5255, low: 5245, close: 5252 }
]
};
auth.tradovateRequest.mockResolvedValueOnce(mockChart);
// Act
const result = await handleGetMarketData(request);
// Assert
expect(auth.tradovateRequest).toHaveBeenCalledWith('GET', 'md/getChart?contractId=1&chartDescription=4h&timeRange=3600', undefined, true);
});
it('should handle API error and generate mock DOM data', async () => {
// Arrange
const request = {
params: {
name: 'get_market_data',
arguments: {
symbol: 'ESZ4',
dataType: 'DOM'
}
}
};
// Mock contract lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 1,
name: 'ESZ4',
description: 'E-mini S&P 500'
});
// Mock API error
auth.tradovateRequest.mockRejectedValueOnce(new Error('API error'));
// Act
const result = await handleGetMarketData(request);
// Assert
expect(result.content[0].text).toContain('Market data for ESZ4 (DOM) [MOCK DATA]:');
expect(result.content[0].text).toContain('"bids":');
expect(result.content[0].text).toContain('"asks":');
});
it('should handle API error and generate mock chart data with 1min timeframe', async () => {
// Arrange
const request = {
params: {
name: 'get_market_data',
arguments: {
symbol: 'ESZ4',
dataType: 'Chart',
chartTimeframe: '1min'
}
}
};
// Mock contract lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 1,
name: 'ESZ4',
description: 'E-mini S&P 500'
});
// Mock API error
auth.tradovateRequest.mockRejectedValueOnce(new Error('API error'));
// Act
const result = await handleGetMarketData(request);
// Assert
expect(result.content[0].text).toContain('Market data for ESZ4 (Chart) [MOCK DATA]:');
expect(result.content[0].text).toContain('"timeframe": "1min"');
expect(result.content[0].text).toContain('"bars":');
});
it('should handle API error and generate mock chart data with 5min timeframe', async () => {
// Arrange
const request = {
params: {
name: 'get_market_data',
arguments: {
symbol: 'ESZ4',
dataType: 'Chart',
chartTimeframe: '5min'
}
}
};
// Mock contract lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 1,
name: 'ESZ4',
description: 'E-mini S&P 500'
});
// Mock API error
auth.tradovateRequest.mockRejectedValueOnce(new Error('API error'));
// Act
const result = await handleGetMarketData(request);
// Assert
expect(result.content[0].text).toContain('Market data for ESZ4 (Chart) [MOCK DATA]:');
expect(result.content[0].text).toContain('"timeframe": "5min"');
});
it('should handle API error and generate mock chart data with 15min timeframe', async () => {
// Arrange
const request = {
params: {
name: 'get_market_data',
arguments: {
symbol: 'ESZ4',
dataType: 'Chart',
chartTimeframe: '15min'
}
}
};
// Mock contract lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 1,
name: 'ESZ4',
description: 'E-mini S&P 500'
});
// Mock API error
auth.tradovateRequest.mockRejectedValueOnce(new Error('API error'));
// Act
const result = await handleGetMarketData(request);
// Assert
expect(result.content[0].text).toContain('Market data for ESZ4 (Chart) [MOCK DATA]:');
expect(result.content[0].text).toContain('"timeframe": "15min"');
});
it('should handle API error and generate mock chart data with 30min timeframe', async () => {
// Arrange
const request = {
params: {
name: 'get_market_data',
arguments: {
symbol: 'ESZ4',
dataType: 'Chart',
chartTimeframe: '30min'
}
}
};
// Mock contract lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 1,
name: 'ESZ4',
description: 'E-mini S&P 500'
});
// Mock API error
auth.tradovateRequest.mockRejectedValueOnce(new Error('API error'));
// Act
const result = await handleGetMarketData(request);
// Assert
expect(result.content[0].text).toContain('Market data for ESZ4 (Chart) [MOCK DATA]:');
expect(result.content[0].text).toContain('"timeframe": "30min"');
});
it('should handle API error and generate mock chart data with 1hour timeframe', async () => {
// Arrange
const request = {
params: {
name: 'get_market_data',
arguments: {
symbol: 'ESZ4',
dataType: 'Chart',
chartTimeframe: '1hour'
}
}
};
// Mock contract lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 1,
name: 'ESZ4',
description: 'E-mini S&P 500'
});
// Mock API error
auth.tradovateRequest.mockRejectedValueOnce(new Error('API error'));
// Act
const result = await handleGetMarketData(request);
// Assert
expect(result.content[0].text).toContain('Market data for ESZ4 (Chart) [MOCK DATA]:');
expect(result.content[0].text).toContain('"timeframe": "1hour"');
});
it('should handle API error and generate mock chart data with 4hour timeframe', async () => {
// Arrange
const request = {
params: {
name: 'get_market_data',
arguments: {
symbol: 'ESZ4',
dataType: 'Chart',
chartTimeframe: '4hour'
}
}
};
// Mock contract lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 1,
name: 'ESZ4',
description: 'E-mini S&P 500'
});
// Mock API error
auth.tradovateRequest.mockRejectedValueOnce(new Error('API error'));
// Act
const result = await handleGetMarketData(request);
// Assert
expect(result.content[0].text).toContain('Market data for ESZ4 (Chart) [MOCK DATA]:');
expect(result.content[0].text).toContain('"timeframe": "4hour"');
});
it('should handle API error and generate mock chart data with 1day timeframe', async () => {
// Arrange
const request = {
params: {
name: 'get_market_data',
arguments: {
symbol: 'ESZ4',
dataType: 'Chart',
chartTimeframe: '1day'
}
}
};
// Mock contract lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 1,
name: 'ESZ4',
description: 'E-mini S&P 500'
});
// Mock API error
auth.tradovateRequest.mockRejectedValueOnce(new Error('API error'));
// Act
const result = await handleGetMarketData(request);
// Assert
expect(result.content[0].text).toContain('Market data for ESZ4 (Chart) [MOCK DATA]:');
expect(result.content[0].text).toContain('"timeframe": "1day"');
});
it('should handle API error with unsupported data type', async () => {
// Arrange
const request = {
params: {
name: 'get_market_data',
arguments: {
symbol: 'ESZ4',
dataType: 'UnsupportedType'
}
}
};
// Mock contract lookup
auth.tradovateRequest.mockResolvedValueOnce({
id: 1,
name: 'ESZ4',
description: 'E-mini S&P 500'
});
// Act & Assert
await expect(handleGetMarketData(request)).rejects.toThrow('Unsupported data type: UnsupportedType');
});
});
});