222 lines
7.8 KiB
Python
222 lines
7.8 KiB
Python
import streamlit as st
|
|
from datetime import datetime
|
|
from typing import Dict, List
|
|
|
|
from monitor_db import monitor_db
|
|
from monitor_service import monitor_service
|
|
from notification_service import notification_service
|
|
|
|
def display_monitor_panel():
|
|
"""显示监测面板"""
|
|
|
|
st.markdown("## 📊 实时监测面板")
|
|
|
|
# 监测服务控制
|
|
col1, col2, col3, col4 = st.columns([1, 1, 1, 1])
|
|
|
|
with col1:
|
|
if st.button("▶️ 启动监测服务", type="primary"):
|
|
monitor_service.start_monitoring()
|
|
|
|
with col2:
|
|
if st.button("⏹️ 停止监测服务"):
|
|
monitor_service.stop_monitoring()
|
|
|
|
with col3:
|
|
if st.button("🔄 手动更新所有"):
|
|
stocks = monitor_service.get_stocks_needing_update()
|
|
for stock in stocks:
|
|
monitor_service.manual_update_stock(stock['id'])
|
|
st.success(f"✅ 已手动更新 {len(stocks)} 只股票")
|
|
|
|
with col4:
|
|
# 显示定时调度状态
|
|
try:
|
|
scheduler = monitor_service.get_scheduler()
|
|
status = scheduler.get_status()
|
|
if status['scheduler_running']:
|
|
st.success("⏰ 定时已启用")
|
|
else:
|
|
st.info("⏰ 定时未启用")
|
|
except:
|
|
st.info("⏰ 定时未配置")
|
|
|
|
# 显示通知
|
|
display_notifications()
|
|
|
|
# 显示监测股票
|
|
display_monitored_stocks()
|
|
|
|
def display_notifications():
|
|
"""显示通知"""
|
|
notifications = notification_service.get_streamlit_notifications()
|
|
|
|
if notifications:
|
|
st.markdown("### 🔔 最新提醒")
|
|
|
|
for notification in notifications[-5:]: # 只显示最近5条
|
|
notification_type = notification['type']
|
|
color_map = {
|
|
'entry': '🟢',
|
|
'take_profit': '🟡',
|
|
'stop_loss': '🔴'
|
|
}
|
|
icon = color_map.get(notification_type, '🔵')
|
|
|
|
st.info(f"{icon} **{notification['symbol']}** - {notification['message']}")
|
|
|
|
if st.button("清空提醒"):
|
|
notification_service.clear_streamlit_notifications()
|
|
st.rerun()
|
|
|
|
def display_monitored_stocks():
|
|
"""显示监测股票卡片"""
|
|
stocks = monitor_db.get_monitored_stocks()
|
|
|
|
if not stocks:
|
|
st.info("📋 暂无监测股票,请在分析完成后点击'加入监测'按钮添加")
|
|
return
|
|
|
|
st.markdown(f"### 📈 监测中 ({len(stocks)} 只)")
|
|
|
|
# 每行显示3个卡片
|
|
cols = st.columns(3)
|
|
|
|
for i, stock in enumerate(stocks):
|
|
col_idx = i % 3
|
|
with cols[col_idx]:
|
|
display_stock_card(stock)
|
|
|
|
def display_stock_card(stock: Dict):
|
|
"""显示单个股票监测卡片"""
|
|
|
|
with st.container():
|
|
st.markdown(f"### {stock['symbol']} - {stock['name']}")
|
|
|
|
# 评级和状态
|
|
col1, col2 = st.columns([1, 1])
|
|
with col1:
|
|
rating_color = {
|
|
'买入': '🟢',
|
|
'持有': '🟡',
|
|
'卖出': '🔴'
|
|
}
|
|
st.metric("评级", f"{rating_color.get(stock['rating'], '⚪')} {stock['rating']}")
|
|
|
|
with col2:
|
|
if stock['current_price'] and stock['current_price'] != 'N/A':
|
|
st.metric("当前价格", f"¥{stock['current_price']}")
|
|
else:
|
|
st.metric("当前价格", "等待更新")
|
|
|
|
# 关键价位
|
|
entry_range = stock['entry_range']
|
|
st.info(f"**进场区间**: ¥{entry_range['min']} - ¥{entry_range['max']}")
|
|
|
|
if stock['take_profit']:
|
|
st.success(f"**止盈位**: ¥{stock['take_profit']}")
|
|
|
|
if stock['stop_loss']:
|
|
st.error(f"**止损位**: ¥{stock['stop_loss']}")
|
|
|
|
# 最后更新时间
|
|
if stock['last_checked']:
|
|
last_checked = datetime.fromisoformat(stock['last_checked'])
|
|
st.caption(f"最后更新: {last_checked.strftime('%m-%d %H:%M')}")
|
|
|
|
# 操作按钮
|
|
col1, col2 = st.columns([1, 1])
|
|
with col1:
|
|
if st.button("🔄 更新", key=f"update_{stock['id']}"):
|
|
if monitor_service.manual_update_stock(stock['id']):
|
|
st.success("✅ 更新成功")
|
|
else:
|
|
st.error("❌ 更新失败")
|
|
|
|
with col2:
|
|
if st.button("🗑️ 移除", key=f"remove_{stock['id']}"):
|
|
monitor_db.remove_monitored_stock(stock['id'])
|
|
st.success("✅ 已移除监测")
|
|
st.rerun()
|
|
|
|
def add_to_monitor_dialog(stock_info: Dict, analysis_result: Dict):
|
|
"""显示添加到监测的对话框"""
|
|
|
|
st.markdown("---")
|
|
st.markdown("## 📈 添加到实时监测")
|
|
|
|
# 从分析结果中提取关键数据
|
|
final_decision = analysis_result.get('final_decision', {})
|
|
rating = final_decision.get('rating', '持有')
|
|
reasoning = final_decision.get('reasoning', '')
|
|
|
|
# 生成唯一的session标识符
|
|
import uuid
|
|
import time
|
|
session_id = f"{stock_info.get('symbol', 'unknown')}_{int(time.time())}_{uuid.uuid4().hex[:6]}"
|
|
|
|
# 解析关键价位(从分析结果中提取或手动输入)
|
|
col1, col2 = st.columns([1, 1])
|
|
|
|
with col1:
|
|
# 进场区间
|
|
st.subheader("🎯 进场区间")
|
|
entry_min = st.number_input("最低进场价", value=0.0, key=f"entry_min_{session_id}")
|
|
entry_max = st.number_input("最高进场价", value=0.0, key=f"entry_max_{session_id}")
|
|
|
|
if entry_min > 0 and entry_max > 0 and entry_max > entry_min:
|
|
entry_range = {"min": entry_min, "max": entry_max}
|
|
else:
|
|
st.warning("请输入有效的进场区间")
|
|
entry_range = None
|
|
|
|
with col2:
|
|
# 止盈止损
|
|
st.subheader("⚖️ 风险控制")
|
|
take_profit = st.number_input("止盈价位", value=0.0, key=f"take_profit_{session_id}")
|
|
stop_loss = st.number_input("止损价位", value=0.0, key=f"stop_loss_{session_id}")
|
|
|
|
if take_profit > 0:
|
|
st.success(f"止盈位: ¥{take_profit}")
|
|
if stop_loss > 0:
|
|
st.error(f"止损位: ¥{stop_loss}")
|
|
|
|
# 监测设置
|
|
st.subheader("⏰ 监测设置")
|
|
check_interval = st.slider("监测间隔(分钟)", 5, 120, 30, key=f"check_interval_{session_id}")
|
|
notification_enabled = st.checkbox("启用提醒", value=True, key=f"notification_enabled_{session_id}")
|
|
|
|
# 添加按钮
|
|
if st.button("✅ 确认加入监测", type="primary", key=f"add_monitor_{session_id}"):
|
|
if entry_range:
|
|
# 添加到监测数据库
|
|
stock_id = monitor_db.add_monitored_stock(
|
|
symbol=stock_info.get('symbol'),
|
|
name=stock_info.get('name'),
|
|
rating=rating,
|
|
entry_range=entry_range,
|
|
take_profit=take_profit if take_profit > 0 else None,
|
|
stop_loss=stop_loss if stop_loss > 0 else None,
|
|
check_interval=check_interval
|
|
)
|
|
|
|
st.success(f"✅ 已成功将 {stock_info.get('symbol')} 加入实时监测")
|
|
st.balloons()
|
|
|
|
# 立即更新一次价格
|
|
monitor_service.manual_update_stock(stock_id)
|
|
else:
|
|
st.error("❌ 请设置有效的进场区间")
|
|
|
|
def get_monitor_summary() -> Dict:
|
|
"""获取监测摘要信息"""
|
|
stocks = monitor_db.get_monitored_stocks()
|
|
|
|
summary = {
|
|
'total_stocks': len(stocks),
|
|
'stocks_needing_update': len(monitor_service.get_stocks_needing_update()),
|
|
'pending_notifications': len(monitor_db.get_pending_notifications()),
|
|
'active_monitoring': monitor_service.running
|
|
}
|
|
|
|
return summary |