Track open positions and unrealized P&L
Chapter 16 introduced kite.positions(). This chapter builds continuous P&L tracking — the number your risk logic (chapters 73-76) and monitoring (chapters 89-92) actually run on.
def compute_unrealized_pnl(positions: list[dict]) -> float:
return sum(p["pnl"] for p in positions if p["quantity"] != 0)
def compute_position_pnl_pct(position: dict) -> float:
cost_basis = abs(position["quantity"]) * position["average_price"]
return position["pnl"] / cost_basis * 100 if cost_basis else 0.0
Live P&L using the price cache instead of polling positions()
Polling kite.positions() on every tick is wasteful and rate-limited. Combine the live price cache (chapter 42) with your own tracked entry price for near-real-time P&L without hammering the REST API:
def live_pnl(entry_price: float, current_price: float, qty: int, side: str) -> float:
direction = 1 if side == "BUY" else -1
return direction * (current_price - entry_price) * qty
class PositionTracker:
def __init__(self, price_cache: PriceCache):
self.price_cache = price_cache
self.positions: dict[str, dict] = {} # symbol -> {token, qty, side, entry_price}
def total_unrealized_pnl(self) -> float:
total = 0.0
for sym, pos in self.positions.items():
current = self.price_cache.get_price(pos["token"])
if current is not None:
total += live_pnl(pos["entry_price"], current, pos["qty"], pos["side"])
return total
Reconcile against broker truth periodically, don't trust this alone
This local, tick-driven P&L is for fast decisions (e.g. checking a per-trade stop, chapter 73) — it can drift from the broker's own positions() P&L due to fees/charges not reflected in raw price movement, or if PositionTracker's entry price ever diverges from reality (partial fills, chapter 56). Periodically reconcile (kite.positions()) as the authoritative number, especially before any decision with real consequence like a daily-loss-limit check (chapter 73).
Realized vs unrealized — don't conflate them in a daily loss check
pnl in positions() combines both; for a strict daily-loss kill switch (chapter 90) you often want realized loss (locked in by closed trades) tracked separately from unrealized (still fluctuating, could recover) — decide explicitly which one your risk rule is meant to gate on.