mirror of
https://github.com/Sabermrddz/QuantCore-FX.git
synced 2026-07-27 18:47:51 +00:00
352 lines
10 KiB
Python
352 lines
10 KiB
Python
"""
|
|
APEX Layer 1 — Application Entry Point
|
|
|
|
Launches the PyQt5 application.
|
|
|
|
Usage:
|
|
python main.py
|
|
|
|
Requirements:
|
|
- Python 3.10+
|
|
- PyQt5 5.15+
|
|
- requests 2.31+
|
|
- pandas 2.0+
|
|
- python-dotenv 1.0+
|
|
- openpyxl 3.1+
|
|
|
|
Installation:
|
|
pip install -r requirements.txt
|
|
|
|
First run:
|
|
1. Ensure .env file exists with FRED_API_KEY set
|
|
2. (Optional for Layer 2) MetaTrader 5 terminal running
|
|
3. Run: python main.py
|
|
4. App initializes database with schema
|
|
5. Auto-fetches rates from FRED if AUTO_FETCH_RATES_ON_STARTUP=true
|
|
6. Ready for manual CPI/PMI entry
|
|
"""
|
|
|
|
import sys
|
|
import traceback
|
|
from PyQt5.QtWidgets import QApplication, QMessageBox
|
|
from PyQt5.QtCore import Qt
|
|
import config
|
|
from main_window import MainWindow
|
|
|
|
|
|
def main():
|
|
"""Application entry point."""
|
|
try:
|
|
# Check critical configuration
|
|
if not config.FRED_API_KEY:
|
|
print("[ERROR] FRED_API_KEY not configured in .env file")
|
|
print("Please:")
|
|
print(" 1. Go to https://fred.stlouisfed.org")
|
|
print(" 2. Register and get a free API key")
|
|
print(" 3. Add to .env: FRED_API_KEY=your_key_here")
|
|
return 1
|
|
|
|
if config.DEBUG:
|
|
print(f"[Main] {config.APP_TITLE}")
|
|
print(f"[Main] Debug: ON")
|
|
print(f"[Main] Database: {config.DB_PATH}")
|
|
|
|
# Create QApplication
|
|
app = QApplication(sys.argv)
|
|
|
|
# Set Fusion style
|
|
app.setStyle('Fusion')
|
|
|
|
# Global professional stylesheet
|
|
app.setStyleSheet(f"""
|
|
/*****************************************************************
|
|
* APEX Professional Trading System — Global Stylesheet
|
|
*****************************************************************/
|
|
|
|
/* ----- Root / Background ----- */
|
|
QMainWindow {{
|
|
background-color: #f0f2f5;
|
|
}}
|
|
QWidget {{
|
|
font-family: "Segoe UI", "Arial", sans-serif;
|
|
font-size: 13px;
|
|
color: #2c3e50;
|
|
}}
|
|
|
|
/* ----- Tab Widget (Navigation Bar) ----- */
|
|
QTabWidget::pane {{
|
|
border: none;
|
|
background: #f0f2f5;
|
|
top: -1px;
|
|
}}
|
|
QTabBar::tab {{
|
|
background: #2c3e50;
|
|
color: #95a5a6;
|
|
padding: 10px 22px;
|
|
margin-right: 2px;
|
|
border-top-left-radius: 6px;
|
|
border-top-right-radius: 6px;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
}}
|
|
QTabBar::tab:selected {{
|
|
background: #f0f2f5;
|
|
color: #2c3e50;
|
|
border-bottom: 2px solid #3498db;
|
|
}}
|
|
QTabBar::tab:hover:!selected {{
|
|
background: #34495e;
|
|
color: #ecf0f1;
|
|
}}
|
|
|
|
/* ----- Cards (QFrame) ----- */
|
|
QFrame#card {{
|
|
background-color: #ffffff;
|
|
border: 1px solid #e0e4e8;
|
|
border-radius: 10px;
|
|
padding: 18px;
|
|
}}
|
|
QFrame#card:hover {{
|
|
border-color: #c0c8d0;
|
|
}}
|
|
QFrame#statusCard {{
|
|
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
|
stop:0 #ffffff, stop:1 #f8f9fb);
|
|
border: 1px solid #e0e4e8;
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
}}
|
|
|
|
/* ----- Labels ----- */
|
|
QLabel {{
|
|
color: #2c3e50;
|
|
}}
|
|
QLabel[heading="true"] {{
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
color: #1a1a2e;
|
|
padding-bottom: 4px;
|
|
}}
|
|
QLabel[subheading="true"] {{
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: #7f8c8d;
|
|
letter-spacing: 1px;
|
|
}}
|
|
QLabel[value="true"] {{
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
}}
|
|
|
|
/* ----- Tables ----- */
|
|
QTableWidget {{
|
|
background-color: #ffffff;
|
|
border: 1px solid #e0e4e8;
|
|
border-radius: 8px;
|
|
gridline-color: #f0f2f5;
|
|
selection-background-color: #ebf5fb;
|
|
selection-color: #2c3e50;
|
|
padding: 4px;
|
|
}}
|
|
QTableWidget::item {{
|
|
padding: 6px 10px;
|
|
border-bottom: 1px solid #f0f2f5;
|
|
}}
|
|
QTableWidget::item:selected {{
|
|
background-color: #ebf5fb;
|
|
color: #2c3e50;
|
|
}}
|
|
QHeaderView::section {{
|
|
background-color: #f8f9fb;
|
|
color: #7f8c8d;
|
|
font-weight: 600;
|
|
font-size: 12px;
|
|
text-transform: uppercase;
|
|
padding: 8px 10px;
|
|
border: none;
|
|
border-bottom: 2px solid #e0e4e8;
|
|
}}
|
|
|
|
/* ----- Buttons ----- */
|
|
QPushButton {{
|
|
background-color: #3498db;
|
|
color: #ffffff;
|
|
border: none;
|
|
border-radius: 6px;
|
|
padding: 8px 20px;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
}}
|
|
QPushButton:hover {{
|
|
background-color: #2980b9;
|
|
}}
|
|
QPushButton:pressed {{
|
|
background-color: #2471a3;
|
|
}}
|
|
QPushButton:disabled {{
|
|
background-color: #bdc3c7;
|
|
color: #95a5a6;
|
|
}}
|
|
|
|
QPushButton#success {{
|
|
background-color: #27ae60;
|
|
}}
|
|
QPushButton#success:hover {{
|
|
background-color: #229954;
|
|
}}
|
|
QPushButton#success:disabled {{
|
|
background-color: #bdc3c7;
|
|
}}
|
|
|
|
QPushButton#danger {{
|
|
background-color: #e74c3c;
|
|
}}
|
|
QPushButton#danger:hover {{
|
|
background-color: #cb4335;
|
|
}}
|
|
|
|
QPushButton#secondary {{
|
|
background-color: #95a5a6;
|
|
}}
|
|
QPushButton#secondary:hover {{
|
|
background-color: #7f8c8d;
|
|
}}
|
|
|
|
/* ----- Progress Bar ----- */
|
|
QProgressBar {{
|
|
background-color: #ecf0f1;
|
|
border: none;
|
|
border-radius: 6px;
|
|
height: 18px;
|
|
text-align: center;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: #2c3e50;
|
|
}}
|
|
QProgressBar::chunk {{
|
|
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
|
stop:0 #3498db, stop:1 #2ecc71);
|
|
border-radius: 6px;
|
|
}}
|
|
|
|
/* ----- Combo Box / Spinner ----- */
|
|
QComboBox {{
|
|
background-color: #ffffff;
|
|
border: 1px solid #d5d8dc;
|
|
border-radius: 6px;
|
|
padding: 6px 12px;
|
|
min-height: 20px;
|
|
}}
|
|
QComboBox:hover {{
|
|
border-color: #3498db;
|
|
}}
|
|
QComboBox::drop-down {{
|
|
border: none;
|
|
width: 24px;
|
|
}}
|
|
QDoubleSpinBox, QSpinBox, QLineEdit {{
|
|
background-color: #ffffff;
|
|
border: 1px solid #d5d8dc;
|
|
border-radius: 6px;
|
|
padding: 6px 10px;
|
|
min-height: 20px;
|
|
}}
|
|
QDoubleSpinBox:focus, QSpinBox:focus, QLineEdit:focus {{
|
|
border-color: #3498db;
|
|
}}
|
|
|
|
/* ----- Group Box ----- */
|
|
QGroupBox {{
|
|
font-size: 14px;
|
|
font-weight: 700;
|
|
color: #2c3e50;
|
|
border: 1px solid #e0e4e8;
|
|
border-radius: 10px;
|
|
margin-top: 12px;
|
|
padding: 20px 16px 16px 16px;
|
|
}}
|
|
QGroupBox::title {{
|
|
subcontrol-origin: margin;
|
|
subcontrol-position: top left;
|
|
padding: 4px 12px;
|
|
background-color: #f0f2f5;
|
|
border-radius: 4px;
|
|
margin-left: 10px;
|
|
}}
|
|
|
|
/* ----- Scroll Area ----- */
|
|
QScrollArea {{
|
|
border: none;
|
|
background: transparent;
|
|
}}
|
|
|
|
/* ----- Check Box ----- */
|
|
QCheckBox {{
|
|
spacing: 8px;
|
|
}}
|
|
QCheckBox::indicator {{
|
|
width: 18px;
|
|
height: 18px;
|
|
border-radius: 4px;
|
|
border: 2px solid #bdc3c7;
|
|
}}
|
|
QCheckBox::indicator:checked {{
|
|
background-color: #3498db;
|
|
border-color: #3498db;
|
|
}}
|
|
|
|
/* ----- Tooltip ----- */
|
|
QToolTip {{
|
|
background-color: #2c3e50;
|
|
color: #ffffff;
|
|
border: none;
|
|
padding: 8px 12px;
|
|
border-radius: 6px;
|
|
font-size: 12px;
|
|
}}
|
|
|
|
/* ----- Status Bar ----- */
|
|
QStatusBar {{
|
|
background-color: #2c3e50;
|
|
color: #ecf0f1;
|
|
font-size: 12px;
|
|
}}
|
|
""")
|
|
|
|
# Create and show main window
|
|
window = MainWindow()
|
|
window.show()
|
|
|
|
# Run application
|
|
exit_code = app.exec_()
|
|
|
|
if config.DEBUG:
|
|
print("[Main] Application closed normally")
|
|
|
|
return exit_code
|
|
|
|
except Exception as e:
|
|
# Show error dialog
|
|
print(f"[CRITICAL ERROR] {str(e)}")
|
|
traceback.print_exc()
|
|
|
|
# Try to show Qt error dialog
|
|
try:
|
|
app = QApplication.instance()
|
|
if app is None:
|
|
app = QApplication(sys.argv)
|
|
|
|
QMessageBox.critical(
|
|
None,
|
|
"Critical Error",
|
|
f"Application failed to start:\n\n{str(e)}\n\nCheck the console for details."
|
|
)
|
|
except:
|
|
pass
|
|
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|