Files
crypto_trader/docs/developer/adding_exchanges.md

5.5 KiB

Adding New Exchange Adapters

This guide explains how to add support for new cryptocurrency exchanges.

Exchange Adapter Architecture

All exchange adapters inherit from BaseExchange and implement a standardized interface. This allows the trading engine to work with any exchange through a common API.

Implementation Steps

1. Create Exchange Module

Create a new file in src/exchanges/:

# src/exchanges/your_exchange.py
from src.exchanges.base import BaseExchange
from src.exchanges.factory import ExchangeFactory
import ccxt.pro as ccxt
import logging

logger = logging.getLogger(__name__)

class YourExchangeAdapter(BaseExchange):
    """Adapter for Your Exchange."""
    
    def __init__(self, name: str, api_key: str, secret_key: str, password: str = None):
        super().__init__(name, api_key, secret_key, password)
        self.exchange = ccxt.yourexchange({
            'apiKey': self.api_key,
            'secret': self.secret_key,
            'enableRateLimit': True,
        })
    
    async def connect(self):
        """Establish connection to exchange."""
        await self.exchange.load_markets()
        self.is_connected = True
        logger.info(f"Connected to {self.name}")
    
    # Implement all required methods from BaseExchange
    # ...

2. Implement Required Methods

Implement all abstract methods from BaseExchange:

  • connect() - Establish connection
  • disconnect() - Close connection
  • fetch_balance() - Get account balance
  • place_order() - Place order
  • cancel_order() - Cancel order
  • fetch_order_status() - Get order status
  • fetch_ohlcv() - Get historical data
  • subscribe_ohlcv() - Real-time OHLCV
  • subscribe_trades() - Real-time trades
  • subscribe_order_book() - Real-time order book
  • fetch_open_orders() - Get open orders
  • fetch_positions() - Get positions (futures)
  • fetch_markets() - Get available markets

3. Register Exchange

Register the exchange in src/exchanges/__init__.py:

from .your_exchange import YourExchangeAdapter
from .factory import ExchangeFactory

ExchangeFactory.register_exchange("your_exchange", YourExchangeAdapter)

4. Handle Exchange-Specific Features

Some exchanges have unique features:

  • Authentication: Some exchanges use different auth methods
  • Rate Limits: Respect exchange rate limits
  • WebSocket: Implement exchange-specific WebSocket protocol
  • Order Types: Support exchange-specific order types

5. Write Tests

Create tests in tests/unit/exchanges/test_your_exchange.py:

import pytest
from unittest.mock import Mock, patch
from src.exchanges.your_exchange import YourExchangeAdapter

class TestYourExchangeAdapter:
    """Tests for Your Exchange adapter."""
    
    @pytest.fixture
    def adapter(self):
        return YourExchangeAdapter(
            name="test",
            api_key="test_key",
            secret_key="test_secret"
        )
    
    @pytest.mark.asyncio
    async def test_connect(self, adapter):
        """Test connection."""
        with patch.object(adapter.exchange, 'load_markets'):
            await adapter.connect()
            assert adapter.is_connected
    
    # Add more tests...

Using CCXT

Most exchanges can use the ccxt library:

import ccxt.pro as ccxt

exchange = ccxt.pro.yourexchange({
    'apiKey': api_key,
    'secret': secret_key,
    'enableRateLimit': True,
})

# Use ccxt methods
balance = await exchange.fetch_balance()
order = await exchange.create_order(...)

Exchange-Specific Considerations

Authentication

Some exchanges require additional authentication:

# Example: Exchange requiring passphrase
self.exchange = ccxt.coinbase({
    'apiKey': api_key,
    'secret': secret_key,
    'password': passphrase,  # Coinbase requires this
})

Rate Limits

Always enable rate limiting:

self.exchange = ccxt.yourexchange({
    'enableRateLimit': True,  # Important!
})

WebSocket Support

For real-time data, implement WebSocket connections:

async def subscribe_ohlcv(self, symbol: str, timeframe: str, callback):
    """Subscribe to OHLCV updates."""
    # Exchange-specific WebSocket implementation
    await self.exchange.watch_ohlcv(symbol, timeframe, callback)

Testing Your Adapter

Unit Tests

Test all methods with mocked exchange responses:

@pytest.mark.asyncio
async def test_fetch_balance(self, adapter):
    """Test balance fetching."""
    mock_balance = {'BTC': {'free': 1.0, 'used': 0.0, 'total': 1.0}}
    with patch.object(adapter.exchange, 'fetch_balance', return_value=mock_balance):
        balance = await adapter.fetch_balance()
        assert 'BTC' in balance

Integration Tests

Test with real exchange (use testnet/sandbox if available):

@pytest.mark.integration
async def test_real_connection(self):
    """Test real connection (requires API keys)."""
    adapter = YourExchangeAdapter(...)
    await adapter.connect()
    assert adapter.is_connected

Documentation

Document your exchange adapter:

  1. Add docstrings to all methods
  2. Document exchange-specific features
  3. Add usage examples
  4. Update API documentation

Best Practices

  1. Error Handling: Handle exchange-specific errors
  2. Rate Limiting: Always respect rate limits
  3. Retry Logic: Implement retry for transient failures
  4. Logging: Log important operations
  5. Testing: Test thoroughly before submitting

Example: Complete Exchange Adapter

See src/exchanges/coinbase.py for a complete example implementation.