3
\$\begingroup\$

I'm building an app that tells you a stock's percentage change in price between two dates by using Yahoo's API. Given a stock ticker and two dates, it currently outputs the percentage return to the console.

I would like to later expand this app to give it a list of stock tickers and dates. For each, it would return the performance relative to another stock between the dates.

I want the app to try to pull stock data from the Yahoo API, but to then try another API if the data for that stock is not available from Yahoo.

Here's what I have so far. I believe I'm using the revealing module pattern (although maybe not correctly). Any advice on how I can structure this code to maintain simplicity and comprehensibility as I add functionality would be appreciated. I'm open to input on whether that should be another design pattern or programming style altogether.

$(function () {

    // This object provides the stock ticker,
    // as well as the dates to compute the percentage 
    // change of the stock's price between. 
    var stock = {
        ticker: 'fb',
        startDate: '2015-08-06',
        endDate: '2016-04-12'
    };

    yahooFinance.getStockData(stock);
});

var utility = {};
(function () {

    var removeLeadingZero = function removeLeadingZero (strNumber) {
        if (strNumber.charAt(0) === '0') {
            return strNumber.substr(1);
        }else{
            return strNumber;
        }
    };

    var formatForConsole = function formatForConsole (dateString) {

        var dateStringArray = dateString.split('-');

        var month = removeLeadingZero(dateStringArray[1]);
        var day = removeLeadingZero(dateStringArray[2]);
        var year = dateStringArray[0];

        return month + '/' + day + '/' + 'year';
    };

    var getPercentChange = function getPercentChange (startingPrice, endingPrice) {
        return ((endingPrice - startingPrice) / startingPrice) * 100;
    };

    $.extend(utility, {
        formatForConsole: formatForConsole,
        getPercentChange: getPercentChange
    });
}());

var yahooFinance = {};
(function () {

    // Returns the URI to request daily price data for the stock in JSON format. 
    var getUri = function getUri (stockObj) {

        return 'http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo'
            + '.finance.historicaldata%20where%20symbol%20%3D%20%22' 
            + stockObj.ticker 
            + '%22%20and%20startDate%20%3D%20%22' 
            + stockObj.startDate
            + '%22%20and%20endDate%20%3D%20%22' 
            + stockObj.endDate 
            + '%22&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys';
    };

    var getStockData = function getStockData (stockObj) {

        $.getJSON(getUri(stockObj), function (priceData) {

            var startDate = utility.formatForConsole(stockObj.startDate);
            var endDate = utility.formatForConsole(stockObj.endDate);
            console.log(startDate + ' to ' + endDate);

            var pricesArray = priceData.query.results.quote;

            var startingPrice = pricesArray[pricesArray.length - 1].Adj_Close;
            var endingPrice = pricesArray[0].Adj_Close;
            var percentChange = utility.getPercentChange(startingPrice, endingPrice);
            console.log(percentChange);
        });
    };

    $.extend(yahooFinance, {
        getStockData: getStockData
    });
}());
\$\endgroup\$
1
\$\begingroup\$

Whenever I see a module named "utility", it always spells danger. It will almost always suffer bloat because one is easily tempted to put everything that's uncategorized into it and will be forgotten. Consider splitting it off while still early. It appears one is a string formatter, the other a console helper. Split them by that instead, much more meaningful.

Also, function names and variables occupy the same namespace. Defining a variable and assigning a named function is overkill. I prefer a just a function declaration over a function expression due to debugability because the names will appear in the stack trace.


var removeLeadingZero = function removeLeadingZero (strNumber) {
    if (strNumber.charAt(0) === '0') {
        return strNumber.substr(1);
    }else{
        return strNumber;
    }
};

Consider trimming the string prior to checking if the leading character is 0. A leading space could throw this function off. Additionally, this can be shortened to:

function removeLeadingZero(string){
  return string.charAt(0) === '0' ? string.trim().substr(1) 
                                  : string;
}

return ((endingPrice - startingPrice) / startingPrice) * 100;

Beware of JavaScript's precision when it comes to numbers. While this works for small applications or where precision isn't really important, it may throw you off when dealing with more complex calculations. You have been warned. 0.1 + 0.2 !== 0.3 in JS :D


var getUri = function getUri (stockObj) {

    return 'http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo'
        + '.finance.historicaldata%20where%20symbol%20%3D%20%22' 
        + stockObj.ticker 
        + '%22%20and%20startDate%20%3D%20%22' 
        + stockObj.startDate
        + '%22%20and%20endDate%20%3D%20%22' 
        + stockObj.endDate 
        + '%22&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys';
};

Since you said you used jQuery, consider using $.param to build the query portion of your url. It will do the proper escaping for you.


getStockData doesn't look complete, but if you want to be functional and all that, the function must return something. $.getJSON returns a Promise-like object which you can listen to for resolution/rejection. You can also parse and return in a resolve handler to do additional logic before handing it off to the caller. You can use this to restructure the data, do additional computations etc.

var getStockData = function getStockData (stockObj) {

    return $.getJSON(getUri(stockObj)).then(function (priceData) {

        var startDate = utility.formatForConsole(stockObj.startDate);
        var endDate = utility.formatForConsole(stockObj.endDate);
        var pricesArray = priceData.query.results.quote;
        var startingPrice = pricesArray[pricesArray.length - 1].Adj_Close;
        var endingPrice = pricesArray[0].Adj_Close;
        var percentChange = utility.getPercentChange(startingPrice, endingPrice);

       // Return all the needed data
       return {
         startDate: startDate,
         endDate: endDate,
         startingPrice: startingPrice,
         endingPrice: endingPrice,
         percentChange: percentChange
       };

    });
};

// Usage
yahooFinance.getStockData(stock).then(function(results){
  console.log(results.startDate);
});

A good practice is to push off anything UI-related nearer to the caller instead of anywhere in the API (unless the API is for rendering). Most of the time, your API will be just sending/grabbing/computing data and it's good that way. It makes it easy to test with lesser things to stub/mock.

Additionally, it's usually the caller that's nearer the UI. This is more apparent in frameworks like Angular, where API consumers reside in the controller, the last level of JS before the templates. API gets called in the controller, therefore the results should also end up in the controller.

\$\endgroup\$
  • \$\begingroup\$ To organize the utility object, should it contain internal objects that separate the string or console formatting methods? For ex, utility.consoleFormatting.formatForConsole and utility.stringFormatting.removeLeadingZero. Should deep nesting be avoided? Defining a variable and assigning a named function to it, instead of using an anonymous function expression, allows the function name to still appear in the stack trace. Is there another reason not to do this? I prefer a function expression over a function declaration since it insures that I define my functions before using them. \$\endgroup\$ – Jackmc1047 Apr 14 '16 at 4:43
  • \$\begingroup\$ @Jackmc1047 "utility" is very generic. It doesn't tell anyone what's inside it unless documented or if one reads the code. Separating it to a string formatting module and console module makes it clearer. \$\endgroup\$ – Joseph Apr 14 '16 at 6:00
  • \$\begingroup\$ @Jackmc1047 Deep nesting/namespacing is fine, but with recent module patterns (AMD, ES6, CommonJS), you'll rarely see deep namespacing. \$\endgroup\$ – Joseph Apr 14 '16 at 6:03
  • \$\begingroup\$ @Jackmc1047 With function declarations, the functions are "hoisted" (defined during parse, not during execution). Technically they're already defined in the scope before execution. This makes your argument of expressions insuring definition somewhat invalid. jsfiddle.net/df0ekyar \$\endgroup\$ – Joseph Apr 14 '16 at 6:08
  • \$\begingroup\$ I understand that you don't have to define functions before using them, I just think it's good practice to do so. In regards to JavaScript precision when it comes to numbers, would this be a problem when computing stock percentage returns? Will the inaccuracy be significant if dollar amounts it's applied to are not really huge (like billions of dollars)? \$\endgroup\$ – Jackmc1047 Apr 14 '16 at 13:57

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.