Skip to content

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 mappings
const 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

  1. Create test orders on the exchange (preferably in demo/testnet mode)
  2. Start the Horizon watcher with your exchange:
Terminal window
node index.js --exchange your-exchange
  1. 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

  1. Test all order types (market, limit, etc.)
  2. Verify leverage calculation
  3. Check balance updates
  4. Confirm WebSocket updates
  5. Validate signal generation
  6. Test error handling

Best Practices

  1. Error Handling: Always include proper error handling in exchange-specific code
  2. Logging: Add meaningful logs for debugging
  3. Documentation: Document any exchange-specific behaviors or limitations
  4. Configuration: Make exchange-specific settings configurable
  5. Testing: Test with both live and demo accounts

Troubleshooting

Common issues and solutions:

  1. Order not detected

    • Check symbol format matching
    • Verify order status mapping
    • Enable debug logging
  2. Invalid leverage calculation

    • Verify balance structure
    • Check position value calculation
    • Log intermediate values
  3. WebSocket disconnections

    • Implement reconnection logic
    • Add heartbeat checks
    • Monitor connection state

Security Considerations

  1. Never commit API credentials
  2. Use environment variables for sensitive data
  3. Implement rate limiting
  4. Handle API errors gracefully
  5. Validate all exchange responses

Additional Resources