Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,15 @@
.env
/.cursor
/openspec
TradEnv/
TradEnv/
<<<<<<< HEAD
*.db
=======
stock_analysis.db
sector_strategy.db
longhubang.db
main_force_batch.db
portfolio_stocks.db
smart_monitor.db
stock_monitor.db
>>>>>>> 3554818 (更新界面布局“)
329 changes: 228 additions & 101 deletions app.py

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
MINIQMT_CONFIG = {
'enabled': os.getenv("MINIQMT_ENABLED", "false").lower() == "true",
'account_id': os.getenv("MINIQMT_ACCOUNT_ID", ""),
'path': os.getenv("MINIQMT_PATH", "D:\\qmt\\userdata_mini"),
'host': os.getenv("MINIQMT_HOST", "127.0.0.1"),
'port': int(os.getenv("MINIQMT_PORT", "58610")),
}
Expand Down
7 changes: 7 additions & 0 deletions config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ def __init__(self, env_file: str = ".env"):
"required": False,
"type": "text"
},
"MINIQMT_PATH": {
"value": "",
"description": "MiniQMT安装路径(例如:C:\\国金证券QMT\\userdata_mini)",
"required": False,
"type": "text"
},
"EMAIL_ENABLED": {
"value": "false",
"description": "启用邮件通知",
Expand Down Expand Up @@ -185,6 +191,7 @@ def write_env(self, config: Dict[str, str]) -> bool:
lines.append(f'MINIQMT_ACCOUNT_ID="{config.get("MINIQMT_ACCOUNT_ID", "")}"')
lines.append(f'MINIQMT_HOST="{config.get("MINIQMT_HOST", "127.0.0.1")}"')
lines.append(f'MINIQMT_PORT="{config.get("MINIQMT_PORT", "58610")}"')
lines.append(f'MINIQMT_PATH="{config.get("MINIQMT_PATH", "")}"')
lines.append("")

# 邮件通知配置
Expand Down
2 changes: 0 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '3.8'

services:
agentsstock:
build:
Expand Down
3 changes: 2 additions & 1 deletion docs/toshare说明.md → docs/tushare说明.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ df = pro.query('trade_cal', exchange='', start_date='20180901', end_date='201810


沪深港通资金流向
接口:moneyflow_hsgt,可以通过数据工具调试和查看数据。
接口:moneyflow_hsgt
可以通过数据工具调试和查看数据。
描述:获取沪股通、深股通、港股通每日资金流向数据,每次最多返回300条记录,总量不限制。每天18~20点之间完成当日更新
积分要求:2000积分起,5000积分每分钟可提取500次

Expand Down
Binary file added docs/北向数据示例.xlsx
Binary file not shown.
Binary file modified longhubang.db
Binary file not shown.
10 changes: 5 additions & 5 deletions longhubang_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ def __init__(self, api_key=None):
"""
print("[智瞰龙虎] 龙虎榜数据获取器初始化...")
# self.base_url = "https://api-lhb.zhongdu.net"
self.base_url = "http://lhb-api.ws4.cn/v1"
# self.base_url = "https://www.stockapi.com.cn/v1"
# self.base_url = "http://lhb-api.ws4.cn/v1"
self.base_url = "https://www.stockapi.com.cn/v1"
self.api_key = api_key
self.max_retries = 3 # 最大重试次数
self.retry_delay = 2 # 重试延迟(秒)
Expand Down Expand Up @@ -81,7 +81,7 @@ def get_longhubang_data(self, date):
"""
print(f"[智瞰龙虎] 获取 {date} 的龙虎榜数据...")

# url = f"{self.base_url}"
# 使用兼容接口获取指定日期数据
url = f"{self.base_url}/youzi/all"
params = {'date': date}

Expand Down Expand Up @@ -128,12 +128,12 @@ def get_longhubang_data_range(self, start_date, end_date):
print(f"[智瞰龙虎] ✓ 共获取 {len(all_data)} 条记录")
return all_data

def get_recent_days_data(self, days=5):
def get_recent_days_data(self, days=3):
"""
获取最近N个交易日的龙虎榜数据

Args:
days: 天数(默认5天
days: 天数(默认3天

Returns:
list: 龙虎榜数据列表
Expand Down
10 changes: 5 additions & 5 deletions longhubang_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def display_analysis_tab():
"最近天数",
min_value=1,
max_value=10,
value=1,
value=3,
help="分析最近N天的龙虎榜数据"
)

Expand Down Expand Up @@ -461,7 +461,7 @@ def display_scoring_ranking(result):
showlegend=False,
height=400
)
st.plotly_chart(fig1, config={'displayModeBar': False}, use_container_width=True)
st.plotly_chart(fig1, config={'displayModeBar': False}, width='stretch')

with col2:
# 五维评分雷达图(显示批量分析数量的股票)
Expand Down Expand Up @@ -509,7 +509,7 @@ def display_scoring_ranking(result):
x=0.5
)
)
st.plotly_chart(fig2, config={'displayModeBar': False}, use_container_width=True)
st.plotly_chart(fig2, config={'displayModeBar': False}, width='stretch')

st.markdown("---")

Expand Down Expand Up @@ -703,7 +703,7 @@ def display_visualizations(result):
labels={'name': '股票名称', 'net_inflow': '净流入金额(元)'}
)
fig.update_layout(xaxis_tickangle=-45)
st.plotly_chart(fig, config={'displayModeBar': False}, use_container_width=True)
st.plotly_chart(fig, config={'displayModeBar': False}, width='stretch')

# 热门概念图表
if summary.get('hot_concepts'):
Expand All @@ -718,7 +718,7 @@ def display_visualizations(result):
names='概念',
title='热门概念出现次数分布'
)
st.plotly_chart(fig, config={'displayModeBar': False}, use_container_width=True)
st.plotly_chart(fig, config={'displayModeBar': False}, width='stretch')


def display_pdf_export_section(result):
Expand Down
Binary file modified low_price_bull_monitor.db
Binary file not shown.
Binary file modified main_force_batch.db
Binary file not shown.
3 changes: 1 addition & 2 deletions main_force_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,7 @@ def display_main_force_selector():
st.markdown("---")

# 开始分析按钮
if st.button("🚀 开始主力选股", type="primary", width='content'):

if st.button("🚀 开始主力选股", type="primary", width='stretch'):
with st.spinner("正在获取数据并分析,这可能需要几分钟..."):

# 创建分析器
Expand Down
59 changes: 46 additions & 13 deletions miniqmt_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@

class TradeAction(Enum):
"""交易动作枚举"""
BUY = "buy" # 买入
SELL = "sell" # 卖出
HOLD = "hold" # 持有
BUY = "买入"
SELL = "卖出"
HOLD = "持有"

class OrderType(Enum):
"""订单类型枚举"""
MARKET = "market" # 市价单
LIMIT = "limit" # 限价单
STOP = "stop" # 止损单
STOP_LIMIT = "stop_limit" # 止损限价单
MARKET = "市价单"
LIMIT = "限价单"
STOP = "止损单"
STOP_LIMIT = "止损限价单"

class PositionSide(Enum):
"""持仓方向枚举"""
LONG = "long" # 多头
SHORT = "short" # 空头
NONE = "none" # 无持仓
LONG = "多头"
SHORT = "空头"
NONE = "无持仓"

class MiniQMTInterface:
"""
Expand Down Expand Up @@ -557,7 +557,36 @@ def from_dict(cls, data: Dict):


# 全局MiniQMT接口实例
miniqmt = MiniQMTInterface()
miniqmt = None

# 应用启动时初始化MiniQMT
config = None

try:
# 优先从config模块导入配置
from config import MINIQMT_CONFIG
config = MINIQMT_CONFIG
except ImportError:
# 如果无法导入config模块,则从环境变量读取配置
import os
config = {
'enabled': os.getenv("MINIQMT_ENABLED", "false").lower() == "true",
'account_id': os.getenv("MINIQMT_ACCOUNT_ID", ""),
'path': os.getenv("MINIQMT_PATH", "D:\\qmt\\userdata_mini"),
'host': os.getenv("MINIQMT_HOST", "127.0.0.1"),
'port': int(os.getenv("MINIQMT_PORT", "58610")),
}

# 初始化MiniQMT实例
miniqmt = MiniQMTInterface(config)

# 如果启用,尝试连接
if config.get('enabled', False):
success, msg = miniqmt.connect()
if success:
print(f"✅ MiniQMT已连接: {msg}")
else:
print(f"⚠️ MiniQMT连接失败: {msg}")


def init_miniqmt(config: Dict = None) -> Tuple[bool, str]:
Expand All @@ -579,9 +608,13 @@ def init_miniqmt(config: Dict = None) -> Tuple[bool, str]:
from config import MINIQMT_CONFIG
config = MINIQMT_CONFIG
except ImportError:
import os
config = {
'enabled': False,
'account_id': None
'enabled': os.getenv("MINIQMT_ENABLED", "false").lower() == "true",
'account_id': os.getenv("MINIQMT_ACCOUNT_ID", ""),
'path': os.getenv("MINIQMT_PATH", "D:\\qmt\\userdata_mini"),
'host': os.getenv("MINIQMT_HOST", "127.0.0.1"),
'port': int(os.getenv("MINIQMT_PORT", "58610")),
}

miniqmt = MiniQMTInterface(config)
Expand Down
2 changes: 1 addition & 1 deletion monitor_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ def display_edit_dialog(stock_id: int):
quant_enabled = st.checkbox("启用量化自动交易", value=stock.get('quant_enabled', False))

if quant_enabled:
quant_config = stock.get('quant_config', {})
quant_config = stock.get('quant_config') or {}
max_position_pct = st.slider("最大仓位比例", 0.05, 0.5,
quant_config.get('max_position_pct', 0.2), 0.05)
auto_stop_loss = st.checkbox("自动止损", value=quant_config.get('auto_stop_loss', True))
Expand Down
8 changes: 4 additions & 4 deletions monitor_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def _check_trigger_conditions(self, stock: Dict, current_price: float):
message = f"股票 {stock['symbol']} ({stock['name']}) 价格 {current_price} 进入进场区间 [{entry_range['min']}-{entry_range['max']}]"
monitor_db.add_notification(stock['id'], 'entry', message)

# 立即发送通知(包括邮件
# 立即发送通知(包括邮件和Webhook
notification_service.send_notifications()

# 如果启用量化交易,执行自动交易
Expand All @@ -207,7 +207,7 @@ def _check_trigger_conditions(self, stock: Dict, current_price: float):
message = f"股票 {stock['symbol']} ({stock['name']}) 价格 {current_price} 达到止盈位 {take_profit}"
monitor_db.add_notification(stock['id'], 'take_profit', message)

# 立即发送通知(包括邮件
# 立即发送通知(包括邮件和Webhook
notification_service.send_notifications()

# 如果启用量化交易,执行自动交易
Expand All @@ -221,7 +221,7 @@ def _check_trigger_conditions(self, stock: Dict, current_price: float):
message = f"股票 {stock['symbol']} ({stock['name']}) 价格 {current_price} 达到止损位 {stop_loss}"
monitor_db.add_notification(stock['id'], 'stop_loss', message)

# 立即发送通知(包括邮件
# 立即发送通知(包括邮件和Webhook
notification_service.send_notifications()

# 如果启用量化交易,执行自动交易
Expand All @@ -237,7 +237,7 @@ def _execute_quant_trade(self, stock: Dict, signal_type: str, current_price: flo
return

# 获取量化配置
quant_config = stock.get('quant_config', {})
quant_config = stock.get('quant_config') or {}
if not quant_config:
print(f"股票 {stock['symbol']} 未配置量化参数")
return
Expand Down
21 changes: 20 additions & 1 deletion monitor_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def display_monitor_panel():
st.markdown("## 📊 实时监测面板")

# 监测服务控制
col1, col2, col3, col4 = st.columns([1, 1, 1, 1])
col1, col2, col3 = st.columns([1, 1, 1])

with col1:
if st.button("▶️ 启动监测服务", type="primary"):
Expand All @@ -29,6 +29,9 @@ def display_monitor_panel():
monitor_service.manual_update_stock(stock['id'])
st.success(f"✅ 已手动更新 {len(stocks)} 只股票")

# 显示定时调度状态和通知配置
col4, col5 = st.columns([1, 2])

with col4:
# 显示定时调度状态
try:
Expand All @@ -41,6 +44,22 @@ def display_monitor_panel():
except:
st.info("⏰ 定时未配置")

with col5:
# 显示通知配置状态
email_status = notification_service.get_email_config_status()
webhook_status = notification_service.get_webhook_config_status()

status_text = []
if email_status['enabled'] and email_status['configured']:
status_text.append("📧 邮件通知已启用")
if webhook_status['enabled'] and webhook_status['configured']:
status_text.append(f"🔗 Webhook通知已启用 ({webhook_status['webhook_type']})")

if status_text:
st.success(" ".join(status_text))
else:
st.info("通知服务未完全配置")

# 显示通知
display_notifications()

Expand Down
5 changes: 5 additions & 0 deletions notification_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ def _load_config(self) -> Dict:
if os.getenv('WEBHOOK_KEYWORD'):
config['webhook_keyword'] = os.getenv('WEBHOOK_KEYWORD')

# 自动启用逻辑:如果配置了webhook_url,自动启用webhook通知
if config['webhook_url'] and not os.getenv('WEBHOOK_ENABLED'):
config['webhook_enabled'] = True
print("[自动启用] Webhook通知已根据WEBHOOK_URL配置自动启用")

return config

def send_notifications(self):
Expand Down
3 changes: 1 addition & 2 deletions pdf_generator_pandoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,6 @@ def display_pdf_export_section(stock_info, agents_results, discussion_result, fi
success = generate_pdf_report(stock_info, agents_results, discussion_result, final_decision)
if success:
st.balloons()

# 生成Markdown报告按钮
if st.button("📝 生成并下载Markdown报告", type="secondary", width='content', key=markdown_button_key):
with st.spinner("正在生成Markdown报告..."):
Expand Down Expand Up @@ -308,7 +307,7 @@ def display_pdf_export_section(stock_info, agents_results, discussion_result, fi

except Exception as e:
st.error(f"❌ 生成Markdown报告时出错: {str(e)}")

# 如果已经生成了报告,显示下载链接
if st.session_state.show_download_links:
generate_pdf_report(stock_info, agents_results, discussion_result, final_decision)
Binary file modified portfolio_stocks.db
Binary file not shown.
Loading