update
This commit is contained in:
parent
cf9e902480
commit
db3df13462
@ -415,13 +415,10 @@ def render_today_plan() -> None:
|
||||
LOGGER.exception("加载 agent_utils 失败", extra=LOG_EXTRA)
|
||||
st.warning("暂未写入部门/代理决策,请先运行回测或策略评估流程。")
|
||||
return
|
||||
|
||||
trade_dates = [row["trade_date"] for row in date_rows]
|
||||
if not trade_dates:
|
||||
st.info("暂无决策记录,完成一次回测后即可在此查看部门意见与投票结果。")
|
||||
return
|
||||
|
||||
# ADD: read default selection from URL
|
||||
q = _get_query_params()
|
||||
default_trade_date = q.get("date", [trade_dates[0]])[0]
|
||||
try:
|
||||
@ -429,7 +426,6 @@ def render_today_plan() -> None:
|
||||
except ValueError:
|
||||
default_idx = 0
|
||||
trade_date = st.selectbox("交易日", trade_dates, index=default_idx)
|
||||
|
||||
with db_session(read_only=True) as conn:
|
||||
code_rows = conn.execute(
|
||||
"""
|
||||
@ -441,22 +437,9 @@ def render_today_plan() -> None:
|
||||
(trade_date,),
|
||||
).fetchall()
|
||||
symbols = [row["ts_code"] for row in code_rows]
|
||||
if not symbols:
|
||||
st.info("所选交易日暂无 agent_utils 记录。")
|
||||
return
|
||||
|
||||
# ADD: 投资助理模式开关(不指定具体标的)
|
||||
assistant_mode = st.checkbox(
|
||||
"投资助理(不指定标的)",
|
||||
value=False,
|
||||
help="开启后不显示具体标的,展示基于候选投资池的组合级建议与汇总信息。",
|
||||
)
|
||||
|
||||
if assistant_mode:
|
||||
ts_code = None
|
||||
batch_symbols = []
|
||||
detail_tab, assistant_tab = st.tabs(["标的详情", "投资助理模式"])
|
||||
with assistant_tab:
|
||||
st.info("已开启投资助理模式:以下内容为组合级(去标的)建议,不包含任何具体标的代码。")
|
||||
# 展示候选池聚合信息(不暴露具体代码)
|
||||
try:
|
||||
candidates = list_investment_pool(trade_date=trade_date)
|
||||
if candidates:
|
||||
@ -525,6 +508,9 @@ def render_today_plan() -> None:
|
||||
except Exception:
|
||||
LOGGER.exception("加载候选池聚合信息失败", extra=LOG_EXTRA)
|
||||
st.error("加载候选池数据时发生错误。")
|
||||
with detail_tab:
|
||||
if not symbols:
|
||||
st.info("所选交易日暂无 agent_utils 记录。")
|
||||
else:
|
||||
default_ts = q.get("code", [symbols[0]])[0]
|
||||
try:
|
||||
@ -532,10 +518,8 @@ def render_today_plan() -> None:
|
||||
except ValueError:
|
||||
default_ts_idx = 0
|
||||
ts_code = st.selectbox("标的", symbols, index=default_ts_idx)
|
||||
# ADD: batch selection for re-evaluation
|
||||
batch_symbols = st.multiselect("批量重评估(可多选)", symbols, default=[])
|
||||
|
||||
# 一键重评估所有标的按钮
|
||||
if st.button("一键重评估所有标的", type="primary", width='stretch'):
|
||||
with st.spinner("正在对所有标的进行重评估,请稍候..."):
|
||||
try:
|
||||
@ -616,10 +600,7 @@ def render_today_plan() -> None:
|
||||
except Exception as exc:
|
||||
LOGGER.exception("一键重评估失败", extra=LOG_EXTRA)
|
||||
st.error(f"一键重评估执行过程中发生错误:{exc}")
|
||||
|
||||
# sync URL params
|
||||
_set_query_params(date=str(trade_date), code=str(ts_code))
|
||||
|
||||
with db_session(read_only=True) as conn:
|
||||
rows = conn.execute(
|
||||
"""
|
||||
@ -630,21 +611,17 @@ def render_today_plan() -> None:
|
||||
""",
|
||||
(trade_date, ts_code),
|
||||
).fetchall()
|
||||
|
||||
if not rows:
|
||||
st.info("未查询到详细决策记录,稍后再试。")
|
||||
return
|
||||
|
||||
try:
|
||||
feasible_actions = json.loads(rows[0]["feasible"] or "[]")
|
||||
except (KeyError, TypeError, json.JSONDecodeError):
|
||||
feasible_actions = []
|
||||
|
||||
global_info = None
|
||||
dept_records: List[Dict[str, object]] = []
|
||||
dept_details: Dict[str, Dict[str, object]] = {}
|
||||
agent_records: List[Dict[str, object]] = []
|
||||
|
||||
for item in rows:
|
||||
agent_name = item["agent"]
|
||||
action = item["action"]
|
||||
@ -715,10 +692,8 @@ def render_today_plan() -> None:
|
||||
"BUY_L": score_map.get("BUY_L", 0.0),
|
||||
}
|
||||
)
|
||||
|
||||
if feasible_actions:
|
||||
st.caption(f"可行操作集合:{', '.join(feasible_actions)}")
|
||||
|
||||
st.subheader("全局策略")
|
||||
if global_info:
|
||||
col1, col2, col3 = st.columns(3)
|
||||
@ -782,7 +757,6 @@ def render_today_plan() -> None:
|
||||
st.json(dept_telemetry)
|
||||
else:
|
||||
st.info("暂未写入全局策略摘要。")
|
||||
|
||||
st.subheader("部门意见")
|
||||
if dept_records:
|
||||
# ADD: keyword filter for department summaries
|
||||
@ -832,7 +806,6 @@ def render_today_plan() -> None:
|
||||
st.json(telemetry)
|
||||
else:
|
||||
st.info("暂无部门记录。")
|
||||
|
||||
st.subheader("代理评分")
|
||||
if agent_records:
|
||||
# ADD: sorting and CSV export for agents
|
||||
@ -857,11 +830,8 @@ def render_today_plan() -> None:
|
||||
pass
|
||||
else:
|
||||
st.info("暂无基础代理评分。")
|
||||
|
||||
# 添加相关新闻展示部分
|
||||
st.divider()
|
||||
st.subheader("相关新闻")
|
||||
# 获取与当前标的相关的最新新闻
|
||||
try:
|
||||
with db_session(read_only=True) as conn:
|
||||
# 解析当前trade_date为datetime对象
|
||||
@ -959,11 +929,8 @@ def render_today_plan() -> None:
|
||||
except Exception as e:
|
||||
LOGGER.exception("获取新闻数据失败", extra=LOG_EXTRA)
|
||||
st.error(f"获取新闻数据时发生错误:{e}")
|
||||
|
||||
st.divider()
|
||||
# 提示用户跳转到单独的“投资池与仓位概览”页
|
||||
st.info("投资池与仓位概览已移至单独页面。请在侧边或页面导航中选择“投资池/仓位”以查看详细信息。")
|
||||
|
||||
st.divider()
|
||||
st.subheader("策略重评估")
|
||||
st.caption("对当前选中的交易日与标的,立即触发一次策略评估并回写 agent_utils。")
|
||||
@ -1081,129 +1048,6 @@ def render_today_plan() -> None:
|
||||
st.error(f"批量重评估失败:{exc}")
|
||||
|
||||
|
||||
def render_log_viewer() -> None:
|
||||
"""渲染日志钻取与历史对比视图页面。"""
|
||||
LOGGER.info("渲染日志视图页面", extra=LOG_EXTRA)
|
||||
st.header("日志钻取与历史对比")
|
||||
st.write("查看系统运行日志,支持时间范围筛选、关键词搜索和历史对比功能。")
|
||||
|
||||
# 日志时间范围选择
|
||||
col1, col2 = st.columns(2)
|
||||
with col1:
|
||||
start_date = st.date_input("开始日期", value=date.today() - timedelta(days=7))
|
||||
with col2:
|
||||
end_date = st.date_input("结束日期", value=date.today())
|
||||
|
||||
# 日志级别筛选
|
||||
log_levels = ["ALL", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
||||
selected_level = st.selectbox("日志级别", log_levels, index=1)
|
||||
|
||||
# 关键词搜索
|
||||
search_query = st.text_input("搜索关键词")
|
||||
|
||||
# 阶段筛选
|
||||
with db_session(read_only=True) as conn:
|
||||
stages = [row["stage"] for row in conn.execute("SELECT DISTINCT stage FROM run_log").fetchall()]
|
||||
stages = [s for s in stages if s] # 过滤空值
|
||||
stages.insert(0, "ALL")
|
||||
selected_stage = st.selectbox("执行阶段", stages)
|
||||
|
||||
# 查询日志
|
||||
with st.spinner("加载日志数据中..."):
|
||||
try:
|
||||
with db_session(read_only=True) as conn:
|
||||
query_parts = ["SELECT ts, stage, level, msg FROM run_log WHERE 1=1"]
|
||||
params = []
|
||||
|
||||
# 添加日期过滤
|
||||
start_ts = f"{start_date.isoformat()}T00:00:00Z"
|
||||
end_ts = f"{end_date.isoformat()}T23:59:59Z"
|
||||
query_parts.append("AND ts BETWEEN ? AND ?")
|
||||
params.extend([start_ts, end_ts])
|
||||
|
||||
# 添加级别过滤
|
||||
if selected_level != "ALL":
|
||||
query_parts.append("AND level = ?")
|
||||
params.append(selected_level)
|
||||
|
||||
# 添加关键词过滤
|
||||
if search_query:
|
||||
query_parts.append("AND msg LIKE ?")
|
||||
params.append(f"%{search_query}%")
|
||||
|
||||
# 添加阶段过滤
|
||||
if selected_stage != "ALL":
|
||||
query_parts.append("AND stage = ?")
|
||||
params.append(selected_stage)
|
||||
|
||||
# 添加排序
|
||||
query_parts.append("ORDER BY ts DESC")
|
||||
|
||||
# 执行查询
|
||||
query = " ".join(query_parts)
|
||||
rows = conn.execute(query, params).fetchall()
|
||||
|
||||
# 转换为DataFrame
|
||||
if rows:
|
||||
# 将sqlite3.Row对象转换为字典列表
|
||||
rows_dict = [{key: row[key] for key in row.keys()} for row in rows]
|
||||
log_df = pd.DataFrame(rows_dict)
|
||||
# 格式化时间戳并确保数据类型一致
|
||||
log_df["ts"] = pd.to_datetime(log_df["ts"]).dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
# 确保所有列都是字符串类型,避免PyArrow序列化错误
|
||||
for col in log_df.columns:
|
||||
log_df[col] = log_df[col].astype(str)
|
||||
else:
|
||||
log_df = pd.DataFrame(columns=["ts", "stage", "level", "msg"])
|
||||
|
||||
# 显示日志表格
|
||||
st.dataframe(
|
||||
log_df,
|
||||
hide_index=True,
|
||||
width="stretch",
|
||||
column_config={
|
||||
"ts": st.column_config.TextColumn("时间"),
|
||||
"stage": st.column_config.TextColumn("执行阶段"),
|
||||
"level": st.column_config.TextColumn("日志级别"),
|
||||
"msg": st.column_config.TextColumn("日志消息", width="large")
|
||||
}
|
||||
)
|
||||
|
||||
# 下载功能
|
||||
if not log_df.empty:
|
||||
csv_data = log_df.to_csv(index=False).encode('utf-8')
|
||||
st.download_button(
|
||||
label="下载日志CSV",
|
||||
data=csv_data,
|
||||
file_name=f"logs_{start_date}_{end_date}.csv",
|
||||
mime="text/csv",
|
||||
key="download_logs"
|
||||
)
|
||||
|
||||
# JSON下载
|
||||
json_data = log_df.to_json(orient='records', force_ascii=False, indent=2)
|
||||
st.download_button(
|
||||
label="下载日志JSON",
|
||||
data=json_data,
|
||||
file_name=f"logs_{start_date}_{end_date}.json",
|
||||
mime="application/json",
|
||||
key="download_logs_json"
|
||||
)
|
||||
except Exception as e:
|
||||
LOGGER.exception("加载日志失败", extra=LOG_EXTRA)
|
||||
st.error(f"加载日志数据失败:{e}")
|
||||
|
||||
# 历史对比功能
|
||||
st.subheader("历史对比")
|
||||
st.write("选择两个时间点的日志进行对比分析。")
|
||||
|
||||
# 第一个时间点选择
|
||||
col3, col4 = st.columns(2)
|
||||
with col3:
|
||||
compare_date1 = st.date_input("对比日期1", value=date.today() - timedelta(days=1))
|
||||
with col4:
|
||||
compare_date2 = st.date_input("对比日期2", value=date.today())
|
||||
|
||||
|
||||
def render_pool_overview() -> None:
|
||||
"""单独的投资池与仓位概览页面(从今日计划中提取)。"""
|
||||
@ -2872,6 +2716,161 @@ def render_tests() -> None:
|
||||
st.write(response)
|
||||
|
||||
|
||||
def render_log_viewer() -> None:
|
||||
"""渲染日志钻取与历史对比视图页面。"""
|
||||
LOGGER.info("渲染日志视图页面", extra=LOG_EXTRA)
|
||||
st.header("日志钻取与历史对比")
|
||||
st.write("查看系统运行日志,支持时间范围筛选、关键词搜索和历史对比功能。")
|
||||
|
||||
col1, col2 = st.columns(2)
|
||||
with col1:
|
||||
start_date = st.date_input("开始日期", value=date.today() - timedelta(days=7))
|
||||
with col2:
|
||||
end_date = st.date_input("结束日期", value=date.today())
|
||||
|
||||
log_levels = ["ALL", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
||||
selected_level = st.selectbox("日志级别", log_levels, index=1)
|
||||
|
||||
search_query = st.text_input("搜索关键词")
|
||||
|
||||
with db_session(read_only=True) as conn:
|
||||
stages = [row["stage"] for row in conn.execute("SELECT DISTINCT stage FROM run_log").fetchall()]
|
||||
stages = [s for s in stages if s]
|
||||
stages.insert(0, "ALL")
|
||||
selected_stage = st.selectbox("执行阶段", stages)
|
||||
|
||||
with st.spinner("加载日志数据中..."):
|
||||
try:
|
||||
with db_session(read_only=True) as conn:
|
||||
query_parts = ["SELECT ts, stage, level, msg FROM run_log WHERE 1=1"]
|
||||
params: list[object] = []
|
||||
|
||||
start_ts = f"{start_date.isoformat()}T00:00:00Z"
|
||||
end_ts = f"{end_date.isoformat()}T23:59:59Z"
|
||||
query_parts.append("AND ts BETWEEN ? AND ?")
|
||||
params.extend([start_ts, end_ts])
|
||||
|
||||
if selected_level != "ALL":
|
||||
query_parts.append("AND level = ?")
|
||||
params.append(selected_level)
|
||||
|
||||
if search_query:
|
||||
query_parts.append("AND msg LIKE ?")
|
||||
params.append(f"%{search_query}%")
|
||||
|
||||
if selected_stage != "ALL":
|
||||
query_parts.append("AND stage = ?")
|
||||
params.append(selected_stage)
|
||||
|
||||
query_parts.append("ORDER BY ts DESC")
|
||||
|
||||
query = " ".join(query_parts)
|
||||
rows = conn.execute(query, params).fetchall()
|
||||
|
||||
if rows:
|
||||
rows_dict = [{key: row[key] for key in row.keys()} for row in rows]
|
||||
log_df = pd.DataFrame(rows_dict)
|
||||
log_df["ts"] = pd.to_datetime(log_df["ts"]).dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
for col in log_df.columns:
|
||||
log_df[col] = log_df[col].astype(str)
|
||||
else:
|
||||
log_df = pd.DataFrame(columns=["ts", "stage", "level", "msg"])
|
||||
|
||||
st.dataframe(
|
||||
log_df,
|
||||
hide_index=True,
|
||||
width="stretch",
|
||||
column_config={
|
||||
"ts": st.column_config.TextColumn("时间"),
|
||||
"stage": st.column_config.TextColumn("执行阶段"),
|
||||
"level": st.column_config.TextColumn("日志级别"),
|
||||
"msg": st.column_config.TextColumn("日志消息", width="large"),
|
||||
},
|
||||
)
|
||||
|
||||
if not log_df.empty:
|
||||
csv_data = log_df.to_csv(index=False).encode("utf-8")
|
||||
st.download_button(
|
||||
label="下载日志CSV",
|
||||
data=csv_data,
|
||||
file_name=f"logs_{start_date}_{end_date}.csv",
|
||||
mime="text/csv",
|
||||
key="download_logs",
|
||||
)
|
||||
|
||||
json_data = log_df.to_json(orient="records", force_ascii=False, indent=2)
|
||||
st.download_button(
|
||||
label="下载日志JSON",
|
||||
data=json_data,
|
||||
file_name=f"logs_{start_date}_{end_date}.json",
|
||||
mime="application/json",
|
||||
key="download_logs_json",
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
LOGGER.exception("加载日志失败", extra=LOG_EXTRA)
|
||||
st.error(f"加载日志数据失败:{exc}")
|
||||
|
||||
st.subheader("历史对比")
|
||||
st.write("选择两个时间点的日志进行对比分析。")
|
||||
|
||||
col3, col4 = st.columns(2)
|
||||
with col3:
|
||||
compare_date1 = st.date_input("对比日期1", value=date.today() - timedelta(days=1))
|
||||
with col4:
|
||||
compare_date2 = st.date_input("对比日期2", value=date.today())
|
||||
|
||||
comparison_stage = st.selectbox("对比阶段", stages, key="compare_stage")
|
||||
st.write("选择需要比较的日志数量。")
|
||||
compare_limit = st.slider("对比日志数量", min_value=10, max_value=200, value=50, step=10)
|
||||
|
||||
if st.button("生成历史对比报告"):
|
||||
with st.spinner("生成对比报告中..."):
|
||||
try:
|
||||
with db_session(read_only=True) as conn:
|
||||
def load_logs(d: date) -> pd.DataFrame:
|
||||
start_ts = f"{d.isoformat()}T00:00:00Z"
|
||||
end_ts = f"{d.isoformat()}T23:59:59Z"
|
||||
query = ["SELECT ts, level, msg FROM run_log WHERE ts BETWEEN ? AND ?"]
|
||||
params: list[object] = [start_ts, end_ts]
|
||||
if comparison_stage != "ALL":
|
||||
query.append("AND stage = ?")
|
||||
params.append(comparison_stage)
|
||||
query.append("ORDER BY ts DESC LIMIT ?")
|
||||
params.append(compare_limit)
|
||||
sql = " ".join(query)
|
||||
rows = conn.execute(sql, params).fetchall()
|
||||
if not rows:
|
||||
return pd.DataFrame(columns=["ts", "level", "msg"])
|
||||
df = pd.DataFrame([{k: row[k] for k in row.keys()} for row in rows])
|
||||
df["ts"] = pd.to_datetime(df["ts"]).dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
return df
|
||||
|
||||
df1 = load_logs(compare_date1)
|
||||
df2 = load_logs(compare_date2)
|
||||
|
||||
if df1.empty and df2.empty:
|
||||
st.info("选定日期暂无日志可对比。")
|
||||
else:
|
||||
st.write("### 对比结果")
|
||||
col_a, col_b = st.columns(2)
|
||||
with col_a:
|
||||
st.write(f"{compare_date1} 日日志")
|
||||
st.dataframe(df1, hide_index=True, width="stretch")
|
||||
with col_b:
|
||||
st.write(f"{compare_date2} 日日志")
|
||||
st.dataframe(df2, hide_index=True, width="stretch")
|
||||
|
||||
summary = {
|
||||
"日期1日志条数": int(len(df1)),
|
||||
"日期2日志条数": int(len(df2)),
|
||||
"新增日志条数": max(len(df2) - len(df1), 0),
|
||||
}
|
||||
st.write("摘要:", summary)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
LOGGER.exception("历史对比生成失败", extra=LOG_EXTRA)
|
||||
st.error(f"生成历史对比失败:{exc}")
|
||||
|
||||
|
||||
def render_data_settings() -> None:
|
||||
"""渲染数据源配置界面."""
|
||||
st.subheader("Tushare 数据源")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user