{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Session 5: Event-Driven Trading Bot Foundation\n", "\n", "## Notebook 1: Event System Foundation\n", "\n", "**Learning Objectives:**\n", "- Understand the Publisher-Subscriber pattern\n", "- Build a basic event queue system\n", "- Create event types for trading bots\n", "- Test component communication through events\n", "\n", "**Why This Matters:**\n", "Instead of components calling each other directly (tight coupling), they communicate through events (loose coupling). This makes your trading bot more professional, testable, and scalable.\n", "\n", "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Part 1: Basic Event System\n", "\n", "Let's start with the simplest possible event system - a basic Event class that carries information between components." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Basic Event:\n", "Event(price_update from market_data_handler at 19:06:23)\n", "\n", "Event Data:\n", "{\n", " \"event_type\": \"price_update\",\n", " \"data\": {\n", " \"symbol\": \"BTC/USDT\",\n", " \"price\": 43250.5\n", " },\n", " \"timestamp\": \"2025-06-25T19:06:23.130343\",\n", " \"source\": \"market_data_handler\"\n", "}\n" ] } ], "source": [ "from datetime import datetime\n", "from typing import Dict, Any, List, Callable\n", "from dataclasses import dataclass\n", "import json\n", "\n", "# Basic Event class - every message in our system\n", "@dataclass\n", "class Event:\n", " \"\"\"Base class for all events in our trading system\"\"\"\n", " event_type: str\n", " data: Dict[str, Any]\n", " timestamp: datetime = None\n", " source: str = \"unknown\"\n", " \n", " def __post_init__(self):\n", " if self.timestamp is None:\n", " self.timestamp = datetime.now()\n", " \n", " def to_dict(self) -> Dict[str, Any]:\n", " \"\"\"Convert event to dictionary for logging/debugging\"\"\"\n", " return {\n", " 'event_type': self.event_type,\n", " 'data': self.data,\n", " 'timestamp': self.timestamp.isoformat(),\n", " 'source': self.source\n", " }\n", " \n", " def __str__(self):\n", " return f\"Event({self.event_type} from {self.source} at {self.timestamp.strftime('%H:%M:%S')})\"\n", "\n", "# Test our basic Event\n", "test_event = Event(\n", " event_type=\"price_update\",\n", " data={\"symbol\": \"BTC/USDT\", \"price\": 43250.50},\n", " source=\"market_data_handler\"\n", ")\n", "\n", "print(\"Basic Event:\")\n", "print(test_event)\n", "print(\"\\nEvent Data:\")\n", "print(json.dumps(test_event.to_dict(), indent=2, default=str))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Part 2: Event Queue (Message Bus)\n", "\n", "Now we need a central place where all events go - the Event Queue. This is the \"post office\" of our trading bot." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "๐Ÿ—๏ธ Event Queue created!\n", "Initial stats: {'events_in_queue': 0, 'total_events_processed': 0, 'event_types_subscribed': [], 'subscribers_count': {}}\n" ] } ], "source": [ "from collections import deque\n", "from threading import Lock\n", "\n", "class EventQueue:\n", " \"\"\"Central event queue - the heart of our event-driven system\"\"\"\n", " \n", " def __init__(self):\n", " self.events = deque() # Fast append/pop from both ends\n", " self.subscribers = {} # {event_type: [callback_functions]}\n", " self.lock = Lock() # Thread safety for production\n", " self.event_history = [] # Keep history for debugging\n", " \n", " def subscribe(self, event_type: str, callback: Callable[[Event], None]):\n", " \"\"\"Subscribe to specific event types\"\"\"\n", " with self.lock:\n", " if event_type not in self.subscribers:\n", " self.subscribers[event_type] = []\n", " self.subscribers[event_type].append(callback)\n", " print(f\"โœ… Subscribed to '{event_type}' events\")\n", " \n", " def publish(self, event: Event):\n", " \"\"\"Publish an event to all subscribers\"\"\"\n", " with self.lock:\n", " # Add to queue\n", " self.events.append(event)\n", " self.event_history.append(event)\n", " \n", " # Notify subscribers immediately\n", " if event.event_type in self.subscribers:\n", " for callback in self.subscribers[event.event_type]:\n", " try:\n", " callback(event)\n", " except Exception as e:\n", " print(f\"โŒ Error in callback for {event.event_type}: {e}\")\n", " \n", " print(f\"๐Ÿ“ค Published: {event}\")\n", " \n", " def get_next_event(self) -> Event:\n", " \"\"\"Get next event from queue (useful for batch processing)\"\"\"\n", " with self.lock:\n", " if self.events:\n", " return self.events.popleft()\n", " return None\n", " \n", " def get_stats(self) -> Dict[str, Any]:\n", " \"\"\"Get queue statistics\"\"\"\n", " with self.lock:\n", " return {\n", " 'events_in_queue': len(self.events),\n", " 'total_events_processed': len(self.event_history),\n", " 'event_types_subscribed': list(self.subscribers.keys()),\n", " 'subscribers_count': {k: len(v) for k, v in self.subscribers.items()}\n", " }\n", "\n", "# Create our central event queue\n", "event_queue = EventQueue()\n", "print(\"๐Ÿ—๏ธ Event Queue created!\")\n", "print(f\"Initial stats: {event_queue.get_stats()}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Part 3: Trading Event Types\n", "\n", "Let's create specific event types that our MARKET components will use to communicate." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "๐Ÿงช Testing Trading Event Types:\n", "\n", "Market Event: Event(market_data from market_data at 19:06:23)\n", "Signal Event: Event(signal from algorithm at 19:06:23)\n", "Order Event: Event(order from order_manager at 19:06:23)\n", "Risk Event: Event(risk from risk_controller at 19:06:23)\n" ] } ], "source": [ "# Specific event types for our trading system\n", "\n", "class MarketDataEvent(Event):\n", " \"\"\"Market data events - price updates, order book changes\"\"\"\n", " def __init__(self, symbol: str, price: float, volume: float = 0, source: str = \"market_data\"):\n", " super().__init__(\n", " event_type=\"market_data\",\n", " data={\n", " \"symbol\": symbol,\n", " \"price\": price,\n", " \"volume\": volume\n", " },\n", " source=source\n", " )\n", "\n", "class SignalEvent(Event):\n", " \"\"\"Trading signal events - buy/sell signals from algorithm\"\"\"\n", " def __init__(self, symbol: str, signal_type: str, strength: float, source: str = \"algorithm\"):\n", " super().__init__(\n", " event_type=\"signal\",\n", " data={\n", " \"symbol\": symbol,\n", " \"signal_type\": signal_type, # 'BUY', 'SELL', 'HOLD'\n", " \"strength\": strength # 0.0 to 1.0\n", " },\n", " source=source\n", " )\n", "\n", "class OrderEvent(Event):\n", " \"\"\"Order events - order requests, fills, cancellations\"\"\"\n", " def __init__(self, symbol: str, order_type: str, quantity: float, price: float = None, source: str = \"order_manager\"):\n", " super().__init__(\n", " event_type=\"order\",\n", " data={\n", " \"symbol\": symbol,\n", " \"order_type\": order_type, # 'MARKET', 'LIMIT', 'STOP'\n", " \"quantity\": quantity,\n", " \"price\": price\n", " },\n", " source=source\n", " )\n", "\n", "class RiskEvent(Event):\n", " \"\"\"Risk management events - warnings, violations, limits\"\"\"\n", " def __init__(self, risk_type: str, message: str, severity: str = \"WARNING\", source: str = \"risk_controller\"):\n", " super().__init__(\n", " event_type=\"risk\",\n", " data={\n", " \"risk_type\": risk_type,\n", " \"message\": message,\n", " \"severity\": severity # 'INFO', 'WARNING', 'ERROR', 'CRITICAL'\n", " },\n", " source=source\n", " )\n", "\n", "# Test our trading event types\n", "print(\"๐Ÿงช Testing Trading Event Types:\\n\")\n", "\n", "# Market data event\n", "market_event = MarketDataEvent(\"BTC/USDT\", 43250.50, 1.25)\n", "print(f\"Market Event: {market_event}\")\n", "\n", "# Signal event\n", "signal_event = SignalEvent(\"BTC/USDT\", \"BUY\", 0.8)\n", "print(f\"Signal Event: {signal_event}\")\n", "\n", "# Order event\n", "order_event = OrderEvent(\"BTC/USDT\", \"MARKET\", 0.1)\n", "print(f\"Order Event: {order_event}\")\n", "\n", "# Risk event\n", "risk_event = RiskEvent(\"position_limit\", \"Position size exceeds 10% of portfolio\", \"WARNING\")\n", "print(f\"Risk Event: {risk_event}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Part 4: Simple Component Example\n", "\n", "Let's create two simple components that communicate through events - this demonstrates the publisher-subscriber pattern." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "๐Ÿ—๏ธ Creating Components...\n", "\n", "โœ… Subscribed to 'market_data' events\n", "โœ… Subscribed to 'signal' events\n", "\n", "๐Ÿ“Š Current Event Queue Stats:\n", "{\n", " \"events_in_queue\": 0,\n", " \"total_events_processed\": 0,\n", " \"event_types_subscribed\": [\n", " \"market_data\",\n", " \"signal\"\n", " ],\n", " \"subscribers_count\": {\n", " \"market_data\": 1,\n", " \"signal\": 1\n", " }\n", "}\n" ] } ], "source": [ "class SimpleMarketDataHandler:\n", " \"\"\"Simulates receiving market data and publishing events\"\"\"\n", " \n", " def __init__(self, event_queue: EventQueue):\n", " self.event_queue = event_queue\n", " self.name = \"market_data_handler\"\n", " \n", " def simulate_price_update(self, symbol: str, price: float):\n", " \"\"\"Simulate receiving a price update from exchange\"\"\"\n", " print(f\"๐Ÿ“Š {self.name}: Received price update for {symbol}: ${price:,.2f}\")\n", " \n", " # Create and publish market data event\n", " event = MarketDataEvent(symbol, price, source=self.name)\n", " self.event_queue.publish(event)\n", "\n", "\n", "class SimpleAlgorithm:\n", " \"\"\"Simple algorithm that listens to market data and generates signals\"\"\"\n", " \n", " def __init__(self, event_queue: EventQueue):\n", " self.event_queue = event_queue\n", " self.name = \"simple_algorithm\"\n", " self.last_price = None\n", " \n", " # Subscribe to market data events\n", " self.event_queue.subscribe(\"market_data\", self.on_market_data)\n", " \n", " def on_market_data(self, event: Event):\n", " \"\"\"Handle market data events\"\"\"\n", " symbol = event.data[\"symbol\"]\n", " price = event.data[\"price\"]\n", " \n", " print(f\"๐Ÿค– {self.name}: Processing {symbol} price: ${price:,.2f}\")\n", " \n", " # Simple momentum strategy\n", " if self.last_price is not None:\n", " price_change = (price - self.last_price) / self.last_price\n", " \n", " if price_change > 0.01: # 1% increase\n", " signal = SignalEvent(symbol, \"BUY\", 0.7, source=self.name)\n", " self.event_queue.publish(signal)\n", " print(f\" ๐Ÿ“ˆ Generated BUY signal (price up {price_change:.2%})\")\n", " \n", " elif price_change < -0.01: # 1% decrease\n", " signal = SignalEvent(symbol, \"SELL\", 0.7, source=self.name)\n", " self.event_queue.publish(signal)\n", " print(f\" ๐Ÿ“‰ Generated SELL signal (price down {price_change:.2%})\")\n", " \n", " self.last_price = price\n", "\n", "\n", "class SimpleRiskController:\n", " \"\"\"Simple risk controller that monitors signals\"\"\"\n", " \n", " def __init__(self, event_queue: EventQueue):\n", " self.event_queue = event_queue\n", " self.name = \"risk_controller\"\n", " \n", " # Subscribe to signal events\n", " self.event_queue.subscribe(\"signal\", self.on_signal)\n", " \n", " def on_signal(self, event: Event):\n", " \"\"\"Validate trading signals\"\"\"\n", " symbol = event.data[\"symbol\"]\n", " signal_type = event.data[\"signal_type\"]\n", " strength = event.data[\"strength\"]\n", " \n", " print(f\"๐Ÿ›ก๏ธ {self.name}: Validating {signal_type} signal for {symbol} (strength: {strength})\")\n", " \n", " # Simple validation - only allow strong signals\n", " if strength >= 0.6:\n", " print(f\" โœ… Signal APPROVED - strength sufficient ({strength})\")\n", " # In real system, would publish order_request event here\n", " else:\n", " risk_event = RiskEvent(\n", " \"weak_signal\", \n", " f\"Signal strength too low: {strength} < 0.6\", \n", " \"WARNING\",\n", " source=self.name\n", " )\n", " self.event_queue.publish(risk_event)\n", " print(f\" โŒ Signal REJECTED - strength too low\")\n", "\n", "\n", "# Create our components\n", "print(\"๐Ÿ—๏ธ Creating Components...\\n\")\n", "\n", "market_handler = SimpleMarketDataHandler(event_queue)\n", "algorithm = SimpleAlgorithm(event_queue)\n", "risk_controller = SimpleRiskController(event_queue)\n", "\n", "print(\"\\n๐Ÿ“Š Current Event Queue Stats:\")\n", "print(json.dumps(event_queue.get_stats(), indent=2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Part 5: Event System in Action\n", "\n", "Now let's test our event-driven system by simulating market data updates!" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "๐ŸŽฌ DEMO: Event-Driven Trading Bot in Action!\n", "\n", "============================================================\n", "\n", "๐Ÿ“ก Market Update #1:\n", "----------------------------------------\n", "๐Ÿ“Š market_data_handler: Received price update for BTC/USDT: $43,000.00\n", "๐Ÿค– simple_algorithm: Processing BTC/USDT price: $43,000.00\n", "๐Ÿ“ค Published: Event(market_data from market_data_handler at 19:06:23)\n", "\n", "๐Ÿ“ก Market Update #2:\n", "----------------------------------------\n", "๐Ÿ“Š market_data_handler: Received price update for BTC/USDT: $43,450.00\n", "๐Ÿค– simple_algorithm: Processing BTC/USDT price: $43,450.00\n" ] }, { "ename": "KeyboardInterrupt", "evalue": "", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mKeyboardInterrupt\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[5]\u001b[39m\u001b[32m, line 20\u001b[39m\n\u001b[32m 17\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33m-\u001b[39m\u001b[33m\"\u001b[39m * \u001b[32m40\u001b[39m)\n\u001b[32m 19\u001b[39m \u001b[38;5;66;03m# This will trigger the entire event chain!\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m20\u001b[39m \u001b[43mmarket_handler\u001b[49m\u001b[43m.\u001b[49m\u001b[43msimulate_price_update\u001b[49m\u001b[43m(\u001b[49m\u001b[43msymbol\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mprice\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 22\u001b[39m \u001b[38;5;66;03m# Small delay to make it readable\u001b[39;00m\n\u001b[32m 23\u001b[39m time.sleep(\u001b[32m0.5\u001b[39m)\n", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 14\u001b[39m, in \u001b[36mSimpleMarketDataHandler.simulate_price_update\u001b[39m\u001b[34m(self, symbol, price)\u001b[39m\n\u001b[32m 12\u001b[39m \u001b[38;5;66;03m# Create and publish market data event\u001b[39;00m\n\u001b[32m 13\u001b[39m event = MarketDataEvent(symbol, price, source=\u001b[38;5;28mself\u001b[39m.name)\n\u001b[32m---> \u001b[39m\u001b[32m14\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mevent_queue\u001b[49m\u001b[43m.\u001b[49m\u001b[43mpublish\u001b[49m\u001b[43m(\u001b[49m\u001b[43mevent\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 32\u001b[39m, in \u001b[36mEventQueue.publish\u001b[39m\u001b[34m(self, event)\u001b[39m\n\u001b[32m 30\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m callback \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m.subscribers[event.event_type]:\n\u001b[32m 31\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m---> \u001b[39m\u001b[32m32\u001b[39m \u001b[43mcallback\u001b[49m\u001b[43m(\u001b[49m\u001b[43mevent\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 33\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m 34\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mโŒ Error in callback for \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mevent.event_type\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00me\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 41\u001b[39m, in \u001b[36mSimpleAlgorithm.on_market_data\u001b[39m\u001b[34m(self, event)\u001b[39m\n\u001b[32m 39\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m price_change > \u001b[32m0.01\u001b[39m: \u001b[38;5;66;03m# 1% increase\u001b[39;00m\n\u001b[32m 40\u001b[39m signal = SignalEvent(symbol, \u001b[33m\"\u001b[39m\u001b[33mBUY\u001b[39m\u001b[33m\"\u001b[39m, \u001b[32m0.7\u001b[39m, source=\u001b[38;5;28mself\u001b[39m.name)\n\u001b[32m---> \u001b[39m\u001b[32m41\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mevent_queue\u001b[49m\u001b[43m.\u001b[49m\u001b[43mpublish\u001b[49m\u001b[43m(\u001b[49m\u001b[43msignal\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 42\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33m ๐Ÿ“ˆ Generated BUY signal (price up \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mprice_change\u001b[38;5;132;01m:\u001b[39;00m\u001b[33m.2%\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m)\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 44\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m price_change < -\u001b[32m0.01\u001b[39m: \u001b[38;5;66;03m# 1% decrease\u001b[39;00m\n", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 23\u001b[39m, in \u001b[36mEventQueue.publish\u001b[39m\u001b[34m(self, event)\u001b[39m\n\u001b[32m 21\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mpublish\u001b[39m(\u001b[38;5;28mself\u001b[39m, event: Event):\n\u001b[32m 22\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Publish an event to all subscribers\"\"\"\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m23\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mwith\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mlock\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 24\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# Add to queue\u001b[39;49;00m\n\u001b[32m 25\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mevents\u001b[49m\u001b[43m.\u001b[49m\u001b[43mappend\u001b[49m\u001b[43m(\u001b[49m\u001b[43mevent\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 26\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mevent_history\u001b[49m\u001b[43m.\u001b[49m\u001b[43mappend\u001b[49m\u001b[43m(\u001b[49m\u001b[43mevent\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[31mKeyboardInterrupt\u001b[39m: " ] } ], "source": [ "import time\n", "\n", "print(\"๐ŸŽฌ DEMO: Event-Driven Trading Bot in Action!\\n\")\n", "print(\"=\" * 60)\n", "\n", "# Simulate market data updates\n", "test_prices = [\n", " (\"BTC/USDT\", 43000.00), # Starting price\n", " (\"BTC/USDT\", 43450.00), # 1.05% increase -> should trigger BUY\n", " (\"BTC/USDT\", 43200.00), # -0.58% change -> no signal\n", " (\"BTC/USDT\", 42750.00), # -1.04% decrease -> should trigger SELL\n", " (\"BTC/USDT\", 43100.00), # 0.82% increase -> no signal\n", "]\n", "\n", "for i, (symbol, price) in enumerate(test_prices, 1):\n", " print(f\"\\n๐Ÿ“ก Market Update #{i}:\")\n", " print(\"-\" * 40)\n", " \n", " # This will trigger the entire event chain!\n", " market_handler.simulate_price_update(symbol, price)\n", " \n", " # Small delay to make it readable\n", " time.sleep(0.5)\n", "\n", "print(\"\\n\" + \"=\" * 60)\n", "print(\"๐Ÿ“ˆ Final Event Queue Statistics:\")\n", "print(json.dumps(event_queue.get_stats(), indent=2))\n", "\n", "print(f\"\\n๐Ÿ“‹ Total Events Processed: {len(event_queue.event_history)}\")\n", "print(\"Event Types:\")\n", "event_types = {}\n", "for event in event_queue.event_history:\n", " event_types[event.event_type] = event_types.get(event.event_type, 0) + 1\n", "\n", "for event_type, count in event_types.items():\n", " print(f\" โ€ข {event_type}: {count} events\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Part 6: Your Turn - Practice Exercise\n", "\n", "**Challenge:** Create a simple `PortfolioTracker` component that:\n", "1. Subscribes to `order` events\n", "2. Keeps track of positions for each symbol\n", "3. Publishes `portfolio_update` events when positions change\n", "\n", "**Hint:** You'll need to create a `PortfolioEvent` class first!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# TODO: Create PortfolioEvent class\n", "class PortfolioEvent(Event):\n", " \"\"\"Portfolio update events\"\"\"\n", " def __init__(self, symbol: str, position: float, avg_price: float, unrealized_pnl: float, source: str = \"portfolio_tracker\"):\n", " # YOUR CODE HERE\n", " pass\n", "\n", "# TODO: Create PortfolioTracker class\n", "class PortfolioTracker:\n", " \"\"\"Tracks portfolio positions and P&L\"\"\"\n", " \n", " def __init__(self, event_queue: EventQueue):\n", " # YOUR CODE HERE\n", " pass\n", " \n", " def on_order(self, event: Event):\n", " \"\"\"Handle order events and update positions\"\"\"\n", " # YOUR CODE HERE\n", " pass\n", "\n", "# Test your implementation\n", "# portfolio_tracker = PortfolioTracker(event_queue)\n", "\n", "# # Simulate some orders\n", "# test_order = OrderEvent(\"BTC/USDT\", \"MARKET\", 0.1, 43000, source=\"test\")\n", "# event_queue.publish(test_order)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Congratulations!\n", "\n", "You've just built the foundation of a professional event-driven trading system! Here's what you accomplished:\n", "\n", "โœ… **Event System**: Created a robust event queue with publisher-subscriber pattern \n", "โœ… **Trading Events**: Built specific event types for market data, signals, orders, and risk \n", "โœ… **Component Communication**: Made components talk through events (loose coupling) \n", "โœ… **Real Demo**: Saw the entire chain work from market data โ†’ algorithm โ†’ risk control \n", "\n", "## Key Takeaways:\n", "\n", "1. **Loose Coupling**: Components don't know about each other - they only know about events\n", "2. **Scalability**: Easy to add new components by subscribing to events\n", "3. **Testability**: Each component can be tested independently\n", "4. **Professional**: This is how real trading firms build their systems\n", "\n", "## Next Steps:\n", "\n", "In the next notebook, we'll build a complete **Order Management System** that uses this event foundation to handle market, limit, and stop orders professionally!\n", "\n", "---\n", "*\"The best trading bots are not just profitable - they're architecturally sound.\"* ๐Ÿ—๏ธ๐Ÿ’ฐ" ] } ], "metadata": { "kernelspec": { "display_name": "venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.3" } }, "nbformat": 4, "nbformat_minor": 4 }