This file provides guidance to CodeBuddy Code when working with code in this repository.
智能基金监控系统 (FundWave) - A professional-grade desktop application for monitoring and analyzing mutual fund investments in China. Built with PySide6 (Qt6) and SQLite, featuring real-time fund data fetching, portfolio analysis, profit/loss calculations, and multi-channel notifications.
Key Features:
- Real-time fund estimation monitoring via TianTian Fund API
- Portfolio analysis dashboard with profit/loss calculations
- Dividend tracking and recording
- Investment calculator (fixed investment vs lump sum comparison)
- System tray integration and background monitoring
- Multi-channel notifications (popup + DingTalk webhook)
- Privacy protection with visibility toggles for sensitive financial data
MVC Pattern Implementation:
- Model Layer (
models/): DatabaseManager handles SQLite persistence with thread-safe connections - View Layer (
ui/): PySide6 widgets, dialogs, and theme system - Controller Layer (
ui/main_window.py): FundMonitor orchestrates business logic and UI updates - Service Layer (
services/): Data fetching, calculations, notifications
Key Design Patterns:
- Singleton pattern for ThemeManager
- Decorator pattern for retry logic (
@retry_on_failure) - Observer pattern via Qt Signal/Slot
- Context manager for database transactions
# Standard launch
python main.py
# Using startup script (handles Qt platform plugin issues)
bash start.sh# Run all tests
pytest tests/ -v
# Run specific test file
pytest tests/test_database.py -v
# Run specific test class
pytest tests/test_database.py::TestDatabaseManager -v
# Run with coverage
pytest tests/ --cov=models --cov=services --cov-report=html
# Parallel execution (requires pytest-xdist)
pytest tests/ -n auto# Style checking
flake8 models/ services/ ui/ utils/
# Import sorting check
isort --check-only .
# Auto-format imports
isort .
# Auto-format code
autopep8 --in-place --aggressive --aggressive <file># Install core dependencies
pip install PySide6 matplotlib requests pytest pytest-cov pytest-qt
# Install all dependencies
pip install -r requirements.txtThe SQLite database (fund_monitor.db) contains these tables:
-
monitored_funds: User's selected fund list
fund_code(TEXT, unique): 6-digit fund codefund_name(TEXT): Fund namecreated_at,updated_at
-
fund_holdings: Portfolio holdings data
fund_code(TEXT, unique)cost_price(REAL): User's cost price per shareshares(REAL): Number of shares heldamount(REAL): Total holding value
-
settings: System settings
refresh_interval(INTEGER): Auto-refresh interval in seconds (default 60, range 5-3600)auto_refresh_enabled(BOOLEAN)key,value: Generic key-value settings
-
notification_settings: Notification configuration
popup_enabled,dingtalk_enableddingtalk_webhook,dingtalk_secretrise_threshold,fall_threshold(percentages)profit_threshold,loss_threshold(amounts)
-
ui_settings: UI preference persistence
profit_visible,daily_profit_visibleposition_cost_visible,current_value_visible
-
dividend_records: Dividend tracking
fund_code,dividend_amount,dividend_date
Database Best Practices:
- Always use
with db_manager.get_cursor() as cursor:for transactions (auto-commits on success, rollback on error) - Thread-safe: each thread gets its own connection via
threading.local() - DatabaseManager automatically migrates old schemas on initialization
Primary Data API:
- Fund estimation:
http://fundgz.1234567.com.cn/js/{code}.js- Returns JSONP format:
jsonpgz({...}) - Fields:
name,gsz(estimated value),gszzl(estimated change %)
- Returns JSONP format:
Fund List API:
- All funds:
https://fund.eastmoney.com/js/fundcode_search.js- Returns:
[[code, type, name, type2, pinyin], ...]
- Returns:
Data Fetching Patterns:
- Use
FundDataFetcher.get_fund(code)for single fund - Use
FundDataFetcher.get_all_funds()for search functionality - Always validate codes with
FundDataFetcher.validate_fund_code(code)before API calls - Built-in retry mechanism (3 retries, 1 second delay) via
@retry_on_failuredecorator
Theme Architecture:
ui/theme/theme_manager.py: Singleton ThemeManager with responsive design- Two themes: ProfessionalTheme (light) and DarkTheme
- Theme can be toggled and persisted in database
- Responsive breakpoints: xs/sm/md/lg/xl/xxl based on screen width
Key Widgets:
ui/widgets/table_widget.py: Custom table with numeric sorting (NumericItem, PercentageItem)ui/widgets/search_widget.py: Fund search with autocompleteui/widgets/portfolio_dashboard.py: Portfolio analysis visualizationui/widgets/investment_calculator_dialog.py: Investment calculator UI
UI Thread Safety:
- Data updates run in
DataUpdateThread(QThread) to avoid blocking UI - Never modify UI directly from background threads - use Qt signals
Key Formulas:
Position Cost = cost_price × shares
Current Value = estimated_value × shares
Daily Profit = current_value × (estimated_change % / 100)
Total Profit = current_value - position_cost + dividend_amount
Profit % = (total_profit / position_cost) × 100
Implementation Location:
services/investment_calculator.py: Fixed investment calculator- Calculations integrated in
ui/main_window.pyviaupdate_total_profit()andget_fund_holdings_detail()
Entry Point:
main.py: Minimal entry that callsui.main_window.main()
Configuration:
config.py: FundConfig dataclass with API URLs, timeouts, retry settings
Logging:
utils/logger.py: Rotating file handler (10MB max, 5 backups) + console output- Log file:
fund_monitor.log
Utilities:
utils/decorators.py:@retry_on_failure,@measure_time
# Standard library
import json
import logging
import os
# Third-party
import requests
from PySide6.QtCore import Qt, QThread
# Local imports
from config import config
from models.database import DatabaseManager
from services.data_fetcher import FundDataFetcher
from utils.logger import loggerAll public functions must have type annotations:
def get_fund(self, code: str) -> Optional[Dict[str, Any]]:
"""Get fund data by code."""Use Google-style docstrings:
def method(self, param1: str, param2: int) -> bool:
"""Brief description.
Args:
param1: Description
param2: Description
Returns:
Description of return value
Raises:
ValueError: When invalid input
"""Always use parameterized queries (never string interpolation):
cursor.execute('SELECT * FROM monitored_funds WHERE fund_code = ?', (fund_code,))Use new-style signals:
class DataUpdateThread(QThread):
data_updated = Signal(object) # Define signal
error_occurred = Signal(str)
# In caller:
thread.data_updated.connect(self.on_data_updated)- Validate code:
FundDataFetcher.validate_fund_code(code) - Fetch data:
FundDataFetcher.get_fund(code) - Insert to database:
cursor.execute('INSERT INTO monitored_funds...') - Trigger async update via signal
- Use
update_fund_holdings_detail(code, cost_price, shares, amount) - Handles both INSERT and UPDATE automatically
- Commits via context manager
- Inherit from
QDialogorQWidget - Call
ProfessionalTheme.apply_to_widget(self)for styling - Use layout managers (QVBoxLayout, QHBoxLayout, QGridLayout)
- Connect signals for inter-widget communication
- Extend
NotificationManagerclass inservices/notification.py - Add settings fields to
notification_settingstable - Create UI settings dialog integration
- Implement cooldown logic to prevent spam
Test File Organization:
tests/test_database.py: DatabaseManager teststests/test_calculator.py: Investment calculator teststests/test_config.py: Configuration teststests/test_new_features.py: Feature-specific tests
Fixture Pattern:
@pytest.fixture(scope='module')
def db_manager():
"""Create temporary test database."""
with tempfile.NamedTemporaryFile(suffix='.db', delete=False) as f:
db_path = f.name
manager = DatabaseManager(db_path=db_path)
yield manager
manager.close()
os.unlink(db_path)UI Testing:
- Use
pytest-qtfor Qt widget tests qtbot.addWidget()to manage widget lifecycle- Test signals with
qtbot.waitSignal()
- Network Requests: 10-second timeout per request, max 3 retries
- Thread Safety: Database uses per-thread connections
- UI Rendering: Data updates in QThread, never block main thread
- Caching: Consider adding cache layer for fund list (9000+ funds)
Qt Platform Issues:
- Use
start.shwhich setsQT_QPA_PLATFORM="xcb" - Check
QT_PLUGIN_PATHenvironment variable
Database Issues:
- Enable SQL logging: add
logging.getLogger('sqlite3').setLevel(logging.DEBUG) - Check transaction commits: look for "数据库操作失败" in logs
Network Issues:
- Check
fund_monitor.logfor timeout/retry messages - Verify API URLs are accessible from your network
- User-Agent header required for some endpoints
UI Layout Issues:
- Use
widget.dumpObjectTree()to print widget hierarchy - Check stylesheet conflicts with
widget.styleSheet()
- Python: 3.8+
- PySide6: ≥ 6.0
- SQLite: Built-in with Python
- pytest: ≥ 7.0
- matplotlib: ≥ 3.5
For detailed architecture and development guides, see:
docs/developer_guide.md: Full development documentationdocs/upgrade_roadmap.md: Feature roadmap and architecture analysisdocs/api_reference.md: API endpoint documentationdocs/user_guide.md: End-user manual