How To Build Your First C++ WebSocket Client
21 February 2024
Welcome to our comprehensive tutorial on accessing Forex, CFD, and Crypto data using TraderMade's Websocket with the powerful C++ programming language. In the financial world, having access to real-time data is crucial for making informed decisions, and this tutorial will guide you through the process seamlessly.
Getting Started
The first step is to sign up for an account with TraderMade and secure a free API Key for a 2-week trial. This key will be our golden ticket to unlocking real-time data. Once obtained, we'll use this key to authenticate our requests. Dive into the Streaming Forex API documentation to better understand the available data.
Let's delve into a practical example to illustrate the entire process. We'll break it down into manageable steps:
Setting up the Coding Environment
C++ is more straightforward to work within a Linux environment. If on Windows, download the Ubuntu app using the search bar.
For those venturing into C++ for the first time, let's ensure we have the GCC compiler for running C++ programs.
sudo apt update
In case you haven't set a password or can't recall it, utilize the following command to reset it:
sudo passwd
With our system up to date, let's install the essential tools for compilation by executing the following:
sudo apt install build-essential
To gain insights into the GCC version we're working with, utilize:
gcc --version
Now that we have the C++ compiler setup, let us import the libraries needed to run our program.
git clone https://github.com/zaphoyd/websocketpp.git cd websocketpp
This will download the WebSocket++ library from its GitHub repository. We also need two more libraries Boost library and SSL library. Use the following commands:
sudo apt-get install libboost-all-dev sudo apt-get install libssl-dev
Writing Code
Let’s import the libraries we need to run our program.
#include <websocketpp/config/asio_client.hpp> #include <websocketpp/client.hpp> #include <iostream>
We will now define aliases using typedef to use further in our code. The websocketpp is a namespace of the WebSocket++ library. The first line of the code is a complex type representing WebSocket client support for Asio (a library for asynchronous I/O operations) and TLS (Transport Layer Security) support.
In the second line shared_ptr is a smart pointer from the WebSocket++ library that manages the object's memory. The type of object that the shared_ptr will manage is defined after the < sign. The context_ptr is an alias that helps you use the longer type defined in the second line. This way it is easier to read the code and have a concise syntax.
typedef websocketpp::client<websocketpp::config::asio_tls_client> client; typedef websocketpp::lib::shared_ptr<websocketpp::lib::asio::ssl::context> context_ptr;
The code below should now be easier to understand, in the first 2 lines _1 and _2 are the first and the second arguments, respectively, and taken by the WebSocket++ function objects and callbacks. The third line defines the bind function that can be used to create function objects with placeholders.
using websocketpp::lib::placeholders::_1; using websocketpp::lib::placeholders::_2; using websocketpp::lib::bind;
Now that we have defined placeholders and bind for callback functions to handle WebSocket events, we will define the functions for each WebSocket event that we are likely to encounter once a connection is established.
We first define the on_open callback function which is invoked when a WebSocket connection is successful. It takes in two parameters: a handle to the connection (hdl) that has opened and a pointer to the WebSocket Client (client* c).
The second line in the code is just a print statement. Followed by an error code for error handling and a connection pointer (connection_ptr ). The “if” statement checks for any error while retrieving the connection pointer. Once no error on the connection, we pass a string payload and send the payload as a text over a WebSocket Connection.
void on_open(websocketpp::connection_hdl hdl, client* c) { std::cout << "WebSocket connection opened!" << std::endl; websocketpp::lib::error_code ec; client::connection_ptr con = c->get_con_from_hdl(hdl, ec); if (ec) { std::cout << "Failed to get connection pointer: " << ec.message() << std::endl; return; } std::string payload = "{"userKey":"API_KEY", "symbol":"EURUSD,GBPUSD"}"; c->send(con, payload, websocketpp::frame::opcode::text); }
Now that we understand the on_open callback function, it is fairly straightforward to understand the following code. The on_message callback function also takes two parameters but the second parameter is the message_ptr msg. This is the forex data received and we print it inside the on_message function.
The on_fail and on_close callbacks don't need much explaining and we can now move to the next set of code.
void on_message(websocketpp::connection_hdl, client::message_ptr msg) { std::cout << "Received message: " << msg->get_payload() << std::endl; } void on_fail(websocketpp::connection_hdl hdl) { std::cout << "WebSocket connection failed!" << std::endl; } void on_close(websocketpp::connection_hdl hdl) { std::cout << "WebSocket connection closed!" << std::endl; }
The following function is responsible for creating and configuring the SSL context used in the WebSocket client for secure communication. We will not go into the details of the function, instead will dive right into the main function where everything comes together.
context_ptr on_tls_init(const char * hostname, websocketpp::connection_hdl) { context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23); try { ctx->set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3 | boost::asio::ssl::context::single_dh_use); ctx->set_verify_mode(boost::asio::ssl::verify_none); } catch (std::exception& e) { std::cout << "TLS Initialization Error: " << e.what() << std::endl; } return ctx; }
In the main function, we first define the client followed by the URL we need to connect to the Forex WebSocket and get sub-second data.
int main(int argc, char* argv[]) { client c; std::string hostname = "marketdata.tradermade.com/feedadv"; std::string uri = "wss://" + hostname; }
We set up a try block inside the main function and catch any exceptions.
int main(int argc, char* argv[]) { .... try { } catch (websocketpp::exception const & e) { std::cout << "WebSocket Exception: " << e.what() << std::endl; } }
Inside the try block, we first set the configuration for the WebSocket++ client followed by initializing Asio for asynchronous operations. We then set up message and event handlers including setting up the TLS (Transport Layer Security) initialization handler. Then we declare an error object ec and check for errors while retrieving a connection. Finally, we create a connection c.connect(con) using a previously created connection and start the WebSocket event loop using c.run() handling the asynchronous events.
try { // Configure WebSocket++ client c.set_access_channels(websocketpp::log::alevel::all); c.clear_access_channels(websocketpp::log::alevel::frame_payload); c.set_error_channels(websocketpp::log::elevel::all); c.init_asio(); // Set message, TLS initialization, open, fail, and close handlers c.set_message_handler(&on_message); c.set_tls_init_handler(bind(&on_tls_init, hostname.c_str(), ::_1)); c.set_open_handler(bind(&on_open, ::_1, &c)); c.set_fail_handler(bind(&on_fail, ::_1)); c.set_close_handler(bind(&on_close, ::_1)); // Enable detailed error logging c.set_error_channels(websocketpp::log::elevel::all); websocketpp::lib::error_code ec; client::connection_ptr con = c.get_connection(uri, ec); if (ec) { std::cout << "Could not create connection because: " << ec.message() << std::endl; return 0; } // Create a connection to the specified url c.connect(con); c.run(); } catch (websocketpp::exception const & e) { std::cout << "WebSocket Exception: " << e.what() << std::endl; }
Once we get all the code we save the code in a file and name it FX_WebSocket.cpp (you can name it as you please) and then compile the program using the following command:
g++ -o ./streaming_forex FX_WebSocket.cpp -lssl -lcrypto -pthread
Your program should now be compiled in the streaming_forex file. Use the following command to run the file:
./streaming_forex
Voila! You should now see a WebSocket connection with the Forex data we subscribed to. Don't forget to replace your API key when sending the payload in the on_open function.
Output
Hurray! We have our output ready
Received message: Connected Received message: {"symbol":"GBPUSD","ts":"1708403061263","bid":1.25867,"ask":1.25871,"mid":1.25869} Received message: {"symbol":"GBPUSD","ts":"1708403070164","bid":1.25867,"ask":1.2587,"mid":1.258685} Received message: {"symbol":"GBPUSD","ts":"1708403070951","bid":1.25866,"ask":1.2587,"mid":1.25868} Received message: {"symbol":"GBPUSD","ts":"1708403071022","bid":1.25866,"ask":1.25871,"mid":1.258685} Received message: {"symbol":"GBPUSD","ts":"1708403071213","bid":1.25866,"ask":1.2587,"mid":1.25868} Received message: {"symbol":"GBPUSD","ts":"1708403071400","bid":1.25866,"ask":1.25869,"mid":1.258675} Received message: {"symbol":"EURUSD","ts":"1708403071512","bid":1.07687,"ask":1.07691,"mid":1.07689} Received message: {"symbol":"EURUSD","ts":"1708403071524","bid":1.07687,"ask":1.0769,"mid":1.076885} Received message: {"symbol":"GBPUSD","ts":"1708403072138","bid":1.25866,"ask":1.2587,"mid":1.25868} Received message: {"symbol":"GBPUSD","ts":"1708403074548","bid":1.25866,"ask":1.25869,"mid":1.258675} Received message: {"symbol":"GBPUSD","ts":"1708403074618","bid":1.25866,"ask":1.2587,"mid":1.25868} Received message: {"symbol":"GBPUSD","ts":"1708403074921","bid":1.25866,"ask":1.25869,"mid":1.258675} Received message: {"symbol":"GBPUSD","ts":"1708403075348","bid":1.25866,"ask":1.2587,"mid":1.25868} Received message: {"symbol":"GBPUSD","ts":"1708403077083","bid":1.25866,"ask":1.25869,"mid":1.258675}
Congratulations! You've just built a real-time Forex data fetcher using C++ and WebSocket++. Feel free to explore and modify the code for your specific needs and enjoy the world of live financial data at your fingertips.
Full code
Here, we are giving full code
#include <websocketpp/config/asio_client.hpp> #include <websocketpp/client.hpp> #include <iostream> typedef websocketpp::client<websocketpp::config::asio_tls_client> client; typedef websocketpp::lib::shared_ptr<websocketpp::lib::asio::ssl::context> context_ptr; using websocketpp::lib::placeholders::_1; using websocketpp::lib::placeholders::_2; using websocketpp::lib::bind; void on_open(websocketpp::connection_hdl hdl, client* c) { std::cout << "WebSocket connection opened!" << std::endl; websocketpp::lib::error_code ec; client::connection_ptr con = c->get_con_from_hdl(hdl, ec); if (ec) { std::cout << "Failed to get connection pointer: " << ec.message() << std::endl; return; } std::string payload = "{"userKey":"API_KEY", "symbol":"EURUSD,GBPUSD"}"; c->send(con, payload, websocketpp::frame::opcode::text); } void on_message(websocketpp::connection_hdl, client::message_ptr msg) { std::cout << "Received message: " << msg->get_payload() << std::endl; } void on_fail(websocketpp::connection_hdl hdl) { std::cout << "WebSocket connection failed!" << std::endl; } void on_close(websocketpp::connection_hdl hdl) { std::cout << "WebSocket connection closed!" << std::endl; } context_ptr on_tls_init(const char * hostname, websocketpp::connection_hdl) { context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23); try { ctx->set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3 | boost::asio::ssl::context::single_dh_use); } catch (std::exception& e) { std::cout << "TLS Initialization Error: " << e.what() << std::endl; } return ctx; } int main(int argc, char* argv[]) { client c; std::string hostname = "marketdata.tradermade.com/feedadv"; std::string uri = "wss://" + hostname; try { c.set_access_channels(websocketpp::log::alevel::all); c.clear_access_channels(websocketpp::log::alevel::frame_payload); c.set_error_channels(websocketpp::log::elevel::all); c.init_asio(); c.set_message_handler(&on_message); c.set_tls_init_handler(bind(&on_tls_init, hostname.c_str(), ::_1)); c.set_open_handler(bind(&on_open, ::_1, &c)); c.set_fail_handler(bind(&on_fail, ::_1)); c.set_close_handler(bind(&on_close, ::_1)); c.set_error_channels(websocketpp::log::elevel::all); // Enable detailed error logging websocketpp::lib::error_code ec; client::connection_ptr con = c.get_connection(uri, ec); if (ec) { std::cout << "Could not create connection because: " << ec.message() << std::endl; return 0; } c.connect(con); c.run(); } catch (websocketpp::exception const & e) { std::cout << "WebSocket Exception: " << e.what() << std::endl; } }
Moving Forward
TraderMade's Live Streaming API is an accessible and intuitive resource for crafting digital solutions and extracting meaningful insights. Feel free to leverage its potential in C++ to create remarkable applications, and don't forget to share your creations with us.
Embark on your journey by registering and delving into the intricacies outlined in the Live Streaming API documentation. If you require further guidance, do not hesitate to contact us through live chat or drop us an email at support@tradermade.com. We're here to assist you in your development endeavors.