Skip to content

Instantly share code, notes, and snippets.

@fcfort
Last active February 28, 2021 02:03
Show Gist options
  • Save fcfort/ad20f4119e781ad4a6b5582f4666695f to your computer and use it in GitHub Desktop.
Save fcfort/ad20f4119e781ad4a6b5582f4666695f to your computer and use it in GitHub Desktop.
Custom function for Google Spreadsheets App Scripts for querying NYSaves.org prices
var _ = Underscore.load();
var NYSaves = {};
NYSaves.currentPriceUrlBase = 'https://www.nysaves.org/nytpl/fund/details.cs?fundId=';
NYSaves.historyUrl = 'https://www.nysaves.org/nytpl/fund/priceHistorySearch.cs';
/**
* NOTE: NySaves will not return prices for weekend dates.
* @private
*/
function _getPriceForRange(tickerId, startDate, endDate) {
// POST data: fundId=1003014&startDate=05%2F03%2F2016&endDate=05%2F03%2F2016
var response = _fetchUrlWithCookies(NYSaves.historyUrl, {
method: 'post',
payload: {
'fundId': tickerId + "",
'startDate': _dateToStringNySaves(startDate),
'endDate': _dateToStringNySaves(endDate)
}
});
var textContent = response.getContentText();
Logger.log('Found textContent %s', textContent);
// And return the first price.
var found = textContent.match(/\$\d+.\d+/g);
// Strip $ symbol
found = _.map(found, function(price) {
return price.replace('$','');
});
Logger.log('Found prices %s', found);
return found;
}
/**
* Looks five days back from the supplied date and returns the
* latest price found. This is so that if there is a holiday or weekend, we
* still return a price (albeit potentially stale) for that date.
*/
NYSaves.getLatestPriceForDate = function(tickerId, date) {
return _.last(_getPriceForRange(tickerId, Util.getDaysAgo(date, 5), date));
}
/**
* @private
*/
function _dateToStringNySaves(date) {
var mon = Util.padDate(date.getMonth()+1);
var day = Util.padDate(date.getDate());
return mon + '/' + day + '/' + date.getYear();
}
/**
* Gets current price of NYSaves.org ticker
* @private
*/
function _getNySavesPrice(tickerId) {
var url = NYSaves.currentPriceUrlBase + tickerId;
// We make another request to get the page content with the cookie detection cookie.
var response = _fetchUrlWithCookies(url, {followRedirects: false});
var textContent = response.getContentText();
// And return the first price, stripping $ symbol.
return textContent.match(/\$\d+.\d+/)[0].replace('$','');
}
/**
* Makes two requests to a URL. The first request is to check for a cookie
* and then store it. The second request is to the same URL with that cookie
* in the header. This is a workaround for sites (like NYSaves.org) with
* cookie detection.
* @private
*/
function _fetchUrlWithCookies(url, data) {
// We need to make an initial request because NYSaves.org has a dumb cookie
// detection system.
var initialResponse = UrlFetchApp.fetch(url, data);
var headers = initialResponse.getHeaders();
var cookies = headers['Set-Cookie'];
data['headers'] = {Cookie: cookies};
// We make another request to get the page content with the cookie detection cookie.
return UrlFetchApp.fetch(url, data);
}
/**
* Returns the latest price of a NY Saves ticker ID.
*
* @param {String} tickerId NYSaves.org ticker ID
* @customfunction
*/
function NYSAVES_PRICE(tickerId) {
return _getNySavesPrice(tickerId);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment