Katkat

Process a Mutable Stock-Price Log

Lead BankSoftware Engineer · Onsite

Last updated

Design an in-memory mutable stock price log that supports recording prices, tracking update history, calculating percentage changes across dates, and comparing performance between tickers.

Problem Statement

Financial ledgers frequently ingest updates, corrections, and queries for market data. Design a class StockPriceTracker that maintains a mutable log of stock closing prices across various dates.

Dates are provided as opaque string identifiers (e.g., "2023-10-01", "D1"). Do not assume any chronological ordering based on lexicographical string order; each date should be treated strictly as a unique identifier.

Implement the following methods:

  • record_price(ticker: str, date: str, price: float) -> None Records a price for a given ticker on a specified date. If a price already exists for that (ticker, date) pair, update it to the new price and preserve the record of previous values (update provenance).

  • get_price(ticker: str, date: str) -> float | None Returns the current price for the given ticker on the specified date, or None if no price has been recorded.

  • get_history(ticker: str, date: str) -> list[float] Returns the complete history of values recorded for the given (ticker, date) pair in the order they were submitted. If no entry exists, return an empty list.

  • get_pct_change(ticker: str, date_from: str, date_to: str) -> str | None Calculates the percentage change from date_from to date_to using the latest recorded prices: ((price_to - price_from) / price_from) * 100. Return the result formatted as a string rounded to two decimal places with a % suffix (e.g., "+5.25%" for positive, "-3.10%" for negative, or "0.00%" for zero). If either date has no recorded price or if price_from is 0, return None.

  • compare_tickers(ticker_a: str, ticker_b: str, date_from: str, date_to: str) -> str | None Compares the percentage change of ticker_a against ticker_b across the same two dates.

    • Returns ticker_a if ticker_a had a strictly greater percentage change.
    • Returns ticker_b if ticker_b had a strictly greater percentage change.
    • Returns "TIE" if both percentage changes are equal.
    • Returns None if either ticker cannot calculate a valid percentage change.

Example

tracker = StockPriceTracker()

tracker.record_price("AAPL", "2023-01-01", 100.0)
tracker.record_price("AAPL", "2023-01-02", 110.0)
tracker.record_price("MSFT", "2023-01-01", 200.0)
tracker.record_price("MSFT", "2023-01-02", 210.0)

# AAPL changed +10.00%, MSFT changed +5.00%
assert tracker.get_pct_change("AAPL", "2023-01-01", "2023-01-02") == "+10.00%"
assert tracker.get_pct_change("MSFT", "2023-01-01", "2023-01-02") == "+5.00%"
assert tracker.compare_tickers("AAPL", "MSFT", "2023-01-01", "2023-01-02") == "AAPL"

# Update AAPL price for 2023-01-02 to 95.0
tracker.record_price("AAPL", "2023-01-02", 95.0)
assert tracker.get_history("AAPL", "2023-01-02") == [110.0, 95.0]
assert tracker.get_pct_change("AAPL", "2023-01-01", "2023-01-02") == "-5.00%"
assert tracker.compare_tickers("AAPL", "MSFT", "2023-01-01", "2023-01-02") == "MSFT"

Constraints

  • Number of total operations: up to 200,000.
  • price >= 0.0 for all record operations.
  • Ticker names and date identifiers are non-empty ASCII strings of length 1 <= len <= 32.
  • All query operations (get_price, get_history, get_pct_change, compare_tickers) should run in O(1) average time.

Hints

Hint 1

Use a nested dictionary mapping each ticker to a dictionary of date keys, where each date maps to a list tracking the chronological sequence of price updates.

Hint 2

Be careful with floating-point formatting: ensure positive values include an explicit `+` sign and `0` renders as `0.00%` rather than `+0.00%` or `-0.00%`.

Hint 3

When comparing percentage changes between two tickers, compare the raw floating-point delta values rather than comparing formatted string outputs to avoid rounding discrepancies.

Follow-up questions

  • How would you support rollbacks or time-travel queries (e.g., query what a ticker's price was as of operation timestamp T)?
  • How would you handle concurrent reads and writes safely without introducing significant lock contention?
  • If the dates were actual timestamps and we needed range queries (e.g., maximum price or average price between date A and date B), how would you adapt the internal data structure?