diff options
-rw-r--r-- | graph.c | 103 | ||||
-rw-r--r-- | graph.h | 5 | ||||
-rw-r--r-- | main.c | 8 |
3 files changed, 99 insertions, 17 deletions
@@ -2,15 +2,38 @@ int zoom_months[] = {60, 48, 36, 24, 12, 9, 6, 3, 1}, zoom_change_x_months[] = {12, 12, 12, 12, 12, 3, 3, 3, 2}; -void graph_main(const char* ticker_name_string) { - double* price_data = api_get_hist_5y(ticker_name_string); - if (price_data == NULL) // If invalid symbol or cryptocurrency - RET_MSG("Invalid symbol.") - +void graph_main(const char* ticker_name_string, const char* ticker_name_string2) { initscr(); + if (ticker_name_string2 != NULL && !has_colors()) { + endwin(); + puts("Your terminal does not support color."); + return; + } + + puts("Loading data..."); + double* price_data = api_get_hist_5y(ticker_name_string), * price_data2 = NULL; + if (price_data == NULL) { // If invalid symbol or cryptocurrency + endwin(); + puts("Invalid symbol."); + return; + } + + if (ticker_name_string2 != NULL) { + price_data2 = api_get_hist_5y(ticker_name_string2); + if (price_data2 == NULL) { // If invalid symbol or cryptocurrency + free(price_data); + endwin(); + puts("Invalid symbol."); + return; + } + } noecho(); // Don't echo keystrokes keypad(stdscr, TRUE); // Enables extra keystrokes curs_set(0); // Hides cursor + if (ticker_name_string2 != NULL) { + start_color(); // Enable colors + init_pair(1, COLOR_RED, COLOR_BLACK); + } time_t now = time(NULL); struct tm today_date = *localtime(&now), start_date = today_date, furthest_back_date = today_date, end; start_date.tm_year -= 5, furthest_back_date.tm_year -= 5; @@ -19,15 +42,21 @@ void graph_main(const char* ticker_name_string) { double seconds = difftime(mktime(&today_date), mktime(&furthest_back_date)); int trading_days = (int) ((1.0 / DAYS_TO_BUSINESS_DAYS_RATIO) * seconds / 86400.0); // Total trading days to print - int total_data_points = 0; + int total_data_points = 0, total_data_points2 = 0; for (int i = 0; price_data[i] != '\0'; i++) total_data_points++; - int difference = trading_days - total_data_points; - if (difference > 0) // On initial print, scrub to make sure that there are 5 years worth of data points + if (price_data2 != NULL) + for (int i = 0; price_data2[i] != '\0'; i++) + total_data_points2++; + + if (trading_days - total_data_points > 0) // On initial print, scrub to make sure that there are 5 years worth of data points price_data = graph_fill_empty(price_data, total_data_points, trading_days); - graph_print(price_data, &start_date, zoom); // Initial graph of 5 year history + if (ticker_name_string2 != NULL && trading_days - total_data_points2 > 0) // On initial print, scrub to make sure that there are 5 years worth of data points + price_data2 = graph_fill_empty(price_data2, total_data_points2, trading_days); + + graph_print(price_data, price_data2, &start_date, zoom, ticker_name_string, ticker_name_string2); // Initial graph of 5 year history while ((ch = getch()) != 'q') { // Main input loop -- end if keypress 'q' if ((ch == KEY_UP && zoom != ZOOM_1m) || (ch == KEY_DOWN && zoom != ZOOM_5y) || @@ -55,21 +84,24 @@ void graph_main(const char* ticker_name_string) { if (difftime(mktime(&start_date), mktime(&furthest_back_date)) < 0) start_date = furthest_back_date; // Can't go back past furthest_date } - graph_print(price_data, &start_date, zoom); + graph_print(price_data, price_data2, &start_date, zoom, ticker_name_string, ticker_name_string2); } } endwin(); free(price_data); + if (price_data2 != NULL) + free(price_data2); } -void graph_print(const double* points, struct tm* start_time, int zoom) { +void graph_print(const double* points, const double* points2, struct tm* start_time, int zoom, + const char* ticker_name_string, const char* ticker_name_string2) { move(0, 0); // Instead of clear()ing, move to the top left corner and re-print int cols, rows; getmaxyx(stdscr, rows, cols); cols -= 11; // 10 offset to give space for graph labels + 1 for right side rows -= 3; // Make space for zoom indicator rows -= rows % ROWS_SPACING; // Round down to multiple of 5 - if (cols < 10 || rows < 10) // Exits if the terminal is too small + if (cols < 50 || rows < 25) // Exits if the terminal is too small RET_MSG("Terminal not large enough.") time_t now = time(NULL); @@ -88,7 +120,6 @@ void graph_print(const double* points, struct tm* start_time, int zoom) { max = points[++k]; if (k > 0) // Do the same thing with min min = max; - for (int i = starting_index + 1; i < trading_days + starting_index; i++) { if (points[i] != EMPTY) { // Ignore EMPTY values if (points[i] > max) // Find max and min values for graph upper/lower bounds @@ -99,14 +130,46 @@ void graph_print(const double* points, struct tm* start_time, int zoom) { } double line_diff = (max - min) / rows, dat; // Each line includes data point up to line_diff below + double max2 = 0, min2 = 0, line_diff2 = 0, dat2 = 0; + if (points2 != NULL) { + max2 = points2[starting_index], min2 = points2[starting_index]; + k = 0; + while (max2 == EMPTY) // If initial max is EMPTY, get first non-EMPTY value + max2 = points2[++k]; + if (k > 0) // Do the same thing with min + min2 = max2; + for (int i = starting_index + 1; i < trading_days + starting_index; i++) { + if (points2[i] != EMPTY) { // Ignore EMPTY values + if (points2[i] > max2) // Find max and min values for graph upper/lower bounds + max2 = points2[i]; + if (points2[i] < min2) + min2 = points2[i]; + } + } + line_diff2 = (max2 - min2) / rows; + } + for (int i = rows; i >= 0; i--) { if (i % ROWS_SPACING == 0) // Print y-axis price labels with width 10 printw("%9.2lf ", (max - ((rows - i) * line_diff))); + else if (points2 != NULL && (i - 1 ) % ROWS_SPACING == 0) {// Print y-axis price labels with width 10 + attron(COLOR_PAIR(1)); + printw("%9.2lf ", (max2 - ((rows - i) * line_diff2))); + attroff(COLOR_PAIR(1)); + } else printw(" "); // Indent width 10 for (int j = 0; j < cols; j++) { dat = points[starting_index + (int) ((double) j * trading_days / cols)]; + if (points2 != NULL) + dat2 = points2[starting_index + (int) ((double) j * trading_days / cols)]; if (dat <= (max - ((rows - i) * line_diff)) && dat > (min + ((i - 1) * line_diff))) addch(ACS_DIAMOND); + else if (points2 != NULL && dat2 <= (max2 - ((rows - i) * line_diff2)) && + dat2 > (min2 + ((i - 1) * line_diff2))) { + attron(COLOR_PAIR(1)); + addch(ACS_DIAMOND); + attroff(COLOR_PAIR(1)); + } else if (i % ROWS_SPACING == 0 && j % COLS_SPACING == 0) // Cross on corners addch(ACS_PLUS); else if (i % ROWS_SPACING == 0) // Horizontal line every ROWS_SPACING lines @@ -134,7 +197,19 @@ void graph_print(const double* points, struct tm* start_time, int zoom) { } printw("\n\n"); // Empty line as spacing - for (int i = 0; i < cols / 2 - 8; i++) + printw(" %s", ticker_name_string); + addch(ACS_DIAMOND); + if (points2 != NULL) { + attron(COLOR_PAIR(1)); + printw(" %s", ticker_name_string2); + addch(ACS_DIAMOND); + attroff(COLOR_PAIR(1)); + } + addch(' '); + size_t offset = (cols / 2) - (11 + strlen(ticker_name_string)); + if (points2 != NULL) + offset -= strlen(ticker_name_string2) + 2; + for (unsigned int i = 0; i < offset; i++) addch(' '); // Center text const char* str[9] = {"5y", "4y", "3y", "2y", "1y", "9m", "6m", "3m", "1m"}; // Zoom level for (int i = 0; i < 9; i++) { @@ -33,7 +33,7 @@ extern int zoom_months[9], zoom_change_x_months[9]; * RIGHT: moves start date forward by one year, three months, or one month. * @param ticker_name_string symbol */ -void graph_main(const char* ticker_name_string); +void graph_main(const char* ticker_name_string, const char* ticker_name_string2); /** * Prints out a NCurses based graph given an array of daily close prices. @@ -43,7 +43,8 @@ void graph_main(const char* ticker_name_string); * @param start_time the starting date of prices to print * @param zoom the zoom level */ -void graph_print(const double* points, struct tm* start_time, int zoom); +void graph_print(const double* points, const double* points2, struct tm* start_time, int zoom, + const char* ticker_name_string, const char* ticker_name_string2); /** * Reallocates the given array with size trading days. Moves all values to end of the array and sets @@ -42,7 +42,13 @@ int main(int argc, char* argv[]) { else if (strcmp(cmd, "info") == 0 && argc == 3) api_print_info(sym); else if (strcmp(cmd, "graph") == 0 && argc == 3) - graph_main(sym); + graph_main(sym, NULL); + else if (strcmp(cmd, "compare") == 0 && argc == 4) { + char sym2[strlen(argv[3]) + 1]; + strcpy(sym2, argv[3]); + strtoupper(sym2); + graph_main(sym, sym2); + } // Check else if (strcmp(cmd, "check") == 0) { |