# Closing Bell

> Every share you own, priced at today's close. Tally the gains.

Canonical URL: <https://datadriven.io/problems/closing_bell>

Domain: Python · Difficulty: medium · Seniority: L6 · Asked in: Amazon

## Problem

A brokerage export gives you `prices`, a dict keyed by ISO date string mapping to that day's close, and `purchases`, a list of `[date, quantity]` buy records where quantity is a whole number of shares. Mark every lot to the price on the latest date in the history and return the portfolio's total gain, where each buy contributes its quantity times how far the price has moved since the day it was bought.

## Example 1

Input:

```
prices = {"2024-01-01":100,"2024-01-15":110,"2024-02-01":120}, purchases = [["2024-01-01",10],["2024-01-15",5]]
```

Output:

```
250
```

## Example 2

Input:

```
prices = {"2024-03-01":50,"2024-03-15":45}, purchases = [["2024-03-01",20]]
```

Output:

```
-100
```

## Worked solution and explanation

### The trap hiding in a friendly dict

Strip the finance costume off and this is a per-record accumulation against one derived reference value: mark every lot to the latest price, then add up quantity times the move. The whole problem hinges on where 'latest' comes from. Modern Python dicts remember insertion order, so it is tempting to grab the last entry or a variable you kept updating as you looped. But a price feed can land out of order, and the moment it does, insertion order lies. The latest price is `prices[max(prices)]`, full stop. Get that one lookup wrong and every lot is marked to the wrong number, so the entire portfolio total is off, not just one row.

> **Trick to solving**
>
> ISO date strings sort lexically the same way they sort chronologically, so `max(prices)` over the keys hands you the most recent date with zero date parsing. That single fact is what lets a plain builtin stand in for real calendar logic.

---

### Walking it

#### Step 1: Anchor to the latest price

The current value of any share is the close on the most recent date. Pull it once, before the loop, as `prices[max(prices)]`. Do it once, not per purchase.

#### Step 2: Price each lot individually

Each [date, quantity] buy paid `prices[date]` per share and those same shares are now worth the latest price. The gain on that lot is quantity times the difference, and it can be negative.

#### Step 3: Sum and return a float

Accumulate every lot's gain into a running total seeded at 0.0 and return it. Seeding with a float means a whole-number result still comes back as a float.

---

### The solution

**Mark every lot to the latest close**

```python
def portfolio_profit_calculator(prices, purchases):
    latest_date = max(prices)
    latest_price = prices[latest_date]
    total_profit = 0.0
    for date, quantity in purchases:
        buy_price = prices[date]
        total_profit += quantity * (latest_price - buy_price)
    return total_profit
```

> **Time and Space Complexity**
>
> **Time:** O(n + k) where n is the number of purchases and k is the number of price entries (the `max` scan over the keys).
> 
> **Space:** O(1) beyond the inputs.

> **Interviewers watch for**
>
> Whether you reached for the latest date with `max(prices)` or leaned on insertion order. A candidate who writes `list(prices)[-1]` looks fine on tidy inputs and quietly breaks the instant a backfilled price lands out of sequence. Naming that risk before it bites is the tell.

> **Common pitfall**
>
> Floating-point drift. Summing many float differences accumulates rounding error, and real money code reaches for `Decimal` or a rounding pass. The tests here accept plain float arithmetic, but flagging the precision concern is free seniority points.

---

## Common follow-up questions

- What if you needed a per-date profit breakdown? _(Tests returning a dict of profit per purchase date instead of a single total.)_
- How would you fold dividends into the P&L? _(Tests adding a dividend income stream alongside capital gains.)_
- What if you need historical P&L at every date instead of just the latest? _(Tests computing unrealized P&L at each date point using the corresponding price.)_
- What about short positions? _(Tests handling negative quantities and the reversed profit logic.)_

## Related

- [All practice problems](https://datadriven.io/problems)
- [Mock interview mode](https://datadriven.io/interview/closing_bell)
- [Python Interview Questions](https://datadriven.io/python-interview-questions)
- [Data Engineering Interview Prep Guide](https://datadriven.io/data-engineer-interview-prep)
- [Daily Challenge](https://datadriven.io/daily)

---

Source: DataDriven (https://datadriven.io). DataDriven is the data engineering interview community. Live code execution in SQL, Python, and Spark sandboxes. Every feature is open to every member.