1
\$\begingroup\$

This post is a continuation of my previous post where I used jsoncpp package to fetch exchange rates from fixer.io. In this post I have reused the above code and used it to fetch stock historical price data using C++ and save the fetched data to a external CSV file. You can edit the api url(https://query.yahooapis.com) to pass parameters for stock details and start/ending dates

#include <string.h>
#include <iostream>
#include <stdio.h>
#include <jsoncpp/json/json.h>
#include <curl/curl.h>
#include<fstream>


//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, "https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.historicaldata%20where%20symbol%20%3D%20%22YHOO%22%20and%20startDate%20%3D%20%222009-09-11%22%20and%20endDate%20%3D%20%222010-03-10%22&format=json&diagnostics=true&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=");

 //adding a user agent
  curl_easy_setopt(myHandle, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 6.1; rv:2.2) Gecko/20110201");


   /* 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 <<"No of Days = "<< root["query"]["count"].asInt() << std::endl;

//for opening and writng files
std::ofstream file;

//directory for saving your csv
file.open("/dir/stock.csv");

//assigning csv column names
file<<"Symbol,Date,Open,High,Low,Close,Adj Close,Volume\n";

//json value for fetching quotes
const Json::Value& quotes = root["query"]["results"]["quote"];

//looping through the Yahoo quote list
for (int i = 0; i < quotes.size(); i++){
        std::cout << " Symbol: " << quotes[i]["Symbol"].asString();
        std::cout << "   Date: " << quotes[i]["Date"].asString();
        std::cout << "   Open: " << quotes[i]["Open"].asString();
        std::cout << "   High: " << quotes[i]["High"].asString();
        std::cout << "    Low: " << quotes[i]["Low"].asString();
        std::cout << "  Close: " << quotes[i]["Close"].asString();
        std::cout << "AdjClose: " << quotes[i]["Adj_Close"].asString();
        std::cout << " Volume: " << quotes[i]["Volume"].asString();
        std::cout << std::endl;

 file << quotes[i]["Symbol"].asString()<<","<<quotes[i]["Date"].asString()<<","\
<< quotes[i]["Open"].asString()<<","<<quotes[i]["High"].asString()<<","\
<< quotes[i]["Low"].asString()<<","<<quotes[i]["Close"].asString()<<","\
<< quotes[i]["Adj_Close"].asString()<<","<<quotes[i]["Volume"].asString()<<std::endl;



    }
file.close();

 }

  }




 //End a libcurl easy handle.This function must be the last function to call for an easy session
 curl_easy_cleanup( myHandle );


 return 0;
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I haven't used curl, so this is just some general feedback:

Indentation

Your indentation spacing is very erratic. Sometimes you indent by 1/2 spaces, sometimes by 0, sometimes by 8. This makes it much more difficult to read the code than it needs to be. If it looks like this in your editor, consider using one that will help your format the code appropriately. If it looks better in your editor, then use the preview when posting to CR to make sure it matches.

Bracing

Your bracing style is similarly erratic. You generally seem to prefer same line bracing however if have at least one if statement where the brace is on the next line and you vary whether or not you put a space before the braces. It doesn't matter that much which style you prefer, but try to be consistent with it, it makes code much easier to read.

WriteCallback

This method has the wrong name (at least if it's doing what I think it is). You write to a connection. You read from the connection. This callback is used to process the response that has been read from the host/webserver. It's storing the information in your readBuffer. The method should be a ReadCallback.

Logical Distribution

On the face of it, you're doing a fairly straightforward operation so it's understandable that you have chosen to put all the functionality into your main, however if your break the functionality up into effectively named functions it will help with the readability of your code. For example at first glance it's not obvious that these two calls:

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

Setup the callback method so that readBuffer is passed as userp:

static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)

Wrapping the calls in a method like setup_read_callback(callback_function, read_buffer) might make it more obvious. There would also be scope for re-usability for some of the methods, between your applications (your previous post has very similar curl connection setup).

Error Handling

On some errors you exit the program (failed to parse JSON), however on others you don't result!=CURL_OK. I would assume if curl returns an error for the easy_perform call you will probably fail to parse the JSON so might as well abort early.

Constants

Consider using constants for your magic strings. Whilst it's obvious that the argument to this call is the targetURL:

curl_easy_setopt(myHandle, CURLOPT_URL, "https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.historicaldata%20where%20symbol%20%3D%20%22YHOO%22%20and%20startDate%20%3D%20%222009-09-11%22%20and%20endDate%20%3D%20%222010-03-10%22&format=json&diagnostics=true&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=");

The line is too long and you have to scroll a relatively long way in order to find out that it's the only parameter passed to the function. If it was:

curl_easy_setopt(myHandle, CURLOPT_URL, targetURL);

It all fits nicely on the screen, which again aids readability.

Comments

Some of your comments are helpful, but others don't really add much and some might actually be misleading:

//after creating handle we ill start transfering webpage

This comment is after you have called curl_easy_init and assigned it to myHandle, so from the comment I would assume that you've established a connection to the remote server and started processing. In the context of the subsequent calls however this seems unlikely. It seems more likely that the connection is actually established when you call curl_easy_perform, after you've setup the URL, Agent and Read callbacks. Try to make your code as self-documenting as possible (through constants, functions, variable names) and use comments to supplement this only if there's actually something to add.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.