LESSON 08 · SECTION 1
Why Error Handling Matters
In production algo trading, errors are inevitable: API timeouts, missing price data, division by zero in P&L calculations, network drops. Without proper error handling, your bot crashes at the worst possible moment — during market hours with open positions.
try / exceptWrap risky code in
try. If an error occurs, the except block runs instead of crashing.finallyRuns whether or not an error occurred — used for cleanup like closing connections.
raiseThrow your own errors intentionally — e.g., raise an error if lot size is invalid.
Custom Exceptions
Create trading-specific error classes:
KillSwitchError, InvalidOrderError.Section 2
Basic try / except
Without error handling — CRASH
data = {}
ltp = data["ltp"] # KeyError: 'ltp'
# Program crashes — open positions unmanaged!
try_except.pypython
# With error handling — safe def get_ltp(data): try: ltp = data["ltp"] if ltp <= 0: raise ValueError(f"Invalid LTP: {ltp}") return ltp except KeyError: print("⚠️ LTP key missing in market data") return None except ValueError as e: print(f"⚠️ Data error: {e}") return None except Exception as e: print(f"❌ Unexpected error: {e}") return None print(get_ltp({"ltp": 22450})) # 22450 print(get_ltp({})) # KeyError caught print(get_ltp({"ltp": -5})) # ValueError caught
Output
22450 ⚠️ LTP key missing in market data None ⚠️ Data error: Invalid LTP: -5 None
Section 3
Common Trading Exceptions
| Exception | When it occurs in trading | How to handle |
|---|---|---|
KeyError | API response missing a field (e.g., no "ltp") | Use .get() or catch KeyError |
ZeroDivisionError | Dividing by zero lot size or zero price | Check denominator before division |
ValueError | Invalid order type string or negative price | Validate inputs before use |
TypeError | Adding int + None (missing data) | Type-check or use try |
ConnectionError | Broker API / WebSocket disconnected | Retry with backoff, alert user |
TimeoutError | API call took too long — order not confirmed | Cancel and re-query order status |
Section 4
finally & Custom Exceptions
custom_exceptions.pypython
# Custom trading exceptions class KillSwitchError(Exception): """Raised when daily loss limit is breached.""" pass class InvalidOrderError(Exception): """Raised when an order has invalid parameters.""" pass def place_order(symbol, side, lots, daily_pnl, loss_limit=-10000): try: # Validate inputs if lots <= 0: raise InvalidOrderError(f"Lots must be > 0, got {lots}") if side not in ("BUY", "SELL"): raise InvalidOrderError(f"Invalid side: {side}") if daily_pnl <= loss_limit: raise KillSwitchError(f"Daily P&L {daily_pnl} ≤ limit {loss_limit}") print(f"✅ Order placed: {side} {lots} lot(s) of {symbol}") except KillSwitchError as e: print(f"🚨 KILL SWITCH: {e}") except InvalidOrderError as e: print(f"❌ INVALID ORDER: {e}") finally: print(f" [Order attempt logged for audit]") place_order("NIFTY", "BUY", 2, daily_pnl=5000) place_order("NIFTY", "BUY", 2, daily_pnl=-12000) place_order("NIFTY", "LONG", 2, daily_pnl=500)
Output
✅ Order placed: BUY 2 lot(s) of NIFTY [Order attempt logged for audit] 🚨 KILL SWITCH: Daily P&L -12000 ≤ limit -10000 [Order attempt logged for audit] ❌ INVALID ORDER: Invalid side: LONG [Order attempt logged for audit]
💡 PRO TIP
The finally block is perfect for audit logging — it always runs, success or failure. SEBI's algo trading guidelines require every order attempt to be logged regardless of outcome.
Section 5
Quick Quiz
QUESTION 1 OF 3
What does the
finally block guarantee?QUESTION 2 OF 3
When should you catch
Exception (the base exception class)?QUESTION 3 OF 3
Why create a custom
KillSwitchError instead of using a plain ValueError?Section 6
Practice Exercises
EXERCISE 1 · EASY
Safe Division
Write a function
safe_rr(sl_pts, tgt_pts) that returns the risk:reward ratio.
Handle ZeroDivisionError if SL points is 0. Return 0 on error.
Hint: wrap the division in try/except ZeroDivisionError.
EXERCISE 2 · MEDIUM
Safe API Response Parser
Write
parse_tick(data) that extracts ltp, volume,
and symbol from a broker API dict. Handle KeyError (missing field),
TypeError (wrong type), and log which field failed.
Hint: use multiple
except clauses with different exception types.EXERCISE 3 · CHALLENGE
Robust Order Manager
Create
InvalidOrderError and KillSwitchError custom exceptions.
Write an order_manager(orders, daily_pnl) function that processes a list of
order dicts, validates each (lots > 0, valid side), checks kill switch, and logs every
attempt in a audit_log list using finally.
Hint: build the audit_log list outside the function and append to it inside finally.
🚀 ALMOST DONE — LESSON 9 NEXT
Your strategy now runs, makes decisions, handles errors. But where does the data live between sessions?
Lesson 9: File I/O & JSON Logs — save trade journals, read config files,
and build a persistent audit trail. The final piece of Phase 1!