Introduction

Being able to add a limit order to the order book or placing a marker order a second or even a couple of microseconds faster is the difference between being in profit or at a loss. This is exacerbated by times of congestions where placing an order through the main site is difficult. By using the Rest API it is possible to create a trading interface or even trading bots that mitigates this.
I will be focusing on how to use C++ with the libcurl, rapidjson, and libcrypto++ libraries to interface with the Rest API. Why use C++ when there are other languages that allow for extremely easy interfacing with Rest APIs?, you might ask. Well, C++ is closer to the metal, per say, leading to smaller latency, if programmed correctly. This specific post will focus on creating a class that will sign the HTTP requests we send to Coinbase Pro's Rest API, and a class that does the sending.
Note: You could use the FIX API for even less latency since you would only need to authenticate once.

Authentication

Firstly, I found that containing the functions and variables that authenticate messages in a class for abstraction was good. I won't say that this is the most optimal or most secure.
It will contain the API key, the secret, and the passphrase variables together with the a function that signs and a function that creates a time stamp string with the current Unix Epoch Time.

// Auth.h
#ifndef AUTH_H
#define AUTH_H

#include <string>

class Auth
{
public:
  std::string Key;
  std::string Secret;
  std::string Passphrase;
  std::string Sign(std::string time_stamp, std::string method, std::string path, std::string body);
  std::string GetTimestamp();
  
  Auth() {}
  Auth(std::string key, std::string secret, std::string passphrase);
};

#endif // AUTH_H

The function that does the signing will use the libcrypto++ library. In ubuntu it can be install with the libcrypto++-dev package. It will take in as parameters the timestamp, the method, for example "GET" or "POST", the path, like for the url "http://api.pro.coinbase.com/orders" it will be "/orders", and the body of the message. It will take in the Secret through the global variable of the class.
Coinbase uses HMAC SHA256 to sign the messages, so we will need the following header files from the libcrypto++ library.

// Auth.cpp
#include "Auth.h"
#include <iostream>
#include "cryptopp/cryptlib.h"
#include "cryptopp/hmac.h"
#include "cryptopp/sha.h"
#include "cryptopp/base64.h"
#include "cryptopp/filters.h"

We will use the following classes from the CryptoPP namespace

using CryptoPP::Exception;
using CryptoPP::HMAC;
using CryptoPP::SHA256;
using CryptoPP::Base64Decoder;
using CryptoPP::Base64Encoder;
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::HashFilter;

Let's start building the function

std::string Auth::Sign(std::string time_stamp, std::string method, std::path, std::string body)
{
  // TODO
}

Declare some temporary variables to store the intermediary forms and create the string that will be the encoded.

std::string mac, encoded, key;
std::string plain = time_stamp + method + path + body;

The Secret is encoded in Base 64 which we will need to decode. We will use the StringSource, Base64Decoder, and StringSink classes.

StringSource(Secret, true, 
                new Base64Decoder(
                    new StringSink(key)));

Now, while wrapped in try, create an HMAC object and pass through the HashFilter

try {
  HMAC<SHA256> hmac((unsigned char*)key.c_str(), key.length());
  StringSource(plain, true, 
                  new HashFilter(hmac,
                      new StringSink(mac)));
}
catch(const CryptoPP::Exception& e) {
  std::cout << e.what() << std::endl;
}

Encode the mac string back to Base64

StringSource(mac, true,
                new Base64Encoder(
                    new StringSink(encoded)));

Finally, Crypto++ likes to add a space at the end of the encoded string and we have to remove it and return the result.

encoded.erase(44, 1);
return encoded;

Big thanks to woodja from github. link

Final Touches

Now just create the constructor and the function to create the time stamp.

std::string Auth::GetTimestamp()
{
  std::time_t t = time(0);
  return std::to_string(t);
}
Auth::Auth(std::string key, std::string secret, std::string passphrase)
{ Key = key; Secret = secret; Passphrase = passphrase; }

This class now can be used to authenticate message.


Sending Messages

Let's create a new class called "API" that will handle the sending of messages.

// Api.h
#ifndef API_H
#define API_H

#include "Auth.h"
#include <string>

class API
{
private:
  std::string Call(std::string method, bool authed, std::string path, std::string body);
public:
  API();
  ~API();
  Auth auth;
  std::string uri;
  std::string product_id;
  std::string Get_Buy_Price();
  double Get_Balance(std::string currency);
  std::string Place_Limit_Order(std::string side, std::string price, std::string size);
};
#endif //API_H

The Call function is the function that will directly interface with libcurl to send and receive information. We will use the constructor to make sure that the curl_global_init is only used once. The uri string will have the uri of the service to interact with like Coinbase Pro's Rest API's URI. The product_id string will have the currency pair to be used like "BTC-USD" where BTC is Bitcoin and USD the US Dollar. Get_Buy_Price gets the buy price, Get_Balance gets your balance, and Place_Limit_Order places a limit order.
For the call function, firstly, make sure libcurl is included

#include "curl/curl.h"

The Instruction on curl.haxx.se will be followed on how to used libcurl with some exceptions. We will create custom headers to include the authentication information and the Content Type. And, for "POST" methods we will attach the body string to the message.

struct curl_slist *chunk = NULL;
curl = curl_slist_append(chunk, "Content-Type: application/json");
if (authed)
{
  std::string time_stamp = auth.GetTimestamp();
  std::string sign = auth.Sign(time_stamp, method, path, body);
  chunk = curl_slist_append(chunk, ("CB-ACCESS-KEY: " + auth.Key).c_str());
  chunk = curl_slist_append(chunk, ("CB-ACCESS-SIGN: " + sign).c_str());
  chunk = curl_slist_append(chunk, ("CB-ACCESS-TIMESTAMP: " + time_stamp).c_str());
  chunk = curl_slist_append(chunk, ("CB-ACCESS-PASSPHRASE: " + auth.Passphrase).c_str());
}
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);

and for the "POST" methods

if (method == "POST")
{
  curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1L);
  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
}

We will create a function called WriteCallback that will be used to create a string from the response.

static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
  ((std::string*)userp)->append((char*)contents, size * nmemb);
  return size * nmemb;
}

And, place it in the call function

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);

Where readBuffer is an empty string. To see the details click on the link below named source code. For the following code, we will be using rapidjson include rapidjson and call the rapidjson namespace.

#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
using namespace rapidjson;

To get the buy price, according to the API Documentation, we will GET "/products/<product-id>/book" and grab the first bids price. So, call the Call function

std::string st = Call("GET", false, "/products/" + product_id + "/book", "");

Then parse it

Document d;
d.Parse(st.c_str());

And, Finally get the first bids value.

const Value& c = d["bids"];
const Value& b = c[0];
std::string ret = b[0].GetString();

I excluded some of the checks that are on the source code.
The Get_Balance function will iterate through the array when calling GET /accounts and find the one that has the same currency as the currency string.

std::string txt = Call("GET", true, "/accounts", "");
Document d;
d.Parse(txt.c_str());
for (SizeType i = 0; i < d.Size(); i++)
{
  std::string cur = d[i]["currency"].GetString();
  if (cur == currency)
    return std::stod(d[i]["available"].GetString());
}
etc...

The last function, "Place_Limit_Order", we will need to create a Document object and add values to it with it's allocator.

Document d;
d.SetObject();
rapidjson::Document::AllocatorType& allocator = d.GetAllocator();

And to add an element to the Document

Value v_side;
v_side = StringRef(side.c_str());
d.AddMember("side", v_size, allocator);

Use the same format for the other properties required to place a Limit Order.


Putting It Together

We can try it out using Coinbase Pro's Sandbox (Link). On the top right side, after logging into your account, there is a menu with the "API" option. It the page you can create a API Key to be used with your application. It will need Trade and View privileges.
For the main function we will declare strings with the parameters and authentication information.

std::string api_key = "";
std::string secret = "";
std::string passcode = "";
std::string uri = "https://api-public.sandbox.pro.coinbase.com";
std::string product_id = "BTC-USD";
std::string currency = "BTC";

Be sure to have a balance in your fake account. And finally, use the classes we created and display the results

Auth auth(api_key, secret, passcode);
API api;
api.uri = uri;
api.product_id = product_id;
api.auth = auth;
std::string buy_price = api.Get_Buy_Price();
double balance = api.Get_Balance(currency);
std::string returned = api.Place_Limit_Order("sell", "20000", "1");
std::cout << "current buy price: " << buy_price << std::endl;
std::cout << "your balance: " << balance << " " << currency << std::endl;
std::cout << "Return from placing Limit Order: " << returned << std::endl;

Source Code