LLD:Design Stock Broker Platform Like Zerodha, Groww
Below is a clean, Low Level Design (LLD) for a Stock Broker Platform like Zerodha/Groww, with Java classes, responsibilities, and a class-diagram explanation
1. Core Requirements (LLD Scope)
Functional
Users can:
Buy / Sell stocks
View account balance
View portfolio (holdings)
System:
Maintains stock prices
Matches and executes orders
Closes open orders when stock price changes
e.g., LIMIT orders becoming executable
Non-Functional (implicitly handled in design)
Thread safety
Extensibility (order types, exchanges)
Separation of concerns
2. Key Concepts & Design Decisions
Order Types
MARKET – executed immediately at current price
LIMIT – executed when price condition satisfies
Order States
OPEN → EXECUTED / CANCELLED
Price Change Handling (Important)
Observer Pattern
Orders subscribe to stock price updates
When price changes → relevant OPEN orders are evaluated
High-Level Class Diagram (Textual)
User
├── Account
│ ├── balance
│ └── Portfolio
│ └── Holding
│
└── Order
Stock
├── symbol
├── price
└── notifyPriceChange()
Order (abstract)
├── BuyOrder
└── SellOrder
OrderService
├── placeOrder()
├── cancelOrder()
OrderBook
├── buyOrders
└── sellOrders
MatchingEngine
└── match()
PriceService
└── updatePrice()
Trade
UML Class Diagram (ASCII – Interview Friendly)
+----------------+
| User |
+----------------+
| - userId |
| - account |
+----------------+
| +getAccount() |
+--------+-------+
|
| 1
| owns
|
+--------v-------+
| Account |
+----------------+
| - balance |
| - portfolio |
+----------------+
| +applyTrade() |
+--------+-------+
|
| 1
|
+--------v-------+
| Portfolio |
+----------------+
| - holdings |
+----------------+
| +addStock() |
| +removeStock() |
+----------------+
------------------------------------------------------------
+----------------+
| Stock |
+----------------+
| - symbol |
| - price |
| - observers |
+----------------+
| +updatePrice() |
| +registerObs() |
+--------+-------+
|
| notifies
|
+--------v------------------+
| PriceObserver |
+---------------------------+
| +onPriceUpdate(Stock) |
+-------------+-------------+
|
| implements
|
+-------------v-------------+
| Order (abstract) |
+---------------------------+
| - orderId |
| - stock |
| - quantity |
| - orderType |
| - orderSide |
| - limitPrice |
| - status |
+---------------------------+
| +isExecutable() |
| +execute() |
+-------------+-------------+
|
-----------------------
| |
+----v------+ +-----v------+
| BuyOrder | | SellOrder |
+-----------+ +-------------+
------------------------------------------------------------
+----------------+
| OrderService |
+----------------+
| +placeOrder() |
| +cancelOrder() |
+----------------+
Java LLD (Core Classes)
Enums
enum OrderType {
MARKET,
LIMIT
}
enum OrderStatus {
OPEN,
EXECUTED,
CANCELLED
}
enum OrderSide {
BUY,
SELL
}
Stock (Observable)
import java.util.*;
class Stock {
private final String symbol;
private double price;
private final List<PriceObserver> observers = new ArrayList<>();
public Stock(String symbol, double price) {
this.symbol = symbol;
this.price = price;
}
public synchronized void updatePrice(double newPrice) {
this.price = newPrice;
notifyObservers();
}
public double getPrice() {
return price;
}
public String getSymbol() {
return symbol;
}
public void registerObserver(PriceObserver observer) {
observers.add(observer);
}
private void notifyObservers() {
for (PriceObserver observer : observers) {
observer.onPriceUpdate(this);
}
}
}
Observer Interface
interface PriceObserver {
void onPriceUpdate(Stock stock);
}
Order (Abstract)
import java.time.Instant;
import java.util.UUID;
abstract class Order implements PriceObserver {
protected final String orderId;
protected final User user;
protected final Stock stock;
protected final int quantity;
protected final OrderType orderType;
protected final OrderSide side;
protected final double limitPrice;
protected OrderStatus status;
protected final Instant createdAt;
protected Order(User user, Stock stock, int quantity,
OrderType orderType, OrderSide side, double limitPrice) {
this.orderId = UUID.randomUUID().toString();
this.user = user;
this.stock = stock;
this.quantity = quantity;
this.orderType = orderType;
this.side = side;
this.limitPrice = limitPrice;
this.status = OrderStatus.OPEN;
this.createdAt = Instant.now();
}
public abstract boolean isExecutable(double marketPrice);
public void execute(double executionPrice) {
this.status = OrderStatus.EXECUTED;
user.getAccount().applyTrade(stock, quantity, side, executionPrice);
}
public OrderStatus getStatus() {
return status;
}
}
Buy & Sell Orders
class BuyOrder extends Order {
public BuyOrder(User user, Stock stock, int quantity,
OrderType type, double limitPrice) {
super(user, stock, quantity, type, OrderSide.BUY, limitPrice);
}
@Override
public boolean isExecutable(double marketPrice) {
if (orderType == OrderType.MARKET) return true;
return marketPrice <= limitPrice;
}
@Override
public void onPriceUpdate(Stock stock) {
if (status == OrderStatus.OPEN && isExecutable(stock.getPrice())) {
execute(stock.getPrice());
}
}
}
class SellOrder extends Order {
public SellOrder(User user, Stock stock, int quantity,
OrderType type, double limitPrice) {
super(user, stock, quantity, type, OrderSide.SELL, limitPrice);
}
@Override
public boolean isExecutable(double marketPrice) {
if (orderType == OrderType.MARKET) return true;
return marketPrice >= limitPrice;
}
@Override
public void onPriceUpdate(Stock stock) {
if (status == OrderStatus.OPEN && isExecutable(stock.getPrice())) {
execute(stock.getPrice());
}
}
}
User, Account & Portfolio
import java.util.*;
class User {
private final String userId;
private final Account account;
public User(String userId, double initialBalance) {
this.userId = userId;
this.account = new Account(initialBalance);
}
public Account getAccount() {
return account;
}
}
class Account {
private double balance;
private final Portfolio portfolio = new Portfolio();
public Account(double balance) {
this.balance = balance;
}
public synchronized void applyTrade(Stock stock, int qty,
OrderSide side, double price) {
double amount = qty * price;
if (side == OrderSide.BUY) {
balance -= amount;
portfolio.addStock(stock.getSymbol(), qty);
} else {
balance += amount;
portfolio.removeStock(stock.getSymbol(), qty);
}
}
}
class Portfolio {
private final Map<String, Integer> holdings = new HashMap<>();
public void addStock(String symbol, int qty) {
holdings.put(symbol, holdings.getOrDefault(symbol, 0) + qty);
}
public void removeStock(String symbol, int qty) {
holdings.put(symbol, holdings.get(symbol) - qty);
}
}
How Price Change Closes Orders (Flow)
Sequence
Stock price updates
Stock notifies all registered orders
Each OPEN order checks:
Is it executable?
If yes →
execute()→ status becomesEXECUTED
Why this is good
No polling
O(number_of_open_orders_for_stock)
Event-driven (production-grade)
Design Patterns Used
| Pattern | Where | Why |
| ------------------------- | --------------------- | ------------------------- |
| **Observer** | Stock → Orders | React to price changes |
| **Strategy (implicit)** | `isExecutable()` | Different execution rules |
| **Single Responsibility** | Order, Stock, Account | Clean separation |
| **Open/Closed** | New order types | No core changes |
Interview Add-Ons (If Asked)
Concurrency → Order execution via locks or queues
Matching Engine → Centralized order book per stock
Persistence → Orders & trades stored in DB
Partial fills → Split execution
Stop-loss orders → Another
OrderType

