Adding a New Exchange to Horizon
This guide will walk you through the process of adding support for a new cryptocurrency exchange to the Horizon system. The Horizon system uses CCXT for exchange integrations and follows a modular architecture for easy expansion.
Prerequisites
Before starting, ensure you have:
- Access to the exchange’s API documentation
- API credentials (key, secret, and password if required)
- Understanding of the exchange’s order structure and market types
Step 1: Add Exchange Constants
First, add your exchange to the supported exchanges list in watcher/constants/index.js:
export const SupportedExchanges = ["mexc", "bybit", "okx", "your-exchange"];
Step 2: Create Exchange Module
Create a new file watcher/modules/exchanges/your-exchange.js
with the following structure:
import _ from "lodash";import { TradePair } from "../../constants/index.js";
// Define order type mappingsconst OrderType = { BUY: "LONG", SELL: "SHORT"};
/** * Transform exchange-specific order format to Horizon's standard format * @param {Object} order - Raw order from exchange * @returns {Object} Standardized order object */export function transformOrder(order) { // Format the symbol if needed (e.g., remove hyphens, add suffix) const formattedSymbol = formatSymbol(order.info.symbol);
return { trade_pair: TradePair[formattedSymbol], order_type: OrderType[_.upperCase(order.info.side)], };}
/** * Transform exchange-specific balance format (if needed) * @param {Object} balance - Raw balance from exchange * @returns {Object} Standardized balance object */export function transformBalance(balance) { return balance;}
/** * Configure exchange-specific settings * @param {Object} exchange - CCXT exchange instance * @param {Object} config - Exchange configuration */export async function configureExchange(exchange, config) { // Handle demo/sandbox mode if supported if (typeof exchange.set_sandbox_mode === "function") { exchange.set_sandbox_mode(config.demo); }
// Add any other exchange-specific configuration}
/** * Optional: Provide custom CCXT options * @returns {Object} CCXT options */export function getOptions() { return { defaultType: "future", // Or other CCXT options // Add any exchange-specific CCXT options };}
Step 3: Register the Exchange Module
Add your exchange module to the registry in watcher/modules/exchanges/index.js
:
import * as yourExchange from "./your-exchange.js";
const exchangeModules = { bybit, okx, mexc, "your-exchange": yourExchange // Add your exchange here};
Step 4: Configure the Exchange
Add exchange configuration to your config/default.json
:
{ "your-exchange": { "apiKey": "your-api-key", "secret": "your-secret", "password": "optional-password", // If required by exchange "market": "future", "demo": false // Other exchange options }}
Step 5: Test the Integration
- Create test orders on the exchange (preferably in demo/testnet mode)
- Start the Horizon watcher with your exchange:
node index.js --exchange your-exchange
- Monitor the logs for order processing and signal generation
Common Challenges and Solutions
Symbol Format Differences
Many exchanges use different symbol formats. You might need to transform them:
function formatSymbol(symbol) { // Example: Convert "BTC-USDT" to "BTCUSDT" return symbol.replace("-", "");}
Order Status Mapping
Exchanges often use different terms for order statuses. Update watcher/modules/ccxt/orderStatus.js
if needed:
export function isFilled(order) { return ( order?.info?.orderStatus === "Filled" || order?.info?.state === "filled" || order?.info?.status === "FILLED" // Add your exchange's status );}
Balance Structure
If the exchange has a unique balance structure, implement the transformation:
export function transformBalance(balance) { return { free: balance.available, used: balance.locked, total: balance.total // Map other fields as needed };}
Testing Guidelines
- Test all order types (market, limit, etc.)
- Verify leverage calculation
- Check balance updates
- Confirm WebSocket updates
- Validate signal generation
- Test error handling
Best Practices
- Error Handling: Always include proper error handling in exchange-specific code
- Logging: Add meaningful logs for debugging
- Documentation: Document any exchange-specific behaviors or limitations
- Configuration: Make exchange-specific settings configurable
- Testing: Test with both live and demo accounts
Troubleshooting
Common issues and solutions:
-
Order not detected
- Check symbol format matching
- Verify order status mapping
- Enable debug logging
-
Invalid leverage calculation
- Verify balance structure
- Check position value calculation
- Log intermediate values
-
WebSocket disconnections
- Implement reconnection logic
- Add heartbeat checks
- Monitor connection state
Security Considerations
- Never commit API credentials
- Use environment variables for sensitive data
- Implement rate limiting
- Handle API errors gracefully
- Validate all exchange responses
Additional Resources
- CCXT Documentation: https://docs.ccxt.com/
- Exchange API Documentation Links
- Horizon Issue Tracker: https://github.com/taoshidev/horizon/issues