I am trying to create my own algorithmic trading system using C++. I have searched the web for a nice tutorial for such systems and I didn't find any. Then I started to learn about curl
and jsoncpp
libraries and even documentation of those libraries are not made for a novice programmer like me. I mostly reused most of the code for creating this scraper especially WriteCallback
function which I directly copy pasted from other C++ code without understanding it. I am sharing this whole code because someone else doesn't need to reinvent the wheel and can use this code as a template. Anyway this code is working properly (Ubuntu 14.04, CodeBlocks IDE, API: fixer.io) and any kind of suggestion or feedback is welcomed.
#include <string.h>
#include <iostream>
#include <stdio.h>
#include <jsoncpp/json/json.h>
#include <curl/curl.h>
//writing call back function for storing fetched values in memory
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;
}
int main(void)
{
std::string readBuffer;
//global initiliation of curl before calling a function
curl_global_init( CURL_GLOBAL_ALL );
//creating session handle
CURL * myHandle;
// We’ll store the result of CURL’s webpage retrieval, for simple error checking.
CURLcode result;
// notice the lack of major error-checking, for brevity
myHandle = curl_easy_init ( ) ;
//after creating handle we ill start transfering webpage
//curl_easy_setopt is used to tell libcurl how to behave.
//By setting the appropriate options, the application can change libcurl's behavior.
//CURLOPT_URL provide the URL to use in the request. Pass in a pointer to the URL to work with.
//sample json output >> {"base":"EUR","date":"2016年07月22日","rates":{"GBP":0.84108,"USD":1.1014}}
curl_easy_setopt(myHandle, CURLOPT_URL, "http://api.fixer.io/latest?symbols=USD,GBP");
/* send all data to this function */
curl_easy_setopt(myHandle, CURLOPT_WRITEFUNCTION, WriteCallback);
/* we pass our 'chunk' struct to the callback function */
curl_easy_setopt(myHandle, CURLOPT_WRITEDATA, &readBuffer);
//perform a blocking file transfer
result = curl_easy_perform( myHandle );
/* check for errors */
if(result != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(result));
}
else {
/*
* Now, our chunk.memory points to a memory block that is chunk.size
* bytes big and contains the remote file.
*
* Do something nice with it!
*/
//std::cout << readBuffer << std::endl;
//json parsing
Json::Value root; // will contains the root value after parsing.
Json::Reader reader;
bool parsingSuccessful = reader.parse( readBuffer, root );
if(not parsingSuccessful)
{
// Report failures and their locations
// in the document.
std::cout<<"Failed to parse JSON"<<std::endl
<<reader.getFormatedErrorMessages()
<<std::endl;
return 1;
}else{
std::cout<<"\nSucess parsing json\n"<<std::endl;
std::cout << root<< std::endl;
std::cout <<"Base = "<< root["base"].asString() << std::endl;
std::cout <<"Date = "<< root["date"].asString() << std::endl;
std::cout <<"Rate GBP ="<< root["rates"]["GBP"].asFloat() << std::endl;
std::cout <<"Rate USD ="<< root["rates"]["USD"].asFloat() << std::endl;
}
}
//End a libcurl easy handle.This function must be the last function to call for an easy session
curl_easy_cleanup( myHandle );
return 0;
}
1 Answer 1
These are C headers
#include <string.h>
#include <stdio.h>
You should probably use the C++ version (I hope you are not using C-String anywhere).
#include <strings>
#include <cstdio>
Header include paths
#include <jsoncpp/json/json.h>
This should probably be json/json.h
and you should specify the location where jsoncpp is installed with compiler flags.
Curl is a C library.
Therefore any callbacks should be C functions (not C++ functions). There is no guarantee in the standard thay they use the same ABI.
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
Should be:
extern "C" size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
Never use C-Casts
C casts are hard to spot (in general). But also using C++ casts you can mark dangerious casts by uisng the appropriate cast.
((std::string*)userp)->append((char*)contents, size * nmemb);
I would use reinterpret_cast<>
here. So that it is easy to spot and you intuitively know that this is a dangerous operation.
reinterpret_cast<std::string*>(userp)->append(static_cast<char*>(contents), size * nmemb);
Always check error codes.
All the following return an error code. You should validate these functions worked by checking this error code.
curl_global_init( CURL_GLOBAL_ALL );
curl_easy_setopt(myHandle, CURLOPT_URL, "http://api.fixer.io/latest?symbols=USD,GBP");
curl_easy_setopt(myHandle, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(myHandle, CURLOPT_WRITEDATA, &readBuffer);
Prefer "\n"
over std::endl
std::cout<<"\nSucess parsing json\n"<<std::endl;
std::cout << root<< std::endl;
std::cout <<"Base = "<< root["base"].asString() << std::endl;
std::cout <<"Date = "<< root["date"].asString() << std::endl;
std::cout <<"Rate GBP ="<< root["rates"]["GBP"].asFloat() << std::endl;
std::cout <<"Rate USD ="<< root["rates"]["USD"].asFloat() << std::endl;
The difference is that std::endl
also calls std::flush
on the stream. There is no need to flush the stream manually. The stream is designed to flush when it needs too. When you do it manually you will make your code much more eneficent.
RAII
myHandle = curl_easy_init ( ) ;
// STUFF
curl_easy_cleanup( myHandle );
When you see this pattern. You should be thinking of using RAII. This will make sure your resources are cleaned up correctly in all situations (including when exceptions are thrown) are the function returns early (aka return 1;
).
ThorSocketStream
library. \$\endgroup\$