diff options
author | Antony Kellermann | 2018-01-31 15:57:42 -0500 |
---|---|---|
committer | Antony Kellermann | 2018-01-31 15:57:42 -0500 |
commit | ae4dc32ef58d61a76a9591b47e0a4fe721d78fd7 (patch) | |
tree | cf2d7df6906b1d39853816d3152021e09eaaa740 | |
parent | 3fd8913733bbb57c38683fdb80aaf12d4719a734 (diff) | |
download | aur-ae4dc32ef58d61a76a9591b47e0a4fe721d78fd7.tar.gz |
Added support for news
-rw-r--r-- | .SRCINFO | 3 | ||||
-rw-r--r-- | LICENSE | 6 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | PKGBUILD | 6 | ||||
-rw-r--r-- | README.md | 20 | ||||
-rw-r--r-- | api.c | 125 | ||||
-rw-r--r-- | api.h | 28 | ||||
-rw-r--r-- | main.c | 8 | ||||
-rw-r--r-- | tick.1 | 19 |
9 files changed, 187 insertions, 30 deletions
@@ -1,11 +1,12 @@ pkgbase = tick pkgdesc = Command line stock and cryptocurrency portfolio tracker. - pkgver = 1.4.0 + pkgver = 1.5.0 pkgrel = 1 url = https://github.com/aokellermann/tick arch = x86_64 license = MIT depends = curl + depends = json-c provides = tick conflicts = tick source = git://github.com/aokellermann/tick @@ -10,4 +10,8 @@ Data provided for free by IEX. Please read IEX's license: https://iextrading.com Data provided for free by Alpha Vantage. https://www.alphavantage.co -Data provided for free by Coinmarketcap. https://coinmarketcap.com/
\ No newline at end of file +Data provided for free by Coinmarketcap. https://coinmarketcap.com + +Data provided for free by News API. https://newsapi.org + +Shortened links provided for free by Google. https://developers.google.com/url-shortener/
\ No newline at end of file @@ -1,7 +1,7 @@ CC = gcc CFLAGS = -g -Wall --std=c99 OBJECTS = main.o api.o portfolio.o -LIBS = -lcurl +LIBS = -lcurl -ljson-c BIN = tick DESTDIR = /usr @@ -1,13 +1,13 @@ # Maintainer: Antony Kellermann <aokellermann@gmail.com> pkgname=tick -pkgver=1.4.0 +pkgver=1.5.0 pkgrel=1 pkgdesc="Command line stock and cryptocurrency portfolio tracker." -arch=("x86_64") +arch=('x86_64') url="https://github.com/aokellermann/${pkgname}" license=('MIT') -depends=("curl") +depends=('curl' 'json-c') provides=("${pkgname}") conflicts=("${pkgname}") source=("git://github.com/aokellermann/${pkgname}") diff --git a/README.md b/README.md index 19c2d38824fb..cc0c06b62cea 100644 --- a/README.md +++ b/README.md @@ -40,15 +40,27 @@ securities may take up to 10 seconds each. This is due to Alpha Vantage's relatively slow API. NYSE and NASDAQ listed stocks and ETFs, as well as cryptocurrencies should take less than one second to print each. +To get the top three news articles on a specific stock or cryptocurrency, +you can run + +```bash +$ tick news [symbol] +``` + Once installed, you may read the man page for more information. #### License MIT License -Stock information is taken from IEX's free API. Mutual fund and over-the-counter information is taken from Alpha Vantage's -free API. Cryptocurrency information is taken from Coinmarketcap's free API. Please do not abuse the APIs by repeatedly requesting -information. Read the provided license for more information. +Stock information is taken from IEX's free API. Mutual fund and over-the-counter +information is taken from Alpha Vantage's free API. Cryptocurrency information +is taken from Coinmarketcap's free API. News information is taken from News +API. Shortened links are provided by Google's URL Shortener API. Please do +not abuse the APIs by repeatedly requesting information. Read the provided +license for more information. #### Future Ideas * Command to get info about a security * More robust information in "check" -* Historical support -- 24h/7d profits
\ No newline at end of file +* Historical support -- 7d/28d profits +* Reduce API calls +* Find a new API to hand all stock calls (quandl?). Alpha Vantage is unreliable
\ No newline at end of file @@ -31,7 +31,7 @@ size_t api_string_writefunc(void* ptr, size_t size, size_t nmemb, String* hStrin return size * nmemb; } -String* api_curl_data(char* url) { +String* api_curl_data(char* url, char* post_field) { String* pString = api_string_init(); CURL* curl = curl_easy_init(); CURLcode res; @@ -41,7 +41,16 @@ String* api_curl_data(char* url) { curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, api_string_writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &pString->data); + struct curl_slist* list = NULL; + if (url[12] == 'g') { + list = curl_slist_append(list, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_field); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(post_field)); + } res = curl_easy_perform(curl); + if (url[12] == 'g') + curl_slist_free_all(list); curl_easy_cleanup(curl); if (res != CURLE_OK) return NULL; @@ -62,7 +71,7 @@ double api_get_current_price(char* ticker_name_string) { return -1; } -double api_get_1d_price(char* ticker_name_string){ +double api_get_1d_price(char* ticker_name_string) { double val = iex_get_1d_price(ticker_name_string); if (val != -1) return val; @@ -82,7 +91,7 @@ double iex_get_current_price(char* ticker_name_string) { memcpy(&iex_api_string[37], ticker_name_string, ticker_name_len); memcpy(&iex_api_string[37 + ticker_name_len], "/quote/latestPrice", 18); - String* pString = api_curl_data(iex_api_string); + String* pString = api_curl_data(iex_api_string, NULL); free(iex_api_string); if (strcmp(pString->data, "Unknown symbol") == 0) { api_string_destroy(&pString); @@ -99,7 +108,7 @@ double alphavantage_get_current_price(char* ticker_name_string) { memcpy(alphavantage_api_string, av_str, 160); size_t av_len = strlen(alphavantage_api_string); memcpy(&alphavantage_api_string[av_len], ticker_name_string, 10); - String* pString = api_curl_data(alphavantage_api_string); + String* pString = api_curl_data(alphavantage_api_string, NULL); if (pString->data[0] == '{') { api_string_destroy(&pString); memset(alphavantage_api_string, '\0', 160); @@ -107,7 +116,7 @@ double alphavantage_get_current_price(char* ticker_name_string) { memcpy(alphavantage_api_string, av_str, 160); av_len = strlen(alphavantage_api_string); memcpy(&alphavantage_api_string[av_len], ticker_name_string, 10); - pString = api_curl_data(alphavantage_api_string); + pString = api_curl_data(alphavantage_api_string, NULL); } if (pString->data[0] == '{') { free(alphavantage_api_string); @@ -133,7 +142,7 @@ double coinmarketcap_get_current_price(char* ticker_name_string) { char* coinmarketcap_api_string = calloc(64, sizeof(char)); memcpy(coinmarketcap_api_string, cmc_str, 40); memcpy(&coinmarketcap_api_string[40], ticker_name_string, 20); - String* pString = api_curl_data(coinmarketcap_api_string); + String* pString = api_curl_data(coinmarketcap_api_string, NULL); if (pString->data[0] == '{') { free(coinmarketcap_api_string); api_string_destroy(&pString); @@ -160,7 +169,7 @@ double iex_get_1d_price(char* ticker_name_string) { memcpy(&iex_api_string[37], ticker_name_string, ticker_name_len); memcpy(&iex_api_string[37 + ticker_name_len], "/previous?format=csv", 21); - String* pString = api_curl_data(iex_api_string); + String* pString = api_curl_data(iex_api_string, NULL); free(iex_api_string); if (strcmp(pString->data, "Unknown symbol") == 0) { @@ -180,13 +189,15 @@ double iex_get_1d_price(char* ticker_name_string) { return ret; } -double alphavantage_get_1d_price(char* ticker_name_string){ +double alphavantage_get_1d_price(char* ticker_name_string) { size_t ticker_name_len = strlen(ticker_name_string); char* alphavantage_api_string = calloc(128, sizeof(char)); - memcpy(alphavantage_api_string, "https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&apikey=DFUMLJ1ILOM2G7IH&datatype=csv&symbol=", 128); + memcpy(alphavantage_api_string, + "https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&apikey=DFUMLJ1ILOM2G7IH&datatype=csv&symbol=", + 128); size_t prefix = strlen(alphavantage_api_string); memcpy(&alphavantage_api_string[prefix], ticker_name_string, ticker_name_len); - String* pString = api_curl_data(alphavantage_api_string); + String* pString = api_curl_data(alphavantage_api_string, NULL); if (pString->data[0] == '{') { free(alphavantage_api_string); api_string_destroy(&pString); @@ -206,12 +217,12 @@ double alphavantage_get_1d_price(char* ticker_name_string){ return ret; } -double coinmarketcap_get_1d_price(char* ticker_name_string){ +double coinmarketcap_get_1d_price(char* ticker_name_string) { char* cmc_str = "https://api.coinmarketcap.com/v1/ticker/"; char* coinmarketcap_api_string = calloc(64, sizeof(char)); memcpy(coinmarketcap_api_string, cmc_str, 40); memcpy(&coinmarketcap_api_string[40], ticker_name_string, 20); - String* pString = api_curl_data(coinmarketcap_api_string); + String* pString = api_curl_data(coinmarketcap_api_string, NULL); if (pString->data[0] == '{') { free(coinmarketcap_api_string); api_string_destroy(&pString); @@ -237,7 +248,95 @@ double coinmarketcap_get_1d_price(char* ticker_name_string){ free(price_string); free(percent_string); api_string_destroy(&pString); - return current_price - (current_price * (percent_change/100)); + return current_price - (current_price * (percent_change / 100)); +} + +void news_print_top_three(char* ticker_name_string) { + char* qchar = calloc(64, 1); + sprintf(qchar, "&q=%s", ticker_name_string); + time_t now = time(NULL); + struct tm* ts; + char* yearchar = calloc(64, 1); + ts = localtime(&now); + ts->tm_mday -= 14; + mktime(ts); + strftime(yearchar, 64, "%Y-%m-%d", ts); + char* news_api_string = calloc(256, sizeof(char)); + sprintf(news_api_string, "%s%s&from=%s", + "https://newsapi.org/v2/everything?sortBy=popularity&pageSize=3&language=en&apiKey=1163c352d041460381f0a8273e60a9d1", + qchar, yearchar); + free(yearchar); + free(qchar); + String* pString = api_curl_data(news_api_string, NULL); + json_print_news(pString->data); + free(news_api_string); + api_string_destroy(&pString); +} + +void json_print_news(char* data) { + Json* jobj = json_tokener_parse(data); + Json* articles = json_object_object_get(jobj, "articles"); + const char* author_string, * title_string, * source_string, * url_string; + char* date_string; + Json* article, * source, * author, * title, * source_name, * date, * url; + for (int i = 0; i < 3; i++) { + article = json_object_array_get_idx(articles, (size_t) i); + author = json_object_object_get(article, "author"); + author_string = json_object_to_json_string(author); + title = json_object_object_get(article, "title"); + title_string = json_object_to_json_string(title); + source = json_object_object_get(article, "source"); + source_name = json_object_object_get(source, "name"); + source_string = json_object_to_json_string(source_name); + date = json_object_object_get(article, "publishedAt"); + date_string = (char*)json_object_to_json_string(date); + date_string[11] = '\"'; + date_string[12] = '\0'; + url = json_object_object_get(article, "url"); + url_string = json_object_to_json_string(url); + char* url_final = calloc(strlen(url_string + 1), 1); + for (int k = 0, j = 0; j < strlen(url_string); k++, j++) { + if (url_string[j] == '\\' || url_string[j] == '\"') + j++; + url_final[k] = url_string[j]; + } + + const char* shorten = google_shorten_link(url_final); + if (author_string != NULL) + printf("Title: %s Source: %s Author: %s Date: %s Url: %s\n", title_string, source_string, author_string, + date_string, shorten); + else printf("Title: %s Source: %s Date: %s Url: %s\n", title_string, source_string, date_string, shorten); + free(url_final); + free((void*) shorten); + } + json_object_put(jobj); +} + +const char* google_shorten_link(char* url_string) { + char* google_api_string = calloc(256, sizeof(char)); + memcpy(google_api_string, + "https://www.googleapis.com/urlshortener/v1/url?key=AIzaSyAoMAvMPpc7U8lfrnGMk2ZKl966tU2pppU", + 256); + char* post_string = calloc(1024, 1); + sprintf(post_string, "{\"longUrl\": \"%s\"}", url_string); + String* pString = api_curl_data(google_api_string, post_string); + free(post_string); + free(google_api_string); + Json* jobj = json_tokener_parse(pString->data); + Json* short_url = json_object_object_get(jobj, "id"); + const char* short_url_string = json_object_to_json_string(short_url); + char* copy = calloc(1024, 1); + strcpy(copy, short_url_string); + char* final = calloc(strlen(copy) + 1, 1); + for (int i = 0, j = 0; j < strlen(copy); i++, j++) { + if (copy[j] == '\\' || copy[j] == '\"') + j++; + final[i] = copy[j]; + } + json_object_put(jobj); + free(copy); + api_string_destroy(&pString); + return final; } void api_string_destroy(String** phString) { @@ -1,8 +1,9 @@ /** - * API data is taken from IEX Trading, Alpha Vantage, and Coinmarketcap. + * API data is taken from IEX Trading, Alpha Vantage, Coinmarketcap, and News API. * https://iextrading.com/developer/docs/ * https://www.alphavantage.co/documentation/ * https://coinmarketcap.com/api/ + * https://newsapi.org/docs */ #ifndef IEX_H @@ -13,6 +14,7 @@ #include <string.h> #include <curl/curl.h> #include <stddef.h> +#include <json-c/json_tokener.h> struct string { char* data; @@ -21,6 +23,8 @@ struct string { typedef struct string String; +typedef struct json_object Json; + /** * Creates and returns a STRING * object with size 1 and no data @@ -31,9 +35,10 @@ String* api_string_init(void); /** * GETs data from API server and returns it in a String * @param url API url to GET + * @param post_field data needed for POST * @return NULL if no response from server. Otherwise, String containing data. */ -String* api_curl_data(char* url); +String* api_curl_data(char* url, char* post_field); /** * Returns current price of a stock or cryptocurrency. @@ -112,6 +117,25 @@ double alphavantage_get_1d_price(char* ticker_name_string); double coinmarketcap_get_1d_price(char* ticker_name_string); /** + * Prints top three news articles in the past week based on the given string + * @param ticker_name_string the string + */ +void news_print_top_three(char* ticker_name_string); + +/** + * Given a JSON formatted string, print title, source, author, and url of articles + * @param data the json formatted data + */ +void json_print_news(char* data); + +/** + * Given a url, returns a shorter link using goo.gl + * @param url_string the link to shorten + * @return the shortened link + */ +const char* google_shorten_link(char* url_string); + +/** * Destroys String object and frees memory * @param phString the String to destroy */ @@ -3,7 +3,13 @@ #include "portfolio.h" int main(int argc, char* argv[]) { - if (argc < 3 || argc == 4 || argc > 5) { + if (argc == 3 && strcmp(argv[1], "news") == 0) { + if (strlen(argv[2]) <= 16 && strlen(argv[2]) > 1) + news_print_top_three(argv[2]); + else printf("Input invalid length.\n"); + return 0; + } + if (argc == 1 || argc == 4 || argc > 5) { printf("Invalid arguments. Type \"man tick\" for help.\n"); return 0; } @@ -1,4 +1,4 @@ -.TH TICK "1" "January 2018" "Tick 1.4.0" "User Commands" +.TH TICK "1" "January 2018" "Tick 1.5.0" "User Commands" .SH NAME Tick - Command line stock and cryptocurrency portfolio tracker. @@ -28,6 +28,14 @@ are not reinvested into a specific security, you should update your portfolio wi Prints information about your current portfolio holdings. Either a symbol or the keyword 'all' can be used. The keyword "all" will print information about all your current holdings, as well as a grand total. +.SS +News: + +.TP +[news] [symbol] +Prints information on the top three news articles in the past 14 days. The article titles, sources, authors, dates, and URLs +will be displayed. Technically, the input may be something completely unrelated to investing. This may be amended in the future. + .SH FILES .I ~/.tick_portfolio .RS @@ -42,6 +50,9 @@ Antony Kellermann -- https://github.com/aokellermann .SH LICENSE MIT License -Stock information is taken from IEX's free API. Mutual fund and over-the-counter information is taken from Alpha Vantage's -free API. Cryptocurrency information is taken from Coinmarketcap's free API. Please do not abuse the APIs by repeatedly requesting -information. Read the provided license for more information.
\ No newline at end of file +Stock information is taken from IEX's free API. Mutual fund and over-the-counter +information is taken from Alpha Vantage's free API. Cryptocurrency information +is taken from Coinmarketcap's free API. News information is taken from News +API. Shortened links are provided by Google's URL Shortener API. Please do +not abuse the APIs by repeatedly requesting information. Read the provided +license for more information.
\ No newline at end of file |