Testing
Comprehensive testing strategies for custom bots
Testing Custom Bots
Comprehensive testing is crucial for reliable trading bots. This guide covers testing strategies, tools, and best practices for custom bot development. Since the0 is framework-agnostic, you can use any testing framework or library you prefer, such as unittest
for Python or Jest
for JavaScript. The examples below demonstrate how to implement tests in both languages.
Testing Strategy Overview
Testing Pyramid
┌─────────────────┐ │ End-to-End │ ← Integration with live systems (usually in CI/CD) │ Tests │ ├─────────────────┤ │ Integration │ ← Component interactions │ Tests │ ├─────────────────┤ │ Unit Tests │ ← Individual functions └─────────────────┘
Test Types
- Unit Tests: Test individual functions and components
- Integration Tests: Test component interactions
- Schema Tests: Validate configuration schemas
- Backtest Tests: Validate backtesting logic
- End-to-End Tests: Test complete bot workflows
- Performance Tests: Test under various loads
- Security Tests: Validate security measures
Unit Testing
Python Unit Tests
Test Structure
# test_trading_logic.py import unittest from unittest.mock import Mock, patch class TestTradingBot(unittest.TestCase): """Test the TradingBot class methods.""" def setUp(self): self.config = { 'symbol': 'BTCUSDT', 'position_size': 100, 'api_key': 'test-key', 'api_secret': 'test-secret', 'dry_run': True } @patch('main.ccxt.binance') def test_rsi_calculation(self, mock_exchange): """Test RSI calculation with sample data.""" prices = [100, 102, 99, 105, 103, 108, 106, 110] # Calculate RSI from main import calculate_rsi rsi = calculate_rsi(prices, period=5) # RSI should be between 0 and 100 self.assertGreaterEqual(rsi, 0) self.assertLessEqual(rsi, 100) @patch('main.ccxt.binance') def test_buy_signal(self, mock_exchange_class): """Test buy signal generation.""" mock_exchange = Mock() mock_exchange_class.return_value = mock_exchange # Mock oversold condition (RSI < 30) mock_exchange.fetch_ohlcv.return_value = [ [1640995200000, 50000, 50100, 49900, 49950, 1000], [1641001200000, 49950, 50000, 49800, 49850, 1100] ] from main import main result = main('test-bot', self.config) self.assertEqual(result['status'], 'success') mock_exchange.fetch_ohlcv.assert_called_once()
Running Python Tests
# Install testing dependencies pip install pytest pytest-cov pytest-mock # Run tests with coverage pytest --cov=main --cov-report=html # Run specific test file pytest test_trading_logic.py # Run with verbose output pytest -v # Run tests matching a pattern pytest -k "test_rsi"
JavaScript Unit Tests
Test Structure
// test/trading-logic.test.js ```javascript // test/trading-logic.test.js const { main, TradingBot } = require('../main'); // Mock ccxt jest.mock('ccxt', () => ({ binance: jest.fn().mockImplementation(() => ({ fetchTicker: jest.fn(), fetchOHLCV: jest.fn(), createMarketBuyOrder: jest.fn(), createMarketSellOrder: jest.fn(), })), })); describe('Trading Logic', () => { const mockConfig = { symbol: 'BTCUSDT', position_size: 100, api_key: 'test-key', api_secret: 'test-secret', dry_run: true, }; beforeEach(() => { jest.clearAllMocks(); }); test('should calculate RSI correctly', () => { const prices = [100, 102, 101, 103, 105, 104, 106, 108]; const rsi = TradingBot.calculateRSI(prices, 5); expect(rsi).toBeGreaterThanOrEqual(0); expect(rsi).toBeLessThanOrEqual(100); }); test('should generate buy signal when oversold', async () => { const ccxt = require('ccxt'); const mockExchange = ccxt.binance(); // Mock declining prices (oversold condition) const ohlcvData = Array(20).fill(null).map((_, i) => [ Date.now() - i * 3600000, 50000 * Math.pow(0.99, i), // declining prices 50000 * Math.pow(0.99, i) * 1.01, 50000 * Math.pow(0.99, i) * 0.99, 50000 * Math.pow(0.99, i), 1000 ]); mockExchange.fetchOHLCV.mockResolvedValue(ohlcvData); mockExchange.fetchTicker.mockResolvedValue({ last: 45000 }); const result = await main('test-bot', mockConfig); expect(result.status).toBe('success'); expect(mockExchange.fetchOHLCV).toHaveBeenCalled(); }); test('should handle exchange errors', async () => { const ccxt = require('ccxt'); const mockExchange = ccxt.binance(); mockExchange.fetchOHLCV.mockRejectedValue(new Error('API Error')); const result = await main('test-bot', mockConfig); expect(result.status).toBe('error'); expect(result.message).toContain('API Error'); }); });
Running JavaScript Tests
# Install testing dependencies npm install --save-dev jest # Run tests npm test # Run tests with coverage npx jest --coverage # Run specific test file npx jest test/trading-logic.test.js # Run tests in watch mode npx jest --watch # Run tests matching a pattern npx jest --testNamePattern="rsi"
Integration Testing
Integration tests validate how different components of your bot interact with each other and external systems like exchanges. These tests ensure that your bot can fetch data, execute trades, and handle errors correctly. Your effectively testing the bot as a whole, including its interaction with the exchange API, data fetching, and trading logic.
Exchange Integration Tests
# test_exchange_integration.py import unittest import ccxt from unittest.mock import Mock class TestExchangeIntegration(unittest.TestCase): """Test bot integration with exchange APIs.""" def setUp(self): """Set up mock exchange for testing.""" self.mock_exchange = Mock(spec=ccxt.binance) self.test_config = { 'symbol': 'BTCUSDT', 'position_size': 100, 'dry_run': True } def test_data_fetching(self): """Test fetching market data.""" # Mock responses self.mock_exchange.fetch_ticker.return_value = { 'symbol': 'BTC/USDT', 'last': 50000, 'bid': 49950, 'ask': 50050 } self.mock_exchange.fetch_ohlcv.return_value = [ [1640995200000, 50000, 50100, 49900, 49950, 1000], [1641001200000, 49950, 50000, 49800, 49850, 1100] ] # Test data structure ticker = self.mock_exchange.fetch_ticker('BTC/USDT') self.assertIn('last', ticker) self.assertGreater(ticker['last'], 0) ohlcv = self.mock_exchange.fetch_ohlcv('BTC/USDT', '1h') self.assertEqual(len(ohlcv[0]), 6) # timestamp, o, h, l, c, v def test_bot_workflow(self): """Test complete bot execution.""" from main import main # Mock exchange responses self.mock_exchange.fetch_ohlcv.return_value = self._generate_ohlcv_data() with patch('main.ccxt.binance', return_value=self.mock_exchange): result = main('test-bot', self.test_config) self.assertEqual(result['status'], 'success') self.mock_exchange.fetch_ohlcv.assert_called_once() def _generate_ohlcv_data(self): """Generate sample OHLCV data.""" return [[1640995200000, 50000, 50100, 49900, 49950, 1000] for _ in range(10)]
Schema Testing
JSON Schema Validation Tests
# test_schemas.py import unittest import json import jsonschema class TestBotSchemas(unittest.TestCase): """Test JSON schema validation for bot configurations.""" def setUp(self): """Load schemas once per test.""" with open('bot-schema.json') as f: self.bot_schema = json.load(f) def test_valid_bot_config(self): """Test that valid configuration passes validation.""" config = { 'symbol': 'BTCUSDT', 'position_size': 100, 'api_key': 'test-key', 'api_secret': 'test-secret' } # Should not raise an exception jsonschema.validate(config, self.bot_schema) def test_invalid_config_fails(self): """Test that invalid configurations are rejected.""" invalid_configs = [ {'symbol': 'btcusdt'}, # lowercase symbol {'position_size': -100}, # negative size {}, # missing required fields ] for config in invalid_configs: with self.assertRaises(jsonschema.ValidationError): jsonschema.validate(config, self.bot_schema)
Other Aspect to consider when testing
- Testing Backtesting Logic: Ensure your backtesting logic correctly simulates trades and ensure it return the expected structure see Backtesting for more details.
- Test the unhappy path: Always test for error conditions, such as API failures, invalid configurations, and edge cases.
- Profile Performance: Use profiling tools to identify bottlenecks in your code. The Overview section outlines limitations that you will need to consider when testing your bot.
Related Documentation
- Quick Start Guide - Build your first bot
- Backtesting Guide - Implementing backtesting
- Overview - Understanding custom bot development