Implement Stock Price Query APIs
Company: Lead Bank
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates skills in time-series data modeling, API and data-structure design, transactional (atomic) batch updates, concurrency control, and date/time parsing for stock price records.
Part 1: Daily StockPriceStore API Processor
Constraints
- 0 <= len(initial_records), len(operations) <= 10^4
- Date strings are intended to use format YYYY-MM-DD, but invalid strings may appear
- Prices are numeric values
- The sum of lengths of all update date ranges is at most 10^5
Examples
Input: ([('AAPL', '2024-01-01', 100.0), ('AAPL', '2024-01-02', 110.0), ('AAPL', '2024-01-03', 120.0)], [('get_price', 'AAPL', '2024-01-02'), ('get_change', 'AAPL', '2024-01-01', '2024-01-03', 'difference'), ('update_prices', 'AAPL', '2024-01-01', '2024-01-02', [101.0, 111.0]), ('get_price', 'AAPL', '2024-01-01'), ('update_prices', 'AAPL', '2024-01-02', '2024-01-03', [112.0, 121.0]), ('get_price', 'AAPL', '2024-01-03')])
Expected Output: [110.0, 20.0, True, 101.0, False, 120.0]
Explanation: The first update succeeds. The second update fails because 2024-01-02 was already updated before, so the update is rejected atomically and 2024-01-03 stays unchanged.
Input: ([('MSFT', '2024-02-01', 200.0), ('MSFT', '2024-02-01', 210.0), ('MSFT', '2024-02-02', 252.0)], [('get_price', 'MSFT', '2024-02-01'), ('get_change', 'MSFT', '2024-02-01', '2024-02-02', 'percent_change')])
Expected Output: [210.0, 0.2]
Explanation: Duplicate initial records are resolved by keeping the later valid one, so the price on 2024-02-01 is 210.0.
Input: ([('TSLA', '2024-03-01', 0.0), ('TSLA', '2024-03-02', 50.0)], [('get_change', 'TSLA', '2024-03-01', '2024-03-02', 'percent_change'), ('get_change', 'TSLA', '2024-03-03', '2024-03-04', 'difference'), ('update_prices', 'TSLA', '2024-03-02', '2024-03-01', [1.0, 2.0]), ('get_price', 'TSLA', 'bad-date')])
Expected Output: [None, None, False, None]
Explanation: Percent change cannot be computed when the start price is zero. Missing dates and invalid date strings also produce failure or `None`.
Input: ([('IBM', '2024-04-01', 10.0), ('IBM', '2024-04-03', 30.0)], [('update_prices', 'IBM', '2024-04-01', '2024-04-03', [11.0, 20.0, 31.0]), ('get_price', 'IBM', '2024-04-01'), ('update_prices', 'IBM', '2024-04-01', '2024-04-01', [15.0, 16.0])])
Expected Output: [False, 10.0, False]
Explanation: The first update fails because 2024-04-02 does not exist, so no prices are changed. The last update fails because the number of replacement prices does not match the date range length.
Hints
- Use a nested dictionary like `store[symbol][date]` so point lookups are fast.
- For atomic updates, first validate the whole date range, and only then write the new prices.
Part 2: Hourly StockPriceStore and Daily Hour List Query
Constraints
- 0 <= len(initial_records), len(operations) <= 10^5
- Timestamp strings are intended to use format YYYY-MM-DD HH:MM, but invalid strings may appear
- Day query strings are intended to use format YYYY-MM-DD
- Prices are numeric values
Examples
Input: ([('AAPL', '2024-01-01 10:00', 101.0), ('AAPL', '2024-01-01 09:00', 100.0), ('AAPL', '2024-01-01 15:00', 105.0), ('MSFT', '2024-01-01 09:00', 200.0)], [('get_hourly_prices', 'AAPL', '2024-01-01'), ('get_price', 'AAPL', '2024-01-01 10:00'), ('get_price', 'MSFT', '2024-01-01 10:00')])
Expected Output: [[('2024-01-01 09:00', 100.0), ('2024-01-01 10:00', 101.0), ('2024-01-01 15:00', 105.0)], 101.0, None]
Explanation: Hourly prices for a day must be returned in time order even if the input records were unsorted.
Input: ([('AAPL', '2024-02-01 09:00', 50.0), ('AAPL', '2024-02-01 09:00', 55.0), ('AAPL', '2024-02-02 09:00', 60.0)], [('get_price', 'AAPL', '2024-02-01 09:00'), ('get_hourly_prices', 'AAPL', '2024-02-01'), ('get_hourly_prices', 'AAPL', '2024-02-02')])
Expected Output: [55.0, [('2024-02-01 09:00', 55.0)], [('2024-02-02 09:00', 60.0)]]
Explanation: Duplicate initial records are resolved by keeping the later valid timestamp entry.
Input: ([('TSLA', '2024-03-05 12:00', 700.0)], [('get_hourly_prices', 'TSLA', '2024-03-06'), ('get_hourly_prices', 'TSLA', 'bad-day'), ('get_price', 'TSLA', '2024-03-05'), ('get_price', 'NONE', '2024-03-05 12:00')])
Expected Output: [[], None, None, None]
Explanation: A valid day with no data returns an empty list. Invalid query strings return `None`.
Input: ([], [('get_hourly_prices', 'AAPL', '2024-01-01'), ('get_price', 'AAPL', '2024-01-01 09:00')])
Expected Output: [[], None]
Explanation: With no initial data, day queries return empty lists and exact timestamp lookups return `None`.
Hints
- A hash map keyed by `(symbol, timestamp)` makes exact lookups simple.
- Group records by `(symbol, day)` and sort each daily bucket once so `get_hourly_prices` can return results quickly.