fix: Multiple bug fixes and improvements
- Fix Invalid Date display in Dashboard notifications - Fix timezone offset (8 hours) in Trading Records time display - Fix position closing failures due to commission discrepancies (fetch actual exchange position size for reduce_only orders) - Fix IBKR connection error 'no current event loop in thread' by ensuring asyncio event loop exists - Fix duplicate orders on same candle by extending signal deduplication to close signals - Add responsive design for Profile page (mobile-friendly) - Remove unused strategy_code module and database table - Fix LLM service to support multiple providers (OpenRouter, OpenAI, DeepSeek, Grok, Google) - Add auto-detection of configured LLM provider based on API key availability - Fix AI code generation to use unified LLMService with proper provider selection - Fix crypto symbol format handling (ETH/USDT no longer becomes ETH/USDT/USDT) - Fix Commission display showing '0E-8' in Trading Records - Fix P&L display for signal-only trades (show '--' for unrealized P&L) - Fix OAuth login not updating last_login_at for new users - Add migration script for notification_settings column - Update env.example with new LLM provider configurations - Remove ESLint rule that was not defined in config
This commit is contained in:
@@ -6,6 +6,7 @@ Uses ib_insync library to connect to TWS or IB Gateway for trading.
|
||||
|
||||
import time
|
||||
import threading
|
||||
import asyncio
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, Dict, Any, List
|
||||
|
||||
@@ -14,6 +15,25 @@ from app.services.ibkr_trading.symbols import normalize_symbol, format_display_s
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
def _ensure_event_loop():
|
||||
"""
|
||||
Ensure there is an event loop in the current thread.
|
||||
|
||||
ib_insync requires an asyncio event loop to function.
|
||||
When called from Flask request threads, there may not be one.
|
||||
"""
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
if loop.is_closed():
|
||||
raise RuntimeError("Event loop is closed")
|
||||
except RuntimeError:
|
||||
# No event loop exists in this thread, create one
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
logger.debug("Created new event loop for IBKR client")
|
||||
return loop
|
||||
|
||||
# Lazy import ib_insync to allow other features to work without it installed
|
||||
ib_insync = None
|
||||
|
||||
@@ -99,6 +119,9 @@ class IBKRClient:
|
||||
return True
|
||||
|
||||
try:
|
||||
# Ensure event loop exists in this thread (required by ib_insync)
|
||||
_ensure_event_loop()
|
||||
|
||||
_ensure_ib_insync()
|
||||
|
||||
if self._ib is None:
|
||||
@@ -145,6 +168,8 @@ class IBKRClient:
|
||||
|
||||
def _ensure_connected(self):
|
||||
"""Ensure connection is established."""
|
||||
# Ensure event loop exists (may be called from different threads)
|
||||
_ensure_event_loop()
|
||||
if not self.connected:
|
||||
if not self.connect():
|
||||
raise ConnectionError("Cannot connect to IBKR")
|
||||
|
||||
Reference in New Issue
Block a user