Remove legacy docs, backup copies, and artifacts
Browse filesDeleted:
- IMPROVEMENT_PLAN.md, README_OPTIQ.md β obsolete planning docs
- StockEx_Developer_Guide.html, StockEx_Technical_Guide.md,
StockEx_User_Guide.html β superseded by README.md
- dashboard/templates/index - Copy (6).html β accidental backup
- dashboard/templates/index_Matcher.html β unused alternate template
- matcher/matcher - Copy.py β accidental backup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- IMPROVEMENT_PLAN.md +0 -271
- README_OPTIQ.md +0 -73
- StockEx_Developer_Guide.html +0 -569
- StockEx_Technical_Guide.md +0 -814
- StockEx_User_Guide.html +0 -428
- dashboard/templates/index - Copy (6).html +0 -198
- dashboard/templates/index_Matcher.html +0 -194
- matcher/matcher - Copy.py +0 -135
IMPROVEMENT_PLAN.md
DELETED
|
@@ -1,271 +0,0 @@
|
|
| 1 |
-
# KafkaTradingSystem Improvement Plan
|
| 2 |
-
|
| 3 |
-
> **Created:** 2026-02-03
|
| 4 |
-
> **Status:** β
Core implementation complete (Phases 1-5 done)
|
| 5 |
-
> **Last Updated:** 2026-02-03
|
| 6 |
-
> **Resume:** Share this file with Claude to continue from where we left off
|
| 7 |
-
|
| 8 |
-
---
|
| 9 |
-
|
| 10 |
-
## Overview
|
| 11 |
-
|
| 12 |
-
This plan outlines improvements to transform the trading system from a demo/prototype into a more robust, production-like system. Improvements are organized by priority and complexity.
|
| 13 |
-
|
| 14 |
-
---
|
| 15 |
-
|
| 16 |
-
## Phase 1: Real-Time Dashboard (WebSocket/SSE)
|
| 17 |
-
|
| 18 |
-
**Problem:** Dashboard polls `/data` every 2 seconds (`setInterval(refresh, 2000)`), causing unnecessary load and delayed updates.
|
| 19 |
-
|
| 20 |
-
**Solution:** Implement Server-Sent Events (SSE) for real-time push updates.
|
| 21 |
-
|
| 22 |
-
### Tasks
|
| 23 |
-
|
| 24 |
-
- [x] **1.1** Add SSE endpoint to `dashboard/dashboard.py` β
|
| 25 |
-
- Create `/stream` endpoint using Flask's `Response` with `text/event-stream`
|
| 26 |
-
- Push events when new orders/trades/snapshots arrive from Kafka
|
| 27 |
-
- File: `dashboard/dashboard.py`
|
| 28 |
-
|
| 29 |
-
- [x] **1.2** Update dashboard frontend to use SSE β
|
| 30 |
-
- Replace `setInterval(refresh, 2000)` with `EventSource('/stream')`
|
| 31 |
-
- Handle reconnection on disconnect
|
| 32 |
-
- File: `dashboard/templates/index.html`
|
| 33 |
-
|
| 34 |
-
- [x] **1.3** Add connection status indicator β
|
| 35 |
-
- Show connected/disconnected state in UI
|
| 36 |
-
- File: `dashboard/templates/index.html`
|
| 37 |
-
|
| 38 |
-
### Why SSE over WebSocket?
|
| 39 |
-
- Simpler implementation (HTTP-based, no special protocol)
|
| 40 |
-
- One-way server-to-client is sufficient for this use case
|
| 41 |
-
- Better browser support and automatic reconnection
|
| 42 |
-
|
| 43 |
-
---
|
| 44 |
-
|
| 45 |
-
## Phase 2: SQLite Persistence
|
| 46 |
-
|
| 47 |
-
**Problem:** All data is in-memory; order books and trades lost on restart.
|
| 48 |
-
|
| 49 |
-
**Solution:** Add SQLite database for persistence with in-memory caching.
|
| 50 |
-
|
| 51 |
-
### Tasks
|
| 52 |
-
|
| 53 |
-
- [x] **2.1** Create database schema and initialization β
|
| 54 |
-
- Tables: `orders`, `trades`, `order_book_entries`
|
| 55 |
-
- Add migration/init script
|
| 56 |
-
- New file: `matcher/database.py`
|
| 57 |
-
|
| 58 |
-
- [x] **2.2** Modify matcher to persist trades β
|
| 59 |
-
- Write trades to SQLite when matched
|
| 60 |
-
- Load recent trades on startup
|
| 61 |
-
- File: `matcher/matcher.py`
|
| 62 |
-
|
| 63 |
-
- [x] **2.3** Persist order book state β
|
| 64 |
-
- Save resting orders to database
|
| 65 |
-
- Restore order books on matcher restart
|
| 66 |
-
- File: `matcher/matcher.py`
|
| 67 |
-
|
| 68 |
-
- [x] **2.4** Add trade history endpoint β
|
| 69 |
-
- `GET /trades?symbol=X&limit=N&offset=M`
|
| 70 |
-
- Support filtering and pagination
|
| 71 |
-
- File: `matcher/matcher.py`
|
| 72 |
-
|
| 73 |
-
- [x] **2.5** Add Docker volume for database persistence β
|
| 74 |
-
- Mount SQLite file to host
|
| 75 |
-
- File: `docker-compose.yml`
|
| 76 |
-
|
| 77 |
-
### Schema Design
|
| 78 |
-
|
| 79 |
-
```sql
|
| 80 |
-
CREATE TABLE trades (
|
| 81 |
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 82 |
-
symbol TEXT NOT NULL,
|
| 83 |
-
price REAL NOT NULL,
|
| 84 |
-
quantity INTEGER NOT NULL,
|
| 85 |
-
buy_order_id TEXT,
|
| 86 |
-
sell_order_id TEXT,
|
| 87 |
-
timestamp REAL NOT NULL,
|
| 88 |
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
| 89 |
-
);
|
| 90 |
-
|
| 91 |
-
CREATE TABLE order_book (
|
| 92 |
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 93 |
-
cl_ord_id TEXT UNIQUE,
|
| 94 |
-
symbol TEXT NOT NULL,
|
| 95 |
-
side TEXT NOT NULL, -- 'BUY' or 'SELL'
|
| 96 |
-
price REAL,
|
| 97 |
-
quantity INTEGER NOT NULL,
|
| 98 |
-
remaining_qty INTEGER NOT NULL,
|
| 99 |
-
status TEXT DEFAULT 'OPEN', -- OPEN, FILLED, CANCELLED
|
| 100 |
-
timestamp REAL NOT NULL,
|
| 101 |
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
| 102 |
-
);
|
| 103 |
-
|
| 104 |
-
CREATE INDEX idx_trades_symbol ON trades(symbol);
|
| 105 |
-
CREATE INDEX idx_trades_timestamp ON trades(timestamp);
|
| 106 |
-
CREATE INDEX idx_orderbook_symbol_side ON order_book(symbol, side);
|
| 107 |
-
```
|
| 108 |
-
|
| 109 |
-
---
|
| 110 |
-
|
| 111 |
-
## Phase 3: FIX Protocol Improvements
|
| 112 |
-
|
| 113 |
-
**Problem:** Basic FIX handling with no validation, no execution reports, no order management.
|
| 114 |
-
|
| 115 |
-
### Tasks
|
| 116 |
-
|
| 117 |
-
- [x] **3.1** Add FIX message validation β
|
| 118 |
-
- Validate required fields (ClOrdID, Symbol, Side, OrderQty)
|
| 119 |
-
- Validate field formats and ranges
|
| 120 |
-
- Return Reject (35=3) for invalid messages
|
| 121 |
-
- File: `fix_oeg/fix_oeg_server.py`
|
| 122 |
-
|
| 123 |
-
- [x] **3.2** Send Execution Reports back to clients β
|
| 124 |
-
- Send 35=8 (ExecutionReport) for order acknowledgment
|
| 125 |
-
- Send fill reports when trades occur
|
| 126 |
-
- Requires Kafka consumer for trades topic in fix_oeg
|
| 127 |
-
- File: `fix_oeg/fix_oeg_server.py`
|
| 128 |
-
|
| 129 |
-
- [x] **3.3** Support Order Cancel Request (35=F) β
|
| 130 |
-
- Parse cancel requests
|
| 131 |
-
- Publish to orders Kafka topic (with type=cancel)
|
| 132 |
-
- Files: `fix_oeg/fix_oeg_server.py`
|
| 133 |
-
|
| 134 |
-
- [x] **3.4** Support Order Cancel/Replace (35=G) β
|
| 135 |
-
- Parse modify requests
|
| 136 |
-
- Implement price/quantity amendments
|
| 137 |
-
- Files: `fix_oeg/fix_oeg_server.py`, `matcher/matcher.py`
|
| 138 |
-
|
| 139 |
-
- [ ] **3.5** Add session-level validation (DEFERRED)
|
| 140 |
-
- Sequence number checking handled by QuickFIX engine
|
| 141 |
-
- Heartbeat/TestRequest handled automatically
|
| 142 |
-
- File: `fix_oeg/fix_oeg_server.py`
|
| 143 |
-
|
| 144 |
-
### Execution Report Fields (35=8)
|
| 145 |
-
|
| 146 |
-
```
|
| 147 |
-
Tag 35 = 8 (ExecutionReport)
|
| 148 |
-
Tag 37 = OrderID (exchange-assigned)
|
| 149 |
-
Tag 11 = ClOrdID (client order ID)
|
| 150 |
-
Tag 17 = ExecID
|
| 151 |
-
Tag 20 = ExecTransType (0=New)
|
| 152 |
-
Tag 39 = OrdStatus (0=New, 1=PartialFill, 2=Filled)
|
| 153 |
-
Tag 150 = ExecType (0=New, F=Trade)
|
| 154 |
-
Tag 55 = Symbol
|
| 155 |
-
Tag 54 = Side
|
| 156 |
-
Tag 38 = OrderQty
|
| 157 |
-
Tag 44 = Price
|
| 158 |
-
Tag 32 = LastShares (fill qty)
|
| 159 |
-
Tag 31 = LastPx (fill price)
|
| 160 |
-
Tag 14 = CumQty
|
| 161 |
-
Tag 151 = LeavesQty
|
| 162 |
-
Tag 6 = AvgPx
|
| 163 |
-
```
|
| 164 |
-
|
| 165 |
-
---
|
| 166 |
-
|
| 167 |
-
## Phase 4: Enhanced Order Types
|
| 168 |
-
|
| 169 |
-
**Problem:** Only limit orders supported.
|
| 170 |
-
|
| 171 |
-
### Tasks
|
| 172 |
-
|
| 173 |
-
- [x] **4.1** Support Market Orders β
|
| 174 |
-
- Match immediately at best available price
|
| 175 |
-
- No price field required (OrdType=1)
|
| 176 |
-
- File: `matcher/matcher.py`
|
| 177 |
-
|
| 178 |
-
- [x] **4.2** Support IOC (Immediate-or-Cancel) β
|
| 179 |
-
- TimeInForce=3: Fill what's available, cancel rest
|
| 180 |
-
- File: `matcher/matcher.py`
|
| 181 |
-
|
| 182 |
-
- [x] **4.3** Support FOK (Fill-or-Kill) β
|
| 183 |
-
- TimeInForce=4: Fill entire order or reject
|
| 184 |
-
- File: `matcher/matcher.py`
|
| 185 |
-
|
| 186 |
-
- [x] **4.4** Support GTC (Good-Till-Cancel) β
|
| 187 |
-
- TimeInForce=1: Persist until explicitly cancelled
|
| 188 |
-
- Implemented via SQLite persistence (Phase 2)
|
| 189 |
-
- File: `matcher/matcher.py`
|
| 190 |
-
|
| 191 |
-
---
|
| 192 |
-
|
| 193 |
-
## Phase 5: Monitoring & Observability
|
| 194 |
-
|
| 195 |
-
### Tasks
|
| 196 |
-
|
| 197 |
-
- [x] **5.1** Add health check endpoints β
|
| 198 |
-
- `/health` endpoint for matcher, dashboard, frontend
|
| 199 |
-
- Check Kafka/DB connectivity and service stats
|
| 200 |
-
- Files: `matcher/matcher.py`, `dashboard/dashboard.py`, `frontend/frontend.py`
|
| 201 |
-
|
| 202 |
-
- [x] **5.2** Add structured logging β
|
| 203 |
-
- JSON log format for parsing
|
| 204 |
-
- Include correlation IDs
|
| 205 |
-
- New file: `shared/logging_utils.py`
|
| 206 |
-
|
| 207 |
-
- [x] **5.3** Add metrics endpoint β
|
| 208 |
-
- Order count, trade count, latency stats
|
| 209 |
-
- Prometheus-compatible format (`/metrics` endpoint)
|
| 210 |
-
- File: `matcher/matcher.py`
|
| 211 |
-
|
| 212 |
-
- [ ] **5.4** Add Kafka lag monitoring (OPTIONAL)
|
| 213 |
-
- Track consumer group lag
|
| 214 |
-
- Alert on high lag
|
| 215 |
-
- New file: `shared/monitoring.py`
|
| 216 |
-
|
| 217 |
-
---
|
| 218 |
-
|
| 219 |
-
## Phase 6: Testing & Quality
|
| 220 |
-
|
| 221 |
-
### Tasks
|
| 222 |
-
|
| 223 |
-
- [x] **6.1** Add unit tests for matcher β
|
| 224 |
-
- Test matching logic (full/partial fills, price-time priority)
|
| 225 |
-
- Test order types (market, limit, IOC, FOK)
|
| 226 |
-
- Test cancel and amend operations
|
| 227 |
-
- New file: `matcher/test_matcher.py`
|
| 228 |
-
|
| 229 |
-
- [ ] **6.2** Add integration tests (OPTIONAL)
|
| 230 |
-
- End-to-end order flow tests
|
| 231 |
-
- FIX client to trade execution
|
| 232 |
-
- New directory: `tests/`
|
| 233 |
-
|
| 234 |
-
- [ ] **6.3** Add load testing script (OPTIONAL)
|
| 235 |
-
- Generate high-volume order flow
|
| 236 |
-
- Measure latency and throughput
|
| 237 |
-
- New file: `tests/load_test.py`
|
| 238 |
-
|
| 239 |
-
---
|
| 240 |
-
|
| 241 |
-
## Implementation Order (Recommended)
|
| 242 |
-
|
| 243 |
-
1. **Phase 1** (Real-Time Dashboard) - Quick win, improves UX
|
| 244 |
-
2. **Phase 2** (SQLite Persistence) - Foundation for reliability
|
| 245 |
-
3. **Phase 3.1-3.2** (FIX Validation + Execution Reports) - Core trading functionality
|
| 246 |
-
4. **Phase 5.1** (Health Checks) - Operational necessity
|
| 247 |
-
5. **Phase 4.1** (Market Orders) - Common order type
|
| 248 |
-
6. **Phase 3.3** (Order Cancellation) - Essential for trading
|
| 249 |
-
7. Remaining phases as needed
|
| 250 |
-
|
| 251 |
-
---
|
| 252 |
-
|
| 253 |
-
## Quick Reference: Key Files
|
| 254 |
-
|
| 255 |
-
| Component | Main File | Port |
|
| 256 |
-
|-----------|-----------|------|
|
| 257 |
-
| FIX Gateway | `fix_oeg/fix_oeg_server.py` | 5001 |
|
| 258 |
-
| Matcher | `matcher/matcher.py` | 6000 |
|
| 259 |
-
| Dashboard | `dashboard/dashboard.py` | 5005 |
|
| 260 |
-
| Frontend | `frontend/frontend.py` | 5000 |
|
| 261 |
-
| Config | `shared/config.py` | - |
|
| 262 |
-
|
| 263 |
-
---
|
| 264 |
-
|
| 265 |
-
## How to Continue
|
| 266 |
-
|
| 267 |
-
When resuming work, tell Claude:
|
| 268 |
-
1. Which phase/task to work on (e.g., "Let's implement Phase 1.1")
|
| 269 |
-
2. Or ask Claude to pick the next logical task
|
| 270 |
-
|
| 271 |
-
Each task is designed to be implementable in a single session.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README_OPTIQ.md
DELETED
|
@@ -1,73 +0,0 @@
|
|
| 1 |
-
# StockEx β Euronext/OPTIQ Trading System Emulator
|
| 2 |
-
|
| 3 |
-
## Overview
|
| 4 |
-
|
| 5 |
-
**StockEx** is a real-time trading simulation platform inspired by Euronext OPTIQ. It provides a complete order flow from FIX protocol entry through matching engine to trade execution, with live visualization via a web dashboard.
|
| 6 |
-
|
| 7 |
-
## Architecture
|
| 8 |
-
|
| 9 |
-
| Component | Description |
|
| 10 |
-
|-----------|-------------|
|
| 11 |
-
| **FIX OEG** | QuickFIX acceptor receiving FIX 4.4 orders |
|
| 12 |
-
| **Kafka** | Message backbone (topics: `orders`, `snapshots`, `trades`) |
|
| 13 |
-
| **Matcher** | Order matching engine with price-time priority |
|
| 14 |
-
| **MDF** | Market Data Feeder publishing BBO snapshots |
|
| 15 |
-
| **Dashboard** | Real-time web UI with SSE streaming |
|
| 16 |
-
|
| 17 |
-
## Data Flow
|
| 18 |
-
|
| 19 |
-
```
|
| 20 |
-
FIX Client β FIX OEG β Kafka [orders] β Matcher β Kafka [trades]
|
| 21 |
-
β
|
| 22 |
-
MDF β Kafka [snapshots] ββββββββββββ Dashboard (Flask)
|
| 23 |
-
```
|
| 24 |
-
|
| 25 |
-
## Dashboard Features
|
| 26 |
-
|
| 27 |
-
- **Orders Panel** β Live order feed with Edit/Cancel actions
|
| 28 |
-
- **Market Snapshot** β Best Bid/Ask with spread, mid price, and scrolling ticker
|
| 29 |
-
- **Trades Panel** β Executed trades with value calculation
|
| 30 |
-
- **Order Book** β Depth of book per symbol (bids/asks)
|
| 31 |
-
- **Trade Chart** β Price and volume visualization
|
| 32 |
-
- **Trading Statistics** β Volume, Value, VWAP per symbol with bar charts
|
| 33 |
-
|
| 34 |
-

|
| 35 |
-
|
| 36 |
-
## Quick Start
|
| 37 |
-
|
| 38 |
-
```bash
|
| 39 |
-
docker compose up --build
|
| 40 |
-
```
|
| 41 |
-
|
| 42 |
-
Open dashboard: **http://localhost:5005**
|
| 43 |
-
|
| 44 |
-
## Directory Structure
|
| 45 |
-
|
| 46 |
-
```
|
| 47 |
-
StockEx/
|
| 48 |
-
βββ docker-compose.yml
|
| 49 |
-
βββ fix_oeg/ # FIX Order Entry Gateway
|
| 50 |
-
βββ fix-ui-client/ # FIX test clients
|
| 51 |
-
βββ matcher/ # Order matching engine
|
| 52 |
-
βββ md_feeder/ # Market data simulator
|
| 53 |
-
βββ dashboard/ # Flask web dashboard
|
| 54 |
-
βββ frontend/ # Manual order entry UI
|
| 55 |
-
βββ shared/ # Common config and utilities
|
| 56 |
-
βββ shared_data/ # Securities and state files
|
| 57 |
-
```
|
| 58 |
-
|
| 59 |
-
## Configuration
|
| 60 |
-
|
| 61 |
-
| Variable | Default | Description |
|
| 62 |
-
|----------|---------|-------------|
|
| 63 |
-
| `KAFKA_BOOTSTRAP` | kafka:9092 | Kafka broker address |
|
| 64 |
-
| `TICK_SIZE` | 0.05 | Minimum price increment |
|
| 65 |
-
| `ORDERS_PER_MIN` | 8 | MDF order generation rate |
|
| 66 |
-
|
| 67 |
-
## Requirements
|
| 68 |
-
|
| 69 |
-
- Docker & Docker Compose
|
| 70 |
-
- Ports: 5005 (Dashboard), 6000 (Matcher), 9092 (Kafka)
|
| 71 |
-
|
| 72 |
-
---
|
| 73 |
-
*StockEx v1.0 β Trading Simulation Platform*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StockEx_Developer_Guide.html
DELETED
|
@@ -1,569 +0,0 @@
|
|
| 1 |
-
<!DOCTYPE html>
|
| 2 |
-
<html>
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="utf-8">
|
| 5 |
-
<title>StockEx - Developer Guide</title>
|
| 6 |
-
<style>
|
| 7 |
-
@page { margin: 1.5cm; size: A4; }
|
| 8 |
-
body { font-family: 'Segoe UI', Arial, sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; line-height: 1.5; color: #333; font-size: 11pt; }
|
| 9 |
-
h1 { color: #1a1a2e; border-bottom: 3px solid #4CAF50; padding-bottom: 10px; font-size: 24pt; }
|
| 10 |
-
h2 { color: #2e7d32; margin-top: 25px; border-bottom: 1px solid #ddd; padding-bottom: 5px; font-size: 16pt; page-break-after: avoid; }
|
| 11 |
-
h3 { color: #1565c0; margin-top: 18px; font-size: 13pt; }
|
| 12 |
-
h4 { color: #555; margin-top: 12px; font-size: 11pt; }
|
| 13 |
-
table { width: 100%; border-collapse: collapse; margin: 12px 0; font-size: 10pt; }
|
| 14 |
-
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
| 15 |
-
th { background: #f5f5f5; font-weight: bold; }
|
| 16 |
-
tr:nth-child(even) { background: #fafafa; }
|
| 17 |
-
.header { text-align: center; margin-bottom: 25px; }
|
| 18 |
-
.subtitle { color: #666; font-size: 14pt; }
|
| 19 |
-
.version { color: #999; font-size: 10pt; }
|
| 20 |
-
.section { page-break-inside: avoid; margin-bottom: 20px; }
|
| 21 |
-
.highlight { background: #e8f5e9; padding: 12px; border-radius: 5px; margin: 10px 0; border-left: 4px solid #4CAF50; }
|
| 22 |
-
.info { background: #e3f2fd; padding: 12px; border-radius: 5px; margin: 10px 0; border-left: 4px solid #2196F3; }
|
| 23 |
-
.warning { background: #fff3e0; padding: 12px; border-radius: 5px; margin: 10px 0; border-left: 4px solid #ff9800; }
|
| 24 |
-
.code-block { background: #263238; color: #aed581; padding: 15px; border-radius: 5px; font-family: 'Consolas', monospace; font-size: 10pt; overflow-x: auto; margin: 10px 0; white-space: pre; }
|
| 25 |
-
.json { background: #37474f; color: #80cbc4; }
|
| 26 |
-
code { background: #f5f5f5; padding: 2px 6px; border-radius: 3px; font-family: 'Consolas', monospace; font-size: 10pt; }
|
| 27 |
-
.green { color: #2e7d32; }
|
| 28 |
-
.red { color: #c62828; }
|
| 29 |
-
.blue { color: #1565c0; }
|
| 30 |
-
hr { border: none; border-top: 1px solid #ddd; margin: 25px 0; }
|
| 31 |
-
.footer { text-align: center; color: #666; font-size: 9pt; margin-top: 30px; padding-top: 15px; border-top: 1px solid #ddd; }
|
| 32 |
-
.toc { background: #fafafa; padding: 15px; border-radius: 5px; margin: 15px 0; columns: 2; }
|
| 33 |
-
.toc ul { margin: 0; padding-left: 20px; }
|
| 34 |
-
.toc li { margin: 4px 0; font-size: 10pt; }
|
| 35 |
-
.arch-diagram { background: #f5f5f5; padding: 15px; border-radius: 5px; font-family: 'Consolas', monospace; white-space: pre; font-size: 9pt; overflow-x: auto; line-height: 1.3; }
|
| 36 |
-
.module-box { border: 1px solid #ddd; border-radius: 6px; padding: 12px; margin: 12px 0; background: #fafafa; page-break-inside: avoid; }
|
| 37 |
-
.module-box h4 { margin: 0 0 8px 0; color: #1a1a2e; border-bottom: 1px solid #eee; padding-bottom: 5px; }
|
| 38 |
-
.port { display: inline-block; background: #e3f2fd; padding: 2px 8px; border-radius: 3px; font-family: monospace; font-size: 9pt; margin-right: 5px; }
|
| 39 |
-
.tech { display: inline-block; background: #f3e5f5; padding: 2px 8px; border-radius: 3px; font-size: 9pt; margin-right: 5px; }
|
| 40 |
-
.endpoint { font-family: monospace; background: #e8f5e9; padding: 2px 6px; border-radius: 3px; }
|
| 41 |
-
.two-col { display: flex; gap: 20px; }
|
| 42 |
-
.two-col > div { flex: 1; }
|
| 43 |
-
.screenshot { text-align: center; margin: 15px 0; }
|
| 44 |
-
.screenshot img { max-width: 100%; border: 1px solid #ddd; border-radius: 5px; }
|
| 45 |
-
</style>
|
| 46 |
-
</head>
|
| 47 |
-
<body>
|
| 48 |
-
|
| 49 |
-
<div class="header">
|
| 50 |
-
<h1>StockEx Trading Platform</h1>
|
| 51 |
-
<p class="subtitle">Developer & Technical Guide</p>
|
| 52 |
-
<p class="version">Version 1.0 | Euronext OPTIQ Inspired</p>
|
| 53 |
-
</div>
|
| 54 |
-
|
| 55 |
-
<div class="toc">
|
| 56 |
-
<strong>Contents</strong>
|
| 57 |
-
<ul>
|
| 58 |
-
<li>1. Overview</li>
|
| 59 |
-
<li>2. Architecture</li>
|
| 60 |
-
<li>3. Modules</li>
|
| 61 |
-
<li>4. Database Persistence</li>
|
| 62 |
-
<li>5. Data Flow Diagrams</li>
|
| 63 |
-
<li>6. Kafka Topics</li>
|
| 64 |
-
<li>7. Message Formats</li>
|
| 65 |
-
<li>8. SSE Events</li>
|
| 66 |
-
<li>9. Configuration</li>
|
| 67 |
-
<li>10. Development</li>
|
| 68 |
-
</ul>
|
| 69 |
-
</div>
|
| 70 |
-
|
| 71 |
-
<hr>
|
| 72 |
-
|
| 73 |
-
<div class="section">
|
| 74 |
-
<h2>1. Overview</h2>
|
| 75 |
-
<p><strong>StockEx</strong> is a real-time trading simulation platform providing complete order-to-trade lifecycle emulation. Built with microservices architecture using Docker containers.</p>
|
| 76 |
-
|
| 77 |
-
<div class="two-col">
|
| 78 |
-
<div class="highlight">
|
| 79 |
-
<strong>Core Features</strong>
|
| 80 |
-
<ul style="margin:5px 0; padding-left:20px;">
|
| 81 |
-
<li>FIX 4.4 protocol gateway</li>
|
| 82 |
-
<li>Price-time priority matching</li>
|
| 83 |
-
<li>Kafka event streaming</li>
|
| 84 |
-
<li>SSE real-time dashboard</li>
|
| 85 |
-
<li>SQLite persistence</li>
|
| 86 |
-
</ul>
|
| 87 |
-
</div>
|
| 88 |
-
<div class="info">
|
| 89 |
-
<strong>Tech Stack</strong>
|
| 90 |
-
<ul style="margin:5px 0; padding-left:20px;">
|
| 91 |
-
<li>Python 3.11 / Flask</li>
|
| 92 |
-
<li>QuickFIX/Python</li>
|
| 93 |
-
<li>Apache Kafka 7.5</li>
|
| 94 |
-
<li>Docker Compose</li>
|
| 95 |
-
<li>SQLite</li>
|
| 96 |
-
</ul>
|
| 97 |
-
</div>
|
| 98 |
-
</div>
|
| 99 |
-
</div>
|
| 100 |
-
|
| 101 |
-
<div class="section">
|
| 102 |
-
<h2>2. System Architecture</h2>
|
| 103 |
-
<div class="arch-diagram">
|
| 104 |
-
ββββββββοΏ½οΏ½οΏ½ββββββββββ βββββββββββββββββββ βββββββββββββββββββ
|
| 105 |
-
β FIX UI Client β β FIX UI Client β β Frontend β
|
| 106 |
-
β (Port 5002) β β (Port 5003) β β (Port 5000) β
|
| 107 |
-
ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ
|
| 108 |
-
β FIX 4.4 β FIX 4.4 β HTTP/JSON
|
| 109 |
-
βββββββββββββββββ¬ββββββββ΄ββββββββββββββββββββββββ
|
| 110 |
-
βΌ
|
| 111 |
-
βββββββββββββββββββββββββββββββββ
|
| 112 |
-
β FIX OEG (Port 5001) β
|
| 113 |
-
β QuickFIX Order Gateway β
|
| 114 |
-
βββββββββββββββββ¬ββββββββββββββββ
|
| 115 |
-
β JSON
|
| 116 |
-
βΌ
|
| 117 |
-
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 118 |
-
β APACHE KAFKA (Port 9092) β
|
| 119 |
-
β ββββββββββββ ββββββββββββ ββββββββββββ β
|
| 120 |
-
β β orders β β trades β β snapshotsβ β
|
| 121 |
-
β ββββββββββββ ββββββββββββ ββββββββββββ β
|
| 122 |
-
βββββββ¬ββββββββββββββββββ¬ββββββββββββββββββ¬ββββββββββββββββββββββββββ
|
| 123 |
-
β β β
|
| 124 |
-
βΌ β βΌ
|
| 125 |
-
βββββββββββββββ β βββββββββββββββ
|
| 126 |
-
β Matcher βββββββββββ β MD Feeder β
|
| 127 |
-
β (Port 6000) β β (MDF) β
|
| 128 |
-
β β β β
|
| 129 |
-
β Order Book βββββββββββββββββββββ Price Sim β
|
| 130 |
-
β Trade Exec β β BBO Publish β
|
| 131 |
-
βββββββββββββββ βββββββββββββββ
|
| 132 |
-
β
|
| 133 |
-
βΌ REST API
|
| 134 |
-
βββββββββββββββββββββββββββββββββββββββββββ
|
| 135 |
-
β Dashboard (Port 5005) β
|
| 136 |
-
β Orders β Trades β Book β Chart β Stats β
|
| 137 |
-
βββββββββββββββββββββββββββββββββββββββββββ
|
| 138 |
-
</div>
|
| 139 |
-
</div>
|
| 140 |
-
|
| 141 |
-
<div class="section">
|
| 142 |
-
<h2>3. Module Specifications</h2>
|
| 143 |
-
|
| 144 |
-
<div class="module-box">
|
| 145 |
-
<h4>Kafka Message Broker</h4>
|
| 146 |
-
<span class="port">9092</span> <span class="port">29092 (host)</span> <span class="tech">Confluent 7.5.0</span>
|
| 147 |
-
<p>Central event bus. All order flow distributed via topics. Zookeeper (port 2181) for coordination.</p>
|
| 148 |
-
</div>
|
| 149 |
-
|
| 150 |
-
<div class="module-box">
|
| 151 |
-
<h4>FIX Order Entry Gateway</h4>
|
| 152 |
-
<span class="port">5001</span> <span class="tech">QuickFIX/Python</span>
|
| 153 |
-
<p>FIX 4.4 acceptor. Receives NewOrderSingle (D), OrderCancelRequest (F), OrderCancelReplaceRequest (G). Normalizes to JSON β Kafka <code>orders</code>.</p>
|
| 154 |
-
</div>
|
| 155 |
-
|
| 156 |
-
<div class="module-box">
|
| 157 |
-
<h4>Matcher Engine</h4>
|
| 158 |
-
<span class="port">6000</span> <span class="tech">Python/Flask/SQLite</span>
|
| 159 |
-
<p>Consumes <code>orders</code>, matches with price-time priority, publishes <code>trades</code>. Maintains order book per symbol. SQLite persistence.</p>
|
| 160 |
-
<table>
|
| 161 |
-
<tr><th>Endpoint</th><th>Method</th><th>Description</th></tr>
|
| 162 |
-
<tr><td><span class="endpoint">/orderbook/<symbol></span></td><td>GET</td><td>Order book depth</td></tr>
|
| 163 |
-
<tr><td><span class="endpoint">/trades</span></td><td>GET</td><td>Recent trades</td></tr>
|
| 164 |
-
<tr><td><span class="endpoint">/health</span></td><td>GET</td><td>Health + stats</td></tr>
|
| 165 |
-
</table>
|
| 166 |
-
</div>
|
| 167 |
-
|
| 168 |
-
<div class="module-box">
|
| 169 |
-
<h4>Market Data Feeder (MDF)</h4>
|
| 170 |
-
<span class="tech">Python</span>
|
| 171 |
-
<p>Simulates market activity. 90% passive orders (book building), 10% aggressive (trades). Publishes BBO snapshots.</p>
|
| 172 |
-
<p>Output: <code>orders</code> + <code>snapshots</code> topics</p>
|
| 173 |
-
</div>
|
| 174 |
-
|
| 175 |
-
<div class="module-box">
|
| 176 |
-
<h4>Dashboard</h4>
|
| 177 |
-
<span class="port">5005</span> <span class="tech">Flask/SSE/JavaScript</span>
|
| 178 |
-
<p>Real-time web UI. Consumes Kafka + Matcher API. Server-Sent Events for live streaming. Edit/Cancel order management.</p>
|
| 179 |
-
<table>
|
| 180 |
-
<tr><th>Endpoint</th><th>Method</th><th>Description</th></tr>
|
| 181 |
-
<tr><td><span class="endpoint">/stream</span></td><td>GET</td><td>SSE event stream</td></tr>
|
| 182 |
-
<tr><td><span class="endpoint">/data</span></td><td>GET</td><td>Polling fallback</td></tr>
|
| 183 |
-
<tr><td><span class="endpoint">/order/cancel</span></td><td>POST</td><td>Cancel order</td></tr>
|
| 184 |
-
<tr><td><span class="endpoint">/order/amend</span></td><td>POST</td><td>Amend order</td></tr>
|
| 185 |
-
</table>
|
| 186 |
-
</div>
|
| 187 |
-
|
| 188 |
-
<div class="module-box">
|
| 189 |
-
<h4>FIX UI Clients</h4>
|
| 190 |
-
<span class="port">5002</span> <span class="port">5003</span> <span class="tech">QuickFIX/Flask</span>
|
| 191 |
-
<p>Web-based FIX initiators. Connect to FIX OEG for institutional order submission.</p>
|
| 192 |
-
</div>
|
| 193 |
-
</div>
|
| 194 |
-
|
| 195 |
-
<div class="section">
|
| 196 |
-
<h2>4. Database Persistence</h2>
|
| 197 |
-
|
| 198 |
-
<div class="info">
|
| 199 |
-
<strong>Storage:</strong> SQLite database at <code>/app/data/matcher.db</code><br>
|
| 200 |
-
<strong>Docker Volume:</strong> <code>stockex_matcher_data</code> (survives container restarts)
|
| 201 |
-
</div>
|
| 202 |
-
|
| 203 |
-
<h4>4.1 Database Schema</h4>
|
| 204 |
-
|
| 205 |
-
<div class="module-box">
|
| 206 |
-
<h4>order_book β All Orders</h4>
|
| 207 |
-
<table>
|
| 208 |
-
<tr><th>Column</th><th>Type</th><th>Description</th></tr>
|
| 209 |
-
<tr><td><code>id</code></td><td>INTEGER</td><td>Auto-increment primary key</td></tr>
|
| 210 |
-
<tr><td><code>cl_ord_id</code></td><td>TEXT</td><td>Unique client order ID</td></tr>
|
| 211 |
-
<tr><td><code>symbol</code></td><td>TEXT</td><td>Security (ALPHA, EXAE, etc.)</td></tr>
|
| 212 |
-
<tr><td><code>side</code></td><td>TEXT</td><td>BUY / SELL</td></tr>
|
| 213 |
-
<tr><td><code>price</code></td><td>REAL</td><td>Limit price</td></tr>
|
| 214 |
-
<tr><td><code>quantity</code></td><td>INTEGER</td><td>Original order quantity</td></tr>
|
| 215 |
-
<tr><td><code>remaining_qty</code></td><td>INTEGER</td><td>Unfilled quantity</td></tr>
|
| 216 |
-
<tr><td><code>status</code></td><td>TEXT</td><td>OPEN / FILLED / CANCELLED</td></tr>
|
| 217 |
-
<tr><td><code>timestamp</code></td><td>REAL</td><td>Order entry time (Unix)</td></tr>
|
| 218 |
-
<tr><td><code>created_at</code></td><td>DATETIME</td><td>DB insert timestamp</td></tr>
|
| 219 |
-
</table>
|
| 220 |
-
</div>
|
| 221 |
-
|
| 222 |
-
<div class="module-box">
|
| 223 |
-
<h4>trades β Executed Trades</h4>
|
| 224 |
-
<table>
|
| 225 |
-
<tr><th>Column</th><th>Type</th><th>Description</th></tr>
|
| 226 |
-
<tr><td><code>id</code></td><td>INTEGER</td><td>Auto-increment primary key</td></tr>
|
| 227 |
-
<tr><td><code>symbol</code></td><td>TEXT</td><td>Traded security</td></tr>
|
| 228 |
-
<tr><td><code>price</code></td><td>REAL</td><td>Execution price</td></tr>
|
| 229 |
-
<tr><td><code>quantity</code></td><td>INTEGER</td><td>Traded quantity</td></tr>
|
| 230 |
-
<tr><td><code>buy_order_id</code></td><td>TEXT</td><td>Buyer's cl_ord_id</td></tr>
|
| 231 |
-
<tr><td><code>sell_order_id</code></td><td>TEXT</td><td>Seller's cl_ord_id</td></tr>
|
| 232 |
-
<tr><td><code>timestamp</code></td><td>REAL</td><td>Trade time (Unix)</td></tr>
|
| 233 |
-
<tr><td><code>created_at</code></td><td>DATETIME</td><td>DB insert timestamp</td></tr>
|
| 234 |
-
</table>
|
| 235 |
-
</div>
|
| 236 |
-
|
| 237 |
-
<h4>4.2 Database Functions</h4>
|
| 238 |
-
<table>
|
| 239 |
-
<tr><th>Function</th><th>Description</th></tr>
|
| 240 |
-
<tr><td><code>save_order(order)</code></td><td>Insert new order into order_book</td></tr>
|
| 241 |
-
<tr><td><code>update_order_quantity(id, qty)</code></td><td>Update remaining_qty after partial fill</td></tr>
|
| 242 |
-
<tr><td><code>cancel_order(cl_ord_id)</code></td><td>Set status = 'CANCELLED'</td></tr>
|
| 243 |
-
<tr><td><code>save_trade(trade)</code></td><td>Insert executed trade</td></tr>
|
| 244 |
-
<tr><td><code>get_open_orders(symbol, side)</code></td><td>Query open orders for matching</td></tr>
|
| 245 |
-
<tr><td><code>load_order_books()</code></td><td>Restore order books on startup</td></tr>
|
| 246 |
-
<tr><td><code>get_trades(symbol, limit)</code></td><td>Retrieve recent trades</td></tr>
|
| 247 |
-
<tr><td><code>delete_filled_orders(days)</code></td><td>Cleanup old filled/cancelled orders</td></tr>
|
| 248 |
-
</table>
|
| 249 |
-
|
| 250 |
-
<h4>4.3 Indexes</h4>
|
| 251 |
-
<div class="code-block">CREATE INDEX idx_trades_symbol ON trades(symbol);
|
| 252 |
-
CREATE INDEX idx_trades_timestamp ON trades(timestamp);
|
| 253 |
-
CREATE INDEX idx_orderbook_symbol_side ON order_book(symbol, side);
|
| 254 |
-
CREATE INDEX idx_orderbook_status ON order_book(status);
|
| 255 |
-
CREATE INDEX idx_orderbook_cl_ord_id ON order_book(cl_ord_id);</div>
|
| 256 |
-
</div>
|
| 257 |
-
|
| 258 |
-
<div class="section">
|
| 259 |
-
<h2>5. Data Flow Diagrams</h2>
|
| 260 |
-
|
| 261 |
-
<h4>4.1 Order Entry Flow</h4>
|
| 262 |
-
<div class="arch-diagram">
|
| 263 |
-
ββββββββββββββββ
|
| 264 |
-
β FIX Client β
|
| 265 |
-
ββββββββ¬ββββββββ
|
| 266 |
-
β FIX 4.4 NewOrderSingle (35=D)
|
| 267 |
-
βΌ
|
| 268 |
-
ββββββββββββββββ
|
| 269 |
-
β FIX OEG β Validate β Normalize β Generate cl_ord_id
|
| 270 |
-
ββββββββ¬ββββββββ
|
| 271 |
-
β JSON
|
| 272 |
-
βΌ
|
| 273 |
-
ββββββββββββββββ
|
| 274 |
-
β Kafka β Topic: orders
|
| 275 |
-
β [orders] β
|
| 276 |
-
ββββββββ¬ββββββββ
|
| 277 |
-
β
|
| 278 |
-
βββββββ΄ββββββ
|
| 279 |
-
βΌ βΌ
|
| 280 |
-
ββββββββββ βοΏ½οΏ½οΏ½βββββββββββ
|
| 281 |
-
βMatcher β β Dashboard β
|
| 282 |
-
ββββββββββ βββββββββββββ
|
| 283 |
-
</div>
|
| 284 |
-
|
| 285 |
-
<h4>4.2 Order Matching Flow</h4>
|
| 286 |
-
<div class="arch-diagram">
|
| 287 |
-
βββββββββββββββββββ
|
| 288 |
-
β Incoming Order β
|
| 289 |
-
β (from Kafka) β
|
| 290 |
-
ββββββββββ¬βββββββββ
|
| 291 |
-
β
|
| 292 |
-
βΌ
|
| 293 |
-
βββββββββββββββββββ
|
| 294 |
-
β Parse & Validateβ
|
| 295 |
-
ββββββββββ¬βββββββββ
|
| 296 |
-
β
|
| 297 |
-
ββββββββββββββββ΄βββββββββββββββ
|
| 298 |
-
βΌ βΌ
|
| 299 |
-
ββββββββββββββ ββββββββββββββ
|
| 300 |
-
β BUY Order β β SELL Order β
|
| 301 |
-
ββββββββ¬ββββββ ββββββββ¬ββββββ
|
| 302 |
-
β β
|
| 303 |
-
βΌ βΌ
|
| 304 |
-
ββββββββββββββββββββ ββββββββββββββββββββ
|
| 305 |
-
β Check SELL book β β Check BUY book β
|
| 306 |
-
β for price β€ bid β β for price β₯ ask β
|
| 307 |
-
ββββββββββ¬ββββββββββ ββββββββββ¬ββββββββββ
|
| 308 |
-
β β
|
| 309 |
-
ββββββββ΄βββββββ ββββββββ΄βββββββ
|
| 310 |
-
βΌ βΌ βΌ βΌ
|
| 311 |
-
βββββββββ ββββββββββ βββββββββ ββββββββββ
|
| 312 |
-
β Match β βNo Matchβ β Match β βNo Matchβ
|
| 313 |
-
β Found β β β β Found β β β
|
| 314 |
-
βββββ¬ββββ βββββ¬βββββ βββββ¬ββββ βββββ¬βββββ
|
| 315 |
-
β β β β
|
| 316 |
-
βΌ βΌ βΌ βΌ
|
| 317 |
-
βββββββββ ββββββββββ βββββββββ ββββββββββ
|
| 318 |
-
βExecuteβ βAdd to β βExecuteβ βAdd to β
|
| 319 |
-
β Trade β βBUY Bookβ β Trade β βSELLBookβ
|
| 320 |
-
βββββ¬ββββ ββββββββββ βββββ¬ββββ ββββββββββ
|
| 321 |
-
β β
|
| 322 |
-
βββββββββββββ¬ββββββββββββββββ
|
| 323 |
-
βΌ
|
| 324 |
-
ββββββββββββββ
|
| 325 |
-
βKafka:tradesβ
|
| 326 |
-
ββββββββββββββ
|
| 327 |
-
</div>
|
| 328 |
-
|
| 329 |
-
<h4>4.3 Real-time Dashboard Flow</h4>
|
| 330 |
-
<div class="arch-diagram">
|
| 331 |
-
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 332 |
-
β BROWSER β
|
| 333 |
-
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
|
| 334 |
-
β β Dashboard UI β β
|
| 335 |
-
β β βββββββββββ βββββββββββ βββββββββββ ββββββββββββββββββββ β
|
| 336 |
-
β β β Orders β β Trades β β Book β β Statistics ββ β
|
| 337 |
-
β β ββββββ²βββββ ββββββ²βββββ ββββββ²βββββ ββββββββββ²ββββββββββ β
|
| 338 |
-
β βββββββββΌββββββββββββΌββββββββββββΌββββββββββββββββΌβββββββββββ β
|
| 339 |
-
β β β β β β
|
| 340 |
-
β βββββββββββββ΄ββββββ¬ββββββ΄ββββββββββββββββ β
|
| 341 |
-
β β β
|
| 342 |
-
β βββββββββΌβββββββββ β
|
| 343 |
-
β β EventSource β SSE Connection β
|
| 344 |
-
β β /stream β β
|
| 345 |
-
β βββββββββ¬βββββββββ β
|
| 346 |
-
ββοΏ½οΏ½οΏ½βββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββ
|
| 347 |
-
β HTTP (SSE)
|
| 348 |
-
βΌ
|
| 349 |
-
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 350 |
-
β DASHBOARD SERVER β
|
| 351 |
-
β β
|
| 352 |
-
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββ β
|
| 353 |
-
β βKafka ConsumerβββββββΆβ SSE BroadcastββββββΆβ Clients β β
|
| 354 |
-
β β (orders, β β Queue β β Queue[] β β
|
| 355 |
-
β β trades, β ββββββββββββββββ ββββββββββββββ β
|
| 356 |
-
β β snapshots) β β
|
| 357 |
-
β ββββββββββββββββ β
|
| 358 |
-
β β
|
| 359 |
-
β ββββββββββββββββ ββββββββββββββββ β
|
| 360 |
-
β β REST API βββββββΆβ Matcher β /orderbook, /trades β
|
| 361 |
-
β β /data β β Proxy β β
|
| 362 |
-
β ββββββββββββββββ ββββββββββββββββ β
|
| 363 |
-
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 364 |
-
</div>
|
| 365 |
-
|
| 366 |
-
<h4>4.4 Complete System Interaction</h4>
|
| 367 |
-
<div class="arch-diagram">
|
| 368 |
-
βββββββββββ βββββββββββ βββββββββββ
|
| 369 |
-
βFIX Cli 1β βFIX Cli 2β βFrontend β
|
| 370 |
-
ββββββ¬βββββ ββββββ¬βββββ ββββββ¬βββββ
|
| 371 |
-
β β β
|
| 372 |
-
βββββββ¬ββββββ΄ββββββββββββ
|
| 373 |
-
β
|
| 374 |
-
βΌ
|
| 375 |
-
βββββββββββββββ
|
| 376 |
-
β FIX OEG ββββββ FIX 4.4 Protocol
|
| 377 |
-
ββββββββ¬βββββββ
|
| 378 |
-
β
|
| 379 |
-
βΌ
|
| 380 |
-
ββββββββββββββββββββββββββββββββββββββ
|
| 381 |
-
β KAFKA CLUSTER β
|
| 382 |
-
β ββββββββββββββββββββββββββββββββ β
|
| 383 |
-
β βorders ββtrades ββsnapshots β β
|
| 384 |
-
β βββββ¬ββββββββββ²βββββββββββ²ββββββ β
|
| 385 |
-
ββββββββΌββββββββββΌβββββββββββΌββββββββ
|
| 386 |
-
β β β
|
| 387 |
-
βββββββΌββββββββββΌβββββββββββΌββββββ
|
| 388 |
-
β βΌ β β β
|
| 389 |
-
β ββββββββββ β β β
|
| 390 |
-
β βMATCHER ββββββ β β
|
| 391 |
-
β β β β β
|
| 392 |
-
β β Book β β β
|
| 393 |
-
β β Match β β β
|
| 394 |
-
β β Trade β β β
|
| 395 |
-
β ββββββββββ β β
|
| 396 |
-
β β² β β
|
| 397 |
-
β β REST β β
|
| 398 |
-
β β β β
|
| 399 |
-
β βββββ΄βββββββββββββββββββββ΄βββ β
|
| 400 |
-
β β DASHBOARD β β
|
| 401 |
-
β β (SSE + Kafka Consumer) β β
|
| 402 |
-
β βββββββββββββββββββββββββββββ β
|
| 403 |
-
β β
|
| 404 |
-
β βββββββββββββββββββββββββββββ β
|
| 405 |
-
β β MD FEEDER (MDF) ββββ
|
| 406 |
-
β β Orders + Snapshots β
|
| 407 |
-
β βββββββββββββββββββββββββββββ
|
| 408 |
-
β DOCKER NETWORK
|
| 409 |
-
ββββββββββββββββββββββββββββββββββ
|
| 410 |
-
</div>
|
| 411 |
-
|
| 412 |
-
<h4>4.5 Message Sequence: New Order to Trade</h4>
|
| 413 |
-
<div class="arch-diagram">
|
| 414 |
-
FIX Client FIX OEG Kafka Matcher Dashboard
|
| 415 |
-
β β β β β
|
| 416 |
-
βββ35=DβββββΆβ β β β
|
| 417 |
-
βNewOrder β β β β
|
| 418 |
-
β βββJSONβββββΆβ β β
|
| 419 |
-
β β [orders] β β β
|
| 420 |
-
β β βββconsumeβββΆβ β
|
| 421 |
-
β β β β β
|
| 422 |
-
β β β βββmatch() β
|
| 423 |
-
β β β β β
|
| 424 |
-
β β ββββtradeβββββ β
|
| 425 |
-
β β β [trades] β β
|
| 426 |
-
β β β β β
|
| 427 |
-
β β βββββββββββββββconsumeββββΆβ
|
| 428 |
-
β β β β β
|
| 429 |
-
β β β β render()
|
| 430 |
-
β β β β β
|
| 431 |
-
</div>
|
| 432 |
-
</div>
|
| 433 |
-
|
| 434 |
-
<div class="section">
|
| 435 |
-
<h2>6. Kafka Topics</h2>
|
| 436 |
-
<table>
|
| 437 |
-
<tr><th>Topic</th><th>Producers</th><th>Consumers</th><th>Content</th></tr>
|
| 438 |
-
<tr><td><code>orders</code></td><td>FIX OEG, MDF, Frontend</td><td>Matcher, Dashboard</td><td>New/Cancel/Amend orders</td></tr>
|
| 439 |
-
<tr><td><code>trades</code></td><td>Matcher</td><td>Dashboard, Consumer</td><td>Executed trades</td></tr>
|
| 440 |
-
<tr><td><code>snapshots</code></td><td>MDF</td><td>Dashboard</td><td>BBO updates</td></tr>
|
| 441 |
-
</table>
|
| 442 |
-
</div>
|
| 443 |
-
|
| 444 |
-
<div class="section">
|
| 445 |
-
<h2>7. Message Formats</h2>
|
| 446 |
-
|
| 447 |
-
<h4>Order (New)</h4>
|
| 448 |
-
<div class="code-block json">{
|
| 449 |
-
"symbol": "ALPHA",
|
| 450 |
-
"side": "BUY",
|
| 451 |
-
"price": 25.50,
|
| 452 |
-
"quantity": 100,
|
| 453 |
-
"cl_ord_id": "MDF-1234567890-1",
|
| 454 |
-
"timestamp": 1234567890.123,
|
| 455 |
-
"source": "MDF"
|
| 456 |
-
}</div>
|
| 457 |
-
|
| 458 |
-
<h4>Order (Cancel)</h4>
|
| 459 |
-
<div class="code-block json">{
|
| 460 |
-
"type": "cancel",
|
| 461 |
-
"orig_cl_ord_id": "MDF-1234567890-1",
|
| 462 |
-
"symbol": "ALPHA",
|
| 463 |
-
"timestamp": 1234567890.456
|
| 464 |
-
}</div>
|
| 465 |
-
|
| 466 |
-
<h4>Order (Amend)</h4>
|
| 467 |
-
<div class="code-block json">{
|
| 468 |
-
"type": "amend",
|
| 469 |
-
"orig_cl_ord_id": "MDF-1234567890-1",
|
| 470 |
-
"cl_ord_id": "amend-1234567890",
|
| 471 |
-
"symbol": "ALPHA",
|
| 472 |
-
"quantity": 150,
|
| 473 |
-
"price": 25.45,
|
| 474 |
-
"timestamp": 1234567890.789
|
| 475 |
-
}</div>
|
| 476 |
-
|
| 477 |
-
<h4>Trade</h4>
|
| 478 |
-
<div class="code-block json">{
|
| 479 |
-
"symbol": "ALPHA",
|
| 480 |
-
"price": 25.50,
|
| 481 |
-
"quantity": 100,
|
| 482 |
-
"buy_order_id": "order-123",
|
| 483 |
-
"sell_order_id": "order-456",
|
| 484 |
-
"timestamp": 1234567890.123
|
| 485 |
-
}</div>
|
| 486 |
-
|
| 487 |
-
<h4>Snapshot (BBO)</h4>
|
| 488 |
-
<div class="code-block json">{
|
| 489 |
-
"symbol": "ALPHA",
|
| 490 |
-
"best_bid": 25.45,
|
| 491 |
-
"best_ask": 25.55,
|
| 492 |
-
"bid_size": 500,
|
| 493 |
-
"ask_size": 300,
|
| 494 |
-
"timestamp": 1234567890.123,
|
| 495 |
-
"source": "MDF"
|
| 496 |
-
}</div>
|
| 497 |
-
</div>
|
| 498 |
-
|
| 499 |
-
<div class="section">
|
| 500 |
-
<h2>8. SSE Events</h2>
|
| 501 |
-
<table>
|
| 502 |
-
<tr><th>Event</th><th>Data</th><th>Trigger</th></tr>
|
| 503 |
-
<tr><td><code>connected</code></td><td>{status}</td><td>Client connects</td></tr>
|
| 504 |
-
<tr><td><code>init</code></td><td>{orders, bbos, trades}</td><td>Initial state dump</td></tr>
|
| 505 |
-
<tr><td><code>order</code></td><td>Order JSON</td><td>New order received</td></tr>
|
| 506 |
-
<tr><td><code>trade</code></td><td>Trade JSON</td><td>Trade executed</td></tr>
|
| 507 |
-
<tr><td><code>snapshot</code></td><td>BBO JSON</td><td>Price update</td></tr>
|
| 508 |
-
</table>
|
| 509 |
-
</div>
|
| 510 |
-
|
| 511 |
-
<div class="section">
|
| 512 |
-
<h2>9. Configuration</h2>
|
| 513 |
-
<table>
|
| 514 |
-
<tr><th>Variable</th><th>Default</th><th>Description</th></tr>
|
| 515 |
-
<tr><td><code>KAFKA_BOOTSTRAP</code></td><td>kafka:9092</td><td>Broker address</td></tr>
|
| 516 |
-
<tr><td><code>MATCHER_URL</code></td><td>http://matcher:6000</td><td>Matcher API</td></tr>
|
| 517 |
-
<tr><td><code>TICK_SIZE</code></td><td>0.05</td><td>Min price increment</td></tr>
|
| 518 |
-
<tr><td><code>ORDERS_PER_MIN</code></td><td>8</td><td>MDF rate</td></tr>
|
| 519 |
-
<tr><td><code>KAFKA_RETRIES</code></td><td>30</td><td>Connection retries</td></tr>
|
| 520 |
-
</table>
|
| 521 |
-
|
| 522 |
-
<h4>Securities (shared_data/securities.txt)</h4>
|
| 523 |
-
<div class="code-block">#SYMBOL start_price current_price
|
| 524 |
-
ALPHA 25.00 25.00
|
| 525 |
-
EXAE 42.00 42.00
|
| 526 |
-
PEIR 18.50 18.50
|
| 527 |
-
QUEST 12.75 12.75</div>
|
| 528 |
-
</div>
|
| 529 |
-
|
| 530 |
-
<div class="section">
|
| 531 |
-
<h2>10. Development Commands</h2>
|
| 532 |
-
|
| 533 |
-
<h4>Build & Run</h4>
|
| 534 |
-
<div class="code-block">docker compose up --build # Start all
|
| 535 |
-
docker compose up -d --build # Background
|
| 536 |
-
docker compose logs -f dashboard # Follow logs
|
| 537 |
-
docker compose down # Stop all</div>
|
| 538 |
-
|
| 539 |
-
<h4>Reset Data</h4>
|
| 540 |
-
<div class="code-block">docker compose down
|
| 541 |
-
docker volume rm stockex_matcher_data
|
| 542 |
-
docker compose up -d</div>
|
| 543 |
-
|
| 544 |
-
<h4>Container Access</h4>
|
| 545 |
-
<div class="code-block">docker exec -it matcher bash
|
| 546 |
-
docker exec -it dashboard bash
|
| 547 |
-
docker logs matcher --tail 50</div>
|
| 548 |
-
|
| 549 |
-
<h4>API Testing</h4>
|
| 550 |
-
<div class="code-block">curl http://localhost:6000/orderbook/ALPHA
|
| 551 |
-
curl http://localhost:6000/trades
|
| 552 |
-
curl http://localhost:5005/data</div>
|
| 553 |
-
</div>
|
| 554 |
-
|
| 555 |
-
<div class="screenshot">
|
| 556 |
-
<img src="screenshots/dashboard.png" alt="StockEx Dashboard">
|
| 557 |
-
<p style="font-size:10pt; color:#666;"><em>StockEx Trading Dashboard - Real-time Market View</em></p>
|
| 558 |
-
</div>
|
| 559 |
-
|
| 560 |
-
<hr>
|
| 561 |
-
|
| 562 |
-
<div class="footer">
|
| 563 |
-
<strong>StockEx Trading Platform v1.0</strong><br>
|
| 564 |
-
Developer Guide | Euronext OPTIQ Inspired<br>
|
| 565 |
-
<p style="margin-top:10px;">Print to PDF: Ctrl+P β Save as PDF</p>
|
| 566 |
-
</div>
|
| 567 |
-
|
| 568 |
-
</body>
|
| 569 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StockEx_Technical_Guide.md
DELETED
|
@@ -1,814 +0,0 @@
|
|
| 1 |
-
# StockEx Trading Platform
|
| 2 |
-
## Complete Technical & Developer Guide v1.0
|
| 3 |
-
|
| 4 |
-
---
|
| 5 |
-
|
| 6 |
-
## Table of Contents
|
| 7 |
-
|
| 8 |
-
1. [Introduction](#1-introduction)
|
| 9 |
-
2. [System Architecture](#2-system-architecture)
|
| 10 |
-
3. [Module Descriptions](#3-module-descriptions)
|
| 11 |
-
4. [Dashboard User Interface](#4-dashboard-user-interface)
|
| 12 |
-
5. [Data Flow & Messaging](#5-data-flow--messaging)
|
| 13 |
-
6. [Configuration](#6-configuration)
|
| 14 |
-
7. [Quick Reference](#7-quick-reference)
|
| 15 |
-
8. [Troubleshooting](#8-troubleshooting)
|
| 16 |
-
|
| 17 |
-
---
|
| 18 |
-
|
| 19 |
-
## 1. Introduction
|
| 20 |
-
|
| 21 |
-
**StockEx** is a comprehensive real-time trading simulation platform inspired by Euronext OPTIQ. It provides a complete electronic trading ecosystem including order entry via FIX protocol, order matching engine, market data distribution, and live visualization through a web dashboard.
|
| 22 |
-
|
| 23 |
-
### Key Features
|
| 24 |
-
|
| 25 |
-
- **FIX 4.4 Protocol Support** β Institutional-grade order entry via QuickFIX
|
| 26 |
-
- **Real-time Order Matching** β Price-time priority matching engine with SQLite persistence
|
| 27 |
-
- **Live Market Data Streaming** β Kafka-based event distribution
|
| 28 |
-
- **Web Dashboard with SSE** β Real-time updates without page refresh
|
| 29 |
-
- **Order Management** β Edit and Cancel capabilities from the UI
|
| 30 |
-
- **Trading Analytics** β Volume, Value, VWAP statistics with visualizations
|
| 31 |
-
|
| 32 |
-
### Access URLs
|
| 33 |
-
|
| 34 |
-
| Service | URL | Purpose |
|
| 35 |
-
|---------|-----|---------|
|
| 36 |
-
| Dashboard | http://localhost:5005 | Main trading view |
|
| 37 |
-
| Frontend | http://localhost:5000 | Manual order entry |
|
| 38 |
-
| FIX Client 1 | http://localhost:5002 | FIX order submission |
|
| 39 |
-
| FIX Client 2 | http://localhost:5003 | FIX order submission |
|
| 40 |
-
| Matcher API | http://localhost:6000 | REST API for order book/trades |
|
| 41 |
-
|
| 42 |
-
---
|
| 43 |
-
|
| 44 |
-
## 2. System Architecture
|
| 45 |
-
|
| 46 |
-
### High-Level Architecture Diagram
|
| 47 |
-
|
| 48 |
-
```
|
| 49 |
-
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
|
| 50 |
-
β FIX UI Client β β FIX UI Client β β Frontend β
|
| 51 |
-
β (Port 5002) β β (Port 5003) β β (Port 5000) β
|
| 52 |
-
ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ
|
| 53 |
-
β β β
|
| 54 |
-
β FIX 4.4 β FIX 4.4 β HTTP
|
| 55 |
-
βΌ βΌ βΌ
|
| 56 |
-
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 57 |
-
β FIX OEG (Port 5001) β
|
| 58 |
-
β QuickFIX Order Entry Gateway β
|
| 59 |
-
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
|
| 60 |
-
β
|
| 61 |
-
βΌ Kafka [orders]
|
| 62 |
-
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 63 |
-
β Apache Kafka (Port 9092) β
|
| 64 |
-
β Topics: orders, trades, snapshots β
|
| 65 |
-
βββββββββ¬βββββββββββββββββββββ¬ββββββββββββββββββββββββ¬ββββββββββββ
|
| 66 |
-
β β β
|
| 67 |
-
βΌ βΌ βΌ
|
| 68 |
-
βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ
|
| 69 |
-
β Matcher β β MD Feeder β β Dashboard β
|
| 70 |
-
β (Port 6000) β β (MDF) β β (Port 5005) β
|
| 71 |
-
β β β β β β
|
| 72 |
-
β Order Book β β Price Sim β β Web UI + SSE β
|
| 73 |
-
β Trade Match β β BBO Publish β β Real-time β
|
| 74 |
-
βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ
|
| 75 |
-
```
|
| 76 |
-
|
| 77 |
-
### Component Summary
|
| 78 |
-
|
| 79 |
-
| Component | Technology | Port | Function |
|
| 80 |
-
|-----------|------------|------|----------|
|
| 81 |
-
| Zookeeper | Confluent 7.5.0 | 2181 | Kafka coordination |
|
| 82 |
-
| Kafka | Confluent 7.5.0 | 9092, 29092 | Message streaming |
|
| 83 |
-
| FIX OEG | QuickFIX/Python | 5001 | FIX protocol gateway |
|
| 84 |
-
| Matcher | Python/Flask/SQLite | 6000 | Order matching engine |
|
| 85 |
-
| MDF | Python | - | Market data simulation |
|
| 86 |
-
| Dashboard | Flask/SSE | 5005 | Real-time web UI |
|
| 87 |
-
| Frontend | Flask | 5000 | Manual order entry |
|
| 88 |
-
| FIX Clients | QuickFIX/Flask | 5002, 5003 | FIX order UI |
|
| 89 |
-
|
| 90 |
-
---
|
| 91 |
-
|
| 92 |
-
## 3. Module Descriptions
|
| 93 |
-
|
| 94 |
-
### 3.1 Zookeeper
|
| 95 |
-
|
| 96 |
-
**Port:** 2181
|
| 97 |
-
|
| 98 |
-
Apache Zookeeper provides distributed coordination for the Kafka cluster. It manages broker metadata, topic configurations, and cluster membership.
|
| 99 |
-
|
| 100 |
-
- **Image:** confluentinc/cp-zookeeper:7.5.0
|
| 101 |
-
- **Dependencies:** None
|
| 102 |
-
- **Environment:** `ZOOKEEPER_CLIENT_PORT=2181`
|
| 103 |
-
|
| 104 |
-
---
|
| 105 |
-
|
| 106 |
-
### 3.2 Apache Kafka
|
| 107 |
-
|
| 108 |
-
**Ports:** 9092 (internal), 29092 (host access)
|
| 109 |
-
|
| 110 |
-
Apache Kafka serves as the central message backbone for the entire trading system. All order flow, trade executions, and market data are distributed through Kafka topics.
|
| 111 |
-
|
| 112 |
-
- **Image:** confluentinc/cp-kafka:7.5.0
|
| 113 |
-
- **Dependencies:** Zookeeper
|
| 114 |
-
|
| 115 |
-
**Topics:**
|
| 116 |
-
|
| 117 |
-
| Topic | Purpose | Producers | Consumers |
|
| 118 |
-
|-------|---------|-----------|-----------|
|
| 119 |
-
| `orders` | Order flow | FIX OEG, MDF, Frontend | Matcher, Dashboard |
|
| 120 |
-
| `trades` | Executed trades | Matcher | Dashboard, Consumer |
|
| 121 |
-
| `snapshots` | BBO updates | MDF | Dashboard, Snapshot Viewer |
|
| 122 |
-
|
| 123 |
-
---
|
| 124 |
-
|
| 125 |
-
### 3.3 FIX Order Entry Gateway (FIX OEG)
|
| 126 |
-
|
| 127 |
-
**Port:** 5001
|
| 128 |
-
|
| 129 |
-
The FIX OEG is a QuickFIX/Python acceptor that receives orders via FIX 4.4 protocol from institutional clients. It validates incoming FIX messages, normalizes them to JSON format, and publishes to the Kafka `orders` topic.
|
| 130 |
-
|
| 131 |
-
**Supported FIX Messages:**
|
| 132 |
-
|
| 133 |
-
| Message Type | Tag 35 | Description |
|
| 134 |
-
|--------------|--------|-------------|
|
| 135 |
-
| NewOrderSingle | D | New order submission |
|
| 136 |
-
| OrderCancelRequest | F | Cancel existing order |
|
| 137 |
-
| OrderCancelReplaceRequest | G | Modify existing order |
|
| 138 |
-
|
| 139 |
-
**Key Functions:**
|
| 140 |
-
- FIX session management (logon, heartbeat, logout)
|
| 141 |
-
- Message validation and normalization
|
| 142 |
-
- Order ID generation
|
| 143 |
-
- Kafka publishing
|
| 144 |
-
|
| 145 |
-
---
|
| 146 |
-
|
| 147 |
-
### 3.4 FIX UI Clients
|
| 148 |
-
|
| 149 |
-
**Ports:** 5002 (Client 1), 5003 (Client 2)
|
| 150 |
-
|
| 151 |
-
Web-based FIX initiator clients that connect to the FIX OEG. They provide a user interface for submitting orders via FIX protocol, simulating institutional trading terminals.
|
| 152 |
-
|
| 153 |
-
**Features:**
|
| 154 |
-
- New Order Single submission
|
| 155 |
-
- Order cancellation
|
| 156 |
-
- Order amendment
|
| 157 |
-
- Real-time execution reports
|
| 158 |
-
|
| 159 |
-
**Configuration Files:**
|
| 160 |
-
- `client1.cfg` β FIX session config for Client 1
|
| 161 |
-
- `client2.cfg` β FIX session config for Client 2
|
| 162 |
-
|
| 163 |
-
---
|
| 164 |
-
|
| 165 |
-
### 3.5 Matcher (Order Matching Engine)
|
| 166 |
-
|
| 167 |
-
**Port:** 6000
|
| 168 |
-
|
| 169 |
-
The core matching engine that maintains order books for all securities. It consumes orders from Kafka, attempts to match them using price-time priority, and publishes resulting trades.
|
| 170 |
-
|
| 171 |
-
**Matching Algorithm:** Price-Time Priority (FIFO)
|
| 172 |
-
- Buy orders sorted by price descending (highest first)
|
| 173 |
-
- Sell orders sorted by price ascending (lowest first)
|
| 174 |
-
- Within same price level, earlier orders have priority
|
| 175 |
-
|
| 176 |
-
**Persistence:** SQLite database (`/app/data/matcher.db`)
|
| 177 |
-
- Order book survives container restarts
|
| 178 |
-
- Volume-mapped for data durability
|
| 179 |
-
|
| 180 |
-
**REST API Endpoints:**
|
| 181 |
-
|
| 182 |
-
| Endpoint | Method | Description |
|
| 183 |
-
|----------|--------|-------------|
|
| 184 |
-
| `/orderbook/<symbol>` | GET | Get order book depth for symbol |
|
| 185 |
-
| `/trades` | GET | Get recent trades |
|
| 186 |
-
| `/health` | GET | Health check with statistics |
|
| 187 |
-
|
| 188 |
-
**Order Book Response Example:**
|
| 189 |
-
```json
|
| 190 |
-
{
|
| 191 |
-
"symbol": "ALPHA",
|
| 192 |
-
"bids": [
|
| 193 |
-
{"price": 25.45, "quantity": 100, "cl_ord_id": "MDF-123"},
|
| 194 |
-
{"price": 25.40, "quantity": 200, "cl_ord_id": "MDF-124"}
|
| 195 |
-
],
|
| 196 |
-
"asks": [
|
| 197 |
-
{"price": 25.55, "quantity": 150, "cl_ord_id": "MDF-125"},
|
| 198 |
-
{"price": 25.60, "quantity": 100, "cl_ord_id": "MDF-126"}
|
| 199 |
-
]
|
| 200 |
-
}
|
| 201 |
-
```
|
| 202 |
-
|
| 203 |
-
---
|
| 204 |
-
|
| 205 |
-
### 3.6 Market Data Feeder (MDF)
|
| 206 |
-
|
| 207 |
-
**Port:** Internal only
|
| 208 |
-
|
| 209 |
-
Simulates market data by generating random orders and publishing Best Bid/Offer (BBO) snapshots. Creates realistic market activity with configurable order rates and price movements.
|
| 210 |
-
|
| 211 |
-
**Order Generation:**
|
| 212 |
-
- **90% Passive Orders** β Placed away from mid price to build order book
|
| 213 |
-
- **10% Aggressive Orders** β Cross the spread to generate trades
|
| 214 |
-
|
| 215 |
-
**Price Simulation:**
|
| 216 |
-
- Small random drift (Β±2 ticks) with 10% probability
|
| 217 |
-
- Prices bounded by minimum (1.00)
|
| 218 |
-
- Half spread: 0.10 (10 cents)
|
| 219 |
-
|
| 220 |
-
**Output:**
|
| 221 |
-
- Orders β Kafka `orders` topic
|
| 222 |
-
- Snapshots β Kafka `snapshots` topic
|
| 223 |
-
|
| 224 |
-
**Configuration:**
|
| 225 |
-
|
| 226 |
-
| Variable | Default | Description |
|
| 227 |
-
|----------|---------|-------------|
|
| 228 |
-
| `ORDERS_PER_MIN` | 8 | Order generation rate |
|
| 229 |
-
| `TICK_SIZE` | 0.05 | Minimum price increment |
|
| 230 |
-
|
| 231 |
-
---
|
| 232 |
-
|
| 233 |
-
### 3.7 Dashboard
|
| 234 |
-
|
| 235 |
-
**Port:** 5005
|
| 236 |
-
|
| 237 |
-
Real-time web dashboard providing comprehensive market visualization. Uses Server-Sent Events (SSE) for live streaming updates without page refresh.
|
| 238 |
-
|
| 239 |
-
**Technology Stack:**
|
| 240 |
-
- Backend: Python/Flask
|
| 241 |
-
- Frontend: HTML5/CSS3/JavaScript
|
| 242 |
-
- Streaming: Server-Sent Events (SSE)
|
| 243 |
-
- Data: Kafka consumer + REST API calls to Matcher
|
| 244 |
-
|
| 245 |
-
**Features:**
|
| 246 |
-
|
| 247 |
-
| Panel | Description |
|
| 248 |
-
|-------|-------------|
|
| 249 |
-
| Orders | Live order feed with Edit/Cancel actions |
|
| 250 |
-
| Market Snapshot | BBO table with scrolling ticker tape |
|
| 251 |
-
| Trades | Executed trades with value calculation |
|
| 252 |
-
| Order Book | Depth of book per symbol |
|
| 253 |
-
| Trade Chart | Price line + volume bars visualization |
|
| 254 |
-
| Trading Statistics | Aggregated metrics with bar charts |
|
| 255 |
-
|
| 256 |
-
**SSE Events:**
|
| 257 |
-
|
| 258 |
-
| Event | Data | Trigger |
|
| 259 |
-
|-------|------|---------|
|
| 260 |
-
| `connected` | Status | Initial connection |
|
| 261 |
-
| `init` | Full state | On connect |
|
| 262 |
-
| `order` | Order JSON | New order received |
|
| 263 |
-
| `trade` | Trade JSON | Trade executed |
|
| 264 |
-
| `snapshot` | BBO JSON | Price update |
|
| 265 |
-
|
| 266 |
-
---
|
| 267 |
-
|
| 268 |
-
### 3.8 Frontend (Manual Order Entry)
|
| 269 |
-
|
| 270 |
-
**Port:** 5000
|
| 271 |
-
|
| 272 |
-
Simple web interface for manual order submission. Allows users to enter orders directly without FIX protocol.
|
| 273 |
-
|
| 274 |
-
**Features:**
|
| 275 |
-
- Symbol selection dropdown
|
| 276 |
-
- Side selection (BUY/SELL)
|
| 277 |
-
- Price and quantity input
|
| 278 |
-
- Submit order to Kafka
|
| 279 |
-
|
| 280 |
-
---
|
| 281 |
-
|
| 282 |
-
### 3.9 Consumer (Debug Utility)
|
| 283 |
-
|
| 284 |
-
**Port:** Internal only
|
| 285 |
-
|
| 286 |
-
Debug utility that consumes and logs messages from Kafka `trades` topic. Outputs trade information to console for monitoring.
|
| 287 |
-
|
| 288 |
-
**Output Format:**
|
| 289 |
-
```
|
| 290 |
-
TRADE: ALPHA - 100 @ 25.50
|
| 291 |
-
TRADE: EXAE - 50 @ 42.10
|
| 292 |
-
```
|
| 293 |
-
|
| 294 |
-
---
|
| 295 |
-
|
| 296 |
-
### 3.10 Snapshot Viewer
|
| 297 |
-
|
| 298 |
-
**Port:** Internal only
|
| 299 |
-
|
| 300 |
-
Utility service that subscribes to the `snapshots` topic and logs BBO updates. Writes to log files in `/app/logs` for analysis.
|
| 301 |
-
|
| 302 |
-
---
|
| 303 |
-
|
| 304 |
-
## 4. Dashboard User Interface
|
| 305 |
-
|
| 306 |
-
### 4.1 Orders Panel
|
| 307 |
-
|
| 308 |
-
Displays real-time incoming orders with full management capabilities.
|
| 309 |
-
|
| 310 |
-
| Column | Description |
|
| 311 |
-
|--------|-------------|
|
| 312 |
-
| Symbol | Stock ticker (ALPHA, EXAE, PEIR, QUEST) |
|
| 313 |
-
| Side | BUY (green) or SELL (red) |
|
| 314 |
-
| Qty | Order quantity in shares |
|
| 315 |
-
| Price | Limit price |
|
| 316 |
-
| Source | Order origin (MDF, FIX, Manual) |
|
| 317 |
-
| Time | Order timestamp |
|
| 318 |
-
| Actions | Edit / Cancel buttons |
|
| 319 |
-
|
| 320 |
-
**Order Actions:**
|
| 321 |
-
- **Edit** β Opens modal to modify quantity and/or price (sends amend to Kafka)
|
| 322 |
-
- **Cancel** β Sends cancel request to Kafka
|
| 323 |
-
- **Row Selection** β Click row to select, use header buttons for actions
|
| 324 |
-
|
| 325 |
-
---
|
| 326 |
-
|
| 327 |
-
### 4.2 Market Snapshot
|
| 328 |
-
|
| 329 |
-
Shows Best Bid/Offer (BBO) for all securities with real-time updates.
|
| 330 |
-
|
| 331 |
-
| Column | Description |
|
| 332 |
-
|--------|-------------|
|
| 333 |
-
| Symbol | Security identifier |
|
| 334 |
-
| Best Bid | Highest buy price (green) |
|
| 335 |
-
| Best Ask | Lowest sell price (red) |
|
| 336 |
-
| Spread | Ask - Bid difference |
|
| 337 |
-
| Mid | Midpoint: (Bid + Ask) / 2 |
|
| 338 |
-
| Updated | Last update timestamp |
|
| 339 |
-
|
| 340 |
-
**Ticker Tape:**
|
| 341 |
-
- Scrolling bar at bottom shows recent trades
|
| 342 |
-
- β² Green: Price up from previous
|
| 343 |
-
- βΌ Red: Price down from previous
|
| 344 |
-
- β Yellow: No change
|
| 345 |
-
- Hover to pause scrolling
|
| 346 |
-
|
| 347 |
-
---
|
| 348 |
-
|
| 349 |
-
### 4.3 Trades Panel
|
| 350 |
-
|
| 351 |
-
Lists all executed trades with calculated values.
|
| 352 |
-
|
| 353 |
-
| Column | Description |
|
| 354 |
-
|--------|-------------|
|
| 355 |
-
| Symbol | Traded security |
|
| 356 |
-
| Qty | Executed quantity |
|
| 357 |
-
| Price | Execution price |
|
| 358 |
-
| Value | Trade value (Qty Γ Price) |
|
| 359 |
-
| Time | Execution timestamp |
|
| 360 |
-
|
| 361 |
-
---
|
| 362 |
-
|
| 363 |
-
### 4.4 Order Book
|
| 364 |
-
|
| 365 |
-
Displays full market depth for selected symbol.
|
| 366 |
-
|
| 367 |
-
**Controls:**
|
| 368 |
-
- **Symbol Dropdown** β Select security to view
|
| 369 |
-
- **Refresh Button** β Manual refresh (auto-refreshes every 3 seconds)
|
| 370 |
-
|
| 371 |
-
**Display:**
|
| 372 |
-
- **Left Side:** Bid Qty | Bid Price (green, sorted price descending)
|
| 373 |
-
- **Right Side:** Ask Price | Ask Qty (red, sorted price ascending)
|
| 374 |
-
- **Header:** Shows count of bids and asks
|
| 375 |
-
|
| 376 |
-
---
|
| 377 |
-
|
| 378 |
-
### 4.5 Trade Chart
|
| 379 |
-
|
| 380 |
-
Visual representation of trade activity over time.
|
| 381 |
-
|
| 382 |
-
**Elements:**
|
| 383 |
-
- **Green Line** β Price trend connecting trade execution prices
|
| 384 |
-
- **Blue Bars** β Volume per trade
|
| 385 |
-
- **Y-Axis** β Price scale
|
| 386 |
-
- **X-Axis** β Trade sequence (last 100 trades)
|
| 387 |
-
|
| 388 |
-
**Controls:**
|
| 389 |
-
- Symbol dropdown to filter by security or view all
|
| 390 |
-
|
| 391 |
-
---
|
| 392 |
-
|
| 393 |
-
### 4.6 Trading Statistics
|
| 394 |
-
|
| 395 |
-
Aggregated metrics calculated from all trades.
|
| 396 |
-
|
| 397 |
-
| Metric | Description | Formula |
|
| 398 |
-
|--------|-------------|---------|
|
| 399 |
-
| Trades | Count of executed trades | COUNT(*) |
|
| 400 |
-
| Volume | Total shares traded | Ξ£ Quantity |
|
| 401 |
-
| Value | Total monetary value | Ξ£ (Qty Γ Price) |
|
| 402 |
-
| Start | First trade price | First price in session |
|
| 403 |
-
| Last | Most recent price | Latest price |
|
| 404 |
-
| VWAP | Volume-Weighted Average Price | Ξ£(Qty Γ Price) / Ξ£ Qty |
|
| 405 |
-
|
| 406 |
-
**Bar Charts:**
|
| 407 |
-
- Grouped by symbol showing Volume (green) and Value (blue) side by side
|
| 408 |
-
- Normalized to maximum value in dataset
|
| 409 |
-
|
| 410 |
-
---
|
| 411 |
-
|
| 412 |
-
## 5. Data Flow & Messaging
|
| 413 |
-
|
| 414 |
-
### 5.1 Order Entry Flow
|
| 415 |
-
|
| 416 |
-
```
|
| 417 |
-
ββββββββββββββββ
|
| 418 |
-
β FIX Client β
|
| 419 |
-
ββββββββ¬ββββββββ
|
| 420 |
-
β FIX 4.4 NewOrderSingle (35=D)
|
| 421 |
-
βΌ
|
| 422 |
-
ββββββββββββββββ
|
| 423 |
-
β FIX OEG β Validate β Normalize β Generate cl_ord_id
|
| 424 |
-
ββββββββ¬ββββββββ
|
| 425 |
-
β JSON
|
| 426 |
-
βΌ
|
| 427 |
-
ββββββββββββββββ
|
| 428 |
-
β Kafka β Topic: orders
|
| 429 |
-
β [orders] β
|
| 430 |
-
ββββββββ¬ββββββββ
|
| 431 |
-
β
|
| 432 |
-
βββββββ΄ββββββ
|
| 433 |
-
βΌ βΌ
|
| 434 |
-
ββββββββββ βββββββββββββ
|
| 435 |
-
βMatcher β β Dashboard β
|
| 436 |
-
ββββββββββ βββββββββββββ
|
| 437 |
-
```
|
| 438 |
-
|
| 439 |
-
### 5.2 Order Matching Flow
|
| 440 |
-
|
| 441 |
-
```
|
| 442 |
-
βββββββββββββββββββ
|
| 443 |
-
β Incoming Order β
|
| 444 |
-
β (from Kafka) β
|
| 445 |
-
ββββββββββ¬βββββββββ
|
| 446 |
-
β
|
| 447 |
-
βΌ
|
| 448 |
-
βββββββββββββββββββ
|
| 449 |
-
β Parse & Validateβ
|
| 450 |
-
ββββββββββ¬βββββββββ
|
| 451 |
-
β
|
| 452 |
-
ββββββββββββββββ΄βββββββββββββββ
|
| 453 |
-
βΌ βΌ
|
| 454 |
-
ββββββββββββββ ββββββββββββββ
|
| 455 |
-
β BUY Order β β SELL Order β
|
| 456 |
-
ββββββββ¬ββββββ ββββββββ¬ββββββ
|
| 457 |
-
β β
|
| 458 |
-
βΌ βΌ
|
| 459 |
-
ββββββββββββββββββββ ββββββββββββββββββββ
|
| 460 |
-
β Check SELL book β β Check BUY book β
|
| 461 |
-
β for price β€ bid β β for price β₯ ask β
|
| 462 |
-
ββββββββββ¬ββββββββββ ββββββββββ¬ββββββββββ
|
| 463 |
-
β β
|
| 464 |
-
ββββββββ΄βββββββ ββββββββ΄βββββββ
|
| 465 |
-
βΌ βΌ βΌ βΌ
|
| 466 |
-
βββββββββ ββββββββββ βββββββββ ββββββββββ
|
| 467 |
-
β Match β βNo Matchβ β Match β βNo Matchβ
|
| 468 |
-
β Found β β β β Found β β β
|
| 469 |
-
βββββ¬ββββ βββββ¬βββββ βββββ¬ββββ βββββ¬βββββ
|
| 470 |
-
β β β β
|
| 471 |
-
βΌ βΌ βΌ βΌ
|
| 472 |
-
βββββββββ ββββββββββ βββββββββ ββββββββββ
|
| 473 |
-
βExecuteβ βAdd to β βExecuteβ βAdd to β
|
| 474 |
-
β Trade β βBUY Bookβ β Trade β βSELLBookβ
|
| 475 |
-
βββββ¬ββββ ββββββββββ βββββ¬ββββ ββββββββββ
|
| 476 |
-
β β
|
| 477 |
-
βββββββββββββ¬ββββββββββββββββ
|
| 478 |
-
βΌ
|
| 479 |
-
ββββββββββββββ
|
| 480 |
-
βKafka:tradesβ
|
| 481 |
-
ββββββββββββββ
|
| 482 |
-
```
|
| 483 |
-
|
| 484 |
-
### 5.3 Real-time Dashboard Flow
|
| 485 |
-
|
| 486 |
-
```
|
| 487 |
-
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 488 |
-
β BROWSER β
|
| 489 |
-
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
|
| 490 |
-
β β Dashboard UI β β
|
| 491 |
-
β β βββββββββββ βββββββββββ βββββββββββ ββββββββββββββββββββ β
|
| 492 |
-
β β β Orders β β Trades β β Book β β Statistics ββ β
|
| 493 |
-
β β ββββββ²βββββ ββββββ²βββββ ββββββ²βββββ ββββββββββ²ββββββββββ β
|
| 494 |
-
β βββββββββΌββββββββββββΌββββββββββββΌββββββββββββββββΌβββββββββββ β
|
| 495 |
-
β βββββββββββββ΄ββββββ¬ββββββ΄ββββββββββββββββ β
|
| 496 |
-
β βββββββββΌβββββββββ β
|
| 497 |
-
β β EventSource β SSE Connection β
|
| 498 |
-
β β /stream β β
|
| 499 |
-
β βββββββββ¬βββββββββ β
|
| 500 |
-
ββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββ
|
| 501 |
-
β HTTP (SSE)
|
| 502 |
-
βΌ
|
| 503 |
-
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 504 |
-
β DASHBOARD SERVER οΏ½οΏ½
|
| 505 |
-
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββ β
|
| 506 |
-
β βKafka ConsumerβββββββΆβ SSE BroadcastββββββΆβ Clients β β
|
| 507 |
-
β β (orders, β β Queue β β Queue[] β β
|
| 508 |
-
β β trades, β ββββββββββββββββ ββββββββββββββ β
|
| 509 |
-
β β snapshots) β β
|
| 510 |
-
β ββββββββββββββββ β
|
| 511 |
-
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 512 |
-
```
|
| 513 |
-
|
| 514 |
-
### 5.4 Complete System Interaction
|
| 515 |
-
|
| 516 |
-
```
|
| 517 |
-
βββββββββββ βββββββββββ βββββββββββ
|
| 518 |
-
βFIX Cli 1β βFIX Cli 2β βFrontend β
|
| 519 |
-
ββββββ¬βββββ ββββββ¬βββββ ββββββ¬βββββ
|
| 520 |
-
βββββββ¬ββββββ΄ββββββββββββ
|
| 521 |
-
βΌ
|
| 522 |
-
βββββββββββββββ
|
| 523 |
-
β FIX OEG ββββββ FIX 4.4 Protocol
|
| 524 |
-
ββββββββ¬βββββββ
|
| 525 |
-
βΌ
|
| 526 |
-
ββββββββββββββββββββββββββββββββββββββ
|
| 527 |
-
β KAFKA CLUSTER β
|
| 528 |
-
β ββββββββββββββββββββββββββββββββ β
|
| 529 |
-
β βorders ββtrades ββsnapshots β β
|
| 530 |
-
β βββββ¬ββββββββββ²βββββββββββ²ββββββ β
|
| 531 |
-
ββββββββΌββββββββββΌβββββββββββΌββββββββ
|
| 532 |
-
β β β
|
| 533 |
-
βββββββΌββββββββββΌβββββββββββΌββββββ
|
| 534 |
-
β βΌ β β β
|
| 535 |
-
β ββββββββββ β β β
|
| 536 |
-
β βMATCHER ββββββ β β
|
| 537 |
-
β β Book β β β
|
| 538 |
-
β β Match β β β
|
| 539 |
-
β ββββββββββ β β
|
| 540 |
-
β β² β β
|
| 541 |
-
β β REST β β
|
| 542 |
-
β βββββ΄βββββββββββββββββββββ΄βββ β
|
| 543 |
-
β β DASHBOARD β β
|
| 544 |
-
β β (SSE + Kafka Consumer) β β
|
| 545 |
-
β βββββββββββββββββββββββββββββ β
|
| 546 |
-
β βββββββββββββββββββββββββββββ β
|
| 547 |
-
β β MD FEEDER (MDF) ββββ
|
| 548 |
-
β β Orders + Snapshots β
|
| 549 |
-
β βββββββββββββββββββββββββββββ
|
| 550 |
-
ββββββββββββββββββββββββββββββββββ
|
| 551 |
-
```
|
| 552 |
-
|
| 553 |
-
### 5.5 Message Sequence: New Order to Trade
|
| 554 |
-
|
| 555 |
-
```
|
| 556 |
-
FIX Client FIX OEG Kafka Matcher Dashboard
|
| 557 |
-
β β β β β
|
| 558 |
-
βββ35=DβββββΆβ β β β
|
| 559 |
-
βNewOrder β β β β
|
| 560 |
-
β βββJSONβββββΆβ β β
|
| 561 |
-
β β [orders] β β β
|
| 562 |
-
β β βββconsumeβββΆβ β
|
| 563 |
-
β β β βββmatch() β
|
| 564 |
-
β β ββββtradeβββββ β
|
| 565 |
-
β β β [trades] β β
|
| 566 |
-
β β βββββββββββββββconsumeββββΆβ
|
| 567 |
-
β β β β render()
|
| 568 |
-
```
|
| 569 |
-
|
| 570 |
-
---
|
| 571 |
-
|
| 572 |
-
### 5.6 Message Formats
|
| 573 |
-
|
| 574 |
-
**Order Message:**
|
| 575 |
-
```json
|
| 576 |
-
{
|
| 577 |
-
"symbol": "ALPHA",
|
| 578 |
-
"side": "BUY",
|
| 579 |
-
"price": 25.50,
|
| 580 |
-
"quantity": 100,
|
| 581 |
-
"cl_ord_id": "MDF-1234567890-1",
|
| 582 |
-
"timestamp": 1234567890.123,
|
| 583 |
-
"source": "MDF"
|
| 584 |
-
}
|
| 585 |
-
```
|
| 586 |
-
|
| 587 |
-
**Cancel Message:**
|
| 588 |
-
```json
|
| 589 |
-
{
|
| 590 |
-
"type": "cancel",
|
| 591 |
-
"orig_cl_ord_id": "MDF-1234567890-1",
|
| 592 |
-
"symbol": "ALPHA",
|
| 593 |
-
"timestamp": 1234567890.456
|
| 594 |
-
}
|
| 595 |
-
```
|
| 596 |
-
|
| 597 |
-
**Amend Message:**
|
| 598 |
-
```json
|
| 599 |
-
{
|
| 600 |
-
"type": "amend",
|
| 601 |
-
"orig_cl_ord_id": "MDF-1234567890-1",
|
| 602 |
-
"cl_ord_id": "amend-1234567890",
|
| 603 |
-
"symbol": "ALPHA",
|
| 604 |
-
"quantity": 150,
|
| 605 |
-
"price": 25.45,
|
| 606 |
-
"timestamp": 1234567890.789
|
| 607 |
-
}
|
| 608 |
-
```
|
| 609 |
-
|
| 610 |
-
**Trade Message:**
|
| 611 |
-
```json
|
| 612 |
-
{
|
| 613 |
-
"symbol": "ALPHA",
|
| 614 |
-
"price": 25.50,
|
| 615 |
-
"quantity": 100,
|
| 616 |
-
"buy_order_id": "order-123",
|
| 617 |
-
"sell_order_id": "order-456",
|
| 618 |
-
"timestamp": 1234567890.123
|
| 619 |
-
}
|
| 620 |
-
```
|
| 621 |
-
|
| 622 |
-
**Snapshot Message:**
|
| 623 |
-
```json
|
| 624 |
-
{
|
| 625 |
-
"symbol": "ALPHA",
|
| 626 |
-
"best_bid": 25.45,
|
| 627 |
-
"best_ask": 25.55,
|
| 628 |
-
"bid_size": 500,
|
| 629 |
-
"ask_size": 300,
|
| 630 |
-
"timestamp": 1234567890.123,
|
| 631 |
-
"source": "MDF"
|
| 632 |
-
}
|
| 633 |
-
```
|
| 634 |
-
|
| 635 |
-
---
|
| 636 |
-
|
| 637 |
-
## 6. Configuration
|
| 638 |
-
|
| 639 |
-
### 6.1 Environment Variables
|
| 640 |
-
|
| 641 |
-
| Variable | Default | Description |
|
| 642 |
-
|----------|---------|-------------|
|
| 643 |
-
| `KAFKA_BOOTSTRAP` | kafka:9092 | Kafka broker address |
|
| 644 |
-
| `MATCHER_URL` | http://matcher:6000 | Matcher API endpoint |
|
| 645 |
-
| `TICK_SIZE` | 0.05 | Minimum price increment |
|
| 646 |
-
| `ORDERS_PER_MIN` | 8 | MDF order generation rate |
|
| 647 |
-
| `SECURITIES_FILE` | /app/data/securities.txt | Securities configuration |
|
| 648 |
-
| `KAFKA_RETRIES` | 30 | Connection retry attempts |
|
| 649 |
-
| `KAFKA_RETRY_DELAY` | 2 | Seconds between retries |
|
| 650 |
-
|
| 651 |
-
### 6.2 Securities Configuration
|
| 652 |
-
|
| 653 |
-
File: `shared_data/securities.txt`
|
| 654 |
-
|
| 655 |
-
```
|
| 656 |
-
#SYMBOL <start_price> <current_price>
|
| 657 |
-
ALPHA 25.00 25.00
|
| 658 |
-
PEIR 18.50 18.50
|
| 659 |
-
EXAE 42.00 42.00
|
| 660 |
-
QUEST 12.75 12.75
|
| 661 |
-
```
|
| 662 |
-
|
| 663 |
-
| Symbol | Start Price | Description |
|
| 664 |
-
|--------|-------------|-------------|
|
| 665 |
-
| ALPHA | 25.00 | Test Security A |
|
| 666 |
-
| EXAE | 42.00 | Test Security B |
|
| 667 |
-
| PEIR | 18.50 | Test Security C |
|
| 668 |
-
| QUEST | 12.75 | Test Security D |
|
| 669 |
-
|
| 670 |
-
### 6.3 Docker Volumes
|
| 671 |
-
|
| 672 |
-
| Volume | Path | Purpose |
|
| 673 |
-
|--------|------|---------|
|
| 674 |
-
| `matcher_data` | /app/data | SQLite database persistence |
|
| 675 |
-
| `shared_data` | /app/data | Securities and order ID files |
|
| 676 |
-
| `logs` | /app/logs | Snapshot viewer logs |
|
| 677 |
-
|
| 678 |
-
---
|
| 679 |
-
|
| 680 |
-
## 7. Quick Reference
|
| 681 |
-
|
| 682 |
-
### 7.1 Starting the System
|
| 683 |
-
|
| 684 |
-
```bash
|
| 685 |
-
# Start all services
|
| 686 |
-
docker compose up --build
|
| 687 |
-
|
| 688 |
-
# Start in background
|
| 689 |
-
docker compose up -d --build
|
| 690 |
-
|
| 691 |
-
# View logs
|
| 692 |
-
docker compose logs -f dashboard
|
| 693 |
-
docker compose logs -f matcher
|
| 694 |
-
|
| 695 |
-
# Stop all services
|
| 696 |
-
docker compose down
|
| 697 |
-
|
| 698 |
-
# Reset matcher database
|
| 699 |
-
docker volume rm stockex_matcher_data
|
| 700 |
-
```
|
| 701 |
-
|
| 702 |
-
### 7.2 Dashboard Actions
|
| 703 |
-
|
| 704 |
-
| Action | How To |
|
| 705 |
-
|--------|--------|
|
| 706 |
-
| View order book depth | Select symbol from Order Book dropdown |
|
| 707 |
-
| Edit an order | Click Edit button on order row |
|
| 708 |
-
| Cancel an order | Click Cancel button on order row |
|
| 709 |
-
| Filter trade chart | Select symbol from Trade Chart dropdown |
|
| 710 |
-
| Pause ticker tape | Hover mouse over the ticker |
|
| 711 |
-
| Select order row | Click on the row |
|
| 712 |
-
| Bulk actions | Select row, use header Edit/Cancel buttons |
|
| 713 |
-
|
| 714 |
-
### 7.3 Connection Status
|
| 715 |
-
|
| 716 |
-
| Status | Indicator | Meaning |
|
| 717 |
-
|--------|-----------|---------|
|
| 718 |
-
| Live | β Green | Connected to SSE stream |
|
| 719 |
-
| Connecting | β Yellow | Establishing connection |
|
| 720 |
-
| Disconnected | β Red | Connection lost, auto-reconnecting |
|
| 721 |
-
|
| 722 |
-
### 7.4 API Endpoints
|
| 723 |
-
|
| 724 |
-
**Matcher API (Port 6000):**
|
| 725 |
-
|
| 726 |
-
| Endpoint | Method | Description |
|
| 727 |
-
|----------|--------|-------------|
|
| 728 |
-
| `/orderbook/<symbol>` | GET | Order book for symbol |
|
| 729 |
-
| `/trades` | GET | Recent trades list |
|
| 730 |
-
| `/health` | GET | Service health status |
|
| 731 |
-
|
| 732 |
-
**Dashboard API (Port 5005):**
|
| 733 |
-
|
| 734 |
-
| Endpoint | Method | Description |
|
| 735 |
-
|----------|--------|-------------|
|
| 736 |
-
| `/` | GET | Main dashboard page |
|
| 737 |
-
| `/data` | GET | Current state (polling fallback) |
|
| 738 |
-
| `/stream` | GET | SSE event stream |
|
| 739 |
-
| `/orderbook/<symbol>` | GET | Proxy to matcher |
|
| 740 |
-
| `/order/cancel` | POST | Cancel order request |
|
| 741 |
-
| `/order/amend` | POST | Amend order request |
|
| 742 |
-
|
| 743 |
-
---
|
| 744 |
-
|
| 745 |
-
## 8. Troubleshooting
|
| 746 |
-
|
| 747 |
-
### Orders/Trades Panel Empty
|
| 748 |
-
|
| 749 |
-
1. Check Kafka connection:
|
| 750 |
-
```bash
|
| 751 |
-
docker logs dashboard | grep "Kafka"
|
| 752 |
-
```
|
| 753 |
-
2. Verify MDF is producing:
|
| 754 |
-
```bash
|
| 755 |
-
docker logs stockex-md_feeder-1 --tail 10
|
| 756 |
-
```
|
| 757 |
-
3. Restart dashboard:
|
| 758 |
-
```bash
|
| 759 |
-
docker restart dashboard
|
| 760 |
-
```
|
| 761 |
-
|
| 762 |
-
### Order Book Empty
|
| 763 |
-
|
| 764 |
-
1. Check matcher is receiving orders:
|
| 765 |
-
```bash
|
| 766 |
-
docker logs matcher | grep "received"
|
| 767 |
-
```
|
| 768 |
-
2. Verify matcher Kafka consumer connected:
|
| 769 |
-
```bash
|
| 770 |
-
docker logs matcher | grep "consumer connected"
|
| 771 |
-
```
|
| 772 |
-
3. Reset matcher database if corrupted:
|
| 773 |
-
```bash
|
| 774 |
-
docker compose down
|
| 775 |
-
docker volume rm stockex_matcher_data
|
| 776 |
-
docker compose up -d
|
| 777 |
-
```
|
| 778 |
-
|
| 779 |
-
### Connection Status Shows Disconnected
|
| 780 |
-
|
| 781 |
-
1. SSE stream timeout β will auto-reconnect after 3 seconds
|
| 782 |
-
2. Check dashboard container is running:
|
| 783 |
-
```bash
|
| 784 |
-
docker ps | grep dashboard
|
| 785 |
-
```
|
| 786 |
-
3. Check browser console for errors (F12)
|
| 787 |
-
|
| 788 |
-
### Prices Going Negative or Extreme
|
| 789 |
-
|
| 790 |
-
1. Old bug in MDF β update to latest version
|
| 791 |
-
2. Reset securities file:
|
| 792 |
-
```
|
| 793 |
-
#SYMBOL <start_price> <current_price>
|
| 794 |
-
ALPHA 25.00 25.00
|
| 795 |
-
PEIR 18.50 18.50
|
| 796 |
-
EXAE 42.00 42.00
|
| 797 |
-
QUEST 12.75 12.75
|
| 798 |
-
```
|
| 799 |
-
3. Restart MDF and matcher:
|
| 800 |
-
```bash
|
| 801 |
-
docker restart stockex-md_feeder-1 matcher
|
| 802 |
-
```
|
| 803 |
-
|
| 804 |
-
---
|
| 805 |
-
|
| 806 |
-
## Document Information
|
| 807 |
-
|
| 808 |
-
- **Version:** 1.0
|
| 809 |
-
- **Platform:** StockEx Trading Simulation
|
| 810 |
-
- **Inspired by:** Euronext OPTIQ
|
| 811 |
-
|
| 812 |
-
---
|
| 813 |
-
|
| 814 |
-
*StockEx v1.0 β Trading Simulation Platform*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StockEx_User_Guide.html
DELETED
|
@@ -1,428 +0,0 @@
|
|
| 1 |
-
<!DOCTYPE html>
|
| 2 |
-
<html>
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="utf-8">
|
| 5 |
-
<title>StockEx User Guide</title>
|
| 6 |
-
<style>
|
| 7 |
-
@page { margin: 2cm; }
|
| 8 |
-
body { font-family: Arial, sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; line-height: 1.6; color: #333; }
|
| 9 |
-
h1 { color: #1a1a2e; border-bottom: 3px solid #4CAF50; padding-bottom: 10px; }
|
| 10 |
-
h2 { color: #2e7d32; margin-top: 30px; border-bottom: 1px solid #ddd; padding-bottom: 5px; page-break-after: avoid; }
|
| 11 |
-
h3 { color: #1565c0; margin-top: 20px; }
|
| 12 |
-
h4 { color: #555; margin-top: 15px; }
|
| 13 |
-
table { width: 100%; border-collapse: collapse; margin: 15px 0; font-size: 14px; }
|
| 14 |
-
th, td { border: 1px solid #ddd; padding: 10px; text-align: left; }
|
| 15 |
-
th { background: #f5f5f5; font-weight: bold; }
|
| 16 |
-
.header { text-align: center; margin-bottom: 30px; }
|
| 17 |
-
.subtitle { color: #666; font-size: 18px; }
|
| 18 |
-
.section { page-break-inside: avoid; margin-bottom: 25px; }
|
| 19 |
-
.highlight { background: #e8f5e9; padding: 15px; border-radius: 5px; margin: 10px 0; border-left: 4px solid #4CAF50; }
|
| 20 |
-
.info { background: #e3f2fd; padding: 15px; border-radius: 5px; margin: 10px 0; border-left: 4px solid #2196F3; }
|
| 21 |
-
.warning { background: #fff3e0; padding: 15px; border-radius: 5px; margin: 10px 0; border-left: 4px solid #ff9800; }
|
| 22 |
-
.green { color: #2e7d32; }
|
| 23 |
-
.red { color: #c62828; }
|
| 24 |
-
.blue { color: #1565c0; }
|
| 25 |
-
.screenshot { text-align: center; margin: 20px 0; }
|
| 26 |
-
.screenshot img { max-width: 100%; border: 1px solid #ddd; border-radius: 5px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
|
| 27 |
-
hr { border: none; border-top: 1px solid #ddd; margin: 30px 0; }
|
| 28 |
-
.footer { text-align: center; color: #666; font-size: 12px; margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; }
|
| 29 |
-
.toc { background: #fafafa; padding: 20px; border-radius: 5px; margin: 20px 0; }
|
| 30 |
-
.toc ul { margin: 0; padding-left: 20px; }
|
| 31 |
-
.toc li { margin: 5px 0; }
|
| 32 |
-
.arch-diagram { background: #f5f5f5; padding: 20px; border-radius: 5px; font-family: monospace; white-space: pre; font-size: 12px; overflow-x: auto; }
|
| 33 |
-
code { background: #f5f5f5; padding: 2px 6px; border-radius: 3px; font-family: monospace; }
|
| 34 |
-
.module-box { border: 1px solid #ddd; border-radius: 8px; padding: 15px; margin: 15px 0; background: #fafafa; }
|
| 35 |
-
.module-box h4 { margin-top: 0; color: #1a1a2e; }
|
| 36 |
-
.port { display: inline-block; background: #e3f2fd; padding: 2px 8px; border-radius: 3px; font-family: monospace; font-size: 12px; }
|
| 37 |
-
</style>
|
| 38 |
-
</head>
|
| 39 |
-
<body>
|
| 40 |
-
|
| 41 |
-
<div class="header">
|
| 42 |
-
<h1>StockEx Trading Dashboard</h1>
|
| 43 |
-
<p class="subtitle">Complete User & Technical Guide v1.0</p>
|
| 44 |
-
</div>
|
| 45 |
-
|
| 46 |
-
<div class="toc">
|
| 47 |
-
<strong>Table of Contents</strong>
|
| 48 |
-
<ul>
|
| 49 |
-
<li>1. Introduction</li>
|
| 50 |
-
<li>2. System Architecture</li>
|
| 51 |
-
<li>3. Module Descriptions</li>
|
| 52 |
-
<li>4. Dashboard User Interface</li>
|
| 53 |
-
<li>5. Data Flow & Messaging</li>
|
| 54 |
-
<li>6. Configuration</li>
|
| 55 |
-
<li>7. Quick Reference</li>
|
| 56 |
-
</ul>
|
| 57 |
-
</div>
|
| 58 |
-
|
| 59 |
-
<hr>
|
| 60 |
-
|
| 61 |
-
<div class="section">
|
| 62 |
-
<h2>1. Introduction</h2>
|
| 63 |
-
<p><strong>StockEx</strong> is a comprehensive real-time trading simulation platform inspired by Euronext OPTIQ. It provides a complete electronic trading ecosystem including order entry, matching engine, market data distribution, and live visualization.</p>
|
| 64 |
-
|
| 65 |
-
<div class="highlight">
|
| 66 |
-
<strong>Key Features:</strong>
|
| 67 |
-
<ul>
|
| 68 |
-
<li>FIX 4.4 protocol support for institutional order entry</li>
|
| 69 |
-
<li>Real-time order matching with price-time priority</li>
|
| 70 |
-
<li>Live market data streaming via Kafka</li>
|
| 71 |
-
<li>Web-based dashboard with Server-Sent Events (SSE)</li>
|
| 72 |
-
<li>Order management (Edit/Cancel) capabilities</li>
|
| 73 |
-
<li>Trading analytics and statistics</li>
|
| 74 |
-
</ul>
|
| 75 |
-
</div>
|
| 76 |
-
|
| 77 |
-
<div class="info">
|
| 78 |
-
<strong>Access URLs:</strong><br>
|
| 79 |
-
Dashboard: <code>http://localhost:5005</code><br>
|
| 80 |
-
Frontend (Order Entry): <code>http://localhost:5000</code><br>
|
| 81 |
-
FIX Gateway: <code>localhost:5001</code><br>
|
| 82 |
-
Matcher API: <code>http://localhost:6000</code>
|
| 83 |
-
</div>
|
| 84 |
-
</div>
|
| 85 |
-
|
| 86 |
-
<div class="screenshot">
|
| 87 |
-
<img src="screenshots/dashboard.png" alt="Trading Dashboard">
|
| 88 |
-
<p><em>StockEx Trading Dashboard - Real-time Market View</em></p>
|
| 89 |
-
</div>
|
| 90 |
-
|
| 91 |
-
<div class="section">
|
| 92 |
-
<h2>2. System Architecture</h2>
|
| 93 |
-
|
| 94 |
-
<div class="arch-diagram">
|
| 95 |
-
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
|
| 96 |
-
β FIX UI Client β β FIX UI Client β β Frontend β
|
| 97 |
-
β (Port 5002) β β (Port 5003) β β (Port 5000) β
|
| 98 |
-
ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ
|
| 99 |
-
β β β
|
| 100 |
-
β FIX 4.4 β FIX 4.4 β HTTP
|
| 101 |
-
βΌ βΌ βΌ
|
| 102 |
-
ββοΏ½οΏ½βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 103 |
-
β FIX OEG (Port 5001) β
|
| 104 |
-
β QuickFIX Order Entry Gateway β
|
| 105 |
-
ββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
|
| 106 |
-
β
|
| 107 |
-
βΌ Kafka [orders]
|
| 108 |
-
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 109 |
-
β Apache Kafka (Port 9092) β
|
| 110 |
-
β Topics: orders, trades, snapshots β
|
| 111 |
-
βββββββββ¬βββββββββββββββββββββ¬ββββββββββββββββββββββββ¬ββββββββββββ
|
| 112 |
-
β β β
|
| 113 |
-
βΌ βΌ βΌ
|
| 114 |
-
βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ
|
| 115 |
-
β Matcher β β MD Feeder β β Dashboard β
|
| 116 |
-
β (Port 6000) β β (MDF) β β (Port 5005) β
|
| 117 |
-
β β β β β β
|
| 118 |
-
β Order Book β β Price Sim β β Web UI + SSE β
|
| 119 |
-
β Trade Match β β BBO Publish β β Real-time β
|
| 120 |
-
βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ
|
| 121 |
-
</div>
|
| 122 |
-
</div>
|
| 123 |
-
|
| 124 |
-
<div class="section">
|
| 125 |
-
<h2>3. Module Descriptions</h2>
|
| 126 |
-
|
| 127 |
-
<div class="module-box">
|
| 128 |
-
<h4>Zookeeper</h4>
|
| 129 |
-
<p><span class="port">Port 2181</span></p>
|
| 130 |
-
<p>Apache Zookeeper provides distributed coordination for the Kafka cluster. It manages broker metadata, topic configurations, and cluster membership.</p>
|
| 131 |
-
<table>
|
| 132 |
-
<tr><th>Function</th><td>Kafka cluster coordination</td></tr>
|
| 133 |
-
<tr><th>Technology</th><td>Confluent Zookeeper 7.5.0</td></tr>
|
| 134 |
-
<tr><th>Dependencies</th><td>None</td></tr>
|
| 135 |
-
</table>
|
| 136 |
-
</div>
|
| 137 |
-
|
| 138 |
-
<div class="module-box">
|
| 139 |
-
<h4>Kafka Message Broker</h4>
|
| 140 |
-
<p><span class="port">Port 9092</span> <span class="port">Port 29092 (host)</span></p>
|
| 141 |
-
<p>Apache Kafka serves as the central message backbone for the entire system. All order flow, trade executions, and market data are distributed through Kafka topics.</p>
|
| 142 |
-
<table>
|
| 143 |
-
<tr><th>Function</th><td>Message streaming and event distribution</td></tr>
|
| 144 |
-
<tr><th>Technology</th><td>Confluent Kafka 7.5.0</td></tr>
|
| 145 |
-
<tr><th>Topics</th><td><code>orders</code>, <code>trades</code>, <code>snapshots</code></td></tr>
|
| 146 |
-
<tr><th>Dependencies</th><td>Zookeeper</td></tr>
|
| 147 |
-
</table>
|
| 148 |
-
</div>
|
| 149 |
-
|
| 150 |
-
<div class="module-box">
|
| 151 |
-
<h4>FIX Order Entry Gateway (FIX OEG)</h4>
|
| 152 |
-
<p><span class="port">Port 5001</span></p>
|
| 153 |
-
<p>The FIX OEG is a QuickFIX/Python acceptor that receives orders via FIX 4.4 protocol from institutional clients. It validates incoming messages, normalizes them to JSON format, and publishes to the Kafka <code>orders</code> topic.</p>
|
| 154 |
-
<table>
|
| 155 |
-
<tr><th>Function</th><td>FIX protocol order reception and normalization</td></tr>
|
| 156 |
-
<tr><th>Technology</th><td>QuickFIX/Python</td></tr>
|
| 157 |
-
<tr><th>Protocol</th><td>FIX 4.4</td></tr>
|
| 158 |
-
<tr><th>Message Types</th><td>NewOrderSingle (D), OrderCancelRequest (F), OrderCancelReplaceRequest (G)</td></tr>
|
| 159 |
-
<tr><th>Output</th><td>Kafka <code>orders</code> topic</td></tr>
|
| 160 |
-
</table>
|
| 161 |
-
</div>
|
| 162 |
-
|
| 163 |
-
<div class="module-box">
|
| 164 |
-
<h4>FIX UI Clients</h4>
|
| 165 |
-
<p><span class="port">Port 5002 (Client 1)</span> <span class="port">Port 5003 (Client 2)</span></p>
|
| 166 |
-
<p>Web-based FIX initiator clients that connect to the FIX OEG. They provide a user interface for submitting orders via FIX protocol, simulating institutional trading terminals.</p>
|
| 167 |
-
<table>
|
| 168 |
-
<tr><th>Function</th><td>FIX order submission interface</td></tr>
|
| 169 |
-
<tr><th>Technology</th><td>QuickFIX/Python + Flask</td></tr>
|
| 170 |
-
<tr><th>Features</th><td>New Order, Cancel, Amend</td></tr>
|
| 171 |
-
<tr><th>Connection</th><td>FIX 4.4 to FIX OEG</td></tr>
|
| 172 |
-
</table>
|
| 173 |
-
</div>
|
| 174 |
-
|
| 175 |
-
<div class="module-box">
|
| 176 |
-
<h4>Matcher (Order Matching Engine)</h4>
|
| 177 |
-
<p><span class="port">Port 6000</span></p>
|
| 178 |
-
<p>The core matching engine that maintains order books for all securities. It consumes orders from Kafka, attempts to match them using price-time priority, and publishes resulting trades. Provides REST API for order book and trade queries.</p>
|
| 179 |
-
<table>
|
| 180 |
-
<tr><th>Function</th><td>Order matching, trade execution, order book management</td></tr>
|
| 181 |
-
<tr><th>Technology</th><td>Python/Flask with SQLite persistence</td></tr>
|
| 182 |
-
<tr><th>Algorithm</th><td>Price-Time Priority (FIFO)</td></tr>
|
| 183 |
-
<tr><th>Input</th><td>Kafka <code>orders</code> topic</td></tr>
|
| 184 |
-
<tr><th>Output</th><td>Kafka <code>trades</code> topic</td></tr>
|
| 185 |
-
<tr><th>API Endpoints</th><td><code>/orderbook/<symbol></code>, <code>/trades</code>, <code>/health</code></td></tr>
|
| 186 |
-
</table>
|
| 187 |
-
</div>
|
| 188 |
-
|
| 189 |
-
<div class="module-box">
|
| 190 |
-
<h4>Market Data Feeder (MDF)</h4>
|
| 191 |
-
<p><span class="port">Internal</span></p>
|
| 192 |
-
<p>Simulates market data by generating random orders and publishing Best Bid/Offer (BBO) snapshots. Creates realistic market activity with configurable order rates and price movements.</p>
|
| 193 |
-
<table>
|
| 194 |
-
<tr><th>Function</th><td>Market simulation and BBO publishing</td></tr>
|
| 195 |
-
<tr><th>Technology</th><td>Python</td></tr>
|
| 196 |
-
<tr><th>Output</th><td>Kafka <code>orders</code> and <code>snapshots</code> topics</td></tr>
|
| 197 |
-
<tr><th>Order Mix</th><td>90% passive (book building), 10% aggressive (trades)</td></tr>
|
| 198 |
-
<tr><th>Rate</th><td>Configurable (default: 8 orders/minute)</td></tr>
|
| 199 |
-
</table>
|
| 200 |
-
</div>
|
| 201 |
-
|
| 202 |
-
<div class="module-box">
|
| 203 |
-
<h4>Dashboard</h4>
|
| 204 |
-
<p><span class="port">Port 5005</span></p>
|
| 205 |
-
<p>Real-time web dashboard providing comprehensive market visualization. Uses Server-Sent Events (SSE) for live streaming updates without page refresh. Displays orders, trades, market snapshots, order book depth, and trading statistics.</p>
|
| 206 |
-
<table>
|
| 207 |
-
<tr><th>Function</th><td>Real-time market visualization and order management</td></tr>
|
| 208 |
-
<tr><th>Technology</th><td>Python/Flask + JavaScript</td></tr>
|
| 209 |
-
<tr><th>Streaming</th><td>Server-Sent Events (SSE)</td></tr>
|
| 210 |
-
<tr><th>Input</th><td>Kafka topics + Matcher REST API</td></tr>
|
| 211 |
-
<tr><th>Features</th><td>Orders, Trades, BBO, Order Book, Charts, Statistics</td></tr>
|
| 212 |
-
</table>
|
| 213 |
-
</div>
|
| 214 |
-
|
| 215 |
-
<div class="module-box">
|
| 216 |
-
<h4>Frontend (Manual Order Entry)</h4>
|
| 217 |
-
<p><span class="port">Port 5000</span></p>
|
| 218 |
-
<p>Simple web interface for manual order submission. Allows users to enter orders directly without FIX protocol, useful for testing and demonstration.</p>
|
| 219 |
-
<table>
|
| 220 |
-
<tr><th>Function</th><td>Manual order entry interface</td></tr>
|
| 221 |
-
<tr><th>Technology</th><td>Python/Flask</td></tr>
|
| 222 |
-
<tr><th>Output</th><td>Kafka <code>orders</code> topic</td></tr>
|
| 223 |
-
</table>
|
| 224 |
-
</div>
|
| 225 |
-
|
| 226 |
-
<div class="module-box">
|
| 227 |
-
<h4>Consumer (Debug)</h4>
|
| 228 |
-
<p><span class="port">Internal</span></p>
|
| 229 |
-
<p>Debug utility that consumes and logs messages from Kafka topics. Useful for monitoring message flow and troubleshooting.</p>
|
| 230 |
-
</div>
|
| 231 |
-
|
| 232 |
-
<div class="module-box">
|
| 233 |
-
<h4>Snapshot Viewer</h4>
|
| 234 |
-
<p><span class="port">Internal</span></p>
|
| 235 |
-
<p>Utility service that subscribes to the <code>snapshots</code> topic and logs BBO updates. Writes to log files for analysis.</p>
|
| 236 |
-
</div>
|
| 237 |
-
</div>
|
| 238 |
-
|
| 239 |
-
<div class="section">
|
| 240 |
-
<h2>4. Dashboard User Interface</h2>
|
| 241 |
-
|
| 242 |
-
<h3>4.1 Orders Panel</h3>
|
| 243 |
-
<p>Displays real-time incoming orders with full management capabilities.</p>
|
| 244 |
-
<table>
|
| 245 |
-
<tr><th>Column</th><th>Description</th></tr>
|
| 246 |
-
<tr><td>Symbol</td><td>Stock ticker (ALPHA, EXAE, PEIR, QUEST)</td></tr>
|
| 247 |
-
<tr><td>Side</td><td><span class="green">BUY</span> (green) or <span class="red">SELL</span> (red)</td></tr>
|
| 248 |
-
<tr><td>Qty</td><td>Order quantity in shares</td></tr>
|
| 249 |
-
<tr><td>Price</td><td>Limit price</td></tr>
|
| 250 |
-
<tr><td>Source</td><td>Order origin (MDF, FIX, Manual)</td></tr>
|
| 251 |
-
<tr><td>Time</td><td>Order timestamp</td></tr>
|
| 252 |
-
<tr><td>Actions</td><td><span class="blue">Edit</span> / <span class="red">Cancel</span> buttons</td></tr>
|
| 253 |
-
</table>
|
| 254 |
-
<p><strong>Row Selection:</strong> Click any row to select, then use header buttons for bulk actions.</p>
|
| 255 |
-
|
| 256 |
-
<h3>4.2 Market Snapshot</h3>
|
| 257 |
-
<p>Shows Best Bid/Offer (BBO) for all securities with real-time updates.</p>
|
| 258 |
-
<table>
|
| 259 |
-
<tr><th>Column</th><th>Description</th></tr>
|
| 260 |
-
<tr><td>Symbol</td><td>Security identifier</td></tr>
|
| 261 |
-
<tr><td>Best Bid</td><td>Highest buy price <span class="green">(green)</span></td></tr>
|
| 262 |
-
<tr><td>Best Ask</td><td>Lowest sell price <span class="red">(red)</span></td></tr>
|
| 263 |
-
<tr><td>Spread</td><td>Ask - Bid difference</td></tr>
|
| 264 |
-
<tr><td>Mid</td><td>Midpoint: (Bid + Ask) / 2</td></tr>
|
| 265 |
-
<tr><td>Updated</td><td>Last update timestamp</td></tr>
|
| 266 |
-
</table>
|
| 267 |
-
<p><strong>Ticker Tape:</strong> Scrolling bar at bottom displays recent trades with price direction (β² green up, βΌ red down).</p>
|
| 268 |
-
|
| 269 |
-
<h3>4.3 Trades Panel</h3>
|
| 270 |
-
<p>Lists all executed trades with calculated values.</p>
|
| 271 |
-
<table>
|
| 272 |
-
<tr><th>Column</th><th>Description</th></tr>
|
| 273 |
-
<tr><td>Symbol</td><td>Traded security</td></tr>
|
| 274 |
-
<tr><td>Qty</td><td>Executed quantity</td></tr>
|
| 275 |
-
<tr><td>Price</td><td>Execution price</td></tr>
|
| 276 |
-
<tr><td>Value</td><td>Trade value (Qty Γ Price)</td></tr>
|
| 277 |
-
<tr><td>Time</td><td>Execution timestamp</td></tr>
|
| 278 |
-
</table>
|
| 279 |
-
|
| 280 |
-
<h3>4.4 Order Book</h3>
|
| 281 |
-
<p>Displays full market depth for selected symbol.</p>
|
| 282 |
-
<ul>
|
| 283 |
-
<li>Select symbol from dropdown menu</li>
|
| 284 |
-
<li>Click <strong>Refresh</strong> to update (auto-refreshes every 3 seconds)</li>
|
| 285 |
-
<li><span class="green">Bid Qty / Bid Price</span> β Buy orders sorted by price (highest first)</li>
|
| 286 |
-
<li><span class="red">Ask Price / Ask Qty</span> β Sell orders sorted by price (lowest first)</li>
|
| 287 |
-
<li>Header shows total bid and ask counts</li>
|
| 288 |
-
</ul>
|
| 289 |
-
|
| 290 |
-
<h3>4.5 Trade Chart</h3>
|
| 291 |
-
<p>Visual representation of trade activity over time.</p>
|
| 292 |
-
<ul>
|
| 293 |
-
<li><span class="green">Green line</span> β Price trend connecting trade prices</li>
|
| 294 |
-
<li><span class="blue">Blue bars</span> β Volume per trade</li>
|
| 295 |
-
<li>Dropdown to filter by symbol or view all</li>
|
| 296 |
-
<li>Displays last 100 trades</li>
|
| 297 |
-
</ul>
|
| 298 |
-
|
| 299 |
-
<h3>4.6 Trading Statistics</h3>
|
| 300 |
-
<p>Aggregated metrics calculated from all trades.</p>
|
| 301 |
-
<table>
|
| 302 |
-
<tr><th>Metric</th><th>Description</th></tr>
|
| 303 |
-
<tr><td>Trades</td><td>Number of executed trades</td></tr>
|
| 304 |
-
<tr><td>Volume</td><td>Total shares traded</td></tr>
|
| 305 |
-
<tr><td>Value</td><td>Total monetary value (Ξ£ Qty Γ Price)</td></tr>
|
| 306 |
-
<tr><td>Start</td><td>First trade price (opening)</td></tr>
|
| 307 |
-
<tr><td>Last</td><td>Most recent trade price</td></tr>
|
| 308 |
-
<tr><td>VWAP</td><td>Volume-Weighted Average Price</td></tr>
|
| 309 |
-
</table>
|
| 310 |
-
<p><strong>Bar Charts:</strong> Visual comparison showing <span class="green">Volume</span> and <span class="blue">Value</span> per symbol side by side.</p>
|
| 311 |
-
</div>
|
| 312 |
-
|
| 313 |
-
<div class="section">
|
| 314 |
-
<h2>5. Data Flow & Messaging</h2>
|
| 315 |
-
|
| 316 |
-
<h3>5.1 Kafka Topics</h3>
|
| 317 |
-
<table>
|
| 318 |
-
<tr><th>Topic</th><th>Producers</th><th>Consumers</th><th>Content</th></tr>
|
| 319 |
-
<tr><td><code>orders</code></td><td>FIX OEG, MDF, Frontend</td><td>Matcher, Dashboard</td><td>New orders, cancels, amends</td></tr>
|
| 320 |
-
<tr><td><code>trades</code></td><td>Matcher</td><td>Dashboard, Consumer</td><td>Executed trades</td></tr>
|
| 321 |
-
<tr><td><code>snapshots</code></td><td>MDF</td><td>Dashboard, Snapshot Viewer</td><td>BBO updates</td></tr>
|
| 322 |
-
</table>
|
| 323 |
-
|
| 324 |
-
<h3>5.2 Message Formats</h3>
|
| 325 |
-
|
| 326 |
-
<h4>Order Message</h4>
|
| 327 |
-
<div class="arch-diagram" style="font-size: 11px;">{
|
| 328 |
-
"symbol": "ALPHA",
|
| 329 |
-
"side": "BUY",
|
| 330 |
-
"price": 25.50,
|
| 331 |
-
"quantity": 100,
|
| 332 |
-
"cl_ord_id": "MDF-1234567890-1",
|
| 333 |
-
"timestamp": 1234567890.123,
|
| 334 |
-
"source": "MDF"
|
| 335 |
-
}</div>
|
| 336 |
-
|
| 337 |
-
<h4>Trade Message</h4>
|
| 338 |
-
<div class="arch-diagram" style="font-size: 11px;">{
|
| 339 |
-
"symbol": "ALPHA",
|
| 340 |
-
"price": 25.50,
|
| 341 |
-
"quantity": 100,
|
| 342 |
-
"buy_order_id": "order-123",
|
| 343 |
-
"sell_order_id": "order-456",
|
| 344 |
-
"timestamp": 1234567890.123
|
| 345 |
-
}</div>
|
| 346 |
-
|
| 347 |
-
<h4>Snapshot Message</h4>
|
| 348 |
-
<div class="arch-diagram" style="font-size: 11px;">{
|
| 349 |
-
"symbol": "ALPHA",
|
| 350 |
-
"best_bid": 25.45,
|
| 351 |
-
"best_ask": 25.55,
|
| 352 |
-
"bid_size": 500,
|
| 353 |
-
"ask_size": 300,
|
| 354 |
-
"timestamp": 1234567890.123,
|
| 355 |
-
"source": "MDF"
|
| 356 |
-
}</div>
|
| 357 |
-
</div>
|
| 358 |
-
|
| 359 |
-
<div class="section">
|
| 360 |
-
<h2>6. Configuration</h2>
|
| 361 |
-
|
| 362 |
-
<table>
|
| 363 |
-
<tr><th>Variable</th><th>Default</th><th>Description</th></tr>
|
| 364 |
-
<tr><td><code>KAFKA_BOOTSTRAP</code></td><td>kafka:9092</td><td>Kafka broker address</td></tr>
|
| 365 |
-
<tr><td><code>MATCHER_URL</code></td><td>http://matcher:6000</td><td>Matcher API endpoint</td></tr>
|
| 366 |
-
<tr><td><code>TICK_SIZE</code></td><td>0.05</td><td>Minimum price increment</td></tr>
|
| 367 |
-
<tr><td><code>ORDERS_PER_MIN</code></td><td>8</td><td>MDF order generation rate</td></tr>
|
| 368 |
-
<tr><td><code>SECURITIES_FILE</code></td><td>/app/data/securities.txt</td><td>Securities configuration</td></tr>
|
| 369 |
-
</table>
|
| 370 |
-
|
| 371 |
-
<h3>6.1 Supported Securities</h3>
|
| 372 |
-
<table>
|
| 373 |
-
<tr><th>Symbol</th><th>Start Price</th><th>Description</th></tr>
|
| 374 |
-
<tr><td>ALPHA</td><td>25.00</td><td>Test Security A</td></tr>
|
| 375 |
-
<tr><td>EXAE</td><td>42.00</td><td>Test Security B</td></tr>
|
| 376 |
-
<tr><td>PEIR</td><td>18.50</td><td>Test Security C</td></tr>
|
| 377 |
-
<tr><td>QUEST</td><td>12.75</td><td>Test Security D</td></tr>
|
| 378 |
-
</table>
|
| 379 |
-
</div>
|
| 380 |
-
|
| 381 |
-
<div class="section">
|
| 382 |
-
<h2>7. Quick Reference</h2>
|
| 383 |
-
|
| 384 |
-
<h3>7.1 Starting the System</h3>
|
| 385 |
-
<div class="highlight">
|
| 386 |
-
<code>docker compose up --build</code>
|
| 387 |
-
</div>
|
| 388 |
-
|
| 389 |
-
<h3>7.2 Service URLs</h3>
|
| 390 |
-
<table>
|
| 391 |
-
<tr><th>Service</th><th>URL</th><th>Purpose</th></tr>
|
| 392 |
-
<tr><td>Dashboard</td><td>http://localhost:5005</td><td>Main trading view</td></tr>
|
| 393 |
-
<tr><td>Frontend</td><td>http://localhost:5000</td><td>Manual order entry</td></tr>
|
| 394 |
-
<tr><td>FIX Client 1</td><td>http://localhost:5002</td><td>FIX order submission</td></tr>
|
| 395 |
-
<tr><td>FIX Client 2</td><td>http://localhost:5003</td><td>FIX order submission</td></tr>
|
| 396 |
-
<tr><td>Matcher API</td><td>http://localhost:6000</td><td>REST API</td></tr>
|
| 397 |
-
</table>
|
| 398 |
-
|
| 399 |
-
<h3>7.3 Dashboard Actions</h3>
|
| 400 |
-
<table>
|
| 401 |
-
<tr><th>Action</th><th>How To</th></tr>
|
| 402 |
-
<tr><td>View order book depth</td><td>Select symbol from Order Book dropdown</td></tr>
|
| 403 |
-
<tr><td>Edit an order</td><td>Click <span class="blue">Edit</span> button on order row</td></tr>
|
| 404 |
-
<tr><td>Cancel an order</td><td>Click <span class="red">Cancel</span> button on order row</td></tr>
|
| 405 |
-
<tr><td>Filter trade chart</td><td>Select symbol from Trade Chart dropdown</td></tr>
|
| 406 |
-
<tr><td>Pause ticker tape</td><td>Hover mouse over the ticker</td></tr>
|
| 407 |
-
<tr><td>Select multiple orders</td><td>Click rows, use header Edit/Cancel buttons</td></tr>
|
| 408 |
-
</table>
|
| 409 |
-
|
| 410 |
-
<h3>7.4 Connection Status Indicators</h3>
|
| 411 |
-
<table>
|
| 412 |
-
<tr><th>Status</th><th>Indicator</th><th>Meaning</th></tr>
|
| 413 |
-
<tr><td>Live</td><td><span class="green">β Green</span></td><td>Connected to real-time stream</td></tr>
|
| 414 |
-
<tr><td>Connecting</td><td><span style="color:#ffc107;">β Yellow</span></td><td>Establishing connection</td></tr>
|
| 415 |
-
<tr><td>Disconnected</td><td><span class="red">β Red</span></td><td>Connection lost, auto-reconnecting</td></tr>
|
| 416 |
-
</table>
|
| 417 |
-
</div>
|
| 418 |
-
|
| 419 |
-
<hr>
|
| 420 |
-
|
| 421 |
-
<div class="footer">
|
| 422 |
-
<p><strong>StockEx v1.0</strong> β Trading Simulation Platform</p>
|
| 423 |
-
<p>Inspired by Euronext OPTIQ</p>
|
| 424 |
-
<p style="margin-top: 15px; font-size: 11px;">To create PDF: Open in browser β Print (Ctrl+P) β Save as PDF</p>
|
| 425 |
-
</div>
|
| 426 |
-
|
| 427 |
-
</body>
|
| 428 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dashboard/templates/index - Copy (6).html
DELETED
|
@@ -1,198 +0,0 @@
|
|
| 1 |
-
<!doctype html>
|
| 2 |
-
<html>
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="utf-8"/>
|
| 5 |
-
<title>Trading Dashboard</title>
|
| 6 |
-
<style>
|
| 7 |
-
body { font-family: Arial, sans-serif; margin: 20px; background: #f7f7f7; }
|
| 8 |
-
h2 { margin: 5px 0; }
|
| 9 |
-
.container { display: grid; grid-template-columns: 1fr 1fr; grid-gap: 20px; }
|
| 10 |
-
.panel {
|
| 11 |
-
background: #fff;
|
| 12 |
-
border-radius: 8px;
|
| 13 |
-
padding: 10px;
|
| 14 |
-
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
| 15 |
-
height: 400px;
|
| 16 |
-
display: flex;
|
| 17 |
-
flex-direction: column;
|
| 18 |
-
}
|
| 19 |
-
.panel pre {
|
| 20 |
-
flex-grow: 1;
|
| 21 |
-
overflow-y: scroll;
|
| 22 |
-
background: #fafafa;
|
| 23 |
-
padding: 10px;
|
| 24 |
-
margin: 0;
|
| 25 |
-
border-radius: 6px;
|
| 26 |
-
font-size: 13px;
|
| 27 |
-
white-space: pre;
|
| 28 |
-
font-family: monospace;
|
| 29 |
-
}
|
| 30 |
-
table {
|
| 31 |
-
width: 100%;
|
| 32 |
-
border-collapse: collapse;
|
| 33 |
-
font-size: 13px;
|
| 34 |
-
}
|
| 35 |
-
th, td {
|
| 36 |
-
border: 1px solid #ccc;
|
| 37 |
-
padding: 4px;
|
| 38 |
-
text-align: center;
|
| 39 |
-
}
|
| 40 |
-
.updated {
|
| 41 |
-
animation: flash 1s ease-in-out;
|
| 42 |
-
}
|
| 43 |
-
@keyframes flash {
|
| 44 |
-
from { background: yellow; }
|
| 45 |
-
to { background: transparent; }
|
| 46 |
-
}
|
| 47 |
-
</style>
|
| 48 |
-
</head>
|
| 49 |
-
<body>
|
| 50 |
-
<h1>π Trading Dashboard</h1>
|
| 51 |
-
<div class="container">
|
| 52 |
-
<div class="panel">
|
| 53 |
-
<h2>π Orders</h2>
|
| 54 |
-
<pre id="orders"></pre>
|
| 55 |
-
</div>
|
| 56 |
-
|
| 57 |
-
<div class="panel">
|
| 58 |
-
<h2>πΉ BBOs (from Order Book API)</h2>
|
| 59 |
-
<pre id="book"></pre>
|
| 60 |
-
</div>
|
| 61 |
-
|
| 62 |
-
<div class="panel">
|
| 63 |
-
<h2>π€ Trades</h2>
|
| 64 |
-
<pre id="trades"></pre>
|
| 65 |
-
</div>
|
| 66 |
-
|
| 67 |
-
<div class="panel">
|
| 68 |
-
<h2>π Full Order Book</h2>
|
| 69 |
-
<label for="symbol-select">Select symbol:</label>
|
| 70 |
-
<select id="symbol-select" onchange="refresh()"></select>
|
| 71 |
-
<div id="full-book" style="flex-grow:1; overflow-y:scroll;"></div>
|
| 72 |
-
</div>
|
| 73 |
-
|
| 74 |
-
<div class="panel">
|
| 75 |
-
<h2>π Market Snapshots (BBO)</h2>
|
| 76 |
-
<table id="bbos-table">
|
| 77 |
-
<thead>
|
| 78 |
-
<tr>
|
| 79 |
-
<th>Symbol</th>
|
| 80 |
-
<th>Best Bid</th>
|
| 81 |
-
<th>Best Ask</th>
|
| 82 |
-
<th>Timestamp</th>
|
| 83 |
-
<th>Source</th>
|
| 84 |
-
</tr>
|
| 85 |
-
</thead>
|
| 86 |
-
<tbody id="bbos-body"></tbody>
|
| 87 |
-
</table>
|
| 88 |
-
</div>
|
| 89 |
-
</div>
|
| 90 |
-
|
| 91 |
-
<script>
|
| 92 |
-
function fmtOrder(o) {
|
| 93 |
-
const sym = (o.symbol ?? "?").padEnd(6);
|
| 94 |
-
const side = (o.type ?? o.side ?? "?").padEnd(6);
|
| 95 |
-
const qty = String(o.quantity ?? o.qty ?? "?").padStart(6);
|
| 96 |
-
const price = o.price !== undefined ? Number(o.price).toFixed(2).padStart(8) : " - ";
|
| 97 |
-
const src = (o.source ?? "?").padEnd(5);
|
| 98 |
-
const ts = o.timestamp ? new Date(o.timestamp * 1000).toLocaleTimeString() : "-";
|
| 99 |
-
return `${sym} | ${side} | ${qty} @ ${price} | ${src} | ${ts}\n`;
|
| 100 |
-
}
|
| 101 |
-
|
| 102 |
-
function fmtTrade(t) {
|
| 103 |
-
const sym = (t.symbol ?? "?").padEnd(6);
|
| 104 |
-
const qty = String(t.quantity ?? t.qty ?? "-").padStart(6);
|
| 105 |
-
const price = t.price !== undefined ? Number(t.price).toFixed(2).padStart(8) : " - ";
|
| 106 |
-
const ts = t.timestamp ? new Date(t.timestamp * 1000).toLocaleTimeString() : "-";
|
| 107 |
-
return `${sym} | ${qty} x ${price} | ${ts}\n`;
|
| 108 |
-
}
|
| 109 |
-
|
| 110 |
-
function renderOrderBook(book) {
|
| 111 |
-
const bids = (book.buy || []).sort((a,b) => b.price - a.price);
|
| 112 |
-
const asks = (book.sell || []).sort((a,b) => a.price - b.price);
|
| 113 |
-
const maxRows = Math.max(bids.length, asks.length);
|
| 114 |
-
let html = "<table><tr><th>Bid Size</th><th>Bid Price</th><th>Ask Price</th><th>Ask Size</th></tr>";
|
| 115 |
-
for (let i=0; i<maxRows; i++) {
|
| 116 |
-
const b = bids[i] || {};
|
| 117 |
-
const a = asks[i] || {};
|
| 118 |
-
html += `<tr>
|
| 119 |
-
<td>${b.quantity ?? ""}</td>
|
| 120 |
-
<td>${b.price !== undefined ? Number(b.price).toFixed(2) : ""}</td>
|
| 121 |
-
<td>${a.price !== undefined ? Number(a.price).toFixed(2) : ""}</td>
|
| 122 |
-
<td>${a.quantity ?? ""}</td>
|
| 123 |
-
</tr>`;
|
| 124 |
-
}
|
| 125 |
-
html += "</table>";
|
| 126 |
-
return html;
|
| 127 |
-
}
|
| 128 |
-
|
| 129 |
-
async function refresh() {
|
| 130 |
-
try {
|
| 131 |
-
const r = await fetch("/data");
|
| 132 |
-
const data = await r.json();
|
| 133 |
-
|
| 134 |
-
// Orders
|
| 135 |
-
document.getElementById("orders").textContent =
|
| 136 |
-
data.orders.slice().reverse().map(fmtOrder).join("");
|
| 137 |
-
|
| 138 |
-
// Trades
|
| 139 |
-
document.getElementById("trades").textContent =
|
| 140 |
-
data.trades.slice().reverse().map(fmtTrade).join("");
|
| 141 |
-
|
| 142 |
-
// Book JSON dump
|
| 143 |
-
document.getElementById("book").textContent =
|
| 144 |
-
JSON.stringify(data.book ?? {}, null, 2);
|
| 145 |
-
|
| 146 |
-
// Snapshots β fill table
|
| 147 |
-
const tbody = document.getElementById("bbos-body");
|
| 148 |
-
tbody.innerHTML = "";
|
| 149 |
-
for (const [symbol, snap] of Object.entries(data.bbos)) {
|
| 150 |
-
const row = document.createElement("tr");
|
| 151 |
-
row.innerHTML = `
|
| 152 |
-
<td>${symbol}</td>
|
| 153 |
-
<td>${snap.best_bid !== null ? Number(snap.best_bid).toFixed(2) : "-"}</td>
|
| 154 |
-
<td>${snap.best_ask !== null ? Number(snap.best_ask).toFixed(2) : "-"}</td>
|
| 155 |
-
<td>${snap.timestamp ? new Date(snap.timestamp*1000).toLocaleTimeString() : "-"}</td>
|
| 156 |
-
<td>${snap.source ?? "-"}</td>
|
| 157 |
-
`;
|
| 158 |
-
row.classList.add("updated");
|
| 159 |
-
setTimeout(() => row.classList.remove("updated"), 1000);
|
| 160 |
-
tbody.appendChild(row);
|
| 161 |
-
}
|
| 162 |
-
|
| 163 |
-
// Populate dropdown once
|
| 164 |
-
const sel = document.getElementById("symbol-select");
|
| 165 |
-
if (!sel.options.length) {
|
| 166 |
-
const symbols = [...new Set([
|
| 167 |
-
...Object.keys(data.bbos),
|
| 168 |
-
...(data.book?.buy || []).map(o => o.symbol),
|
| 169 |
-
...(data.book?.sell || []).map(o => o.symbol)
|
| 170 |
-
])];
|
| 171 |
-
symbols.forEach(sym => {
|
| 172 |
-
const opt = document.createElement("option");
|
| 173 |
-
opt.value = sym;
|
| 174 |
-
opt.textContent = sym;
|
| 175 |
-
sel.appendChild(opt);
|
| 176 |
-
});
|
| 177 |
-
}
|
| 178 |
-
|
| 179 |
-
// Render order book for selected symbol
|
| 180 |
-
const sym = sel.value;
|
| 181 |
-
if (sym) {
|
| 182 |
-
const bookForSymbol = {
|
| 183 |
-
buy: (data.book?.buy || []).filter(o => o.symbol === sym),
|
| 184 |
-
sell: (data.book?.sell || []).filter(o => o.symbol === sym),
|
| 185 |
-
};
|
| 186 |
-
document.getElementById("full-book").innerHTML = renderOrderBook(bookForSymbol);
|
| 187 |
-
}
|
| 188 |
-
|
| 189 |
-
} catch(e) {
|
| 190 |
-
console.error("Refresh error", e);
|
| 191 |
-
}
|
| 192 |
-
}
|
| 193 |
-
|
| 194 |
-
setInterval(refresh, 2000);
|
| 195 |
-
refresh();
|
| 196 |
-
</script>
|
| 197 |
-
</body>
|
| 198 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dashboard/templates/index_Matcher.html
DELETED
|
@@ -1,194 +0,0 @@
|
|
| 1 |
-
<!doctype html>
|
| 2 |
-
<html>
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="utf-8"/>
|
| 5 |
-
<title>Trading Dashboard</title>
|
| 6 |
-
<style>
|
| 7 |
-
body { font-family: Arial, sans-serif; margin: 20px; background: #f7f7f7; }
|
| 8 |
-
h2 { margin: 5px 0; }
|
| 9 |
-
.container { display: grid; grid-template-columns: 1fr 1fr; grid-gap: 20px; }
|
| 10 |
-
.panel {
|
| 11 |
-
background: #fff;
|
| 12 |
-
border-radius: 8px;
|
| 13 |
-
padding: 10px;
|
| 14 |
-
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
| 15 |
-
height: 400px;
|
| 16 |
-
display: flex;
|
| 17 |
-
flex-direction: column;
|
| 18 |
-
}
|
| 19 |
-
.panel pre {
|
| 20 |
-
flex-grow: 1;
|
| 21 |
-
overflow-y: scroll;
|
| 22 |
-
background: #fafafa;
|
| 23 |
-
padding: 10px;
|
| 24 |
-
margin: 0;
|
| 25 |
-
border-radius: 6px;
|
| 26 |
-
font-size: 13px;
|
| 27 |
-
white-space: pre;
|
| 28 |
-
font-family: monospace;
|
| 29 |
-
}
|
| 30 |
-
table {
|
| 31 |
-
width: 100%;
|
| 32 |
-
border-collapse: collapse;
|
| 33 |
-
font-size: 13px;
|
| 34 |
-
}
|
| 35 |
-
th, td {
|
| 36 |
-
border: 1px solid #ccc;
|
| 37 |
-
padding: 4px;
|
| 38 |
-
text-align: center;
|
| 39 |
-
}
|
| 40 |
-
.updated {
|
| 41 |
-
animation: flash 1s ease-in-out;
|
| 42 |
-
}
|
| 43 |
-
@keyframes flash {
|
| 44 |
-
from { background: yellow; }
|
| 45 |
-
to { background: transparent; }
|
| 46 |
-
}
|
| 47 |
-
</style>
|
| 48 |
-
</head>
|
| 49 |
-
<body>
|
| 50 |
-
<h1>π Trading Dashboard</h1>
|
| 51 |
-
<div class="container">
|
| 52 |
-
<div class="panel">
|
| 53 |
-
<h2>π Orders</h2>
|
| 54 |
-
<pre id="orders"></pre>
|
| 55 |
-
</div>
|
| 56 |
-
|
| 57 |
-
<div class="panel">
|
| 58 |
-
<h2>πΉ BBOs (from Order Book API)</h2>
|
| 59 |
-
<pre id="book"></pre>
|
| 60 |
-
</div>
|
| 61 |
-
|
| 62 |
-
<div class="panel">
|
| 63 |
-
<h2>π€ Trades</h2>
|
| 64 |
-
<pre id="trades"></pre>
|
| 65 |
-
</div>
|
| 66 |
-
|
| 67 |
-
<div class="panel">
|
| 68 |
-
<h2>π Full Order Book</h2>
|
| 69 |
-
<label for="symbol-select">Select symbol:</label>
|
| 70 |
-
<select id="symbol-select" onchange="refresh()"></select>
|
| 71 |
-
<div id="full-book" style="flex-grow:1; overflow-y:scroll;"></div>
|
| 72 |
-
</div>
|
| 73 |
-
|
| 74 |
-
<div class="panel">
|
| 75 |
-
<h2>π Market Snapshots (BBO)</h2>
|
| 76 |
-
<table id="bbos-table">
|
| 77 |
-
<thead>
|
| 78 |
-
<tr>
|
| 79 |
-
<th>Symbol</th>
|
| 80 |
-
<th>Best Bid</th>
|
| 81 |
-
<th>Best Ask</th>
|
| 82 |
-
<th>Timestamp</th>
|
| 83 |
-
</tr>
|
| 84 |
-
</thead>
|
| 85 |
-
<tbody id="bbos-body"></tbody>
|
| 86 |
-
</table>
|
| 87 |
-
</div>
|
| 88 |
-
</div>
|
| 89 |
-
|
| 90 |
-
<script>
|
| 91 |
-
function fmtOrder(o) {
|
| 92 |
-
const sym = o.symbol ?? "?";
|
| 93 |
-
const side = o.type ?? o.side ?? "?";
|
| 94 |
-
const price = o.price !== undefined ? Number(o.price).toFixed(2) : "?";
|
| 95 |
-
const qty = o.quantity ?? o.qty ?? "?";
|
| 96 |
-
return `${sym} | ${side} | ${qty} @ ${price}\n`;
|
| 97 |
-
}
|
| 98 |
-
|
| 99 |
-
function fmtTrade(t) {
|
| 100 |
-
const sym = (t.symbol ?? "?").padEnd(6);
|
| 101 |
-
const qty = String(t.quantity ?? t.qty ?? "-").padStart(6);
|
| 102 |
-
const price = t.price !== undefined ? Number(t.price).toFixed(2).padStart(8) : " - ";
|
| 103 |
-
const ts = t.timestamp ? new Date(t.timestamp * 1000).toLocaleTimeString() : "-";
|
| 104 |
-
return `${sym} | ${qty} x ${price} | ${ts}\n`;
|
| 105 |
-
}
|
| 106 |
-
|
| 107 |
-
function renderOrderBook(book) {
|
| 108 |
-
const bids = (book.buy || []).sort((a,b) => b.price - a.price);
|
| 109 |
-
const asks = (book.sell || []).sort((a,b) => a.price - b.price);
|
| 110 |
-
const maxRows = Math.max(bids.length, asks.length);
|
| 111 |
-
let html = "<table><tr><th>Bid Size</th><th>Bid Price</th><th>Ask Price</th><th>Ask Size</th></tr>";
|
| 112 |
-
for (let i=0; i<maxRows; i++) {
|
| 113 |
-
const b = bids[i] || {};
|
| 114 |
-
const a = asks[i] || {};
|
| 115 |
-
html += `<tr>
|
| 116 |
-
<td>${b.quantity ?? ""}</td>
|
| 117 |
-
<td>${b.price !== undefined ? Number(b.price).toFixed(2) : ""}</td>
|
| 118 |
-
<td>${a.price !== undefined ? Number(a.price).toFixed(2) : ""}</td>
|
| 119 |
-
<td>${a.quantity ?? ""}</td>
|
| 120 |
-
</tr>`;
|
| 121 |
-
}
|
| 122 |
-
html += "</table>";
|
| 123 |
-
return html;
|
| 124 |
-
}
|
| 125 |
-
|
| 126 |
-
async function refresh() {
|
| 127 |
-
try {
|
| 128 |
-
const r = await fetch("/data");
|
| 129 |
-
const data = await r.json();
|
| 130 |
-
|
| 131 |
-
// Orders
|
| 132 |
-
document.getElementById("orders").textContent =
|
| 133 |
-
data.orders.slice().reverse().map(fmtOrder).join("");
|
| 134 |
-
|
| 135 |
-
// Trades
|
| 136 |
-
document.getElementById("trades").textContent =
|
| 137 |
-
data.trades.slice().reverse().map(fmtTrade).join("");
|
| 138 |
-
|
| 139 |
-
// Book JSON dump
|
| 140 |
-
document.getElementById("book").textContent =
|
| 141 |
-
JSON.stringify(data.book, null, 2);
|
| 142 |
-
|
| 143 |
-
// Snapshots β fill table
|
| 144 |
-
const tbody = document.getElementById("bbos-body");
|
| 145 |
-
tbody.innerHTML = "";
|
| 146 |
-
for (const [symbol, snap] of Object.entries(data.bbos)) {
|
| 147 |
-
const row = document.createElement("tr");
|
| 148 |
-
row.innerHTML = `
|
| 149 |
-
<td>${symbol}</td>
|
| 150 |
-
<td>${snap.best_bid !== null ? Number(snap.best_bid).toFixed(2) : "-"}</td>
|
| 151 |
-
<td>${snap.best_ask !== null ? Number(snap.best_ask).toFixed(2) : "-"}</td>
|
| 152 |
-
<td>${snap.timestamp ? new Date(snap.timestamp*1000).toLocaleTimeString() : "-"}</td>
|
| 153 |
-
`;
|
| 154 |
-
row.classList.add("updated");
|
| 155 |
-
setTimeout(() => row.classList.remove("updated"), 1000);
|
| 156 |
-
tbody.appendChild(row);
|
| 157 |
-
}
|
| 158 |
-
|
| 159 |
-
// Populate dropdown once
|
| 160 |
-
const sel = document.getElementById("symbol-select");
|
| 161 |
-
if (!sel.options.length) {
|
| 162 |
-
const symbols = [...new Set([
|
| 163 |
-
...Object.keys(data.bbos),
|
| 164 |
-
...(data.book.buy || []).map(o => o.symbol),
|
| 165 |
-
...(data.book.sell || []).map(o => o.symbol)
|
| 166 |
-
])];
|
| 167 |
-
symbols.forEach(sym => {
|
| 168 |
-
const opt = document.createElement("option");
|
| 169 |
-
opt.value = sym;
|
| 170 |
-
opt.textContent = sym;
|
| 171 |
-
sel.appendChild(opt);
|
| 172 |
-
});
|
| 173 |
-
}
|
| 174 |
-
|
| 175 |
-
// Render order book for selected symbol
|
| 176 |
-
const sym = sel.value;
|
| 177 |
-
if (sym) {
|
| 178 |
-
const bookForSymbol = {
|
| 179 |
-
buy: (data.book.buy || []).filter(o => o.symbol === sym),
|
| 180 |
-
sell: (data.book.sell || []).filter(o => o.symbol === sym),
|
| 181 |
-
};
|
| 182 |
-
document.getElementById("full-book").innerHTML = renderOrderBook(bookForSymbol);
|
| 183 |
-
}
|
| 184 |
-
|
| 185 |
-
} catch(e) {
|
| 186 |
-
console.error("Refresh error", e);
|
| 187 |
-
}
|
| 188 |
-
}
|
| 189 |
-
|
| 190 |
-
setInterval(refresh, 2000);
|
| 191 |
-
refresh();
|
| 192 |
-
</script>
|
| 193 |
-
</body>
|
| 194 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
matcher/matcher - Copy.py
DELETED
|
@@ -1,135 +0,0 @@
|
|
| 1 |
-
ο»Ώimport threading, time, json
|
| 2 |
-
from flask import Flask, jsonify
|
| 3 |
-
from kafka import KafkaConsumer, KafkaProducer
|
| 4 |
-
from kafka.errors import NoBrokersAvailable
|
| 5 |
-
|
| 6 |
-
BOOTSTRAP = 'kafka:9092'
|
| 7 |
-
|
| 8 |
-
import json, sys, datetime
|
| 9 |
-
def jlog(event_type, payload):
|
| 10 |
-
out = {
|
| 11 |
-
"ts": datetime.datetime.utcnow().isoformat() + "Z",
|
| 12 |
-
"component": "matcher",
|
| 13 |
-
"event": event_type,
|
| 14 |
-
"payload": payload
|
| 15 |
-
}
|
| 16 |
-
sys.stdout.write(json.dumps(out, default=str) + "\n")
|
| 17 |
-
sys.stdout.flush()
|
| 18 |
-
|
| 19 |
-
app = Flask(__name__)
|
| 20 |
-
|
| 21 |
-
order_book = {'buy': [], 'sell': []}
|
| 22 |
-
trades = []
|
| 23 |
-
producer = None
|
| 24 |
-
|
| 25 |
-
def create_kafka_producer(retries=20, delay=2):
|
| 26 |
-
global producer
|
| 27 |
-
for i in range(retries):
|
| 28 |
-
try:
|
| 29 |
-
producer = KafkaProducer(
|
| 30 |
-
bootstrap_servers=BOOTSTRAP,
|
| 31 |
-
value_serializer=lambda v: json.dumps(v).encode('utf-8')
|
| 32 |
-
)
|
| 33 |
-
print('Producer connected')
|
| 34 |
-
return
|
| 35 |
-
except NoBrokersAvailable:
|
| 36 |
-
print('Producer: Kafka not ready, retry', i)
|
| 37 |
-
time.sleep(delay)
|
| 38 |
-
raise RuntimeError('Producer: cannot connect to Kafka')
|
| 39 |
-
|
| 40 |
-
def create_kafka_consumer(topic='orders', retries=20, delay=2):
|
| 41 |
-
for i in range(retries):
|
| 42 |
-
try:
|
| 43 |
-
consumer = KafkaConsumer(
|
| 44 |
-
topic,
|
| 45 |
-
bootstrap_servers=BOOTSTRAP,
|
| 46 |
-
value_deserializer=lambda m: json.loads(m.decode('utf-8')),
|
| 47 |
-
auto_offset_reset='earliest',
|
| 48 |
-
enable_auto_commit=True,
|
| 49 |
-
group_id='matcher-group'
|
| 50 |
-
)
|
| 51 |
-
print('Consumer connected to topic', topic)
|
| 52 |
-
return consumer
|
| 53 |
-
except NoBrokersAvailable:
|
| 54 |
-
print('Consumer: Kafka not ready, retry', i)
|
| 55 |
-
time.sleep(delay)
|
| 56 |
-
raise RuntimeError('Consumer: cannot connect to Kafka')
|
| 57 |
-
|
| 58 |
-
def find_match(order):
|
| 59 |
-
side = order['type']
|
| 60 |
-
opp = 'sell' if side == 'buy' else 'buy'
|
| 61 |
-
candidates = [o for o in order_book[opp] if o['symbol'] == order['symbol'] and o['quantity'] > 0]
|
| 62 |
-
if not candidates:
|
| 63 |
-
return None
|
| 64 |
-
# best-price matching: for buy -> lowest sell price; for sell -> highest buy price
|
| 65 |
-
if side == 'buy':
|
| 66 |
-
valid = [o for o in candidates if o['price'] <= order['price']]
|
| 67 |
-
if not valid: return None
|
| 68 |
-
best_price = min(o['price'] for o in valid)
|
| 69 |
-
bests = [o for o in valid if o['price'] == best_price]
|
| 70 |
-
return min(bests, key=lambda x: x['timestamp'])
|
| 71 |
-
else:
|
| 72 |
-
valid = [o for o in candidates if o['price'] >= order['price']]
|
| 73 |
-
if not valid: return None
|
| 74 |
-
best_price = max(o['price'] for o in valid)
|
| 75 |
-
bests = [o for o in valid if o['price'] == best_price]
|
| 76 |
-
return min(bests, key=lambda x: x['timestamp'])
|
| 77 |
-
|
| 78 |
-
def process_order(order):
|
| 79 |
-
print('Processing order:', order)
|
| 80 |
-
while order['quantity'] > 0:
|
| 81 |
-
matched = find_match(order)
|
| 82 |
-
if not matched:
|
| 83 |
-
order_book[order['type']].append(order.copy())
|
| 84 |
-
print('Added to book:', order)
|
| 85 |
-
return
|
| 86 |
-
traded_qty = min(order['quantity'], matched['quantity'])
|
| 87 |
-
trade_price = matched['price']
|
| 88 |
-
trade = {
|
| 89 |
-
'buy_order_id': order['order_id'] if order['type']=='buy' else matched['order_id'],
|
| 90 |
-
'sell_order_id': matched['order_id'] if order['type']=='buy' else order['order_id'],
|
| 91 |
-
'symbol': order['symbol'],
|
| 92 |
-
'price': trade_price,
|
| 93 |
-
'quantity': traded_qty,
|
| 94 |
-
'timestamp': time.time()
|
| 95 |
-
}
|
| 96 |
-
trades.append(trade)
|
| 97 |
-
if producer:
|
| 98 |
-
producer.send('trades', value=trade)
|
| 99 |
-
producer.flush()
|
| 100 |
-
print('TRADE:', trade)
|
| 101 |
-
order['quantity'] -= traded_qty
|
| 102 |
-
matched['quantity'] -= traded_qty
|
| 103 |
-
if matched['quantity'] == 0:
|
| 104 |
-
try:
|
| 105 |
-
order_book['sell' if order['type']=='buy' else 'buy'].remove(matched)
|
| 106 |
-
print('Removed matched from book:', matched)
|
| 107 |
-
except ValueError:
|
| 108 |
-
pass
|
| 109 |
-
|
| 110 |
-
def consumer_loop():
|
| 111 |
-
consumer = create_kafka_consumer('orders')
|
| 112 |
-
for msg in consumer:
|
| 113 |
-
try:
|
| 114 |
-
order = msg.value
|
| 115 |
-
order['price'] = float(order['price'])
|
| 116 |
-
order['quantity'] = int(order['quantity'])
|
| 117 |
-
order.setdefault('timestamp', time.time())
|
| 118 |
-
process_order(order)
|
| 119 |
-
except Exception as e:
|
| 120 |
-
print('Error processing message:', e)
|
| 121 |
-
|
| 122 |
-
@app.route('/book')
|
| 123 |
-
def get_book():
|
| 124 |
-
# present best bids/asks sorted
|
| 125 |
-
return jsonify(order_book)
|
| 126 |
-
|
| 127 |
-
@app.route('/trades')
|
| 128 |
-
def get_trades():
|
| 129 |
-
return jsonify(trades)
|
| 130 |
-
|
| 131 |
-
if __name__ == '__main__':
|
| 132 |
-
create_kafka_producer()
|
| 133 |
-
t = threading.Thread(target=consumer_loop, daemon=True)
|
| 134 |
-
t.start()
|
| 135 |
-
app.run(host='0.0.0.0', port=6000)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|