mirror of
https://github.com/Sabermrddz/QuantCore-FX.git
synced 2026-07-27 18:47:51 +00:00
262 lines
8.9 KiB
Python
262 lines
8.9 KiB
Python
"""
|
|
APEX Professional Trading System — Main Application Window
|
|
|
|
Assembles all 6 tabs:
|
|
- Tab 1: Dashboard (Layer 1 fundamental signals)
|
|
- Tab 2: Monthly Entry (CPI + PMI data input)
|
|
- Tab 3: Layer 2 Monitor (real-time technical analysis)
|
|
- Tab 4: Confluence Signals (merged Layer 1 + Layer 2)
|
|
- Tab 5: History (past signals)
|
|
- Tab 6: Settings (configuration)
|
|
|
|
Responsibilities:
|
|
- Create QMainWindow with QTabWidget
|
|
- Instantiate all UI tabs
|
|
- Manage database connection
|
|
- Run FRED API fetch in background thread
|
|
- Run Alpha Vantage real-time data fetching
|
|
- Connect inter-tab signals
|
|
- Handle window events and cleanup
|
|
"""
|
|
|
|
from PyQt5.QtWidgets import QMainWindow, QTabWidget, QMessageBox
|
|
from PyQt5.QtCore import QThread, pyqtSignal, QTimer
|
|
from typing import Dict, Optional
|
|
import config
|
|
from database import Database
|
|
from layer2_technical import TechnicalAnalyzer
|
|
from currency_strength_matrix import CurrencyStrengthMatrix
|
|
from ui.dashboard_tab import DashboardTab
|
|
from ui.entry_tab import MonthlyEntryTab
|
|
from ui.layer2_monitor_tab import Layer2MonitorTab
|
|
from ui.confluence_tab import ConfluenceSignalsTab
|
|
from ui.history_tab import HistoryTab
|
|
from ui.settings_tab import SettingsTab
|
|
from fred_client import FredClient
|
|
|
|
|
|
class FredFetchWorker(QThread):
|
|
"""Background worker thread for fetching rates from FRED API."""
|
|
|
|
# Signals
|
|
rates_fetched = pyqtSignal(dict) # Emitted with {currency: rate} dict
|
|
error_occurred = pyqtSignal(str) # Emitted on error
|
|
|
|
def __init__(self, db: Database):
|
|
"""
|
|
Initialize FRED fetch worker.
|
|
|
|
Args:
|
|
db: Database instance to save rates
|
|
"""
|
|
super().__init__()
|
|
self.db = db
|
|
self.client = FredClient()
|
|
|
|
def run(self):
|
|
"""
|
|
Fetch rates from FRED API and save to database.
|
|
"""
|
|
try:
|
|
if config.DEBUG:
|
|
print("[Worker] Starting FRED rate fetch...")
|
|
|
|
rates = self.client.fetch_all_rates()
|
|
|
|
# Save to database
|
|
for currency, rate in rates.items():
|
|
if rate is not None:
|
|
self.db.upsert_rate(currency, rate, source="FRED")
|
|
|
|
if config.DEBUG:
|
|
print("[Worker] FRED fetch complete")
|
|
|
|
self.rates_fetched.emit(rates)
|
|
|
|
except Exception as e:
|
|
error_msg = f"FRED fetch error: {str(e)}"
|
|
if config.DEBUG:
|
|
print(f"[Worker] {error_msg}")
|
|
self.error_occurred.emit(error_msg)
|
|
|
|
|
|
class MainWindow(QMainWindow):
|
|
"""Main application window — Professional hybrid trading system."""
|
|
|
|
def __init__(self):
|
|
"""Initialize main window."""
|
|
super().__init__()
|
|
|
|
# Initialize database
|
|
try:
|
|
self.db = Database()
|
|
except Exception as e:
|
|
QMessageBox.critical(
|
|
self,
|
|
"Database Error",
|
|
f"Failed to initialize database: {e}\n\nPlease check your configuration."
|
|
)
|
|
raise
|
|
|
|
# Initialize Layer 2 components
|
|
bars = config.TIMEFRAMES[config.DEFAULT_TIMEFRAME]["bars"]
|
|
self.tech_analyzer = TechnicalAnalyzer(lookback=bars)
|
|
|
|
# UI components
|
|
self.dashboard_tab = None
|
|
self.entry_tab = None
|
|
self.layer2_tab = None
|
|
self.confluence_tab = None
|
|
self.history_tab = None
|
|
self.settings_tab = None
|
|
|
|
# Worker threads
|
|
self.fred_worker = None
|
|
|
|
self._init_ui()
|
|
self._connect_signals()
|
|
self._setup_auto_fetch()
|
|
self._setup_live_signal_timer()
|
|
|
|
def _init_ui(self):
|
|
"""Build the main window UI."""
|
|
self.setWindowTitle(config.APP_TITLE)
|
|
self.setGeometry(100, 100, config.WINDOW_WIDTH, config.WINDOW_HEIGHT)
|
|
|
|
# Tab widget
|
|
tabs = QTabWidget()
|
|
|
|
# Tab 1: Dashboard (Live + Macro Backdrop)
|
|
self.dashboard_tab = DashboardTab(self.db, tech_analyzer=self.tech_analyzer)
|
|
tabs.addTab(self.dashboard_tab, config.TAB_NAMES["dashboard"])
|
|
|
|
# Tab 2: Monthly Entry (Data input)
|
|
self.entry_tab = MonthlyEntryTab(self.db)
|
|
tabs.addTab(self.entry_tab, config.TAB_NAMES["entry"])
|
|
|
|
# Tab 3: Layer 2 Monitor (Real-time technical)
|
|
self.layer2_tab = Layer2MonitorTab(self.tech_analyzer)
|
|
tabs.addTab(self.layer2_tab, config.TAB_NAMES["layer2"])
|
|
|
|
# Tab 4: Confluence Signals (Layer 1 + Layer 2)
|
|
self.confluence_tab = ConfluenceSignalsTab(self.db, self.tech_analyzer)
|
|
tabs.addTab(self.confluence_tab, config.TAB_NAMES["confluence"])
|
|
|
|
# Tab 5: History
|
|
self.history_tab = HistoryTab(self.db)
|
|
tabs.addTab(self.history_tab, config.TAB_NAMES["history"])
|
|
|
|
# Tab 6: Settings
|
|
self.settings_tab = SettingsTab()
|
|
tabs.addTab(self.settings_tab, config.TAB_NAMES["settings"])
|
|
|
|
# Set main widget
|
|
self.setCentralWidget(tabs)
|
|
|
|
def _connect_signals(self):
|
|
"""Connect inter-tab signals."""
|
|
# Entry tab saves data → Dashboard tab refreshes
|
|
self.entry_tab.data_saved.connect(self.dashboard_tab.on_data_saved)
|
|
|
|
# Entry tab saves data → History tab refreshes
|
|
self.entry_tab.data_saved.connect(self.history_tab.refresh_history)
|
|
|
|
# Dashboard generates signal → Confluence tab receives signal
|
|
self.dashboard_tab.signal_generated.connect(self._on_dashboard_signal)
|
|
|
|
# Dashboard requests FRED fetch → Start worker thread
|
|
self.dashboard_tab.fetch_rates_requested.connect(self._fetch_rates)
|
|
|
|
def _setup_auto_fetch(self):
|
|
"""Auto-fetch rates on startup if enabled."""
|
|
if config.AUTO_FETCH_RATES_ON_STARTUP:
|
|
if config.DEBUG:
|
|
print("[Main] Auto-fetch enabled, fetching rates on startup...")
|
|
self._fetch_rates()
|
|
|
|
def _setup_live_signal_timer(self):
|
|
"""Periodically refresh the live intraday signal on the dashboard."""
|
|
self._live_signal_timer = QTimer()
|
|
self._live_signal_timer.timeout.connect(self._tick_live_signal)
|
|
self._live_signal_timer.start(3000)
|
|
|
|
def _tick_live_signal(self):
|
|
"""Refresh live signal on dashboard."""
|
|
if self.dashboard_tab:
|
|
self.dashboard_tab.update_live_signal()
|
|
|
|
def _fetch_rates(self):
|
|
"""
|
|
Trigger background FRED rate fetch.
|
|
Emits results to dashboard when complete.
|
|
"""
|
|
if self.fred_worker is not None and self.fred_worker.isRunning():
|
|
# Already fetching
|
|
return
|
|
|
|
self.fred_worker = FredFetchWorker(self.db)
|
|
self.fred_worker.rates_fetched.connect(self._on_rates_fetched)
|
|
self.fred_worker.error_occurred.connect(self._on_fetch_error)
|
|
self.fred_worker.start()
|
|
|
|
def _on_rates_fetched(self, rates: Dict[str, Optional[float]]):
|
|
"""
|
|
Handle successful FRED fetch.
|
|
|
|
Args:
|
|
rates: Dict mapping currency to rate
|
|
"""
|
|
if config.DEBUG:
|
|
print("[Main] Rates fetched successfully, updating dashboard...")
|
|
|
|
# Update dashboard display
|
|
self.dashboard_tab.on_rates_updated(rates)
|
|
|
|
def _on_fetch_error(self, error_msg: str):
|
|
"""
|
|
Handle FRED fetch error.
|
|
|
|
Args:
|
|
error_msg: Error message string
|
|
"""
|
|
print(f"[ERROR] {error_msg}")
|
|
# Don't show error message to user; display gracefully in dashboard
|
|
|
|
def _on_dashboard_signal(self, strongest: str, weakest: str, gap: float,
|
|
bias_matrix: dict = None):
|
|
"""
|
|
Handle dashboard signal generation.
|
|
Pass to confluence tab for Layer 2 analysis.
|
|
|
|
Args:
|
|
strongest: Strongest currency
|
|
weakest: Weakest currency
|
|
gap: Gap score
|
|
bias_matrix: Monthly directional bias matrix from Layer 1
|
|
"""
|
|
if config.DEBUG:
|
|
print(f"[Main] Signal generated: {strongest}/{weakest} gap={gap:.1f}")
|
|
|
|
# Update confluence tab with new Layer 1 signal + bias matrix
|
|
self.confluence_tab.set_layer1_signal(strongest, weakest, gap, bias_matrix)
|
|
|
|
def closeEvent(self, event):
|
|
"""Handle window close event."""
|
|
try:
|
|
# Stop any running threads
|
|
if self.fred_worker is not None and self.fred_worker.isRunning():
|
|
self.fred_worker.quit()
|
|
self.fred_worker.wait()
|
|
|
|
# Stop Layer 2 monitoring
|
|
if self.layer2_tab:
|
|
self.layer2_tab.closeEvent(event)
|
|
|
|
# Close database
|
|
self.db.close()
|
|
|
|
event.accept()
|
|
except Exception as e:
|
|
print(f"[ERROR] Error during shutdown: {e}")
|
|
event.accept()
|