What the Day Left Open
A medium Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- medium
- Seniority
- junior
- Asked in
- Fidelity
Problem
A brokerage's position-keeping service replays the day's executed trades to reconstruct each account's holdings. Each trade names a ticker, a side of buy or sell, and a share count, where buys add to a position and sells subtract from it. Return the net shares held per ticker, dropping any ticker whose position was fully closed out by the close, and keeping the tickers that went net short.
Every fill moves a position. Reconstruct where the book settled by the close.
Example 1
Input:
trades = [{"side":"buy","shares":100,"ticker":"AAPL"},{"side":"sell","shares":40,"ticker":"AAPL"},{"side":"buy","shares":50,"ticker":"MSFT"}]Output:
{"AAPL":60,"MSFT":50}Example 2
Input:
trades = [{"side":"buy","shares":30,"ticker":"TSLA"},{"side":"sell","shares":30,"ticker":"TSLA"},{"side":"buy","shares":10,"ticker":"NVDA"}]Output:
{"NVDA":10}Worked solution and explanation
Why this problem exists in real interviews
This is a signed group-and-sum wearing a portfolio costume. The skill being probed is whether you can fold the side field into the sign of the quantity BEFORE you accumulate, and whether you understand that a fully closed position is not a zero you report, it is a key you drop. Anyone can tally shares per ticker. The two things that separate candidates are the sign convention (sells are negative) and the final filter (net zero disappears, net short stays). Get the filter backwards and you either leak closed-out tickers into the book or, worse, throw away the short positions the risk desk most wants to see.
Break down the requirements
Step 1: Normalize each trade to a signed quantity
A buy of 40 and a sell of 40 should cancel. The cleanest way is to make the quantity itself carry the direction: keep shares as-is for a buy, negate it for a sell. Now every trade is just a number to add, and the buy/sell distinction never has to be revisited downstream.
Step 2: Accumulate per ticker in one pass
Use a dict keyed by ticker and add the signed quantity with positions.get(ticker, 0) + qty. One pass, O(1) per trade. Reaching for collections.Counter or defaultdict(int) is equally fine; the point is that you never scan the list more than once.
Step 3: Drop the closed-out positions at the end
A ticker that nets to zero was fully closed during the day and must not appear in the result. Filter it out in a final dict comprehension: keep entries where net != 0. Do this AFTER accumulating, not during, because a position can cross zero mid-day and reopen. Negatives are real net-short positions and stay.
The solution
def net_positions(trades):
positions = {}
for trade in trades:
qty = trade['shares']
if trade['side'] == 'sell':
qty = -qty
positions[trade['ticker']] = positions.get(trade['ticker'], 0) + qty
return {ticker: net for ticker, net in positions.items() if net != 0}Common follow-up questions
- How would you also return the total traded volume per ticker (shares bought plus shares sold, ignoring direction) alongside the net position? (Tests accumulating two aggregates in one pass, one signed and one absolute, without a second scan.)
- The fills now arrive as an unbounded stream rather than a finished list. How does your design change? (Tests moving from a batch dict to a stateful operator and deciding when net-zero tickers are emitted or suppressed.)
- What if a trade can carry an unknown side value or a negative share count from a bad upstream feed? (Tests input validation and whether bad records are skipped, raised on, or quarantined rather than silently corrupting the book.)