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 connectiondisconnect()- Close connectionfetch_balance()- Get account balanceplace_order()- Place ordercancel_order()- Cancel orderfetch_order_status()- Get order statusfetch_ohlcv()- Get historical datasubscribe_ohlcv()- Real-time OHLCVsubscribe_trades()- Real-time tradessubscribe_order_book()- Real-time order bookfetch_open_orders()- Get open ordersfetch_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:
- Add docstrings to all methods
- Document exchange-specific features
- Add usage examples
- Update API documentation
Best Practices
- Error Handling: Handle exchange-specific errors
- Rate Limiting: Always respect rate limits
- Retry Logic: Implement retry for transient failures
- Logging: Log important operations
- Testing: Test thoroughly before submitting
Example: Complete Exchange Adapter
See src/exchanges/coinbase.py for a complete example implementation.