diff --git a/src/data.cpp b/src/data.cpp index ea05e9a..b8c4bd5 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -111,7 +111,7 @@ std::vector> fetchOHLCData(const std::string &symbol, c "?period1=" + startTimestamp.str() + "&period2=" + endTimestamp.str() + "&interval=1d&events=history"; - // Fetch historical stock data using the URL + // Fetch historical price data using the URL std::string response = httpGet(url); // Check if response contains an error or is empty @@ -158,10 +158,10 @@ std::vector> fetchOHLCData(const std::string &symbol, c std::string getFormattedStockPrice(const std::string &symbol, bool markdown) { // Also fetch metrics to get the currency - StockMetrics stockMetrics = fetchStockMetrics(symbol); + Metrics equityMetrics = fetchMetrics(symbol); // Check if there is a price - if (stockMetrics.latestPrice == 0) + if (equityMetrics.latestPrice == 0) { return "Could not fetch latest price data. Symbol may be invalid."; } @@ -170,28 +170,28 @@ std::string getFormattedStockPrice(const std::string &symbol, bool markdown) std::ostringstream resultStream; if (!markdown) { - resultStream << "Latest Price for " << stockMetrics.name << ": " << std::fixed << std::setprecision(2) << stockMetrics.latestPrice << " " << stockMetrics.currency - << " (" << (stockMetrics.latestChange >= 0 ? "+" : "") << std::fixed << std::setprecision(2) << stockMetrics.latestChange << "%)"; + resultStream << "Latest Price for " << equityMetrics.name << ": " << std::fixed << std::setprecision(2) << equityMetrics.latestPrice << " " << equityMetrics.currency + << " (" << (equityMetrics.latestChange >= 0 ? "+" : "") << std::fixed << std::setprecision(2) << equityMetrics.latestChange << "%)"; } else { - resultStream << "### Latest Price for " << stockMetrics.name << "\n" - << "`" << std::fixed << std::setprecision(2) << stockMetrics.latestPrice << " " << stockMetrics.currency; + resultStream << "### Latest Price for " << equityMetrics.name << "\n" + << "`" << std::fixed << std::setprecision(2) << equityMetrics.latestPrice << " " << equityMetrics.currency; - if (stockMetrics.latestChange >= 0) + if (equityMetrics.latestChange >= 0) { - resultStream << " (+" << std::fixed << std::setprecision(2) << stockMetrics.latestChange << "%)`:chart_with_upwards_trend:"; + resultStream << " (+" << std::fixed << std::setprecision(2) << equityMetrics.latestChange << "%)`:chart_with_upwards_trend:"; } else { - resultStream << " (" << std::fixed << std::setprecision(2) << stockMetrics.latestChange << "%)`:chart_with_downwards_trend:"; + resultStream << " (" << std::fixed << std::setprecision(2) << equityMetrics.latestChange << "%)`:chart_with_downwards_trend:"; } } return resultStream.str(); } -void fetchAndWriteStockData(const std::string &symbol, const std::string &duration) +void fetchAndWriteEquityData(const std::string &symbol, const std::string &duration) { // Define the current timestamp as the end time std::time_t endTime = std::time(nullptr); @@ -210,12 +210,12 @@ void fetchAndWriteStockData(const std::string &symbol, const std::string &durati startTimestamp << startTime; endTimestamp << endTime; - // Build the URL for historical stock data + // Build the URL for historical data std::string url = "https://query1.finance.yahoo.com/v7/finance/download/" + symbol + "?period1=" + startTimestamp.str() + "&period2=" + endTimestamp.str() + "&interval=1d&events=history"; - // Fetch historical stock data using the URL + // Fetch historical data using the URL std::string response = httpGet(url); // Check if response contains an error or is empty @@ -256,9 +256,9 @@ void fetchAndWriteStockData(const std::string &symbol, const std::string &durati outFile.close(); } -StockMetrics fetchStockMetrics(const std::string &symbol) +Metrics fetchMetrics(const std::string &symbol) { - StockMetrics stockMetrics; + Metrics equityMetrics; // Construct the Yahoo Finance API URL with the symbol std::string apiUrl = "https://query1.finance.yahoo.com/v7/finance/options/" + symbol; @@ -270,12 +270,12 @@ StockMetrics fetchStockMetrics(const std::string &symbol) if (response.find("{\"optionChain\":{\"result\":[],\"error\":null}}") != std::string::npos) { std::cerr << "Symbol not found or delisted: " << symbol << std::endl; - return stockMetrics; + return equityMetrics; } if (response.empty()) { std::cerr << "Failed to fetch data from the server." << std::endl; - return stockMetrics; + return equityMetrics; } // Parse the JSON response @@ -290,75 +290,75 @@ StockMetrics fetchStockMetrics(const std::string &symbol) // Extract metrics from quote object if (quote.HasMember("displayName") && quote["displayName"].IsString()) // name from displayName { - stockMetrics.name = quote["displayName"].GetString(); + equityMetrics.name = quote["displayName"].GetString(); } else if ((quote.HasMember("shortName") && quote["shortName"].IsString())) // name from shortName { - stockMetrics.name = quote["shortName"].GetString(); + equityMetrics.name = quote["shortName"].GetString(); } if (quote.HasMember("symbol") && quote["symbol"].IsString()) // symbol { - stockMetrics.symbol = quote["symbol"].GetString(); + equityMetrics.symbol = quote["symbol"].GetString(); } if (quote.HasMember("currency") && quote["currency"].IsString()) // currency { - stockMetrics.currency = quote["currency"].GetString(); + equityMetrics.currency = quote["currency"].GetString(); } if (quote.HasMember("marketCap") && quote["marketCap"].IsNumber()) // marketCap { - stockMetrics.marketCap = quote["marketCap"].GetDouble(); + equityMetrics.marketCap = quote["marketCap"].GetDouble(); } if (quote.HasMember("dividendYield") && quote["dividendYield"].IsNumber()) // dividendYield { - stockMetrics.dividendYield = quote["dividendYield"].GetDouble(); + equityMetrics.dividendYield = quote["dividendYield"].GetDouble(); } if (quote.HasMember("trailingPE") && quote["trailingPE"].IsNumber()) // peRatio { - stockMetrics.peRatio = quote["trailingPE"].GetDouble(); + equityMetrics.peRatio = quote["trailingPE"].GetDouble(); } if (quote.HasMember("regularMarketPrice") && quote["regularMarketPrice"].IsNumber()) // latestPrice { - stockMetrics.latestPrice = quote["regularMarketPrice"].GetDouble(); + equityMetrics.latestPrice = quote["regularMarketPrice"].GetDouble(); } if (quote.HasMember("regularMarketChangePercent") && quote["regularMarketChangePercent"].IsNumber()) // latestChange { - stockMetrics.latestChange = quote["regularMarketChangePercent"].GetDouble(); + equityMetrics.latestChange = quote["regularMarketChangePercent"].GetDouble(); } if (quote.HasMember("regularMarketOpen") && quote["regularMarketOpen"].IsNumber()) // openPrice { - stockMetrics.openPrice = quote["regularMarketOpen"].GetDouble(); + equityMetrics.openPrice = quote["regularMarketOpen"].GetDouble(); } if (quote.HasMember("regularMarketDayLow") && quote["regularMarketDayLow"].IsNumber()) // dayLow { - stockMetrics.dayLow = quote["regularMarketDayLow"].GetDouble(); + equityMetrics.dayLow = quote["regularMarketDayLow"].GetDouble(); } if (quote.HasMember("regularMarketDayHigh") && quote["regularMarketDayHigh"].IsNumber()) // dayHigh { - stockMetrics.dayHigh = quote["regularMarketDayHigh"].GetDouble(); + equityMetrics.dayHigh = quote["regularMarketDayHigh"].GetDouble(); } if (quote.HasMember("regularMarketPreviousClose") && quote["regularMarketPreviousClose"].IsNumber()) // prevClose { - stockMetrics.prevClose = quote["regularMarketPreviousClose"].GetDouble(); + equityMetrics.prevClose = quote["regularMarketPreviousClose"].GetDouble(); } if (quote.HasMember("fiftyTwoWeekLow") && quote["fiftyTwoWeekLow"].IsNumber()) // fiftyTwoWeekLow { - stockMetrics.fiftyTwoWeekLow = quote["fiftyTwoWeekLow"].GetDouble(); + equityMetrics.fiftyTwoWeekLow = quote["fiftyTwoWeekLow"].GetDouble(); } if (quote.HasMember("fiftyTwoWeekHigh") && quote["fiftyTwoWeekHigh"].IsNumber()) // fiftyTwoWeekHigh { - stockMetrics.fiftyTwoWeekHigh = quote["fiftyTwoWeekHigh"].GetDouble(); + equityMetrics.fiftyTwoWeekHigh = quote["fiftyTwoWeekHigh"].GetDouble(); } if (quote.HasMember("fiftyDayAverage") && quote["fiftyDayAverage"].IsNumber()) // avg_50 { - stockMetrics.avg_50 = quote["fiftyDayAverage"].GetDouble(); + equityMetrics.avg_50 = quote["fiftyDayAverage"].GetDouble(); } if (quote.HasMember("twoHundredDayAverage") && quote["twoHundredDayAverage"].IsNumber()) // avg_200 { - stockMetrics.avg_200 = quote["twoHundredDayAverage"].GetDouble(); + equityMetrics.avg_200 = quote["twoHundredDayAverage"].GetDouble(); } if (quote.HasMember("averageDailyVolume3Month") && quote["averageDailyVolume3Month"].IsNumber()) // avgVol_3mo { - stockMetrics.avgVol_3mo = quote["averageDailyVolume3Month"].GetDouble(); + equityMetrics.avgVol_3mo = quote["averageDailyVolume3Month"].GetDouble(); } } else @@ -366,13 +366,13 @@ StockMetrics fetchStockMetrics(const std::string &symbol) std::cerr << "JSON parsing error or missing member" << std::endl; } - return stockMetrics; + return equityMetrics; } -std::string getFormattedStockMetrics(const std::string &symbol, bool markdown) +std::string getFormattedMetrics(const std::string &symbol, bool markdown) { - // Fetch stock metrics for the given symbol - StockMetrics metrics = fetchStockMetrics(symbol); + // Fetch metrics for the given symbol + Metrics metrics = fetchMetrics(symbol); // Check if data is valid if (metrics.symbol == "-") @@ -383,7 +383,7 @@ std::string getFormattedStockMetrics(const std::string &symbol, bool markdown) // Create a string stream to hold the formatted metrics std::ostringstream formattedMetrics; - // Format the stock metrics + // Format the metrics if (!markdown) { formattedMetrics << "Metrics for " << metrics.name << ":\n"; @@ -427,10 +427,10 @@ std::string getFormattedStockMetrics(const std::string &symbol, bool markdown) return formattedMetrics.str(); } -std::string getFormattedPrices(std::vector indicesSymbols, std::vector indicesNames, std::vector indicesDescriptions, bool markdown) +std::string getFormattedPrices(std::vector symbols, std::vector names, std::vector descriptions, bool markdown) { // Check if data is available - if (indicesSymbols.empty()) + if (symbols.empty()) { return "No data available."; } @@ -438,36 +438,36 @@ std::string getFormattedPrices(std::vector indicesSymbols, std::vec // Check if there are names and descriptions available bool addNames = false; bool addDescription = false; - if ((!indicesNames.empty()) && (indicesNames.size() == indicesSymbols.size())) + if ((!names.empty()) && (names.size() == symbols.size())) { addNames = true; } - if ((!indicesDescriptions.empty()) && (indicesDescriptions.size() == indicesSymbols.size())) + if ((!descriptions.empty()) && (descriptions.size() == symbols.size())) { addDescription = true; } std::ostringstream formattedString; - for (int i = 0; i < indicesSymbols.size(); i++) + for (int i = 0; i < symbols.size(); i++) { // First fetch price data - StockMetrics data = fetchStockMetrics(indicesSymbols[i]); + Metrics data = fetchMetrics(symbols[i]); // Now create string if (!markdown) { if (addNames) // Display name { - formattedString << indicesNames[i] << std::endl; + formattedString << names[i] << std::endl; } else // Otherwise just add the symbol { - formattedString << indicesSymbols[i] << std::endl; + formattedString << symbols[i] << std::endl; } if (addDescription) // Add description if available { - formattedString << indicesDescriptions[i] << std::endl; + formattedString << descriptions[i] << std::endl; } formattedString << std::fixed << std::setprecision(2); formattedString << "- Latest price: " << data.latestPrice << " " << data.currency; @@ -484,15 +484,15 @@ std::string getFormattedPrices(std::vector indicesSymbols, std::vec { if (addNames) // Display name { - formattedString << "### " << indicesNames[i] << std::endl; + formattedString << "### " << names[i] << std::endl; } else // Otherwise just add the symbol { - formattedString << "### " << indicesSymbols[i] << std::endl; + formattedString << "### " << symbols[i] << std::endl; } if (addDescription) // Add description if available { - formattedString << indicesDescriptions[i] << std::endl; + formattedString << descriptions[i] << std::endl; } formattedString << std::fixed << std::setprecision(2); formattedString << "- Latest price: `" << data.latestPrice << " " << data.currency; diff --git a/src/include/data.h b/src/include/data.h index 6b85f6f..aa6243c 100644 --- a/src/include/data.h +++ b/src/include/data.h @@ -21,14 +21,14 @@ #include #include "rapidjson/document.h" -// Struct with stock metrics -// Note that this can also be used for futures and indices, but in that case some attributes will remain empty -struct StockMetrics +// Struct with equity metrics +// Note that this can also be used for futures, indices and crypto, but in that case some attributes will remain empty +struct Metrics { - std::string name = "-"; // Name of stock(company)/future/index (extracted from "displayName", or else from "shortName", which is usually the case with futures/indices) + std::string name = "-"; // Name of stock/future/index/crypto (extracted from "displayName", or else from "shortName", which is usually the case with futures/indices) std::string currency = "-"; // Currency std::string symbol = "-"; // Symbol - double marketCap = 0; // Market capitalization of the company's outstanding shares (amount of shares * price of share) + double marketCap = 0; // Market capitalization, i.e. amount of shares (or coins in case of crypto) * price double dividendYield = 0; // Dividend yield as a percentage double peRatio = 0; // Price-to-earnings ratio (P/E ratio) indicating stock valuation double latestPrice = 0; // Latest price @@ -68,7 +68,7 @@ static std::string httpGet(const std::string &url); /// Function to fetch historical stock/future/index data from Yahoo Finance and store OHLC data (and dates and volumes) in a 2D vector. /// The interval of the data is one day. -/// @param symbol The symbol of the stock/future/index. +/// @param symbol The symbol of the stock/future/index/crypto. /// @param duration The duration/period in the format: 1y, 6mo, 3mo, 2mo, 1mo, 3w, 2w, or 1w. /// @return A 2D vector where each row contains the following data: date, open, high, low, close, volume (in that order). /// @note Function is named fetch*OHLC*Data but this also includes dates (in format y/m/d) and volumes. @@ -77,42 +77,42 @@ std::vector> fetchOHLCData(const std::string &symbol, c /// Function to fetch the latest price and % of change compared to the opening price of a stock/future/index from Yahoo Finance. /// Data will be returned in a string as follows: "The latest price of {symbol}: {latestPrice} (%change)". /// If something went wrong, it will return the following string: "Could not fetch latest price data. Symbol may be invalid." -/// @param symbol The symbol of the stock/future/index. +/// @param symbol The symbol of the stock/future/index/crypto. /// @param markdown When set to true, the formatted string contains Markdown syntax to make it more visually appealing. /// @return A string containing the latest price and % change information. std::string getFormattedStockPrice(const std::string &symbol, bool markdown = false); -/// Function to fetch historical stock/future/index data from Yahoo Finance and write to a txt file. +/// Function to fetch historical stock/future/index/crypto data from Yahoo Finance and write to a txt file. /// The txt file will be named {symbol}_{duration}.txt and will be saved in the "data" folder. /// The interval of the data is one day. -/// @param symbol The symbol of the stock/future/index. +/// @param symbol The symbol of the stock/future/index/crypto. /// @param duration The duration string in the format: 1y, 6mo, 3mo, 2mo, 1mo, 3w, 2w, or 1w. -void fetchAndWriteStockData(const std::string &symbol, const std::string &duration); +void fetchAndWriteEquityData(const std::string &symbol, const std::string &duration); -/// Function to fetch stock/future/index metrics from Yahoo Finance API for a single symbol. -/// @param symbol The symbol of the stock/future/index. +/// Function to fetch stock/future/index/crypto metrics from Yahoo Finance API for a single symbol. +/// @param symbol The symbol of the stock/future/index/crypto. /// @return StockMetrics struct containing price info, dividend yield, market capitalization and more. -StockMetrics fetchStockMetrics(const std::string &symbol); +Metrics fetchMetrics(const std::string &symbol); -/// Function to get stock/future/index metrics in a readable way. +/// Function to get stock/future/index/crypto metrics in a readable way. /// When data is not available, it will return "Could not fetch data. Symbol may be invalid.". -/// @param symbol The symbol of the stock/future/index. +/// @param symbol The symbol of the stock/future/index/crypto. /// @param markdown When set to true, the formatted string contains Markdown syntax to make it more visually appealing. /// @return A string with the metrics. -std::string getFormattedStockMetrics(const std::string &symbol, bool markdown = false); +std::string getFormattedMetrics(const std::string &symbol, bool markdown = false); /// Function that takes a vector of symbols, loops over all of them and fetches their latest price data. /// It then formats the data (latest price + percentage of change compared to open price) in a readable way. /// If no symbols are provided, it will return "No data available.". /// If it cannot fetch the data (for example if a symbol is invalid), the formatted string will not contain any info about that symbol. /// Meaning that if it could not fetch any data at all, the returned string will be empty. -/// @param indicesSymbols Vector of stock/index/future symbols. -/// @param indicesNames Vector of names of the stocks(companies)/futures/indices. If names are given, they will be added instead of the symbols. -/// @param indicesDescriptions Vector of descriptions of the stocks(companies)/futures/indices. These will be added under the symbol (or name). +/// @param symbols Vector of stock/index/future/crypto symbols. +/// @param names Vector of names of the stocks/futures/indices/crypto. If names are given, they will be added instead of the symbols. +/// @param descriptions Vector of descriptions of the stocks/futures/indices/crypto. These will be added under the symbol (or name). /// @param markdown When set to true, the formatted string will contain Markdown syntax to make it more visually appealing. /// @return A string with the formatted price data (and optionally descriptions). -std::string getFormattedPrices(std::vector indicesSymbols, std::vector indicesNames = {}, - std::vector indicesDescriptions = {}, bool markdown = false); +std::string getFormattedPrices(std::vector symbols, std::vector names = {}, + std::vector descriptions = {}, bool markdown = false); /// This function reads JSON data containing symbols, names and optionally descriptions of things related to /// financial markets. It extracts the symbols and names (and descriptions) and formats the data using diff --git a/src/visualize.cpp b/src/visualize.cpp index e63a68e..92c2731 100644 --- a/src/visualize.cpp +++ b/src/visualize.cpp @@ -64,7 +64,7 @@ void priceGraph(const std::vector> &ohlcData, int mode) matplot::figure()->quiet_mode(true); matplot::hold(matplot::on); - matplot::xlim({-1, xAxis[xAxis.size()-1]+1}); // Small offset from edges of figure, to make sure the price line(s) does not cross the axis + matplot::xlim({-1, xAxis[xAxis.size()-1]+1}); // Small offset from edges of figure, to make sure the line(s) does not cross the axis if (mode == 1 || mode == 3) {