138 lines
4.7 KiB
Python
138 lines
4.7 KiB
Python
"""Streamlit UI scaffold for the investment assistant."""
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import streamlit as st
|
||
from streamlit_option_menu import option_menu
|
||
|
||
ROOT = Path(__file__).resolve().parents[2]
|
||
if str(ROOT) not in sys.path:
|
||
sys.path.insert(0, str(ROOT))
|
||
|
||
from app.data.schema import initialize_database
|
||
from app.ingest.checker import run_boot_check
|
||
from app.ingest.rss import ingest_configured_rss
|
||
from app.ui.portfolio_config import render_portfolio_config
|
||
from app.ui.progress_state import render_factor_progress
|
||
from app.ui.shared import LOGGER, LOG_EXTRA
|
||
from app.ui.views import (
|
||
render_backtest_review,
|
||
render_config_overview,
|
||
render_data_settings,
|
||
render_global_dashboard,
|
||
render_llm_settings,
|
||
render_log_viewer,
|
||
render_market_visualization,
|
||
render_pool_overview,
|
||
render_stock_evaluation,
|
||
render_tests,
|
||
render_today_plan,
|
||
render_factor_calculation,
|
||
)
|
||
from app.utils.config import get_config
|
||
|
||
|
||
def main() -> None:
|
||
LOGGER.info("初始化 Streamlit UI", extra=LOG_EXTRA)
|
||
st.set_page_config(page_title="多智能体个人投资助理", layout="wide")
|
||
|
||
initialize_database()
|
||
|
||
cfg = get_config()
|
||
if cfg.auto_update_data:
|
||
LOGGER.info("检测到自动更新数据选项已启用,开始执行数据拉取", extra=LOG_EXTRA)
|
||
try:
|
||
with st.spinner("正在自动更新数据..."):
|
||
def progress_hook(message: str, progress: float) -> None:
|
||
st.write(f"📊 {message} ({progress:.1%})")
|
||
|
||
report = run_boot_check(
|
||
days=30,
|
||
auto_fetch=True,
|
||
progress_hook=progress_hook,
|
||
force_refresh=False,
|
||
)
|
||
rss_count = ingest_configured_rss(hours_back=24, max_items_per_feed=50)
|
||
LOGGER.info(
|
||
"自动数据更新完成:日线数据覆盖%s-%s,RSS新闻%s条",
|
||
report.start,
|
||
report.end,
|
||
rss_count,
|
||
extra=LOG_EXTRA,
|
||
)
|
||
st.success(f"✅ 自动数据更新完成:获取RSS新闻 {rss_count} 条")
|
||
except Exception as exc: # noqa: BLE001
|
||
LOGGER.exception("自动数据更新失败", extra=LOG_EXTRA)
|
||
st.error(f"❌ 自动数据更新失败:{exc}")
|
||
|
||
render_global_dashboard()
|
||
|
||
# --- 顶部导航(第三方组件 streamlit-option-menu) ---
|
||
top_labels = ["今日计划", "投资池/仓位", "回测与复盘", "行情可视化", "日志钻取", "数据与设置", "自检测试"]
|
||
selected_top = option_menu(
|
||
menu_title=None,
|
||
options=top_labels,
|
||
icons=["calendar", "briefcase", "bar-chart", "activity", "file-text", "gear", "bug"],
|
||
orientation="horizontal",
|
||
)
|
||
LOGGER.debug("Top menu selected: %s", selected_top, extra=LOG_EXTRA)
|
||
|
||
# --- 仅渲染当前选中页(懒加载) ---
|
||
if selected_top == "今日计划":
|
||
render_today_plan()
|
||
|
||
elif selected_top == "投资池/仓位":
|
||
render_pool_overview()
|
||
|
||
elif selected_top == "回测与复盘":
|
||
sub_labels = ["回测复盘", "股票评估", "因子计算"]
|
||
selected_backtest = option_menu(
|
||
menu_title=None,
|
||
options=sub_labels,
|
||
icons=["bar-chart", "graph-up", "calculator"],
|
||
orientation="horizontal",
|
||
)
|
||
LOGGER.debug("Backtest submenu selected: %s", selected_backtest, extra=LOG_EXTRA)
|
||
|
||
if selected_backtest == "回测复盘":
|
||
render_backtest_review()
|
||
elif selected_backtest == "股票评估":
|
||
render_stock_evaluation()
|
||
else:
|
||
render_factor_calculation()
|
||
|
||
elif selected_top == "行情可视化":
|
||
render_market_visualization()
|
||
|
||
elif selected_top == "日志钻取":
|
||
render_log_viewer()
|
||
|
||
elif selected_top == "数据与设置":
|
||
st.header("系统设置")
|
||
settings_labels = ["配置概览", "LLM 设置", "投资组合", "数据源"]
|
||
selected_settings = option_menu(
|
||
menu_title=None,
|
||
options=settings_labels,
|
||
icons=["list-task", "cpu", "bank", "database"],
|
||
orientation="horizontal",
|
||
)
|
||
LOGGER.debug("Settings submenu selected: %s", selected_settings, extra=LOG_EXTRA)
|
||
|
||
if selected_settings == "配置概览":
|
||
render_config_overview()
|
||
elif selected_settings == "LLM 设置":
|
||
render_llm_settings()
|
||
elif selected_settings == "投资组合":
|
||
render_portfolio_config()
|
||
else:
|
||
render_data_settings()
|
||
|
||
elif selected_top == "自检测试":
|
||
render_tests()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|