Skip to content

SlickQuant/coinbase-advanced-cpp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

70 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Coinbase Advanced API C++ SDK

C++20 License: MIT Static Library Lock-free

A modern C++ SDK for interacting with the Coinbase Advanced API, providing both REST and WebSocket functionality for trading, market data, and account management.

Features

  • REST API Support: Complete implementation of Coinbase Advanced API endpoints for accounts, orders, products, trades, and more
  • WebSocket Support: Real-time market data streaming with level2, ticker, market trades, and user data channels
    • Connection lifecycle callbacks for connect/disconnect events
    • Per-client sequence number tracking for multiple concurrent connections
    • Explicit shutdown control with stop() method
  • Shared Multiplexer: Multiple WebSocketClient instances share one slick::stream_buffer_multiplexer — unified lock-free ring-buffer allocation and a single fan-in queue across all symbols
  • Cross-process IPC: Producer buffers and the fan-in queue can be placed in named shared memory; a second process can attach and read the same raw JSON stream without an extra network connection
  • Comprehensive Order Types: Support for market, limit, stop limit, bracket, and TWAP orders
  • Type Safety: Full C++ type definitions for all API responses with JSON serialization/deserialization
  • Modern C++: Uses C++20 features, RAII, smart pointers, and modern C++ best practices
  • Async/Await Support: Built-in async support using C++ coroutines
  • Thread-Safe: Designed for multi-threaded applications with lock-free data structures

Architecture

The SDK is organized into several key components:

include/coinbase/
├── account.hpp          # Account and balance management
├── auth.hpp             # Authentication utilities
├── candle.hpp           # Candlestick data
├── common.hpp           # Common types and enums
├── convert.hpp          # Currency conversion (Convert) data models
├── fill.hpp             # Fill data
├── futures.hpp          # Futures (CFM) data models
├── key_permissions.hpp  # API key permissions (Data API) data models
├── logging.hpp          # Deprecated logging compatibility wrapper
├── market_data.hpp      # Market data structures
├── order.hpp            # Order management
├── payment_method.hpp   # Payment methods data models
├── perpetuals.hpp       # Perpetuals (INTX) data models
├── portfolio.hpp        # Portfolios data models
├── position.hpp         # Position management
├── price_book.hpp       # Price book data
├── product.hpp          # Product information
├── rest.hpp             # REST client implementation
├── rest_awaitable.hpp   # Async REST operations
├── side.hpp             # Order side definitions
├── trades.hpp           # Trade data
├── utils.hpp            # Utility functions
└── websocket.hpp        # WebSocket client implementation

Installation

Prerequisites

  • C++20 compatible compiler (GCC 10+, Clang 10+, MSVC 2019+)
  • CMake 3.20+
  • OpenSSL
  • nlohmann/json (JSON library)
  • jwt-cpp (JSON Web Token library)
  • slick-net (networking library - automatically fetched via CMake)
  • vcpkg (optional, dependency management)

Building

mkdir build
cd build
cmake ..
cmake --build . --config Release
cmake --install . --prefix /usr/local  # or your preferred install location

The library is built as a static library for optimal compilation performance in downstream projects.

Using vcpkg (optional)

This repo includes a vcpkg.json manifest. If you use vcpkg, dependencies are installed automatically via the toolchain file. If a vcpkg port for slick-net is available, it will be used; otherwise CMake falls back to FetchContent.

cmake -S . -B build \
  -DCMAKE_TOOLCHAIN_FILE=C:/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake
cmake --build build

Usage

REST Client

#include <coinbase/rest.hpp>

// Create client
coinbase::CoinbaseRestClient client;

// Get server time
auto timestamp = client.get_server_time();

// List accounts
auto accounts = client.list_accounts();

// Get product information
auto product = client.get_product("BTC-USD");

// Create order
auto response = client.create_order(
    "client_order_id_123",
    "BTC-USD",
    coinbase::Side::BUY,
    coinbase::OrderType::LIMIT,
    coinbase::TimeInForce::GOOD_UNTIL_CANCELLED,
    0.001,
    50000.0,
    true  // post_only
);

// Cancel order
auto cancel_response = client.cancel_orders({"order_id_123"});

// Portfolios
auto portfolios = client.list_portfolios();
auto breakdown = client.get_portfolio_breakdown(portfolios.front().uuid);

// Convert (quote only - commit_convert_trade executes a real conversion)
auto quote = client.create_convert_quote(from_account_uuid, to_account_uuid, 10.0);

// Futures (CFM)
auto balance_summary = client.get_futures_balance_summary();
auto positions = client.list_futures_positions();

Async REST Client

CoinbaseAwaitableRestClient mirrors every CoinbaseRestClient method as a C++20 coroutine returning asio::awaitable<T>, so the same endpoints (including all of the ones listed in API Endpoints) can be awaited from coroutine-based code:

#include <coinbase/rest_awaitable.hpp>

coinbase::CoinbaseAwaitableRestClient client;

asio::awaitable<void> run() {
    auto accounts = co_await client.list_accounts();
    auto portfolios = co_await client.list_portfolios();
    auto permissions = co_await client.get_api_key_permissions();
    // ...
}

WebSocket Client

The SDK provides two callback mechanisms for handling WebSocket data:

  1. WebsocketCallbacks: Callbacks are invoked directly on the WebSocket thread. Use this for simple applications or when you want immediate processing.

  2. UserThreadWebsocketCallbacks: Callbacks are invoked on your own thread. The WebSocket data is queued in a lock-free queue, and you control when to process it by calling processData(). Use this for better control over threading and to avoid blocking the WebSocket thread.

Using WebsocketCallbacks (WebSocket Thread)
#include <coinbase/websocket.hpp>

class MyCallbacks : public coinbase::WebsocketCallbacks {
public:
    // Connection lifecycle callbacks
    void onMarketDataConnected(coinbase::WebSocketClient* client) override {
        // Handle market data connection established
    }

    void onMarketDataDisconnected(coinbase::WebSocketClient* client) override {
        // Handle market data disconnection
    }

    void onUserDataConnected(coinbase::WebSocketClient* client) override {
        // Handle user data connection established
    }

    void onUserDataDisconnected(coinbase::WebSocketClient* client) override {
        // Handle user data disconnection
    }

    // Data callbacks
    void onLevel2Snapshot(coinbase::WebSocketClient* client, uint64_t seq_num,
                          const coinbase::Level2UpdateBatch& snapshot) override {
        // Handle level2 snapshot
    }

    void onLevel2Updates(coinbase::WebSocketClient* client, uint64_t seq_num,
                         const coinbase::Level2UpdateBatch& updates) override {
        // Handle level2 updates
    }

    void onMarketTrades(coinbase::WebSocketClient* client, uint64_t seq_num,
                        const std::vector<coinbase::MarketTrade>& trades) override {
        // Handle market trades
    }

    // Error callbacks
    void onMarketDataError(coinbase::WebSocketClient* client, std::string&& err) override {
        // Handle market data errors
    }

    void onUserDataError(coinbase::WebSocketClient* client, std::string&& err) override {
        // Handle user data errors
    }

    // ... other callback methods
};

// Create WebSocket client
MyCallbacks callbacks;
coinbase::WebSocketClient client(&callbacks);

// Subscribe to channels
std::vector<std::string> product_ids = {"BTC-USD", "ETH-USD"};
std::vector<coinbase::WebSocketChannel> channels = {
    coinbase::WebSocketChannel::LEVEL2,
    coinbase::WebSocketChannel::TICKER
};
client.subscribe(product_ids, channels);

// Explicitly stop WebSocket connections when done
client.stop();
Using UserThreadWebsocketCallbacks (User Thread)
#include <coinbase/websocket.hpp>

class MyCallbacks : public coinbase::UserThreadWebsocketCallbacks {
public:
    // Same callback signatures as WebsocketCallbacks
    void onMarketDataConnected(coinbase::WebSocketClient* client) override {
        // Handle market data connection established
    }

    void onLevel2Snapshot(coinbase::WebSocketClient* client, uint64_t seq_num,
                          const coinbase::Level2UpdateBatch& snapshot) override {
        // Handle level2 snapshot
    }

    // ... other callback methods
};

// Create callbacks and WebSocket client
MyCallbacks callbacks;
coinbase::WebSocketClient client(&callbacks);

// Subscribe to channels
client.subscribe({"BTC-USD", "ETH-USD"}, {
    coinbase::WebSocketChannel::LEVEL2,
    coinbase::WebSocketChannel::TICKER
});

// In your main loop or dedicated thread, process queued data
while (running) {
    // Process up to 100 queued messages per call
    callbacks.processData(100);

    // Your other application logic here
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
}

client.stop();

Key Differences:

  • WebsocketCallbacks: Immediate processing on WebSocket I/O thread. Simple but can block WebSocket operations if callbacks are slow.
  • UserThreadWebsocketCallbacks: Deferred processing on your thread. Better performance and control, but requires calling processData() regularly. Uses lock-free queues for efficient data transfer between threads.
Multiple symbols with a shared multiplexer

Multiple WebSocketClient instances can share one slick::stream_buffer_multiplexer. Each client is assigned a non-overlapping range of producer IDs via producer_offset. A single processData() drains messages from all symbols in arrival order.

slick::stream_buffer_multiplexer mux(1u << 17);

// BTC uses producer IDs 0–3, ETH uses 4–7
coinbase::WebSocketClient ws_btc(&callbacks, mux, MD_URL, "", 0);
coinbase::WebSocketClient ws_eth(&callbacks, mux, MD_URL, "",
                                  coinbase::ProducerType::_PRODUCER_TYPE_COUNT_);

while (running) {
    callbacks.processData(200);  // drains both symbols
}
Cross-process market data logging

Passing md_read_buffer_shm_name to the WebSocketClient constructor places the MD_DATA producer buffer in named shared memory. Combining this with a shared-memory fan-in queue lets a second process attach and read the same raw JSON stream — no extra network connection required.

// Process A — producer
slick::stream_buffer_multiplexer mux(1u << 17, "my_mux_queue");
coinbase::WebSocketClient ws(&callbacks, mux, MD_URL, "", 0,
    1u << 26, 1u << 16, "my_btc_md_buf");

// Process B — reader (no WebSocket, no coinbase parsing)
slick::stream_buffer_multiplexer reader_mux("my_mux_queue");
reader_mux.add_producer(0 /*MD_DATA producer_id*/, "my_btc_md_buf");

uint64_t cursor = reader_mux.initial_reading_index();
while (running) {
    if (auto rec = reader_mux.read(cursor)) {
        // rec.data points to raw JSON bytes; rec.length is the frame size
        std::cout.write(reinterpret_cast<const char*>(rec.data), rec.length);
    }
}

See examples/multi_websockets_ws_callbacks.cpp (producer) and examples/multi_websockets_ws_callbacks_reader.cpp (cross-process reader) for a complete two-symbol demo.

Logging

The SDK uses slick-net's logging hooks directly. Include <slick/net/logging.hpp>, install a process-wide handler with slick::net::set_log_handler(), and then use the LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, and LOG_FATAL macros from slick-net.

For source compatibility, <coinbase/logging.hpp> remains available as a deprecated forwarding header. New code should include <slick/net/logging.hpp> and use slick::net::* directly.

set_log_handler() takes two callables:

  • A LogHandler: void(slick::net::LogLevel level, const char* format_text, std::format_args args)
  • An optional LogLevelGetter: slick::net::LogLevel(), which returns the current minimum enabled level

The level getter is checked before the macro evaluates the format arguments. For example, when the getter returns LogLevel::Info, LOG_DEBUG("{}", expensive()) does not call expensive(). Return LogLevel::Off to disable every log level.

Install the handler before starting REST or WebSocket work, and clear it after shutdown if the objects captured by the handler are about to be destroyed. The handler can run on slick-net worker threads, so captured sinks should be thread-safe or otherwise synchronized.

#include <format>
#include <iostream>

#include <slick/net/logging.hpp>

namespace {

const char* log_level_name(slick::net::LogLevel level) noexcept {
    switch (level) {
        case slick::net::LogLevel::Trace: return "TRACE";
        case slick::net::LogLevel::Debug: return "DEBUG";
        case slick::net::LogLevel::Info:  return "INFO";
        case slick::net::LogLevel::Warn:  return "WARN";
        case slick::net::LogLevel::Error: return "ERROR";
        case slick::net::LogLevel::Fatal: return "FATAL";
        case slick::net::LogLevel::Off:   return "OFF";
    }
    return "UNKNOWN";
}

} // namespace

int main() {
    slick::net::set_log_handler(
        [](slick::net::LogLevel level, const char* format_text, std::format_args args) {
            std::cout << '[' << log_level_name(level) << "] ";

            try {
                std::cout << std::vformat(format_text, args);
            } catch (...) {
                // Keep logging non-throwing even if formatting fails.
                std::cout << format_text;
            }

            std::cout << '\n';
        },
        [] {
            return slick::net::LogLevel::Info; // Trace/Debug are skipped.
        }
    );

    LOG_INFO("Coinbase SDK initialized for {}", "BTC-USD");
    LOG_DEBUG("This message and its arguments are skipped at Info level");

    // Create/use Coinbase REST or WebSocket clients here.

    slick::net::clear_log_handler();
}

If your application already uses slick-logger, bridge slick-net into it instead of formatting messages yourself:

#include <slick/logger.hpp>
#include <slick/net/logging.hpp>

void configure_logging() {
    auto& logger = slick::logger::Logger::instance();
    logger.clear_sinks();
    logger.add_console_sink(true, true);
    logger.set_level(slick::logger::LogLevel::L_DEBUG);
    logger.init(1024, 16 * 1024 * 1024);

    slick::net::set_log_handler(
        [&logger](slick::net::LogLevel level, const char* format_text,
                  std::format_args args) {
            logger.log(static_cast<slick::logger::LogLevel>(level),
                       format_text, args);
        },
        [] {
            return static_cast<slick::net::LogLevel>(logger.get_level());
        }
    );
}

void shutdown_logging() {
    slick::net::clear_log_handler();
}

When using the slick-logger bridge, link your application to slick::logger in addition to this SDK:

target_link_libraries(my_app PRIVATE coinbase-advanced-cpp slick::logger)

API Endpoints

REST API

  • Accounts: List accounts, get account details
  • Products: List products, get product details
  • Orders: Create, list, get, modify, and cancel orders
  • Fills: List fills
  • Fees: Get taker and maker fee rates
  • Market Data: Get best bid/ask, price book, market trades, candles
  • Time: Get server time
  • Portfolios: List portfolios, create/edit/delete a portfolio, get portfolio breakdown, move funds between portfolios
  • Convert: Create a convert quote, get a convert trade, commit a convert trade
  • Payment Methods: List payment methods, get payment method details
  • Data API: Get API key permissions
  • Futures (CFM): Get balance summary, list/get positions, schedule/list/cancel sweeps, get/set intraday margin settings, get current margin window
  • Perpetuals (INTX): Allocate portfolio, get portfolio summary, list/get positions, get portfolio balances, opt in/out of multi-asset collateral

All REST endpoints are available on both the synchronous CoinbaseRestClient and the coroutine-based CoinbaseAwaitableRestClient (see Async REST Client).

WebSocket API

  • Level2: Order book updates
  • Ticker: Price updates
  • Market Trades: Trade updates
  • User: User-specific data (orders, positions)
  • Candles: Candlestick data
  • Status: Product status updates

Order Types

The SDK supports multiple order types:

  • Market Orders: Execute immediately at market price
  • Limit Orders: Execute at specified price
  • Stop Limit Orders: Execute when stop price is hit, then as limit order
  • Bracket Orders: Place both stop loss and take profit orders
  • TWAP Orders: Split order execution over time

Authentication

The SDK handles JWT authentication automatically using the coinbase::generate_coinbase_jwt function. You need to provide your API key and secret.

Examples

The examples/ directory contains five self-contained programs. Build them with -DBUILD_COINBASE_ADVANCED_EXAMPLES=ON:

Executable Description
market_data_ws_callbacks Single-client market data with WebsocketCallbacks (I/O thread)
market_data_user_thread_callbacks Single-client market data with UserThreadWebsocketCallbacks; also queries REST endpoints
multi_websockets_ws_callbacks BTC-USD + ETH-USD, one WebSocketClient each, shared mux; callbacks on I/O thread; mux and MD buffers in named shared memory
multi_websockets_user_thread_callbacks BTC-USD + ETH-USD, shared mux, single processData() loop; per-symbol order books printed every 5 s
multi_websockets_ws_callbacks_reader Cross-process reader — attaches to the shared memory written by multi_websockets_ws_callbacks and logs raw JSON; start the producer first

All examples require no API credentials for public market-data channels (TICKER, LEVEL2, MARKET_TRADES).

Testing

The SDK includes comprehensive unit tests using Google Test. To run tests:

cd build
cmake -DBUILD_COINBASE_ADVANCED_TESTS=ON ..
cmake --build .
cd tests
./coinbase_advance_tests

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please submit a pull request with your changes.

Support

For issues and questions, please open an issue on the GitHub repository.

About

A header-only modern C++20 SDK for the Coinbase Advanced API: REST + WebSocket trading, market data, and accounts

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Contributors