Home » C++ Crypto Trading Application Part I: Project Creation
trading systems

C++ Crypto Trading Application Part I: Project Creation

This article, “C++ Trading Application Part I: Project Creation” is the first write up of a series aimed at developers and crypto-enthusiasts interested in building their own automated low-latency trading systems using C++. We will explore how to set up a C++ application that simulates purchasing Bitcoin every month, leveraging modern development tools and practices. Our focus will not only be on implementing the functional aspects of the trading algorithm but also on ensuring that our setup is robust, scalable, and easily testable.

In this part I, we provide a guide to setting up a C++ application designed to simulate Dollar Cost Averaging (DCA) into Bitcoin using a test Coinbase API. It covers the setup of the development environment, configuring and building a project with CMake, crafting a Dockerfile for containerization, and implementing the core logic to mock buying Bitcoin using exchange APIs.

1. Setting Up a Simple Code Structure

Let’s first start with a basic tree of files for our C++ trading application:

trade/
├── Dockerfile
├── LICENSE
├── README.md
├── CMakeLists.txt
└── src
    ├── CMakeLists.txt
    └── trade.cpp

At the root level, we find several key files:

  • Dockerfile: This file contains all the necessary commands to assemble the Docker image for the application, ensuring that the environment is consistent and all dependencies are correctly installed wherever the Docker container is deployed.
  • LICENSE: This document provides the legal framework under which the software is distributed, detailing the permissions, limitations, and responsibilities associated with the use of the project.
  • README.md: Essential for any project, this markdown file offers an overview of the project, including setup instructions, a description of its functionality, and any other critical information necessary for users and contributors.
  • CMakeLists.txt: Located at the root of the project, this configuration file is used by CMake to generate the build files for the project. It typically includes project-wide settings, the list of subdirectories to process, and any global dependencies or compiler options.
See also  Cardano ADA Breaks $0.4!

Within the src directory, the organization focuses on the source files and local settings:

  • CMakeLists.txt: Specific to the src directory, this CMakeLists file handles configurations related to the compilation of source files within this directory. It might specify additional compilation flags, include directories, or link libraries specific to the source files.
  • trade.cpp: This is the main source file for the project, containing the core logic for the trading application. Here, the functionalities to connect to the cryptocurrency exchange, execute trade commands, and any utility functions necessary to handle data processing are implemented.

This structure not only ensures that essential elements like licensing and project documentation are not overlooked but also that the build configurations are cleanly separated from the source code, promoting better maintenance and scalability of the project.

2. Source Code

This is a first prototype in trade.cpp calling the coinbase API with a simple transaction (we start gentle!) buying Bitcoin.

The order is in bold, $10 of BTC is purchased at the execution of the application:

#include <iostream>
#include <vector>
#include <string>
#include <ctime>
#include <curl/curl.h>
#include <openssl/hmac.h>
#include <nlohmann/json.hpp>

// Helper function to write data received from libcurl
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
    userp->append((char*)contents, size * nmemb);
    return size * nmemb;
}

// Function to generate HMAC-SHA256 signature
std::string create_signature(const std::string& secret_key, const std::string& timestamp, const std::string& method, const std::string& request_path, const std::string& body = "") {
    std::vector<unsigned char> key(secret_key.begin(), secret_key.end());
    std::string data = timestamp + method + request_path + body;

    unsigned int len = EVP_MAX_MD_SIZE;
    std::vector<unsigned char> hash(len);

    HMAC(EVP_sha256(), key.data(), key.size(),
         reinterpret_cast<const unsigned char*>(data.c_str()), data.length(),
         hash.data(), &len);

    hash.resize(len);
    return std::string(hash.begin(), hash.end());
}

// Function to perform the API request to buy Bitcoin
void buy_bitcoin(const std::string& api_key, const std::string& passphrase, const std::string& secret_key) {
    std::string timestamp = std::to_string(std::time(nullptr));  // Current Unix timestamp
    std::string method = "POST";
    std::string request_path = "/orders";
    nlohmann::json order = {
        {"type", "market"},
        {"side", "buy"},
        {"product_id", "BTC-USD"},
        {"funds", "10"}
    };

    std::string body = order.dump();
    std::string signature = create_signature(secret_key, timestamp, method, request_path, body);
    
    std::string url = "https://api-public.sandbox.pro.coinbase.com" + request_path;
    CURL* curl = curl_easy_init();
    std::string response;

    if (curl) {
        struct curl_slist* headers = NULL;
        headers = curl_slist_append(headers, "Content-Type: application/json");
        headers = curl_slist_append(headers, ("CB-ACCESS-KEY: " + api_key).c_str());
        headers = curl_slist_append(headers, ("CB-ACCESS-SIGN: " + signature).c_str());
        headers = curl_slist_append(headers, ("CB-ACCESS-TIMESTAMP: " + timestamp).c_str());
        headers = curl_slist_append(headers, ("CB-ACCESS-PASSPHRASE: " + passphrase).c_str());

        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

        CURLcode res = curl_easy_perform(curl);
        if (res != CURLE_OK) {
            std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
        } else {
            std::cout << "Response: " << response << std::endl;
        }

        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    }
}

int main() {
    std::string api_key = "your_api_key";
    std::string passphrase = "your_api_passphrase";
    std::string secret_key = "your_api_secret";

    buy_bitcoin(api_key, passphrase, secret_key);
    return 0;
}

You can replace your coinbase API keys here, we will elaborate on the details in the next article:

std::string api_key = "your_api_key";     
std::string passphrase = "your_api_passphrase";     
std::string secret_key = "your_api_secret";

3. Compilation

Before talking about compilation, let’s talk about the different files to make it happen.

2 files in this project: the CMakeLists.txt at the root and the one in src/.

The root one contains:

# Specify the minimum version of CMake required to build this project
cmake_minimum_required(VERSION 3.10)

# Define the project name and the languages used
project(trade LANGUAGES CXX)

# Set C++ Standard
set(CMAKE_CXX_STANDARD 17)

# Find OpenSSL package
find_package(OpenSSL REQUIRED)

# Include external projects (like CPR)
add_subdirectory(external)

# Include the src directory
add_subdirectory(src)

# Ensure the include directories for OpenSSL are available to the project
include_directories(${OPENSSL_INCLUDE_DIR})

Related Posts