Rust LogoStock Tracker

A Stock Tracker is an application or system designed to monitor and manage investments in the stock market. It allows users to keep an eye on the performance of individual stocks, entire portfolios, and potentially receive updates or alerts regarding price changes. The core functionality revolves around acquiring, storing, processing, and displaying stock market data.

Key Features and Concepts:

1. Data Acquisition: A real-world stock tracker needs to fetch up-to-date stock prices, company information (like market cap, sector, P/E ratio), and historical data. This is typically achieved by integrating with financial APIs (e.g., Alpha Vantage, Yahoo Finance API, Twelve Data, IEX Cloud). For simplicity, our example will simulate price updates.

2. Portfolio Management: Users can add stocks they own or are interested in tracking. This involves recording the stock symbol, company name, the number of shares owned, and sometimes the purchase price or date.

3. Price Updates: Stock prices are dynamic. A tracker continuously or periodically updates the prices of stocks in the user's portfolio to reflect current market conditions.

4. Performance Monitoring: Based on current prices and shares owned, the tracker calculates the total value of the portfolio, individual stock gains or losses (both absolute and percentage-based), and overall portfolio performance.

5. Display and Reporting: The application presents the data in a user-friendly format, often through a dashboard, tables, or charts. This includes listing all stocks, their current prices, shares, total value, and change over time.

6. Alerts (Advanced): More sophisticated trackers can send notifications when a stock's price crosses a certain threshold, a significant percentage change occurs, or news related to a tracked company breaks.

Implementation Considerations (in Rust):

* Structs: Define clear data structures (e.g., `Stock`, `Portfolio`) to model financial instruments and the collection of investments.
* Collections: Use `HashMap` or `Vec` to store multiple `Stock` instances within a `Portfolio`, often mapping stock symbols to their respective `Stock` objects for efficient lookup.
* Methods: Implement methods on these structs to encapsulate behavior like adding stocks, updating prices, calculating portfolio value, and displaying information.
* Error Handling: For real-world applications, robust error handling using `Result` is crucial, especially when dealing with external API calls or user input.
* Concurrency/Asynchronicity: When fetching data from multiple sources or for many stocks simultaneously, Rust's `async`/`await` and concurrency features (like `tokio`) would be employed to make API calls non-blocking and efficient.
* Serialization/Deserialization: To persist portfolio data, you would use crates like `serde` to serialize `Portfolio` structs to disk (e.g., JSON, CSV) and deserialize them back when the application starts.

Our example will focus on the core data structures and operations within a simple in-memory portfolio.

Example Code

```rust
use std::collections::HashMap;

/// Represents a single stock holding in the portfolio.
#[derive(Debug, Clone)]
pub struct Stock {
    pub symbol: String,
    pub name: String,
    pub current_price: f64,
    pub shares_owned: u32,
}

impl Stock {
    /// Creates a new Stock instance.
    pub fn new(symbol: String, name: String, current_price: f64, shares_owned: u32) -> Self {
        Stock {
            symbol,
            name,
            current_price,
            shares_owned,
        }
    }

    /// Calculates the total value of this stock holding.
    pub fn total_value(&self) -> f64 {
        self.current_price * self.shares_owned as f64
    }
}

/// Represents a collection of stocks a user is tracking or owns.
pub struct Portfolio {
    stocks: HashMap<String, Stock>,
}

impl Portfolio {
    /// Creates a new empty Portfolio.
    pub fn new() -> Self {
        Portfolio {
            stocks: HashMap::new(),
        }
    }

    /// Adds a stock to the portfolio. If a stock with the same symbol already exists,
    /// it updates the existing entry.
    pub fn add_stock(&mut self, stock: Stock) {
        self.stocks.insert(stock.symbol.clone(), stock);
        println!("Added/Updated stock: {}", self.stocks.get(&stock.symbol).unwrap().name);
    }

    /// Updates the current price of a stock by its symbol.
    /// Returns `true` if the stock was found and updated, `false` otherwise.
    pub fn update_stock_price(&mut self, symbol: &str, new_price: f64) -> bool {
        if let Some(stock) = self.stocks.get_mut(symbol) {
            let old_price = stock.current_price;
            stock.current_price = new_price;
            println!(
                "Updated {} price from {:.2} to {:.2}",
                stock.name,
                old_price,
                new_price
            );
            true
        } else {
            println!("Stock with symbol '{}' not found in portfolio.", symbol);
            false
        }
    }

    /// Calculates the total market value of all stocks in the portfolio.
    pub fn calculate_total_portfolio_value(&self) -> f64 {
        self.stocks.values().map(|s| s.total_value()).sum()
    }

    /// Displays the details of each stock in the portfolio and the total portfolio value.
    pub fn display_portfolio(&self) {
        println!("\n--- Current Portfolio ---");
        if self.stocks.is_empty() {
            println!("Portfolio is empty.");
            return;
        }

        for stock in self.stocks.values() {
            println!(
                "{} ({}): {:.2} per share, {} shares, Total: {:.2}",
                stock.name,
                stock.symbol,
                stock.current_price,
                stock.shares_owned,
                stock.total_value()
            );
        }
        println!("-------------------------");
        println!("Total Portfolio Value: {:.2}", self.calculate_total_portfolio_value());
        println!("-------------------------\n");
    }

    /// Retrieves a reference to a stock by its symbol.
    pub fn get_stock(&self, symbol: &str) -> Option<&Stock> {
        self.stocks.get(symbol)
    }
}

fn main() {
    let mut my_portfolio = Portfolio::new();

    // Add some initial stocks
    let apple = Stock::new("AAPL".to_string(), "Apple Inc.".to_string(), 170.50, 10);
    let microsoft = Stock::new("MSFT".to_string(), "Microsoft Corp.".to_string(), 420.15, 5);
    let google = Stock::new("GOOG".to_string(), "Alphabet Inc. (Class C)".to_string(), 150.80, 7);

    my_portfolio.add_stock(apple);
    my_portfolio.add_stock(microsoft);
    my_portfolio.add_stock(google);

    // Display initial portfolio
    my_portfolio.display_portfolio();

    // Simulate price updates
    println!("Simulating price updates...");
    my_portfolio.update_stock_price("AAPL", 172.00);
    my_portfolio.update_stock_price("MSFT", 425.50);
    my_portfolio.update_stock_price("GOOG", 149.90);
    my_portfolio.update_stock_price("AMZN", 180.00); // This stock is not in the portfolio

    // Display updated portfolio
    my_portfolio.display_portfolio();

    // Add a new stock later
    let tesla = Stock::new("TSLA".to_string(), "Tesla Inc.".to_string(), 185.30, 3);
    my_portfolio.add_stock(tesla);

    // Display final portfolio
    my_portfolio.display_portfolio();

    // Example of retrieving a specific stock
    if let Some(stock) = my_portfolio.get_stock("AAPL") {
        println!("\nDetails for {}: Current Price = {:.2}", stock.name, stock.current_price);
    }
}
```