增加选股策略
This commit is contained in:
@@ -2,6 +2,26 @@
|
|||||||
|
|
||||||
- 初心:在股市摸爬滚打多年,自学自编各种指标,花冤柉钱学习了各种战法各种策略,也曾入各种小班,总是赚少赔多,逐渐失去在股市玩的信心。自从去年deepseek上市,一直探索用ai辅助分析,且近日受tradingagents项目启发(感谢原作),多agent结合跟踪主力资金战法(某指每年收费6000rmb),用各种ai辅助编程,拼凑了这么个小程序,根据软件提供的辅助信息,实盘测试盈率还是挺高的,并且逐步形成了自己的交易系统,近一个月来,账户也慢慢在扰亏为盈。开源此软件的目的,就是为了使像我一样的小散,不再迷范。也许这个软件不能让你发大财,但是他能给你足够的信心。最后提醒:股市有风险,入市需谨慎!
|
- 初心:在股市摸爬滚打多年,自学自编各种指标,花冤柉钱学习了各种战法各种策略,也曾入各种小班,总是赚少赔多,逐渐失去在股市玩的信心。自从去年deepseek上市,一直探索用ai辅助分析,且近日受tradingagents项目启发(感谢原作),多agent结合跟踪主力资金战法(某指每年收费6000rmb),用各种ai辅助编程,拼凑了这么个小程序,根据软件提供的辅助信息,实盘测试盈率还是挺高的,并且逐步形成了自己的交易系统,近一个月来,账户也慢慢在扰亏为盈。开源此软件的目的,就是为了使像我一样的小散,不再迷范。也许这个软件不能让你发大财,但是他能给你足够的信心。最后提醒:股市有风险,入市需谨慎!
|
||||||
|
|
||||||
|
## ⭐ 1214更新 - 小市值策略 📊
|
||||||
|
|
||||||
|
新增“小市值策略”选股板块,专注小盘高成长股票:
|
||||||
|
|
||||||
|
**核心功能:**
|
||||||
|
- ✅ **精准筛选** - 总市值≤50亿 + 营收增长率≥10% + 净利润增长率≥100% + 沪深A股
|
||||||
|
- ✅ **小盘优先** - 按总市值由小到大排序,优选最小盘股票
|
||||||
|
- ✅ **策略监控** - 复用低价擒牛监控框架,自动跟踪持仓
|
||||||
|
- ✅ **双重信号** - 持股5天 + MA5下穿MA20智能提醒
|
||||||
|
- ✅ **仓位管理** - 资金10万、个股最大3成、最多4股
|
||||||
|
- ✅ **铉钉通知** - 选股结果和卖出信号自动通知
|
||||||
|
|
||||||
|
**使用流程:**
|
||||||
|
1. 在选股板块中点击“📊 小市值策略”
|
||||||
|
2. 执行选股,查看筛选结果
|
||||||
|
3. 点击“➕ 加入策略监控”加入监控
|
||||||
|
4. 在策略监控面板统一管理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## ⭐ 1212更新 - 低价擒牛策略监控 🐂
|
## ⭐ 1212更新 - 低价擒牛策略监控 🐂
|
||||||
|
|
||||||
新增“低价擒牛”选股板块,自动监控持仓并智能提醒卖出:
|
新增“低价擒牛”选股板块,自动监控持仓并智能提醒卖出:
|
||||||
|
|||||||
@@ -314,7 +314,14 @@ def main():
|
|||||||
if st.button("🐂 低价擒牛", width='stretch', key="nav_low_price_bull", help="低价高成长股票筛选策略"):
|
if st.button("🐂 低价擒牛", width='stretch', key="nav_low_price_bull", help="低价高成长股票筛选策略"):
|
||||||
st.session_state.show_low_price_bull = True
|
st.session_state.show_low_price_bull = True
|
||||||
for key in ['show_history', 'show_monitor', 'show_config', 'show_sector_strategy',
|
for key in ['show_history', 'show_monitor', 'show_config', 'show_sector_strategy',
|
||||||
'show_longhubang', 'show_portfolio', 'show_main_force']:
|
'show_longhubang', 'show_portfolio', 'show_main_force', 'show_small_cap']:
|
||||||
|
if key in st.session_state:
|
||||||
|
del st.session_state[key]
|
||||||
|
|
||||||
|
if st.button("📊 小市值策略", width='stretch', key="nav_small_cap", help="小盘高成长股票筛选策略"):
|
||||||
|
st.session_state.show_small_cap = True
|
||||||
|
for key in ['show_history', 'show_monitor', 'show_config', 'show_sector_strategy',
|
||||||
|
'show_longhubang', 'show_portfolio', 'show_main_force', 'show_low_price_bull']:
|
||||||
if key in st.session_state:
|
if key in st.session_state:
|
||||||
del st.session_state[key]
|
del st.session_state[key]
|
||||||
|
|
||||||
@@ -473,6 +480,12 @@ def main():
|
|||||||
from low_price_bull_ui import display_low_price_bull
|
from low_price_bull_ui import display_low_price_bull
|
||||||
display_low_price_bull()
|
display_low_price_bull()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 检查是否显示小市值策略
|
||||||
|
if 'show_small_cap' in st.session_state and st.session_state.show_small_cap:
|
||||||
|
from small_cap_ui import display_small_cap
|
||||||
|
display_small_cap()
|
||||||
|
return
|
||||||
|
|
||||||
# 检查是否显示智策板块
|
# 检查是否显示智策板块
|
||||||
if 'show_sector_strategy' in st.session_state and st.session_state.show_sector_strategy:
|
if 'show_sector_strategy' in st.session_state and st.session_state.show_sector_strategy:
|
||||||
|
|||||||
@@ -0,0 +1,286 @@
|
|||||||
|
# 小市值策略功能说明
|
||||||
|
|
||||||
|
## 📊 功能概述
|
||||||
|
|
||||||
|
"小市值策略"是专注于小盘高成长股票的选股策略,通过严格的财务指标筛选,挖掘市值小、成长性强的优质股票。
|
||||||
|
|
||||||
|
## 🎯 筛选条件
|
||||||
|
|
||||||
|
### 核心指标
|
||||||
|
|
||||||
|
| 指标 | 条件 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 总市值 | ≤ 50亿 | 小盘股票,成长空间大 |
|
||||||
|
| 营收增长率 | ≥ 10% | 营业收入持续增长 |
|
||||||
|
| 净利润增长率 | ≥ 100% | 利润高速增长 |
|
||||||
|
| 市场范围 | 沪深A股 | 仅限A股市场 |
|
||||||
|
| ST股票 | 排除 | 避免风险股票 |
|
||||||
|
| 创业板 | 排除 | 聚焦主板和中小板 |
|
||||||
|
| 科创板 | 排除 | 避免高估值板块 |
|
||||||
|
|
||||||
|
### 排序规则
|
||||||
|
|
||||||
|
按**总市值由小到大**排序,优先选择最小盘的股票。
|
||||||
|
|
||||||
|
## 💰 量化交易策略
|
||||||
|
|
||||||
|
### 资金管理
|
||||||
|
|
||||||
|
| 参数 | 设置 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 资金量 | 10万元 | 初始投资金额 |
|
||||||
|
| 交易仓位 | 满仓 | 100%仓位运作 |
|
||||||
|
| 个股最大持仓 | 30% | 单只股票最多3万元 |
|
||||||
|
| 账户最大持股数 | 4只 | 分散风险 |
|
||||||
|
| 单日最大买入数 | 2只 | 控制买入节奏 |
|
||||||
|
|
||||||
|
### 买卖时机
|
||||||
|
|
||||||
|
**买入时机**:
|
||||||
|
- 开盘买入
|
||||||
|
- 按总市值由小到大排序
|
||||||
|
- 优先买入市值更小的股票
|
||||||
|
|
||||||
|
**卖出时机**:
|
||||||
|
1. **持股到期**:持股满5天自动提醒卖出
|
||||||
|
2. **技术信号**:MA5下穿MA20时提醒卖出
|
||||||
|
|
||||||
|
## 📊 策略监控
|
||||||
|
|
||||||
|
### 监控功能
|
||||||
|
|
||||||
|
小市值策略复用"低价擒牛"的监控框架,提供完整的监控服务:
|
||||||
|
|
||||||
|
- ✅ 自动跟踪持仓股票
|
||||||
|
- ✅ 每分钟扫描一次(可配置)
|
||||||
|
- ✅ 计算MA5和MA20均线
|
||||||
|
- ✅ 双重卖出信号检测
|
||||||
|
- ✅ 钉钉自动通知
|
||||||
|
- ✅ 自动移除已触发股票
|
||||||
|
|
||||||
|
### 使用流程
|
||||||
|
|
||||||
|
```
|
||||||
|
1. 执行选股
|
||||||
|
↓
|
||||||
|
2. 查看筛选结果
|
||||||
|
↓
|
||||||
|
3. 点击"➕ 加入策略监控"
|
||||||
|
↓
|
||||||
|
4. 进入"📊 策略监控"面板
|
||||||
|
↓
|
||||||
|
5. 统一管理所有监控股票
|
||||||
|
↓
|
||||||
|
6. 收到卖出提醒
|
||||||
|
↓
|
||||||
|
7. 执行卖出操作
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔔 通知功能
|
||||||
|
|
||||||
|
### 选股通知
|
||||||
|
|
||||||
|
选股完成后可以发送钉钉通知,包含:
|
||||||
|
- 筛选策略说明
|
||||||
|
- 筛选数量
|
||||||
|
- 精选股票列表
|
||||||
|
- 生成时间
|
||||||
|
|
||||||
|
### 卖出提醒
|
||||||
|
|
||||||
|
触发卖出条件时自动发送通知:
|
||||||
|
- 股票代码和名称
|
||||||
|
- 提醒类型(持股到期/MA死叉)
|
||||||
|
- 提醒原因
|
||||||
|
- 当前价格和MA数据
|
||||||
|
- 持有天数
|
||||||
|
- 卖出建议
|
||||||
|
|
||||||
|
## 💡 策略优势
|
||||||
|
|
||||||
|
### 1. 小盘优势
|
||||||
|
- **成长空间大**:小市值股票有更大的成长空间
|
||||||
|
- **灵活性强**:容易被市场关注和炒作
|
||||||
|
- **爆发力强**:小盘股涨幅潜力更大
|
||||||
|
|
||||||
|
### 2. 高成长性
|
||||||
|
- **营收增长**:营收增长率≥10%保证业务增长
|
||||||
|
- **利润爆发**:净利润增长率≥100%筛选高速成长股
|
||||||
|
- **双重保障**:营收和利润双重验证
|
||||||
|
|
||||||
|
### 3. 风险控制
|
||||||
|
- **排除ST**:避免财务风险股票
|
||||||
|
- **排除高估值板块**:不选创业板和科创板
|
||||||
|
- **仓位管理**:单股最多30%,最多持有4只
|
||||||
|
|
||||||
|
### 4. 智能监控
|
||||||
|
- **自动跟踪**:无需人工盯盘
|
||||||
|
- **双重信号**:时间+技术双重保护
|
||||||
|
- **即时通知**:钉钉消息实时接收
|
||||||
|
|
||||||
|
## 📋 与低价擒牛的对比
|
||||||
|
|
||||||
|
| 特性 | 低价擒牛 | 小市值策略 |
|
||||||
|
|------|---------|-----------|
|
||||||
|
| **筛选重点** | 股价 | 市值 |
|
||||||
|
| **价格条件** | <10元 | 无限制 |
|
||||||
|
| **市值条件** | 无限制 | ≤50亿 |
|
||||||
|
| **营收要求** | 无 | ≥10% |
|
||||||
|
| **净利增长** | ≥100% | ≥100% |
|
||||||
|
| **板块限制** | 深圳A股 | 沪深A股,非创业/科创 |
|
||||||
|
| **资金量** | 100万 | 10万 |
|
||||||
|
| **个股仓位** | 40% | 30% |
|
||||||
|
| **最大持股** | 4只 | 4只 |
|
||||||
|
|
||||||
|
## 🚀 使用示例
|
||||||
|
|
||||||
|
### 步骤1:选股
|
||||||
|
|
||||||
|
1. 点击左侧菜单"📊 小市值策略"
|
||||||
|
2. 设置筛选数量(默认5只)
|
||||||
|
3. 点击"🚀 开始小市值策略选股"
|
||||||
|
4. 等待筛选结果
|
||||||
|
|
||||||
|
### 步骤2:查看结果
|
||||||
|
|
||||||
|
筛选结果显示:
|
||||||
|
- 股票代码和名称
|
||||||
|
- 总市值
|
||||||
|
- 营收增长率
|
||||||
|
- 净利润增长率
|
||||||
|
- 所属行业
|
||||||
|
- 其他财务指标
|
||||||
|
|
||||||
|
### 步骤3:加入监控
|
||||||
|
|
||||||
|
1. 在股票详情中点击"➕ 加入策略监控"
|
||||||
|
2. 系统自动记录买入信息
|
||||||
|
3. 提示"已加入监控列表"
|
||||||
|
|
||||||
|
### 步骤4:监控管理
|
||||||
|
|
||||||
|
1. 点击右上角"📊 策略监控"
|
||||||
|
2. 查看所有监控中的股票
|
||||||
|
3. 启动监控服务
|
||||||
|
4. 等待卖出提醒
|
||||||
|
|
||||||
|
### 步骤5:接收通知
|
||||||
|
|
||||||
|
- 钉钉收到卖出提醒
|
||||||
|
- 查看提醒详情
|
||||||
|
- 执行卖出操作
|
||||||
|
|
||||||
|
## ⚙️ 配置说明
|
||||||
|
|
||||||
|
### 环境变量
|
||||||
|
|
||||||
|
小市值策略使用与低价擒牛相同的配置:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# .env文件
|
||||||
|
TDX_BASE_URL=http://192.168.1.222:8181 # TDX API地址
|
||||||
|
LOW_PRICE_BULL_SCAN_INTERVAL=60 # 扫描间隔(秒)
|
||||||
|
LOW_PRICE_BULL_HOLDING_DAYS=5 # 持股天数限制
|
||||||
|
|
||||||
|
# Webhook通知
|
||||||
|
WEBHOOK_ENABLED=true
|
||||||
|
WEBHOOK_URL=your_dingtalk_webhook_url
|
||||||
|
WEBHOOK_TYPE=dingtalk
|
||||||
|
WEBHOOK_KEYWORD=aiagents通知
|
||||||
|
```
|
||||||
|
|
||||||
|
### 监控服务
|
||||||
|
|
||||||
|
小市值策略和低价擒牛共享同一个监控服务:
|
||||||
|
- 统一的监控面板
|
||||||
|
- 统一的数据库
|
||||||
|
- 统一的提醒机制
|
||||||
|
|
||||||
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
|
### 1. 风险提示
|
||||||
|
- 小盘股流动性相对较差
|
||||||
|
- 波动性较大
|
||||||
|
- 需要设置止损
|
||||||
|
|
||||||
|
### 2. 数据要求
|
||||||
|
- 需要TDX API支持
|
||||||
|
- 至少20天K线数据
|
||||||
|
- 实时数据更新
|
||||||
|
|
||||||
|
### 3. 监控服务
|
||||||
|
- 需要手动启动
|
||||||
|
- 保持系统运行
|
||||||
|
- 定期检查状态
|
||||||
|
|
||||||
|
### 4. 策略执行
|
||||||
|
- 建议分批买入
|
||||||
|
- 严格执行止损
|
||||||
|
- 不要追涨杀跌
|
||||||
|
|
||||||
|
## 📈 策略逻辑
|
||||||
|
|
||||||
|
### 选股逻辑
|
||||||
|
|
||||||
|
```
|
||||||
|
总市值≤50亿
|
||||||
|
↓
|
||||||
|
营收增长率≥10%
|
||||||
|
↓
|
||||||
|
净利润增长率≥100%
|
||||||
|
↓
|
||||||
|
沪深A股
|
||||||
|
↓
|
||||||
|
排除ST/创业板/科创板
|
||||||
|
↓
|
||||||
|
按总市值排序
|
||||||
|
↓
|
||||||
|
选择前N只
|
||||||
|
```
|
||||||
|
|
||||||
|
### 监控逻辑
|
||||||
|
|
||||||
|
```
|
||||||
|
加入监控
|
||||||
|
↓
|
||||||
|
记录买入信息
|
||||||
|
↓
|
||||||
|
每分钟扫描
|
||||||
|
↓
|
||||||
|
检查持股天数
|
||||||
|
↓
|
||||||
|
检查MA5/MA20
|
||||||
|
↓
|
||||||
|
触发卖出条件?
|
||||||
|
↓
|
||||||
|
生成提醒
|
||||||
|
↓
|
||||||
|
发送通知
|
||||||
|
↓
|
||||||
|
自动移除
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 适用场景
|
||||||
|
|
||||||
|
### 适合
|
||||||
|
- ✅ 寻找高成长小盘股
|
||||||
|
- ✅ 短线波段操作
|
||||||
|
- ✅ 资金量较小的投资者
|
||||||
|
- ✅ 愿意承担一定风险
|
||||||
|
|
||||||
|
### 不适合
|
||||||
|
- ❌ 追求稳健收益
|
||||||
|
- ❌ 长期价值投资
|
||||||
|
- ❌ 风险承受能力低
|
||||||
|
- ❌ 对流动性要求高
|
||||||
|
|
||||||
|
## 📚 相关文档
|
||||||
|
|
||||||
|
- [低价擒牛策略监控配置说明](./低价擒牛策略监控配置说明.md)
|
||||||
|
- [TDX API配置快速指南](./TDX_API配置快速指南.md)
|
||||||
|
- [Webhook通知配置指南](./Webhook通知配置指南.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**更新时间**:2024-12-14
|
||||||
|
**版本**:v1.0
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
小市值策略选股模块
|
||||||
|
使用pywencai进行股票筛选
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Tuple, Optional
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
|
||||||
|
class SmallCapSelector:
|
||||||
|
"""小市值策略选股器"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def get_small_cap_stocks(self, top_n: int = 5) -> Tuple[bool, Optional[pd.DataFrame], str]:
|
||||||
|
"""
|
||||||
|
获取符合小市值策略的股票
|
||||||
|
|
||||||
|
筛选条件:
|
||||||
|
- 总市值 ≤ 50亿
|
||||||
|
- 营收增长率 ≥ 10%
|
||||||
|
- 净利润增长率 ≥ 100%
|
||||||
|
- 沪深A股
|
||||||
|
- 非ST
|
||||||
|
- 非创业板
|
||||||
|
- 非科创板
|
||||||
|
- 按总市值由小到大排名
|
||||||
|
|
||||||
|
Args:
|
||||||
|
top_n: 返回前N只股票
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(是否成功, 数据DataFrame, 消息)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import pywencai
|
||||||
|
|
||||||
|
# 构建查询语句(按总市值由小至大排名)
|
||||||
|
query = (
|
||||||
|
"总市值≤50亿,"
|
||||||
|
"营收增长率≥10%,"
|
||||||
|
"净利润增长率(净利润同比增长率)≥100%,"
|
||||||
|
"沪深A股,"
|
||||||
|
"非ST,"
|
||||||
|
"非创业板,"
|
||||||
|
"非科创板,"
|
||||||
|
"总市值由小至大排名"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.logger.info(f"开始执行小市值策略选股,查询条件: {query}")
|
||||||
|
|
||||||
|
# 调用pywencai
|
||||||
|
result = pywencai.get(query=query, loop=True)
|
||||||
|
|
||||||
|
if result is None or result.empty:
|
||||||
|
self.logger.warning("未获取到符合条件的股票")
|
||||||
|
return False, None, "未找到符合条件的股票"
|
||||||
|
|
||||||
|
self.logger.info(f"获取到 {len(result)} 只股票")
|
||||||
|
|
||||||
|
# 取前N只
|
||||||
|
if len(result) > top_n:
|
||||||
|
result = result.head(top_n)
|
||||||
|
self.logger.info(f"筛选前 {top_n} 只股票")
|
||||||
|
|
||||||
|
return True, result, f"成功获取 {len(result)} 只股票"
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
error_msg = "pywencai模块未安装,请执行: pip install pywencai"
|
||||||
|
self.logger.error(error_msg)
|
||||||
|
return False, None, error_msg
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = f"选股失败: {str(e)}"
|
||||||
|
self.logger.error(error_msg)
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return False, None, error_msg
|
||||||
|
|
||||||
|
def format_stock_info(self, df: pd.DataFrame) -> str:
|
||||||
|
"""
|
||||||
|
格式化股票信息为文本
|
||||||
|
|
||||||
|
Args:
|
||||||
|
df: 股票数据DataFrame
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
格式化后的文本
|
||||||
|
"""
|
||||||
|
if df is None or df.empty:
|
||||||
|
return "无数据"
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
for idx, row in df.iterrows():
|
||||||
|
stock_code = row.get('股票代码', 'N/A')
|
||||||
|
stock_name = row.get('股票简称', 'N/A')
|
||||||
|
market_cap = row.get('总市值', row.get('总市值[20241211]', 'N/A'))
|
||||||
|
revenue_growth = row.get('营收增长率', row.get('营业收入增长率', 'N/A'))
|
||||||
|
profit_growth = row.get('净利润增长率', row.get('净利润同比增长率', 'N/A'))
|
||||||
|
|
||||||
|
line = f"{idx+1}. {stock_code} {stock_name}"
|
||||||
|
|
||||||
|
# 添加详细信息
|
||||||
|
details = []
|
||||||
|
if market_cap != 'N/A':
|
||||||
|
try:
|
||||||
|
cap_val = float(market_cap)
|
||||||
|
if cap_val >= 100000000:
|
||||||
|
details.append(f"市值:{cap_val/100000000:.2f}亿")
|
||||||
|
else:
|
||||||
|
details.append(f"市值:{cap_val/10000:.2f}万")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if revenue_growth != 'N/A':
|
||||||
|
try:
|
||||||
|
details.append(f"营收增长:{float(revenue_growth):.2f}%")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if profit_growth != 'N/A':
|
||||||
|
try:
|
||||||
|
details.append(f"净利增长:{float(profit_growth):.2f}%")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if details:
|
||||||
|
line += f" - {', '.join(details)}"
|
||||||
|
|
||||||
|
lines.append(line)
|
||||||
|
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
# 全局实例
|
||||||
|
small_cap_selector = SmallCapSelector()
|
||||||
+283
@@ -0,0 +1,283 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
小市值策略UI模块
|
||||||
|
"""
|
||||||
|
|
||||||
|
import streamlit as st
|
||||||
|
import pandas as pd
|
||||||
|
from datetime import datetime
|
||||||
|
from small_cap_selector import small_cap_selector
|
||||||
|
from notification_service import notification_service
|
||||||
|
from low_price_bull_monitor import low_price_bull_monitor
|
||||||
|
from low_price_bull_service import low_price_bull_service
|
||||||
|
|
||||||
|
|
||||||
|
def display_small_cap():
|
||||||
|
"""显示小市值策略界面"""
|
||||||
|
|
||||||
|
# 检查是否显示监控面板
|
||||||
|
if st.session_state.get('show_small_cap_monitor'):
|
||||||
|
from low_price_bull_monitor_ui import display_monitor_panel
|
||||||
|
display_monitor_panel()
|
||||||
|
|
||||||
|
# 返回按钮
|
||||||
|
if st.button("🔙 返回选股", type="secondary"):
|
||||||
|
del st.session_state.show_small_cap_monitor
|
||||||
|
st.rerun()
|
||||||
|
return
|
||||||
|
|
||||||
|
st.markdown("顶部按钮区")
|
||||||
|
col_select, col_monitor = st.columns([3, 1])
|
||||||
|
|
||||||
|
with col_select:
|
||||||
|
st.markdown("## 📊 小市值策略 - 小盘高成长股票筛选")
|
||||||
|
|
||||||
|
with col_monitor:
|
||||||
|
st.write("") # 占位
|
||||||
|
if st.button("📊 策略监控", type="primary", use_container_width=True):
|
||||||
|
st.session_state.show_small_cap_monitor = True
|
||||||
|
st.rerun()
|
||||||
|
|
||||||
|
st.markdown("---")
|
||||||
|
|
||||||
|
st.markdown("""
|
||||||
|
### 📋 选股策略说明
|
||||||
|
|
||||||
|
**筛选条件**:
|
||||||
|
- ✅ 总市值 ≤ 50亿
|
||||||
|
- ✅ 营收增长率 ≥ 10%
|
||||||
|
- ✅ 净利润增长率 ≥ 100%(净利润同比增长率)
|
||||||
|
- ✅ 沪深A股
|
||||||
|
- ✅ 非ST股票
|
||||||
|
- ✅ 非创业板
|
||||||
|
- ✅ 非科创板
|
||||||
|
- ✅ 按总市值由小至大排名
|
||||||
|
|
||||||
|
**量化交易策略**:
|
||||||
|
- 💰 资金量:10万元
|
||||||
|
- 📅 持股周期:5天
|
||||||
|
- 💼 仓位控制:满仓
|
||||||
|
- 📊 个股最大持仓:3成(30%)
|
||||||
|
- 🎯 账户最大持股数:4只
|
||||||
|
- 🛒 单日最大买入数:2只
|
||||||
|
- 📈 买入时机:开盘买入
|
||||||
|
- 📉 卖出时机:MA5下穿MA20或持股满5天
|
||||||
|
""")
|
||||||
|
|
||||||
|
st.markdown("---")
|
||||||
|
|
||||||
|
# 参数设置
|
||||||
|
col1, col2 = st.columns([2, 1])
|
||||||
|
|
||||||
|
with col1:
|
||||||
|
top_n = st.slider(
|
||||||
|
"筛选数量",
|
||||||
|
min_value=3,
|
||||||
|
max_value=10,
|
||||||
|
value=5,
|
||||||
|
step=1,
|
||||||
|
help="选择展示的股票数量"
|
||||||
|
)
|
||||||
|
|
||||||
|
with col2:
|
||||||
|
st.info(f"💡 将筛选市值最小的前{top_n}只股票")
|
||||||
|
|
||||||
|
st.markdown("---")
|
||||||
|
|
||||||
|
# 开始选股按钮
|
||||||
|
if st.button("🚀 开始小市值策略选股", type="primary", use_container_width=True):
|
||||||
|
|
||||||
|
with st.spinner("正在获取数据,请稍候..."):
|
||||||
|
# 创建选股器
|
||||||
|
success, stocks_df, message = small_cap_selector.get_small_cap_stocks(top_n)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
st.error(f"❌ {message}")
|
||||||
|
return
|
||||||
|
|
||||||
|
st.success(f"✅ {message}")
|
||||||
|
|
||||||
|
# 保存到session_state
|
||||||
|
st.session_state.small_cap_stocks = stocks_df
|
||||||
|
st.session_state.small_cap_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
# 显示选股结果
|
||||||
|
if 'small_cap_stocks' in st.session_state and st.session_state.small_cap_stocks is not None:
|
||||||
|
st.markdown("---")
|
||||||
|
st.markdown("## 📈 选股结果")
|
||||||
|
|
||||||
|
stocks_df = st.session_state.small_cap_stocks
|
||||||
|
select_time = st.session_state.small_cap_time
|
||||||
|
|
||||||
|
st.info(f"🕒 选股时间:{select_time} | 📊 股票数量:{len(stocks_df)} 只")
|
||||||
|
|
||||||
|
# 显示股票列表
|
||||||
|
display_stock_list(stocks_df)
|
||||||
|
|
||||||
|
# 发送钉钉通知
|
||||||
|
st.markdown("---")
|
||||||
|
if st.button("📲 发送钉钉通知", type="secondary", use_container_width=True):
|
||||||
|
send_dingtalk_notification(stocks_df)
|
||||||
|
|
||||||
|
|
||||||
|
def display_stock_list(stocks_df: pd.DataFrame):
|
||||||
|
"""显示股票列表"""
|
||||||
|
|
||||||
|
for idx, row in stocks_df.iterrows():
|
||||||
|
stock_code = row.get('股票代码', 'N/A')
|
||||||
|
stock_name = row.get('股票简称', 'N/A')
|
||||||
|
|
||||||
|
with st.expander(f"📊 {idx+1}. {stock_code} {stock_name}", expanded=True):
|
||||||
|
display_stock_detail(row)
|
||||||
|
|
||||||
|
|
||||||
|
def display_stock_detail(row: pd.Series):
|
||||||
|
"""显示股票详细信息"""
|
||||||
|
|
||||||
|
# 获取所有可能的字段
|
||||||
|
financial_fields = [
|
||||||
|
('总市值', row.get('总市值', row.get('总市值[20241211]', None))),
|
||||||
|
('营收增长率', row.get('营收增长率', row.get('营业收入增长率', None))),
|
||||||
|
('净利润增长率', row.get('净利润增长率', row.get('净利润同比增长率', None))),
|
||||||
|
('股价', row.get('股价', row.get('最新价', None))),
|
||||||
|
('市盈率', row.get('市盈率', row.get('市盈率TTM', None))),
|
||||||
|
('市净率', row.get('市净率', row.get('市净率PB', None))),
|
||||||
|
('所属行业', row.get('所属行业', row.get('所属同花顺行业', None))),
|
||||||
|
]
|
||||||
|
|
||||||
|
# 检查是否有任何有效数据
|
||||||
|
has_any_data = any(is_valid_value(value) for _, value in financial_fields)
|
||||||
|
|
||||||
|
# 决定布局
|
||||||
|
if has_any_data:
|
||||||
|
col1, col2 = st.columns(2)
|
||||||
|
else:
|
||||||
|
col1 = st.container()
|
||||||
|
col2 = None
|
||||||
|
|
||||||
|
with col1:
|
||||||
|
st.markdown("#### 📊 基本信息")
|
||||||
|
st.markdown(f"**股票代码**: {row.get('股票代码', 'N/A')}")
|
||||||
|
st.markdown(f"**股票名称**: {row.get('股票简称', 'N/A')}")
|
||||||
|
|
||||||
|
# 只有当有财务数据时才显示财务指标
|
||||||
|
if col2 is not None:
|
||||||
|
with col2:
|
||||||
|
st.markdown("#### 💼 财务指标")
|
||||||
|
|
||||||
|
for field_name, value in financial_fields:
|
||||||
|
if is_valid_value(value):
|
||||||
|
formatted_value = format_value(value, get_suffix(field_name))
|
||||||
|
st.markdown(f"**{field_name}**: {formatted_value}")
|
||||||
|
|
||||||
|
# 添加监控按钮
|
||||||
|
st.markdown("---")
|
||||||
|
st.markdown("#### 📊 策略监控")
|
||||||
|
|
||||||
|
from low_price_bull_monitor_ui import add_stock_to_monitor_button
|
||||||
|
|
||||||
|
stock_code = row.get('股票代码', '')
|
||||||
|
stock_name = row.get('股票简称', '')
|
||||||
|
price = row.get('股价', row.get('最新价', None))
|
||||||
|
|
||||||
|
# 去掉代码后缀
|
||||||
|
if isinstance(stock_code, str) and '.' in stock_code:
|
||||||
|
stock_code = stock_code.split('.')[0]
|
||||||
|
|
||||||
|
# 转换价格
|
||||||
|
try:
|
||||||
|
price_float = float(price) if price and not pd.isna(price) else None
|
||||||
|
except:
|
||||||
|
price_float = None
|
||||||
|
|
||||||
|
if stock_code and stock_name:
|
||||||
|
add_stock_to_monitor_button(stock_code, stock_name, price_float)
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_value(value):
|
||||||
|
"""判断值是否有效"""
|
||||||
|
if value is None:
|
||||||
|
return False
|
||||||
|
if pd.isna(value):
|
||||||
|
return False
|
||||||
|
if str(value).strip() in ['', 'N/A', 'nan', 'None']:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def format_value(value, suffix=''):
|
||||||
|
"""格式化显示值"""
|
||||||
|
if isinstance(value, (int, float)):
|
||||||
|
if abs(value) >= 100000000: # 亿
|
||||||
|
return f"{value/100000000:.2f}亿{suffix}"
|
||||||
|
elif abs(value) >= 10000: # 万
|
||||||
|
return f"{value/10000:.2f}万{suffix}"
|
||||||
|
else:
|
||||||
|
return f"{value:.2f}{suffix}"
|
||||||
|
return f"{value}{suffix}"
|
||||||
|
|
||||||
|
|
||||||
|
def get_suffix(field_name: str) -> str:
|
||||||
|
"""获取字段后缀"""
|
||||||
|
suffix_map = {
|
||||||
|
'总市值': '元',
|
||||||
|
'股价': '元',
|
||||||
|
'营收增长率': '%',
|
||||||
|
'净利润增长率': '%',
|
||||||
|
}
|
||||||
|
return suffix_map.get(field_name, '')
|
||||||
|
|
||||||
|
|
||||||
|
def send_dingtalk_notification(stocks_df: pd.DataFrame):
|
||||||
|
"""发送钉钉通知"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not notification_service.config['webhook_enabled']:
|
||||||
|
st.warning("⚠️ Webhook通知未启用,请在系统配置中启用")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 构建消息
|
||||||
|
keyword = notification_service.config.get('webhook_keyword', 'aiagents通知')
|
||||||
|
|
||||||
|
message_text = f"### {keyword} - 小市值策略选股完成\n\n"
|
||||||
|
message_text += "**筛选策略**: 总市值≤50亿 + 营收增长率≥10% + 净利润增长率≥100% + 沪深A股\n\n"
|
||||||
|
message_text += f"**筛选数量**: {len(stocks_df)} 只\n\n"
|
||||||
|
message_text += "**精选股票**:\n\n"
|
||||||
|
|
||||||
|
for idx, row in stocks_df.iterrows():
|
||||||
|
stock_code = row.get('股票代码', 'N/A')
|
||||||
|
stock_name = row.get('股票简称', 'N/A')
|
||||||
|
message_text += f"{idx+1}. {stock_code} {stock_name}\n\n"
|
||||||
|
|
||||||
|
message_text += f"**生成时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
|
||||||
|
message_text += "_此消息由AI股票分析系统自动发送_"
|
||||||
|
|
||||||
|
# 直接发送钉钉Webhook
|
||||||
|
if notification_service.config['webhook_type'] == 'dingtalk':
|
||||||
|
import requests
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"msgtype": "markdown",
|
||||||
|
"markdown": {
|
||||||
|
"title": f"{keyword}",
|
||||||
|
"text": message_text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
notification_service.config['webhook_url'],
|
||||||
|
json=data,
|
||||||
|
headers={'Content-Type': 'application/json'},
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
st.success("✅ 钉钉通知发送成功")
|
||||||
|
else:
|
||||||
|
st.error(f"❌ 钉钉通知发送失败: HTTP {response.status_code}")
|
||||||
|
else:
|
||||||
|
st.warning("⚠️ 当前仅支持钉钉通知")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
st.error(f"❌ 发送通知失败: {str(e)}")
|
||||||
Reference in New Issue
Block a user