FIX_EVENT_LOOP_ALREADY_RUNNING.md•3.68 kB
# Fix: "this event loop is already running" Error
## Issue Fixed ✅
**Error:** `ibkr_stream_market_data` returned error "this event loop is already running."
## Root Cause
The `ib_async` library's `waitOnUpdate()` method was trying to run an event loop inside an already-running event loop (the MCP server's event loop). This is not allowed in asyncio.
## Solution
Replaced `self.ib.waitOnUpdate(timeout=...)` with proper event-based waiting using `updateEvent`:
### Before (Broken):
```python
async def stream_market_data(self, req):
while True:
await self.ib.waitOnUpdate(timeout=2) # ❌ Tries to run event loop
yield data
```
### After (Fixed):
```python
async def stream_market_data(self, req):
while True:
try:
await asyncio.wait_for(
ticker.updateEvent.wait(), # ✅ Properly awaits event
timeout=2.0
)
ticker.updateEvent.clear()
except asyncio.TimeoutError:
yield {} # Keep-alive
continue
yield data
```
## Files Modified
### 1. `src/tws_client.py` ✅
- **`stream_market_data()`** - Fixed to use `ticker.updateEvent.wait()`
- **`stream_account_updates()`** - Fixed to use `ib.updateEvent.wait()`
### 2. `tests/unit/test_tws_client.py` ✅
- Updated `MockIB` to include sync methods: `positions()`, `placeOrder()`
- Updated `test_stream_market_data_generator` to mock `updateEvent`
- Updated assertions to check sync methods instead of async ones
## Changes Summary
| Method | Old Approach | New Approach |
|--------|-------------|--------------|
| `stream_market_data()` | `await self.ib.waitOnUpdate(timeout=2)` | `await asyncio.wait_for(ticker.updateEvent.wait(), 2.0)` |
| `stream_account_updates()` | `await self.ib.waitOnUpdate(timeout=5)` | `await asyncio.wait_for(self.ib.updateEvent.wait(), 5.0)` |
## Why This Works
**ib_async provides event objects** that fire when updates occur:
- `ticker.updateEvent` - Fires when market data updates
- `ib.updateEvent` - Fires when any IB object updates
These are standard `asyncio.Event` objects that can be properly awaited without trying to run a nested event loop.
## Test Results
```bash
$ uv run pytest tests/unit/test_tws_client.py -v
# Result: 6 passed in 1.23s ✅
```
All unit tests passing, including:
- ✅ `test_stream_market_data_generator` - Now properly mocks `updateEvent`
- ✅ `test_get_positions` - Updated for sync `positions()` method
- ✅ `test_place_market_order` - Updated for sync `placeOrder()` method
## Benefits
1. **✅ No event loop errors** - Properly integrates with MCP server's event loop
2. **✅ Efficient** - Uses event-based waiting instead of polling
3. **✅ Compatible with ib_async** - Uses the library's recommended pattern
4. **✅ Keep-alive support** - Yields empty dicts on timeout to prevent SSE disconnects
## Next Steps
The streaming functions should now work properly when called through MCP:
### Test Streaming
1. **Start TWS** (make sure it's on port 7497 since that worked for you)
2. **Update .env** to use port 7497:
```env
TWS_PORT=7497
```
3. **Start MCP server**:
```bash
uv run python main.py
```
4. **Connect via MCP Inspector** and test:
- `ibkr_connect` - Should work (you already tested port 7497 ✅)
- `ibkr_stream_market_data` - Should now work without event loop errors
- `ibkr_stream_account_updates` - Should also work
## Status
- ✅ Fixed "event loop already running" error
- ✅ All unit tests passing
- ✅ Connection to TWS working (port 7497)
- ⏳ Ready to test streaming through MCP
**The ibkr_stream_market_data tool should now work!** 🎉