From 01825eab57fb77a8d84dbb0b8ad3d0e698a1bd33 Mon Sep 17 00:00:00 2001 From: zyj118 Date: Mon, 13 Jan 2025 19:48:17 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=EF=BC=9A=E4=BB=B7=E5=80=BC=E6=8A=95=E8=B5=84=E7=9B=AF=E7=9B=98?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 38 + README.md | 64 + app/__init__.py | 25 + app/api/stock_routes.py | 82 + app/config.py | 29 + app/models/stock.py | 32 + app/services/ai_analysis_service.py | 308 ++++ app/services/stock_service.py | 573 +++++++ app/templates/index.html | 1514 ++++++++++++++++++ app/templates/market.html | 223 +++ config.json.example | 4 + requirements.txt | 54 + run.py | 12 + stock_cache.json | 2283 +++++++++++++++++++++++++++ watchlist.json | 38 + 15 files changed, 5279 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 app/__init__.py create mode 100644 app/api/stock_routes.py create mode 100644 app/config.py create mode 100644 app/models/stock.py create mode 100644 app/services/ai_analysis_service.py create mode 100644 app/services/stock_service.py create mode 100644 app/templates/index.html create mode 100644 app/templates/market.html create mode 100644 config.json.example create mode 100644 requirements.txt create mode 100644 run.py create mode 100644 stock_cache.json create mode 100644 watchlist.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4ec4ccc --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# 虚拟环境 +venv/ +ENV/ + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# 项目特定 +config.json +*.log +.env +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..96e9c0e --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# 价值投资盯盘系统 + +一个基于 Python FastAPI 开发的智能股票分析与监控平台。 + +## 功能特点 + +- 实时股票数据监控 +- AI 智能分析 +- 财务指标分析 +- 价值投资建议 +- 股东信息查询 +- 指数行情展示 + +## 技术栈 + +- 后端:FastAPI + Python +- 前端:Bootstrap 5 + ECharts +- 数据源:Tushare API +- 部署:Uvicorn + +## 安装使用 + +1. 克隆项目 +```bash +git clone https://github.com/你的用户名/stock-monitor.git +cd stock-monitor +``` + +2. 安装依赖 +```bash +pip install -r requirements.txt +``` + +3. 配置 +- 复制 `config.json.example` 为 `config.json` +- 在 `config.json` 中配置你的 Tushare Token + +4. 运行 +```bash +python run.py +``` + +5. 访问 +打开浏览器访问 `http://localhost:8000` + +## 配置说明 + +主要配置项在 `app/config.py` 中: +- TUSHARE_TOKEN:Tushare API Token +- 其他配置项... + +## 开发说明 + +- 遵循 PEP 8 编码规范 +- 使用 Python 3.8 或以上版本 +- 保持代码简洁清晰 + +## 贡献指南 + +欢迎提交 Issue 和 Pull Request + +## 许可证 + +MIT License \ No newline at end of file diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..c8e2d15 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,25 @@ +from fastapi import FastAPI +from fastapi.staticfiles import StaticFiles +from fastapi.templating import Jinja2Templates +import tushare as ts +from app.config import Config + +# 确保必要的目录和文件存在 +Config.ensure_directories() + +# 创建FastAPI实例 +app = FastAPI() + +# 设置tushare token +ts.set_token(Config.TUSHARE_TOKEN) +pro = ts.pro_api() + +# Mount static files +app.mount("/static", StaticFiles(directory=Config.STATIC_DIR), name="static") + +# Set up templates +templates = Jinja2Templates(directory=Config.TEMPLATES_DIR) + +# 导入路由 +from app.api import stock_routes +app.include_router(stock_routes.router) \ No newline at end of file diff --git a/app/api/stock_routes.py b/app/api/stock_routes.py new file mode 100644 index 0000000..b908a32 --- /dev/null +++ b/app/api/stock_routes.py @@ -0,0 +1,82 @@ +from fastapi import APIRouter, Request, Form +from typing import Optional +from app.services.stock_service import StockService +from app.services.ai_analysis_service import AIAnalysisService +from app import templates + +router = APIRouter(prefix="") +stock_service = StockService() +ai_service = AIAnalysisService() + +@router.get("/") +async def home(request: Request): + return templates.TemplateResponse("index.html", {"request": request}) + +@router.get("/api/stock_info/{stock_code}") +async def get_stock_info(stock_code: str, force_refresh: bool = False): + return stock_service.get_stock_info(stock_code, force_refresh) + +@router.get("/api/watchlist") +async def get_watchlist(): + return stock_service.get_watchlist() + +@router.post("/api/add_watch") +async def add_watch( + stock_code: str = Form(...), + target_market_value_min: Optional[float] = Form(None), + target_market_value_max: Optional[float] = Form(None) +): + return stock_service.add_watch(stock_code, target_market_value_min, target_market_value_max) + +@router.delete("/api/remove_watch/{stock_code}") +async def remove_watch(stock_code: str): + return stock_service.remove_watch(stock_code) + +@router.get("/api/index_info") +async def get_index_info(): + return stock_service.get_index_info() + +@router.get("/market") +async def market(request: Request): + return templates.TemplateResponse("market.html", {"request": request}) + +@router.get("/api/company_detail/{stock_code}") +async def get_company_detail(stock_code: str): + return stock_service.get_company_detail(stock_code) + +@router.get("/api/holders/{stock_code}") +async def get_top_holders(stock_code: str): + """获取前十大股东数据""" + return stock_service.get_top_holders(stock_code) + +@router.get("/api/performance_forecast/{stock_code}") +async def get_performance_forecast(stock_code: str): + """获取业绩预告数据""" + # 处理股票代码格式 + if stock_code.startswith('6'): + ts_code = f"{stock_code}.SH" + elif stock_code.startswith(('0', '3')): + ts_code = f"{stock_code}.SZ" + else: + return {"error": "不支持的股票代码"} + + return stock_service.get_forecast_data(ts_code) + +@router.get("/api/value_analysis/{stock_code}") +async def get_value_analysis(stock_code: str): + """获取价值投资分析数据""" + return stock_service.get_value_analysis_data(stock_code) + +@router.get("/api/ai_analysis/{stock_code}") +async def get_ai_analysis(stock_code: str): + """获取AI价值投资分析结果""" + try: + # 首先获取价值分析数据 + analysis_data = stock_service.get_value_analysis_data(stock_code) + if "error" in analysis_data: + return analysis_data + + # 使用AI服务进行分析 + return ai_service.analyze_value_investment(analysis_data) + except Exception as e: + return {"error": f"AI分析失败: {str(e)}"} \ No newline at end of file diff --git a/app/config.py b/app/config.py new file mode 100644 index 0000000..7ea0fc3 --- /dev/null +++ b/app/config.py @@ -0,0 +1,29 @@ +import os + +# 基础配置 +class Config: + # 项目根目录 + BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + # Tushare API配置 + TUSHARE_TOKEN = '90f8a141125e1decb952cd49032b7b8409a2d7fa370745f6c9f45c96' + + # 配置文件路径 + CONFIG_FILE = os.path.join(BASE_DIR, "config.json") + + # 模板目录 + TEMPLATES_DIR = os.path.join(BASE_DIR, "app", "templates") + + # 静态文件目录 + STATIC_DIR = os.path.join(BASE_DIR, "app", "static") + + # 确保目录存在 + @classmethod + def ensure_directories(cls): + os.makedirs(cls.STATIC_DIR, exist_ok=True) + os.makedirs(cls.TEMPLATES_DIR, exist_ok=True) + + # 确保配置文件存在 + if not os.path.exists(cls.CONFIG_FILE): + with open(cls.CONFIG_FILE, 'w', encoding='utf-8') as f: + f.write('{"watchlist": {}}') \ No newline at end of file diff --git a/app/models/stock.py b/app/models/stock.py new file mode 100644 index 0000000..4ccf75b --- /dev/null +++ b/app/models/stock.py @@ -0,0 +1,32 @@ +from pydantic import BaseModel +from typing import Dict, Optional, Union, List + +class MarketValueTarget(BaseModel): + min: Optional[float] = None + max: Optional[float] = None + +class StockTarget(BaseModel): + target_market_value: Optional[MarketValueTarget] = None + +class StockInfo(BaseModel): + code: str + name: str + market_value: float + pe_ratio: float + pb_ratio: float + ps_ratio: float + dividend_yield: float + price: float + change_percent: float + roe: float + gross_profit_margin: float + net_profit_margin: float + debt_to_assets: float + revenue_yoy: float + net_profit_yoy: float + bps: float + ocfps: float + +class StockResponse(BaseModel): + stock_info: StockInfo + targets: StockTarget \ No newline at end of file diff --git a/app/services/ai_analysis_service.py b/app/services/ai_analysis_service.py new file mode 100644 index 0000000..a44e68e --- /dev/null +++ b/app/services/ai_analysis_service.py @@ -0,0 +1,308 @@ +import json +import os +import re +from openai import OpenAI + +class AIAnalysisService: + def __init__(self): + self.model = "ep-20250111143839-vn8l8" # endpoint ID + self.client = OpenAI( + api_key = "cf4edd4d-55cd-4e0f-82f6-49072660bdaf", # 直接使用API Key + base_url = "https://ark.cn-beijing.volces.com/api/v3" + ) + + def analyze_value_investment(self, analysis_data): + """ + 对股票进行价值投资分析 + :param analysis_data: 包含各项财务指标的字典 + :return: AI分析结果 + """ + try: + # 打印输入数据用于调试 + print(f"输入的分析数据: {json.dumps(analysis_data, ensure_ascii=False, indent=2)}") + + # 构建提示词 + prompt = self._build_analysis_prompt(analysis_data) + + # 打印提示词用于调试 + print(f"AI分析提示词: {prompt}") + + # 调用API + response = self.client.chat.completions.create( + model=self.model, + messages=[ + { + "role": "user", + "content": [ + { + "type": "text", + "text": prompt + } + ] + } + ] + ) + + # 获取分析结果 + analysis_text = response.choices[0].message.content + print(f"AI原始返回结果: {analysis_text}") + + try: + # 尝试解析JSON + analysis_result = json.loads(analysis_text) + print(f"解析后的JSON结果: {json.dumps(analysis_result, ensure_ascii=False, indent=2)}") + + # 构建完整的返回结果 + result = analysis_result + + print(f"最终返回结果: {json.dumps(result, ensure_ascii=False, indent=2)}") + return result + + except json.JSONDecodeError as e: + print(f"JSON解析失败: {str(e)}") + # 如果JSON解析失败,返回错误信息 + return { + 'stock_info': analysis_data.get('stock_info', {}), + 'valuation': analysis_data.get('valuation', {}), + 'profitability': analysis_data.get('profitability', {}), + 'growth': analysis_data.get('growth', {}), + 'operation': analysis_data.get('operation', {}), + 'solvency': analysis_data.get('solvency', {}), + 'cash_flow': analysis_data.get('cash_flow', {}), + 'per_share': analysis_data.get('per_share', {}), + 'analysis_result': { + "error": "AI返回的结果不是有效的JSON格式", + "raw_text": analysis_text + } + } + + except Exception as e: + print(f"AI分析失败: {str(e)}") + print(f"错误详情: {e.__class__.__name__}") + import traceback + print(f"错误堆栈: {traceback.format_exc()}") + return {"error": f"AI分析失败: {str(e)}"} + + def _parse_analysis_result(self, analysis_text, current_price): + """ + 解析AI返回的分析文本,提取结构化信息 + """ + try: + print(f"开始解析分析文本...") + + # 提取投资建议 + suggestion_pattern = r"投资建议[::]([\s\S]*?)(?=\n\n|$)" + suggestion_match = re.search(suggestion_pattern, analysis_text, re.MULTILINE | re.DOTALL) + investment_suggestion = suggestion_match.group(1).strip() if suggestion_match else "" + print(f"提取到的投资建议: {investment_suggestion}") + + # 提取合理价格区间 + price_pattern = r"合理股价区间[::]\s*(\d+\.?\d*)\s*[元-]\s*(\d+\.?\d*)[元]" + price_match = re.search(price_pattern, analysis_text) + if price_match: + price_min = float(price_match.group(1)) + price_max = float(price_match.group(2)) + else: + price_min = current_price * 0.8 + price_max = current_price * 1.2 + print(f"提取到的价格区间: {price_min}-{price_max}") + + # 提取目标市值区间(单位:亿元) + market_value_pattern = r"目标市值区间[::]\s*(\d+\.?\d*)\s*[亿-]\s*(\d+\.?\d*)[亿]" + market_value_match = re.search(market_value_pattern, analysis_text) + if market_value_match: + market_value_min = float(market_value_match.group(1)) + market_value_max = float(market_value_match.group(2)) + else: + # 尝试从文本中提取计算得出的市值 + calc_pattern = r"最低市值[=≈约]*(\d+\.?\d*)[亿].*最高市值[=≈约]*(\d+\.?\d*)[亿]" + calc_match = re.search(calc_pattern, analysis_text) + if calc_match: + market_value_min = float(calc_match.group(1)) + market_value_max = float(calc_match.group(2)) + else: + market_value_min = 0 + market_value_max = 0 + print(f"提取到的市值区间: {market_value_min}-{market_value_max}") + + # 提取各个分析维度的内容 + analysis_patterns = { + "valuation_analysis": r"估值分析([\s\S]*?)(?=###\s*财务状况分析|###\s*成长性分析|$)", + "financial_health": r"财务状况分析([\s\S]*?)(?=###\s*成长性分析|###\s*风险评估|$)", + "growth_potential": r"成长性分析([\s\S]*?)(?=###\s*风险评估|###\s*投资建议|$)", + "risk_assessment": r"风险评估([\s\S]*?)(?=###\s*投资建议|$)" + } + + analysis_results = {} + for key, pattern in analysis_patterns.items(): + match = re.search(pattern, analysis_text, re.MULTILINE | re.DOTALL) + content = match.group(1).strip() if match else "" + # 移除markdown标记和多余的空白字符 + content = re.sub(r'[#\-*]', '', content).strip() + analysis_results[key] = content + print(f"提取到的{key}: {content[:100]}...") + + return { + "investment_suggestion": investment_suggestion, + "analysis": analysis_results, + "price_analysis": { + "reasonable_price_range": { + "min": price_min, + "max": price_max + }, + "target_market_value": { + "min": market_value_min, + "max": market_value_max + } + } + } + + except Exception as e: + print(f"解析分析结果失败: {str(e)}") + print(f"错误详情: {e.__class__.__name__}") + import traceback + print(f"错误堆栈: {traceback.format_exc()}") + return { + "investment_suggestion": "分析结果解析失败", + "analysis": { + "valuation_analysis": "解析失败", + "financial_health": "解析失败", + "growth_potential": "解析失败", + "risk_assessment": "解析失败" + }, + "price_analysis": { + "reasonable_price_range": { + "min": current_price * 0.8, + "max": current_price * 1.2 + }, + "target_market_value": { + "min": 0, + "max": 0 + } + } + } + + def _build_analysis_prompt(self, data): + """ + 构建AI分析提示词 + """ + stock_info = data.get('stock_info', {}) + valuation = data.get('valuation', {}) + profitability = data.get('profitability', {}) + growth = data.get('growth', {}) + operation = data.get('operation', {}) + solvency = data.get('solvency', {}) + cash_flow = data.get('cash_flow', {}) + per_share = data.get('per_share', {}) + + # 格式化数值,保留4位小数 + def format_number(value): + try: + if value is None: + return "0.0000" + if isinstance(value, (int, float)): + if abs(value) < 0.0001: # 对于非常小的数值 + return "0.0000" + return f"{value:.4f}" + if isinstance(value, str): + try: + value = float(value) + if abs(value) < 0.0001: + return "0.0000" + return f"{value:.4f}" + except: + pass + return str(value) + except: + return "0.0000" + + # 格式化百分比,保留2位小数 + def format_percent(value): + try: + if value is None: + return "0.00%" + if isinstance(value, (int, float)): + # 如果值已经是小数形式(如0.5代表50%),则乘以100 + if abs(value) <= 1: + value = value * 100 + return f"{value:.2f}%" + if isinstance(value, str): + try: + value = float(value) + if abs(value) <= 1: + value = value * 100 + return f"{value:.2f}%" + except: + pass + return "0.00%" + except: + return "0.00%" + + # 构建数据部分 + data_section = f"""请作为一位专业的价值投资分析师,对{stock_info.get('name', '')}({stock_info.get('code', '')})进行深入的价值投资分析。 + +当前市场信息: +- 市盈率(PE):{format_number(valuation.get('pe_ratio'))} +- 市净率(PB):{format_number(valuation.get('pb_ratio'))} +- 市销率(PS):{format_number(valuation.get('ps_ratio'))} +- 股息率:{format_percent(valuation.get('dividend_yield'))} +- 总市值(亿元):{format_number(valuation.get('total_market_value'))} +- 当前股价:{format_number(stock_info.get('current_price'))}元 + +盈利能力指标: +- ROE:{format_percent(profitability.get('roe'))} +- 毛利率:{format_percent(profitability.get('gross_margin'))} +- 净利率:{format_percent(profitability.get('net_margin'))} + +成长能力指标: +- 净利润增长率:{format_percent(growth.get('net_profit_growth'))} +- 扣非净利润增长率:{format_percent(growth.get('deducted_net_profit_growth'))} +- 营收增长率:{format_percent(growth.get('revenue_growth'))} + +运营能力指标: +- 总资产周转率:{format_number(operation.get('asset_turnover'))}次/年 +- 存货周转率:{format_number(operation.get('inventory_turnover'))}次/年 +- 应收账款周转率:{format_number(operation.get('receivables_turnover'))}次/年 + +偿债能力指标: +- 流动比率:{format_number(solvency.get('current_ratio'))} +- 速动比率:{format_number(solvency.get('quick_ratio'))} +- 资产负债率:{format_percent(solvency.get('debt_to_assets'))} + +现金流指标: +- 经营现金流/营收比:{format_percent(cash_flow.get('ocf_to_revenue'))} +- 经营现金流同比增长:{format_percent(cash_flow.get('ocf_growth'))} + +每股指标: +- 每股收益(EPS):{format_number(per_share.get('eps'))}元 +- 每股净资产(BPS):{format_number(per_share.get('bps'))}元 +- 每股现金流(CFPS):{format_number(per_share.get('cfps'))}元 +- 每股经营现金流(OCFPS):{format_number(per_share.get('ocfps'))}元 +- 每股未分配利润:{format_number(per_share.get('retained_eps'))}元""" + + # 构建分析要求部分 + analysis_requirements = """ +请基于以上数据,从价值投资的角度进行分析。请特别注意: +1. 结合行业特点、公司竞争力、成长性等因素,给出合理的估值区间 +2. 存货周转率为0可能表示数据缺失,分析时需要谨慎对待 +3. 考虑当前市场环境和行业整体估值水平 + +在给出估值区间时,请充分考虑: +1. 公司所处行业特点和竞争格局 +2. 公司的竞争优势和市场地位 +3. 当前的盈利能力和成长性 +4. 财务健康状况和风险因素 +5. 宏观经济环境和行业周期 +6. 可比公司的估值水平 + +请以JSON格式返回分析结果,包含以下内容: +1. investment_suggestion: 投资建议,包含summary(总体建议)、action(具体操作建议)和key_points(关注重点) +2. analysis: 详细分析,包含估值分析、财务健康状况、成长潜力和风险评估 +3. price_analysis: 价格分析,包含合理价格区间和目标市值区间 + +请确保返回的是一个有效的JSON格式,数值使用数字而不是字符串(价格、市值等),文本分析使用字符串。分析要客观、专业、详细。""" + + # 组合完整的提示词 + prompt = data_section + analysis_requirements + + return prompt \ No newline at end of file diff --git a/app/services/stock_service.py b/app/services/stock_service.py new file mode 100644 index 0000000..7fa008a --- /dev/null +++ b/app/services/stock_service.py @@ -0,0 +1,573 @@ +import json +import os +from datetime import datetime +import pandas as pd +from app import pro +from app.config import Config +import numpy as np + +class StockService: + def __init__(self): + self.watchlist = {} + self.cache_file = os.path.join(Config.BASE_DIR, "stock_cache.json") + self.load_watchlist() + self.load_cache() + + def load_watchlist(self): + try: + if os.path.exists(Config.CONFIG_FILE): + with open(Config.CONFIG_FILE, 'r', encoding='utf-8') as f: + data = json.load(f) + self.watchlist = data.get('watchlist', {}) + except Exception as e: + print(f"Error loading watchlist: {str(e)}") + self.watchlist = {} + + def _save_watchlist(self): + try: + with open(Config.CONFIG_FILE, 'w', encoding='utf-8') as f: + json.dump({'watchlist': self.watchlist}, f, ensure_ascii=False, indent=4) + except Exception as e: + print(f"Error saving watchlist: {str(e)}") + + def load_cache(self): + try: + if os.path.exists(self.cache_file): + with open(self.cache_file, 'r', encoding='utf-8') as f: + self.cache_data = json.load(f) + else: + self.cache_data = {} + except Exception as e: + print(f"Error loading cache: {str(e)}") + self.cache_data = {} + + def save_cache(self, stock_code, data): + try: + self.cache_data[stock_code] = { + 'data': data, + 'timestamp': datetime.now().strftime('%Y-%m-%d') + } + with open(self.cache_file, 'w', encoding='utf-8') as f: + json.dump(self.cache_data, f, ensure_ascii=False, indent=4) + except Exception as e: + print(f"Error saving cache: {str(e)}") + + def get_stock_info(self, stock_code: str, force_refresh: bool = False): + try: + # 检查缓存 + today = datetime.now().strftime('%Y-%m-%d') + if not force_refresh and stock_code in self.cache_data and self.cache_data[stock_code]['timestamp'] == today: + print(f"从缓存获取股票 {stock_code} 的数据") + cached_data = self.cache_data[stock_code]['data'] + cached_data['stock_info']['from_cache'] = True + return cached_data + + # 如果强制刷新或缓存不存在或已过期,从API获取数据 + print(f"从API获取股票 {stock_code} 的数据...") + + # 处理股票代码格式 + if len(stock_code) != 6: + return {"error": "股票代码格式错误"} + + # 确定交易所 + if stock_code.startswith('6'): + ts_code = f"{stock_code}.SH" + elif stock_code.startswith(('0', '3')): + ts_code = f"{stock_code}.SZ" + else: + return {"error": "不支持的股票代码"} + + # 获取基本信息和总市值 + basic_info = pro.daily_basic(ts_code=ts_code, fields='ts_code,total_mv', limit=1) + if basic_info.empty: + return {"error": "股票代码不存在"} + + # 获取股票名称 + stock_name = pro.stock_basic(ts_code=ts_code, fields='name').iloc[0]['name'] + + # 获取最新财务指标 + fina_indicator = pro.fina_indicator(ts_code=ts_code, period=datetime.now().strftime('%Y%m%d'), fields='roe,grossprofit_margin,netprofit_margin,debt_to_assets,op_income_yoy,netprofit_yoy,bps,ocfps') + if fina_indicator.empty: + fina_indicator = pro.fina_indicator(ts_code=ts_code, limit=1) + + # 获取实时行情 + today = datetime.now().strftime('%Y%m%d') + daily_data = pro.daily(ts_code=basic_info['ts_code'].iloc[0], start_date=today, end_date=today) + if daily_data.empty: + daily_data = pro.daily(ts_code=basic_info['ts_code'].iloc[0], limit=1) + if daily_data.empty: + return {"error": "无法获取股票行情数据"} + + # 获取市值信息(用于其他指标) + daily_basic = pro.daily_basic(ts_code=basic_info['ts_code'].iloc[0], + fields='ts_code,trade_date,pe,pb,ps,dv_ratio', + limit=1) + + if daily_basic.empty: + return {"error": "无法获取股票基础数据"} + + latest_basic = daily_basic.iloc[0] + latest_fina = fina_indicator.iloc[0] if not fina_indicator.empty else pd.Series() + + # 计算实时总市值(单位:亿元) + current_price = float(daily_data['close'].iloc[0]) + market_value = float(basic_info['total_mv'].iloc[0]) / 10000 # 转换为亿元 + print(f"市值计算: 当前价格={current_price}, 总市值={market_value}亿元") + + # 处理股息率:tushare返回的是百分比值,需要转换为小数 + dv_ratio = float(latest_basic['dv_ratio']) if pd.notna(latest_basic['dv_ratio']) else 0 + dividend_yield = round(dv_ratio / 100, 4) # 转换为小数 + + # 处理财务指标,确保所有值都有默认值0,转换为小数 + roe = round(float(latest_fina['roe']) / 100, 4) if pd.notna(latest_fina.get('roe')) else 0 + gross_profit_margin = round(float(latest_fina['grossprofit_margin']) / 100, 4) if pd.notna(latest_fina.get('grossprofit_margin')) else 0 + net_profit_margin = round(float(latest_fina['netprofit_margin']) / 100, 4) if pd.notna(latest_fina.get('netprofit_margin')) else 0 + debt_to_assets = round(float(latest_fina['debt_to_assets']) / 100, 4) if pd.notna(latest_fina.get('debt_to_assets')) else 0 + revenue_yoy = round(float(latest_fina['op_income_yoy']) / 100, 4) if pd.notna(latest_fina.get('op_income_yoy')) else 0 + net_profit_yoy = round(float(latest_fina['netprofit_yoy']) / 100, 4) if pd.notna(latest_fina.get('netprofit_yoy')) else 0 + bps = round(float(latest_fina['bps']), 3) if pd.notna(latest_fina.get('bps')) else 0 # 保留3位小数 + ocfps = round(float(latest_fina['ocfps']), 3) if pd.notna(latest_fina.get('ocfps')) else 0 # 保留3位小数 + + stock_info = { + "code": stock_code, + "name": stock_name, + "market_value": round(market_value, 2), # 总市值(亿元) + "pe_ratio": round(float(latest_basic['pe']), 2) if pd.notna(latest_basic['pe']) else 0, # 市盈率 + "pb_ratio": round(float(latest_basic['pb']), 2) if pd.notna(latest_basic['pb']) else 0, # 市净率 + "ps_ratio": round(float(latest_basic['ps']), 2) if pd.notna(latest_basic['ps']) else 0, # 市销率 + "dividend_yield": dividend_yield, # 股息率(小数) + "price": round(current_price, 2), # 股价保留2位小数 + "change_percent": round(float(daily_data['pct_chg'].iloc[0]) / 100, 4), # 涨跌幅转换为小数 + # 财务指标(全部转换为小数) + "roe": roe, # ROE(小数) + "gross_profit_margin": gross_profit_margin, # 毛利率(小数) + "net_profit_margin": net_profit_margin, # 净利率(小数) + "debt_to_assets": debt_to_assets, # 资产负债率(小数) + "revenue_yoy": revenue_yoy, # 营收增长率(小数) + "net_profit_yoy": net_profit_yoy, # 净利润增长率(小数) + "bps": bps, # 每股净资产 + "ocfps": ocfps, # 每股经营现金流 + "from_cache": False + } + + # 获取目标值 + targets = self.watchlist.get(stock_code, {}) + + result = { + "stock_info": stock_info, + "targets": targets + } + + # 保存到缓存 + self.save_cache(stock_code, result) + + return result + except Exception as e: + print(f"Error fetching stock info: {str(e)}") + import traceback + print(f"详细错误: {traceback.format_exc()}") + return {"error": f"获取股票数据失败: {str(e)}"} + + def get_watchlist(self): + result = [] + for stock_code, targets in self.watchlist.items(): + try: + # 从缓存获取数据 + today = datetime.now().strftime('%Y-%m-%d') + if stock_code in self.cache_data and self.cache_data[stock_code]['timestamp'] == today: + result.append(self.cache_data[stock_code]['data']) + continue + + # 如果没有缓存,只获取基本信息 + if stock_code.startswith('6'): + ts_code = f"{stock_code}.SH" + elif stock_code.startswith(('0', '3')): + ts_code = f"{stock_code}.SZ" + else: + print(f"不支持的股票代码: {stock_code}") + continue + + # 获取股票名称 + stock_name = pro.stock_basic(ts_code=ts_code, fields='name').iloc[0]['name'] + + result.append({ + "stock_info": { + "code": stock_code, + "name": stock_name + }, + "targets": targets + }) + except Exception as e: + print(f"Error getting watchlist info for {stock_code}: {str(e)}") + continue + return result + + def add_watch(self, stock_code: str, target_market_value_min: float = None, target_market_value_max: float = None): + self.watchlist[stock_code] = { + "target_market_value": { + "min": target_market_value_min, + "max": target_market_value_max + } + } + self._save_watchlist() + return {"status": "success"} + + def remove_watch(self, stock_code: str): + if stock_code in self.watchlist: + del self.watchlist[stock_code] + # 同时删除缓存 + if stock_code in self.cache_data: + del self.cache_data[stock_code] + try: + with open(self.cache_file, 'w', encoding='utf-8') as f: + json.dump(self.cache_data, f, ensure_ascii=False, indent=4) + except Exception as e: + print(f"Error saving cache after removal: {str(e)}") + self._save_watchlist() + return {"status": "success"} + + def get_index_info(self): + """获取主要指数数据""" + try: + # 主要指数代码列表 + index_codes = { + '000001.SH': '上证指数', + '399001.SZ': '深证成指', + '399006.SZ': '创业板指', + '000016.SH': '上证50', + '000300.SH': '沪深300', + '000905.SH': '中证500', + '000852.SH': '中证1000', + '899050.BJ': '北证50', + } + + result = [] + for ts_code, name in index_codes.items(): + try: + # 获取指数基本信息 + df = pro.index_daily(ts_code=ts_code, limit=1) + if not df.empty: + data = df.iloc[0] + # 获取K线数据(最近20天) + kline_df = pro.index_daily(ts_code=ts_code, limit=20) + kline_data = [] + if not kline_df.empty: + for _, row in kline_df.iterrows(): + kline_data.append({ + 'date': row['trade_date'], + 'open': float(row['open']), + 'close': float(row['close']), + 'high': float(row['high']), + 'low': float(row['low']), + 'vol': float(row['vol']) + }) + + result.append({ + 'code': ts_code, + 'name': name, + 'price': float(data['close']), + 'change': float(data['pct_chg']), + 'kline_data': kline_data + }) + except Exception as e: + print(f"获取指数 {ts_code} 数据失败: {str(e)}") + continue + + return result + except Exception as e: + print(f"获取指数数据失败: {str(e)}") + return [] + + def get_company_detail(self, stock_code: str): + try: + print(f"开始获取公司详情: {stock_code}") + + # 处理股票代码格式 + if stock_code.startswith('6'): + ts_code = f"{stock_code}.SH" + elif stock_code.startswith(('0', '3')): + ts_code = f"{stock_code}.SZ" + else: + print(f"不支持的股票代码格式: {stock_code}") + return {"error": "不支持的股票代码"} + + print(f"转换后的ts_code: {ts_code}") + + # 获取公司基本信息 + basic = pro.stock_basic(ts_code=ts_code, fields='name,industry,area,list_date') + if basic.empty: + print(f"无法获取公司基本信息: {ts_code}") + return {"error": "无法获取公司信息"} + + company_info = basic.iloc[0] + print(f"获取到的公司基本信息: {company_info.to_dict()}") + + # 获取公司详细信息 + try: + company_detail = pro.stock_company(ts_code=ts_code) + if not company_detail.empty: + detail_info = company_detail.iloc[0] + company_detail_dict = { + "com_name": str(detail_info.get('com_name', '')), + "chairman": str(detail_info.get('chairman', '')), + "manager": str(detail_info.get('manager', '')), + "secretary": str(detail_info.get('secretary', '')), + "reg_capital": float(detail_info.get('reg_capital', 0)) if pd.notna(detail_info.get('reg_capital')) else 0, + "setup_date": str(detail_info.get('setup_date', '')), + "province": str(detail_info.get('province', '')), + "city": str(detail_info.get('city', '')), + "introduction": str(detail_info.get('introduction', '')), + "website": f"http://{str(detail_info.get('website', '')).strip('http://').strip('https://')}" if detail_info.get('website') else "", + "email": str(detail_info.get('email', '')), + "office": str(detail_info.get('office', '')), + "employees": int(detail_info.get('employees', 0)) if pd.notna(detail_info.get('employees')) else 0, + "main_business": str(detail_info.get('main_business', '')), + "business_scope": str(detail_info.get('business_scope', '')) + } + else: + company_detail_dict = { + "com_name": "", "chairman": "", "manager": "", "secretary": "", + "reg_capital": 0, "setup_date": "", "province": "", "city": "", + "introduction": "", "website": "", "email": "", "office": "", + "employees": 0, "main_business": "", "business_scope": "" + } + except Exception as e: + print(f"获取公司详细信息失败: {str(e)}") + company_detail_dict = { + "com_name": "", "chairman": "", "manager": "", "secretary": "", + "reg_capital": 0, "setup_date": "", "province": "", "city": "", + "introduction": "", "website": "", "email": "", "office": "", + "employees": 0, "main_business": "", "business_scope": "" + } + + # 获取最新财务指标 + try: + fina = pro.fina_indicator(ts_code=ts_code, period=datetime.now().strftime('%Y%m%d')) + if fina.empty: + print("当前期间无财务数据,尝试获取最新一期数据") + fina = pro.fina_indicator(ts_code=ts_code, limit=1) + + if fina.empty: + print(f"无法获取财务指标数据: {ts_code}") + return {"error": "无法获取财务数据"} + + fina_info = fina.iloc[0] + print(f"获取到的财务指标: {fina_info.to_dict()}") + except Exception as e: + print(f"获取财务指标失败: {str(e)}") + return {"error": "获取财务指标失败"} + + # 获取市值信息(用于PE、PB等指标) + try: + daily_basic = pro.daily_basic(ts_code=ts_code, fields='pe,pb,ps,dv_ratio', limit=1) + if not daily_basic.empty: + latest_basic = daily_basic.iloc[0] + else: + print("无法获取PE/PB数据") + latest_basic = pd.Series({'pe': 0, 'pb': 0, 'ps': 0, 'dv_ratio': 0}) + except Exception as e: + print(f"获取PE/PB失败: {str(e)}") + latest_basic = pd.Series({'pe': 0, 'pb': 0, 'ps': 0, 'dv_ratio': 0}) + + result = { + "basic_info": { + "name": str(company_info['name']), + "industry": str(company_info['industry']), + "list_date": str(company_info['list_date']), + "area": str(company_info['area']), + **company_detail_dict + }, + "financial_info": { + # 估值指标 + "pe_ratio": float(latest_basic['pe']) if pd.notna(latest_basic['pe']) else 0, + "pb_ratio": float(latest_basic['pb']) if pd.notna(latest_basic['pb']) else 0, + "ps_ratio": float(latest_basic['ps']) if pd.notna(latest_basic['ps']) else 0, + "dividend_yield": float(latest_basic['dv_ratio'])/100 if pd.notna(latest_basic['dv_ratio']) else 0, + + # 盈利能力 + "roe": float(fina_info['roe']) if pd.notna(fina_info.get('roe')) else 0, + "roe_dt": float(fina_info['roe_dt']) if pd.notna(fina_info.get('roe_dt')) else 0, + "roa": float(fina_info['roa']) if pd.notna(fina_info.get('roa')) else 0, + "grossprofit_margin": float(fina_info['grossprofit_margin']) if pd.notna(fina_info.get('grossprofit_margin')) else 0, + "netprofit_margin": float(fina_info['netprofit_margin']) if pd.notna(fina_info.get('netprofit_margin')) else 0, + + # 成长能力 + "netprofit_yoy": float(fina_info['netprofit_yoy']) if pd.notna(fina_info.get('netprofit_yoy')) else 0, + "dt_netprofit_yoy": float(fina_info['dt_netprofit_yoy']) if pd.notna(fina_info.get('dt_netprofit_yoy')) else 0, + "tr_yoy": float(fina_info['tr_yoy']) if pd.notna(fina_info.get('tr_yoy')) else 0, + "or_yoy": float(fina_info['or_yoy']) if pd.notna(fina_info.get('or_yoy')) else 0, + + # 营运能力 + "assets_turn": float(fina_info['assets_turn']) if pd.notna(fina_info.get('assets_turn')) else 0, + "inv_turn": float(fina_info['inv_turn']) if pd.notna(fina_info.get('inv_turn')) else 0, + "ar_turn": float(fina_info['ar_turn']) if pd.notna(fina_info.get('ar_turn')) else 0, + "ca_turn": float(fina_info['ca_turn']) if pd.notna(fina_info.get('ca_turn')) else 0, + + # 偿债能力 + "current_ratio": float(fina_info['current_ratio']) if pd.notna(fina_info.get('current_ratio')) else 0, + "quick_ratio": float(fina_info['quick_ratio']) if pd.notna(fina_info.get('quick_ratio')) else 0, + "debt_to_assets": float(fina_info['debt_to_assets']) if pd.notna(fina_info.get('debt_to_assets')) else 0, + "debt_to_eqt": float(fina_info['debt_to_eqt']) if pd.notna(fina_info.get('debt_to_eqt')) else 0, + + # 现金流 + "ocf_to_or": float(fina_info['ocf_to_or']) if pd.notna(fina_info.get('ocf_to_or')) else 0, + "ocf_to_opincome": float(fina_info['ocf_to_opincome']) if pd.notna(fina_info.get('ocf_to_opincome')) else 0, + "ocf_yoy": float(fina_info['ocf_yoy']) if pd.notna(fina_info.get('ocf_yoy')) else 0, + + # 每股指标 + "eps": float(fina_info['eps']) if pd.notna(fina_info.get('eps')) else 0, + "dt_eps": float(fina_info['dt_eps']) if pd.notna(fina_info.get('dt_eps')) else 0, + "bps": float(fina_info['bps']) if pd.notna(fina_info.get('bps')) else 0, + "ocfps": float(fina_info['ocfps']) if pd.notna(fina_info.get('ocfps')) else 0, + "retainedps": float(fina_info['retainedps']) if pd.notna(fina_info.get('retainedps')) else 0, + "cfps": float(fina_info['cfps']) if pd.notna(fina_info.get('cfps')) else 0, + "ebit_ps": float(fina_info['ebit_ps']) if pd.notna(fina_info.get('ebit_ps')) else 0, + "fcff_ps": float(fina_info['fcff_ps']) if pd.notna(fina_info.get('fcff_ps')) else 0, + "fcfe_ps": float(fina_info['fcfe_ps']) if pd.notna(fina_info.get('fcfe_ps')) else 0 + } + } + + print(f"返回结果: {result}") + return result + + except Exception as e: + print(f"Error getting company detail: {str(e)}") + import traceback + print(f"详细错误: {traceback.format_exc()}") + return {"error": f"获取公司详情失败: {str(e)}"} + + def get_top_holders(self, stock_code: str): + """获取前十大股东数据""" + try: + # 处理股票代码格式 + if stock_code.startswith('6'): + ts_code = f"{stock_code}.SH" + elif stock_code.startswith(('0', '3')): + ts_code = f"{stock_code}.SZ" + else: + return {"error": "不支持的股票代码"} + + # 获取最新一期的股东数据 + df = pro.top10_holders(ts_code=ts_code, limit=10) + if df.empty: + return {"error": "暂无股东数据"} + + # 按持股比例降序排序 + df = df.sort_values('hold_ratio', ascending=False) + + # 获取最新的报告期 + latest_end_date = df['end_date'].max() + latest_data = df[df['end_date'] == latest_end_date] + + holders = [] + for _, row in latest_data.iterrows(): + holders.append({ + "holder_name": str(row['holder_name']), + "hold_amount": float(row['hold_amount']) if pd.notna(row['hold_amount']) else 0, + "hold_ratio": float(row['hold_ratio']) if pd.notna(row['hold_ratio']) else 0, + "hold_change": float(row['hold_change']) if pd.notna(row['hold_change']) else 0, + "ann_date": str(row['ann_date']), + "end_date": str(row['end_date']) + }) + + result = { + "holders": holders, + "total_ratio": sum(holder['hold_ratio'] for holder in holders), # 合计持股比例 + "report_date": str(latest_end_date) # 报告期 + } + + return result + + except Exception as e: + print(f"获取股东数据失败: {str(e)}") + import traceback + print(f"详细错误: {traceback.format_exc()}") + return {"error": f"获取股东数据失败: {str(e)}"} + + def get_value_analysis_data(self, stock_code: str): + """获取价值投资分析所需的关键财务指标""" + try: + # 处理股票代码格式 + if stock_code.startswith('6'): + ts_code = f"{stock_code}.SH" + elif stock_code.startswith(('0', '3')): + ts_code = f"{stock_code}.SZ" + else: + return {"error": "不支持的股票代码"} + + # 获取最新每日指标(估值数据) + daily_basic = pro.daily_basic(ts_code=ts_code, fields='pe,pb,ps,dv_ratio,total_mv', limit=1) + if daily_basic.empty: + return {"error": "无法获取股票估值数据"} + + # 获取最新财务指标 + fina = pro.fina_indicator(ts_code=ts_code, fields='''roe,grossprofit_margin,netprofit_margin, + netprofit_yoy,dt_netprofit_yoy,tr_yoy,or_yoy,assets_turn,inv_turn,ar_turn,current_ratio, + quick_ratio,debt_to_assets,ocf_to_or,ocf_yoy,eps,bps,cfps,ocfps,retainedps''', limit=1) + if fina.empty: + return {"error": "无法获取财务指标数据"} + + # 获取股票名称和当前价格 + basic_info = pro.daily(ts_code=ts_code, fields='close,trade_date', limit=1) + stock_name = pro.stock_basic(ts_code=ts_code, fields='name').iloc[0]['name'] + + # 整合数据 + latest_daily = daily_basic.iloc[0] + latest_fina = fina.iloc[0] + latest_price = basic_info.iloc[0] + + analysis_data = { + "stock_info": { + "code": stock_code, + "name": stock_name, + "current_price": float(latest_price['close']), + "trade_date": str(latest_price['trade_date']) + }, + "valuation": { + "pe_ratio": float(latest_daily['pe']) if pd.notna(latest_daily['pe']) else None, + "pb_ratio": float(latest_daily['pb']) if pd.notna(latest_daily['pb']) else None, + "ps_ratio": float(latest_daily['ps']) if pd.notna(latest_daily['ps']) else None, + "dividend_yield": float(latest_daily['dv_ratio'])/100 if pd.notna(latest_daily['dv_ratio']) else None, + "total_market_value": float(latest_daily['total_mv'])/10000 if pd.notna(latest_daily['total_mv']) else None # 转换为亿元 + }, + "profitability": { + "roe": float(latest_fina['roe'])/100 if pd.notna(latest_fina['roe']) else None, + "gross_margin": float(latest_fina['grossprofit_margin'])/100 if pd.notna(latest_fina['grossprofit_margin']) else None, + "net_margin": float(latest_fina['netprofit_margin'])/100 if pd.notna(latest_fina['netprofit_margin']) else None + }, + "growth": { + "net_profit_growth": float(latest_fina['netprofit_yoy'])/100 if pd.notna(latest_fina['netprofit_yoy']) else None, + "deducted_net_profit_growth": float(latest_fina['dt_netprofit_yoy'])/100 if pd.notna(latest_fina['dt_netprofit_yoy']) else None, + "revenue_growth": float(latest_fina['tr_yoy'])/100 if pd.notna(latest_fina['tr_yoy']) else None, + "operating_revenue_growth": float(latest_fina['or_yoy'])/100 if pd.notna(latest_fina['or_yoy']) else None + }, + "operation": { + "asset_turnover": float(latest_fina['assets_turn']) if pd.notna(latest_fina['assets_turn']) else None, + "inventory_turnover": float(latest_fina['inv_turn']) if pd.notna(latest_fina['inv_turn']) else None, + "receivables_turnover": float(latest_fina['ar_turn']) if pd.notna(latest_fina['ar_turn']) else None + }, + "solvency": { + "current_ratio": float(latest_fina['current_ratio']) if pd.notna(latest_fina['current_ratio']) else None, + "quick_ratio": float(latest_fina['quick_ratio']) if pd.notna(latest_fina['quick_ratio']) else None, + "debt_to_assets": float(latest_fina['debt_to_assets'])/100 if pd.notna(latest_fina['debt_to_assets']) else None + }, + "cash_flow": { + "ocf_to_revenue": float(latest_fina['ocf_to_or'])/100 if pd.notna(latest_fina['ocf_to_or']) else None, + "ocf_growth": float(latest_fina['ocf_yoy'])/100 if pd.notna(latest_fina['ocf_yoy']) else None + }, + "per_share": { + "eps": float(latest_fina['eps']) if pd.notna(latest_fina['eps']) else None, + "bps": float(latest_fina['bps']) if pd.notna(latest_fina['bps']) else None, + "cfps": float(latest_fina['cfps']) if pd.notna(latest_fina['cfps']) else None, + "ocfps": float(latest_fina['ocfps']) if pd.notna(latest_fina['ocfps']) else None, + "retained_eps": float(latest_fina['retainedps']) if pd.notna(latest_fina['retainedps']) else None + } + } + + return analysis_data + + except Exception as e: + print(f"获取价值投资分析数据失败: {str(e)}") + import traceback + print(f"详细错误: {traceback.format_exc()}") + return {"error": f"获取价值投资分析数据失败: {str(e)}"} \ No newline at end of file diff --git a/app/templates/index.html b/app/templates/index.html new file mode 100644 index 0000000..5928e94 --- /dev/null +++ b/app/templates/index.html @@ -0,0 +1,1514 @@ + + + + + + + 价值投资盯盘系统 - 智能股票分析与监控平台 + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
+ +
+ +
+
+ 添加监控股票 +
+
+
+
+
+ + +
+
+ +
+ + - + +
+
+
+ +
+
+
+
+
+ + +
+
+ 监控列表 +
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
代码名称现价涨跌幅 监控目标市值 市盈率 市净率 市销率 股息率 ROE 毛利率 净利率 资产负债率 净利润增长率 每股净资产 每股经营现金流 操作
统计(0只)
+
+
+
+
+ + + + + + + + + + + + \ No newline at end of file diff --git a/app/templates/market.html b/app/templates/market.html new file mode 100644 index 0000000..6ae10ea --- /dev/null +++ b/app/templates/market.html @@ -0,0 +1,223 @@ + + + + + + 指数行情 - 价值投资盯盘系统 + + + + + + + +
+
+ +
+
+ + + + + + \ No newline at end of file diff --git a/config.json.example b/config.json.example new file mode 100644 index 0000000..0ff06d7 --- /dev/null +++ b/config.json.example @@ -0,0 +1,4 @@ +{ + "watchlist": {}, + "tushare_token": "your_tushare_token_here" +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4768a4e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,54 @@ +aiofiles==23.2.1 +akshare==1.11.22 +annotated-types==0.7.0 +anyio==3.7.1 +baostock==0.8.9 +beautifulsoup4==4.12.3 +bs4==0.0.2 +certifi==2024.12.14 +charset-normalizer==3.4.1 +click==8.1.8 +decorator==5.1.1 +efinance==0.4.8 +et_xmlfile==2.0.0 +fastapi==0.104.1 +h11==0.14.0 +html5lib==1.1 +idna==3.10 +Jinja2==3.1.2 +jsonpath==0.82.2 +lxml==5.3.0 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +multitasking==0.0.11 +numpy==2.2.1 +openpyxl==3.1.5 +pandas==2.2.3 +py==1.11.0 +py_mini_racer==0.6.0 +pydantic==2.10.4 +pydantic_core==2.27.2 +Pygments==2.19.1 +pypinyin==0.53.0 +python-dateutil==2.9.0.post0 +python-multipart==0.0.6 +pytz==2024.2 +requests==2.32.3 +retry==0.9.2 +rich==13.9.4 +simplejson==3.19.3 +six==1.17.0 +sniffio==1.3.1 +soupsieve==2.6 +starlette==0.27.0 +tabulate==0.9.0 +tqdm==4.67.1 +tushare==1.4.16 +typing_extensions==4.12.2 +tzdata==2024.2 +urllib3==2.3.0 +uvicorn==0.24.0 +webencodings==0.5.1 +websocket-client==1.8.0 +xlrd==2.0.1 diff --git a/run.py b/run.py new file mode 100644 index 0000000..cdff16b --- /dev/null +++ b/run.py @@ -0,0 +1,12 @@ +import uvicorn +from app import app + +if __name__ == "__main__": + uvicorn.run( + "app:app", + host="0.0.0.0", + port=8000, # 修改为8000端口 + reload=True, # 启用热重载 + log_level="debug", # 设置日志级别为debug + workers=1 # 开发模式使用单个worker + ) \ No newline at end of file diff --git a/stock_cache.json b/stock_cache.json new file mode 100644 index 0000000..6f39f01 --- /dev/null +++ b/stock_cache.json @@ -0,0 +1,2283 @@ +{ + "300059": { + "data": { + "stock_info": { + "code": "300059", + "name": "东方财富", + "market_value": 3491.76, + "pe_ratio": 42.62, + "pb_ratio": 4.54, + "ps_ratio": 89.81, + "dividend_yield": 0.0018, + "price": 22.12, + "change_percent": 0.011, + "roe": 0.0811, + "gross_profit_margin": 0.8372, + "net_profit_margin": 2.694, + "debt_to_assets": 0.7643, + "revenue_yoy": 0, + "net_profit_yoy": -0.0269, + "bps": 4.874, + "ocfps": 4.062, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 2500.0, + "max": 2800.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "601318": { + "data": { + "stock_info": { + "code": "601318", + "name": "中国平安", + "market_value": 8848.35, + "pe_ratio": 10.33, + "pb_ratio": 0.99, + "ps_ratio": 0.97, + "dividend_yield": 0.0498, + "price": 48.59, + "change_percent": -0.0074, + "roe": 0.1319, + "gross_profit_margin": 0, + "net_profit_margin": 0.1806, + "debt_to_assets": 0.8979, + "revenue_yoy": 0, + "net_profit_yoy": 0.3609, + "bps": 49.859, + "ocfps": 22.933, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 8000.0, + "max": 9500.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "600085": { + "data": { + "stock_info": { + "code": "600085", + "name": "同仁堂", + "market_value": 507.86, + "pe_ratio": 30.43, + "pb_ratio": 4.13, + "ps_ratio": 2.84, + "dividend_yield": 0.027, + "price": 37.03, + "change_percent": 0.0082, + "roe": 0.1037, + "gross_profit_margin": 0.437, + "net_profit_margin": 0.1435, + "debt_to_assets": 0.3435, + "revenue_yoy": 0, + "net_profit_yoy": -0.0292, + "bps": 9.463, + "ocfps": -0.038, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 336.0, + "max": 429.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "002714": { + "data": { + "stock_info": { + "code": "002714", + "name": "牧原股份", + "market_value": 1980.25, + "pe_ratio": 0, + "pb_ratio": 2.97, + "ps_ratio": 1.79, + "dividend_yield": 0.0227, + "price": 36.25, + "change_percent": 0.0014, + "roe": 0.1552, + "gross_profit_margin": 0.1727, + "net_profit_margin": 0.1162, + "debt_to_assets": 0.5838, + "revenue_yoy": 0, + "net_profit_yoy": 6.689, + "bps": 13.034, + "ocfps": 5.339, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 1650.0, + "max": 2200.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "000538": { + "data": { + "stock_info": { + "code": "000538", + "name": "云南白药", + "market_value": 1001.86, + "pe_ratio": 24.47, + "pb_ratio": 2.61, + "ps_ratio": 2.56, + "dividend_yield": 0.0586, + "price": 56.15, + "change_percent": -0.0083, + "roe": 0.1076, + "gross_profit_margin": 0.284, + "net_profit_margin": 0.1447, + "debt_to_assets": 0.271, + "revenue_yoy": 0, + "net_profit_yoy": 0.0493, + "bps": 22.711, + "ocfps": 2.283, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 807.0, + "max": 1210.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "000423": { + "data": { + "stock_info": { + "code": "000423", + "name": "东阿阿胶", + "market_value": 391.86, + "pe_ratio": 34.05, + "pb_ratio": 3.95, + "ps_ratio": 8.31, + "dividend_yield": 0.0481, + "price": 60.85, + "change_percent": -0.0178, + "roe": 0.1117, + "gross_profit_margin": 0.7413, + "net_profit_margin": 0.2661, + "debt_to_assets": 0.2188, + "revenue_yoy": 0, + "net_profit_yoy": 0.47, + "bps": 15.392, + "ocfps": 2.238, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 270.0, + "max": 330.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "000963": { + "data": { + "stock_info": { + "code": "000963", + "name": "华东医药", + "market_value": 567.33, + "pe_ratio": 19.98, + "pb_ratio": 2.57, + "ps_ratio": 1.4, + "dividend_yield": 0.0288, + "price": 32.34, + "change_percent": -0.0089, + "roe": 0.1189, + "gross_profit_margin": 0.3255, + "net_profit_margin": 0.0814, + "debt_to_assets": 0.398, + "revenue_yoy": 0, + "net_profit_yoy": 0.1705, + "bps": 12.565, + "ocfps": 1.429, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 500.0, + "max": 650.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "002415": { + "data": { + "stock_info": { + "code": "002415", + "name": "海康威视", + "market_value": 2554.83, + "pe_ratio": 18.11, + "pb_ratio": 3.33, + "ps_ratio": 2.86, + "dividend_yield": 0.0329, + "price": 27.67, + "change_percent": -0.0047, + "roe": 0.1059, + "gross_profit_margin": 0.4476, + "net_profit_margin": 0.1371, + "debt_to_assets": 0.3296, + "revenue_yoy": 0, + "net_profit_yoy": -0.084, + "bps": 8.321, + "ocfps": 0.282, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 2000.0, + "max": 2200.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "002007": { + "data": { + "stock_info": { + "code": "002007", + "name": "华兰生物", + "market_value": 284.92, + "pe_ratio": 19.23, + "pb_ratio": 2.44, + "ps_ratio": 5.33, + "dividend_yield": 0.0193, + "price": 15.58, + "change_percent": -0.0133, + "roe": 0.0801, + "gross_profit_margin": 0.6177, + "net_profit_margin": 0.2897, + "debt_to_assets": 0.1485, + "revenue_yoy": 0, + "net_profit_yoy": -0.1346, + "bps": 6.396, + "ocfps": 0.251, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 240.0, + "max": 288.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "600132": { + "data": { + "stock_info": { + "code": "600132", + "name": "重庆啤酒", + "market_value": 277.32, + "pe_ratio": 20.75, + "pb_ratio": 19.67, + "ps_ratio": 1.87, + "dividend_yield": 0.075, + "price": 57.3, + "change_percent": 0.0145, + "roe": 0.623, + "gross_profit_margin": 0.492, + "net_profit_margin": 0.2041, + "debt_to_assets": 0.6684, + "revenue_yoy": 0, + "net_profit_yoy": -0.009, + "bps": 4.413, + "ocfps": 7.153, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 225.0, + "max": 260.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "002049": { + "data": { + "stock_info": { + "code": "002049", + "name": "紫光国微", + "market_value": 492.36, + "pe_ratio": 19.46, + "pb_ratio": 4.1, + "ps_ratio": 6.51, + "dividend_yield": 0.0116, + "price": 57.95, + "change_percent": -0.0113, + "roe": 0.0847, + "gross_profit_margin": 0.5681, + "net_profit_margin": 0.2383, + "debt_to_assets": 0.2684, + "revenue_yoy": 0, + "net_profit_yoy": -0.5027, + "bps": 14.13, + "ocfps": 1.145, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 360.0, + "max": 480.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "600436": { + "data": { + "stock_info": { + "code": "600436", + "name": "片仔癀", + "market_value": 1236.8, + "pe_ratio": 44.21, + "pb_ratio": 9.28, + "ps_ratio": 12.3, + "dividend_yield": 0.0169, + "price": 205.0, + "change_percent": 0.0029, + "roe": 0.1961, + "gross_profit_margin": 0.4544, + "net_profit_margin": 0.3216, + "debt_to_assets": 0.1882, + "revenue_yoy": 0, + "net_profit_yoy": 0.1173, + "bps": 23.24, + "ocfps": 2.153, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 960.0, + "max": 1080.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "002216": { + "data": { + "stock_info": { + "code": "002216", + "name": "三全食品", + "market_value": 97.77, + "pe_ratio": 13.05, + "pb_ratio": 2.28, + "ps_ratio": 1.39, + "dividend_yield": 0.045, + "price": 11.12, + "change_percent": 0.0091, + "roe": 0.0914, + "gross_profit_margin": 0.2503, + "net_profit_margin": 0.0769, + "debt_to_assets": 0.4028, + "revenue_yoy": 0, + "net_profit_yoy": -0.2861, + "bps": 4.878, + "ocfps": 0.835, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 72.0, + "max": 96.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "601966": { + "data": { + "stock_info": { + "code": "601966", + "name": "玲珑轮胎", + "market_value": 251.87, + "pe_ratio": 18.11, + "pb_ratio": 1.18, + "ps_ratio": 1.25, + "dividend_yield": 0.0371, + "price": 17.21, + "change_percent": -0.0023, + "roe": 0.0801, + "gross_profit_margin": 0.2435, + "net_profit_margin": 0.1073, + "debt_to_assets": 0.5242, + "revenue_yoy": 0, + "net_profit_yoy": 0.7839, + "bps": 14.836, + "ocfps": 0.953, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 220.0, + "max": 280.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "603195": { + "data": { + "stock_info": { + "code": "603195", + "name": "公牛集团", + "market_value": 861.35, + "pe_ratio": 22.26, + "pb_ratio": 5.8, + "ps_ratio": 5.49, + "dividend_yield": 0.0321, + "price": 66.66, + "change_percent": -0.0141, + "roe": 0.2228, + "gross_profit_margin": 0.4312, + "net_profit_margin": 0.2589, + "debt_to_assets": 0.231, + "revenue_yoy": 0, + "net_profit_yoy": 0.16, + "bps": 11.493, + "ocfps": 2.333, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 730.0, + "max": 950.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "000001": { + "data": { + "stock_info": { + "code": "000001", + "name": "平安银行", + "market_value": 2173.46, + "pe_ratio": 4.68, + "pb_ratio": 0.52, + "ps_ratio": 1.32, + "dividend_yield": 0.0862, + "price": 11.2, + "change_percent": -0.0089, + "roe": 0.0825, + "gross_profit_margin": 0, + "net_profit_margin": 0.3561, + "debt_to_assets": 0.9146, + "revenue_yoy": 0, + "net_profit_yoy": 0.0024, + "bps": 21.67, + "ocfps": 7.07, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 2300.0, + "max": 2800.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "600867": { + "data": { + "stock_info": { + "code": "600867", + "name": "通化东宝", + "market_value": 145.52, + "pe_ratio": 12.46, + "pb_ratio": 2.33, + "ps_ratio": 4.73, + "dividend_yield": 0.034, + "price": 7.43, + "change_percent": 0.0095, + "roe": -0.0097, + "gross_profit_margin": 0.7447, + "net_profit_margin": -0.0459, + "debt_to_assets": 0.0974, + "revenue_yoy": 0, + "net_profit_yoy": -1.0873, + "bps": 3.243, + "ocfps": 0.114, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 120.0, + "max": 160.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "603087": { + "data": { + "stock_info": { + "code": "603087", + "name": "甘李药业", + "market_value": 250.34, + "pe_ratio": 73.62, + "pb_ratio": 2.3, + "ps_ratio": 9.6, + "dividend_yield": 0.0168, + "price": 41.65, + "change_percent": 0.0161, + "roe": 0.0462, + "gross_profit_margin": 0.7536, + "net_profit_margin": 0.226, + "debt_to_assets": 0.083, + "revenue_yoy": 0, + "net_profit_yoy": 0.9036, + "bps": 18.645, + "ocfps": 0.774, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 170.0, + "max": 283.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "603290": { + "data": { + "stock_info": { + "code": "603290", + "name": "斯达半导", + "market_value": 193.85, + "pe_ratio": 21.29, + "pb_ratio": 2.94, + "ps_ratio": 5.29, + "dividend_yield": 0.0141, + "price": 80.95, + "change_percent": 0.0021, + "roe": 0.065, + "gross_profit_margin": 0.3169, + "net_profit_margin": 0.1773, + "debt_to_assets": 0.2998, + "revenue_yoy": 0, + "net_profit_yoy": -0.3569, + "bps": 27.532, + "ocfps": 2.727, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 150.0, + "max": 200.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "600332": { + "data": { + "stock_info": { + "code": "600332", + "name": "白云山", + "market_value": 433.11, + "pe_ratio": 10.68, + "pb_ratio": 1.22, + "ps_ratio": 0.57, + "dividend_yield": 0.0431, + "price": 26.64, + "change_percent": 0.0019, + "roe": 0.0888, + "gross_profit_margin": 0.177, + "net_profit_margin": 0.0557, + "debt_to_assets": 0.5199, + "revenue_yoy": 0, + "net_profit_yoy": -0.1668, + "bps": 22.28, + "ocfps": -0.462, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 380.0, + "max": 460.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "300124": { + "data": { + "stock_info": { + "code": "300124", + "name": "汇川技术", + "market_value": 1570.93, + "pe_ratio": 33.13, + "pb_ratio": 5.86, + "ps_ratio": 5.16, + "dividend_yield": 0.0077, + "price": 58.35, + "change_percent": -0.0273, + "roe": 0.1316, + "gross_profit_margin": 0.3102, + "net_profit_margin": 0.1345, + "debt_to_assets": 0.4929, + "revenue_yoy": 0, + "net_profit_yoy": 0.0104, + "bps": 9.885, + "ocfps": 1.44, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 1200.0, + "max": 1400.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "300146": { + "data": { + "stock_info": { + "code": "300146", + "name": "汤臣倍健", + "market_value": 192.2, + "pe_ratio": 11.01, + "pb_ratio": 1.7, + "ps_ratio": 2.04, + "dividend_yield": 0.0789, + "price": 11.3, + "change_percent": 0.0116, + "roe": 0.074, + "gross_profit_margin": 0.6793, + "net_profit_margin": 0.1504, + "debt_to_assets": 0.1611, + "revenue_yoy": 0, + "net_profit_yoy": -0.5431, + "bps": 6.659, + "ocfps": 0.201, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 150.0, + "max": 200.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "000999": { + "data": { + "stock_info": { + "code": "000999", + "name": "华润三九", + "market_value": 546.6, + "pe_ratio": 19.16, + "pb_ratio": 2.84, + "ps_ratio": 2.21, + "dividend_yield": 0.0506, + "price": 42.56, + "change_percent": -0.0014, + "roe": 0.1499, + "gross_profit_margin": 0.5268, + "net_profit_margin": 0.1644, + "debt_to_assets": 0.3607, + "revenue_yoy": 0, + "net_profit_yoy": 0.2319, + "bps": 15.987, + "ocfps": 2.495, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 450.0, + "max": 580.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "000516": { + "data": { + "stock_info": { + "code": "000516", + "name": "国际医学", + "market_value": 109.4, + "pe_ratio": 0, + "pb_ratio": 3.0, + "ps_ratio": 2.37, + "dividend_yield": 0.0, + "price": 4.84, + "change_percent": -0.032, + "roe": -0.0561, + "gross_profit_margin": 0.0919, + "net_profit_margin": -0.0816, + "debt_to_assets": 0.6704, + "revenue_yoy": 0, + "net_profit_yoy": 0.0459, + "bps": 1.616, + "ocfps": 0.276, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 100.0, + "max": 120.0 + } + } + }, + "timestamp": "2025-01-10" + }, + "002466": { + "data": { + "stock_info": { + "code": "002466", + "name": "天齐锂业", + "market_value": 529.95, + "pe_ratio": 7.26, + "pb_ratio": 1.22, + "ps_ratio": 1.31, + "dividend_yield": 0.0418, + "price": 32.29, + "change_percent": 0.0416, + "roe": -0.12, + "gross_profit_margin": 0.4903, + "net_profit_margin": 0.1723, + "debt_to_assets": 0.2843, + "revenue_yoy": 0, + "net_profit_yoy": -1.704, + "bps": 26.51, + "ocfps": 2.554, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": 330.0, + "max": 415.0 + } + } + }, + "timestamp": "2025-01-13" + }, + "index_data": { + "data": { + "data": [ + { + "code": "000001.SH", + "name": "上证指数", + "price": 3168.5238, + "change": -1.3349, + "kline_data": [ + { + "date": "20250110", + "open": 3211.7068, + "close": 3168.5238, + "high": 3220.1073, + "low": 3168.5238, + "vol": 403663348.0 + }, + { + "date": "20250109", + "open": 3220.7205, + "close": 3211.3933, + "high": 3228.9725, + "low": 3205.9103, + "vol": 382943557.0 + }, + { + "date": "20250108", + "open": 3218.8577, + "close": 3230.1679, + "high": 3246.2908, + "low": 3175.7247, + "vol": 472864390.0 + }, + { + "date": "20250107", + "open": 3203.3068, + "close": 3229.6439, + "high": 3230.8529, + "low": 3190.4612, + "vol": 409660529.0 + }, + { + "date": "20250106", + "open": 3209.7832, + "close": 3206.9228, + "high": 3219.4877, + "low": 3185.4631, + "vol": 430978403.0 + }, + { + "date": "20250103", + "open": 3267.0766, + "close": 3211.4299, + "high": 3273.5656, + "low": 3205.7755, + "vol": 517592014.0 + }, + { + "date": "20250102", + "open": 3347.9392, + "close": 3262.5607, + "high": 3351.722, + "low": 3242.0865, + "vol": 561375199.0 + }, + { + "date": "20241231", + "open": 3406.9652, + "close": 3351.763, + "high": 3413.4545, + "low": 3351.763, + "vol": 502731062.0 + }, + { + "date": "20241230", + "open": 3395.3962, + "close": 3407.3259, + "high": 3412.8414, + "low": 3394.9648, + "vol": 455262383.0 + }, + { + "date": "20241227", + "open": 3397.2939, + "close": 3400.142, + "high": 3418.952, + "low": 3388.3215, + "vol": 500488130.0 + }, + { + "date": "20241226", + "open": 3389.3383, + "close": 3398.0765, + "high": 3401.0951, + "low": 3380.255, + "vol": 422177271.0 + }, + { + "date": "20241225", + "open": 3395.1072, + "close": 3393.3501, + "high": 3406.2125, + "low": 3374.0113, + "vol": 471315592.0 + }, + { + "date": "20241224", + "open": 3353.5354, + "close": 3393.5281, + "high": 3394.9044, + "low": 3352.9474, + "vol": 471992673.0 + }, + { + "date": "20241223", + "open": 3367.9037, + "close": 3351.2571, + "high": 3384.9918, + "low": 3348.277, + "vol": 556747248.0 + }, + { + "date": "20241220", + "open": 3364.481, + "close": 3368.0693, + "high": 3390.6182, + "low": 3362.8193, + "vol": 490964621.0 + }, + { + "date": "20241219", + "open": 3355.9041, + "close": 3370.0331, + "high": 3377.5321, + "low": 3346.469, + "vol": 513909063.0 + }, + { + "date": "20241218", + "open": 3371.2955, + "close": 3382.2081, + "high": 3396.4603, + "low": 3371.2955, + "vol": 512524979.0 + }, + { + "date": "20241217", + "open": 3381.8141, + "close": 3361.485, + "high": 3396.2074, + "low": 3357.7694, + "vol": 568941505.0 + }, + { + "date": "20241216", + "open": 3390.083, + "close": 3386.3312, + "high": 3401.9263, + "low": 3376.5358, + "vol": 625578996.0 + }, + { + "date": "20241213", + "open": 3442.9259, + "close": 3391.8782, + "high": 3442.9259, + "low": 3390.7537, + "vol": 777464144.0 + } + ] + }, + { + "code": "399001.SZ", + "name": "深证成指", + "price": 9795.9438, + "change": -1.8049, + "kline_data": [ + { + "date": "20250110", + "open": 9957.8343, + "close": 9795.9438, + "high": 10004.0607, + "low": 9795.9438, + "vol": 616360985.0 + }, + { + "date": "20250109", + "open": 9906.9088, + "close": 9976.0038, + "high": 10021.9397, + "low": 9901.6345, + "vol": 605202920.0 + }, + { + "date": "20250108", + "open": 9940.1968, + "close": 9944.6404, + "high": 10016.2192, + "low": 9731.4379, + "vol": 683561380.0 + }, + { + "date": "20250107", + "open": 9860.578, + "close": 9998.7565, + "high": 10000.3408, + "low": 9822.0008, + "vol": 588919068.0 + }, + { + "date": "20250106", + "open": 9890.9841, + "close": 9885.6545, + "high": 9975.9921, + "low": 9817.7509, + "vol": 586235482.0 + }, + { + "date": "20250103", + "open": 10097.9312, + "close": 9897.1203, + "high": 10139.9895, + "low": 9891.8398, + "vol": 685235256.0 + }, + { + "date": "20250102", + "open": 10400.6644, + "close": 10088.0589, + "high": 10409.6132, + "low": 10006.5558, + "vol": 681116709.0 + }, + { + "date": "20241231", + "open": 10668.168, + "close": 10414.6113, + "high": 10678.7711, + "low": 10414.6113, + "vol": 646653008.0 + }, + { + "date": "20241230", + "open": 10644.1537, + "close": 10671.155, + "high": 10726.6603, + "low": 10632.7442, + "vol": 609052769.0 + }, + { + "date": "20241227", + "open": 10679.3433, + "close": 10659.9751, + "high": 10780.5327, + "low": 10632.5252, + "vol": 679934587.0 + }, + { + "date": "20241226", + "open": 10586.957, + "close": 10673.9684, + "high": 10692.8864, + "low": 10570.0857, + "vol": 593997426.0 + }, + { + "date": "20241225", + "open": 10675.729, + "close": 10603.0998, + "high": 10677.6475, + "low": 10544.8803, + "vol": 667963062.0 + }, + { + "date": "20241224", + "open": 10548.1453, + "close": 10671.4274, + "high": 10673.3372, + "low": 10548.1453, + "vol": 664977041.0 + }, + { + "date": "20241223", + "open": 10650.575, + "close": 10537.4021, + "high": 10708.8229, + "low": 10527.1463, + "vol": 795687830.0 + }, + { + "date": "20241220", + "open": 10631.4447, + "close": 10646.6176, + "high": 10731.0762, + "low": 10618.7944, + "vol": 732375378.0 + }, + { + "date": "20241219", + "open": 10483.5035, + "close": 10649.0339, + "high": 10673.9616, + "low": 10460.1961, + "vol": 760693343.0 + }, + { + "date": "20241218", + "open": 10561.9354, + "close": 10584.2695, + "high": 10639.7699, + "low": 10535.7611, + "vol": 740846621.0 + }, + { + "date": "20241217", + "open": 10557.2316, + "close": 10537.4285, + "high": 10677.7429, + "low": 10526.2556, + "vol": 858140531.0 + }, + { + "date": "20241216", + "open": 10705.5166, + "close": 10573.9194, + "high": 10721.7095, + "low": 10535.5265, + "vol": 922676962.0 + }, + { + "date": "20241213", + "open": 10876.1903, + "close": 10713.072, + "high": 10876.1903, + "low": 10709.1393, + "vol": 1075547182.0 + } + ] + }, + { + "code": "399006.SZ", + "name": "创业板指", + "price": 1975.3048, + "change": -1.7584, + "kline_data": [ + { + "date": "20250110", + "open": 2006.5527, + "close": 1975.3048, + "high": 2024.4268, + "low": 1975.3048, + "vol": 178384005.0 + }, + { + "date": "20250109", + "open": 1994.81, + "close": 2010.6601, + "high": 2025.306, + "low": 1994.7047, + "vol": 174244807.0 + }, + { + "date": "20250108", + "open": 2011.7489, + "close": 2008.4441, + "high": 2029.0233, + "low": 1963.8057, + "vol": 195527838.0 + }, + { + "date": "20250107", + "open": 1996.0003, + "close": 2028.3582, + "high": 2028.7513, + "low": 1988.418, + "vol": 168570528.0 + }, + { + "date": "20250106", + "open": 2018.2457, + "close": 2014.1899, + "high": 2033.2681, + "low": 2000.8615, + "vol": 169375612.0 + }, + { + "date": "20250103", + "open": 2062.4025, + "close": 2015.9671, + "high": 2067.4089, + "low": 2015.9633, + "vol": 206064912.0 + }, + { + "date": "20250102", + "open": 2136.9221, + "close": 2060.4422, + "high": 2137.6549, + "low": 2040.3792, + "vol": 196587394.0 + }, + { + "date": "20241231", + "open": 2202.1811, + "close": 2141.5958, + "high": 2207.3112, + "low": 2141.5958, + "vol": 199428413.0 + }, + { + "date": "20241230", + "open": 2198.8648, + "close": 2206.2852, + "high": 2218.7352, + "low": 2198.1247, + "vol": 187201460.0 + }, + { + "date": "20241227", + "open": 2209.7101, + "close": 2204.8965, + "high": 2235.5559, + "low": 2196.2938, + "vol": 207513529.0 + }, + { + "date": "20241226", + "open": 2200.3188, + "close": 2209.8464, + "high": 2219.6932, + "low": 2193.5882, + "vol": 184436466.0 + }, + { + "date": "20241225", + "open": 2215.6123, + "close": 2201.296, + "high": 2216.3572, + "low": 2186.9997, + "vol": 203868100.0 + }, + { + "date": "20241224", + "open": 2190.4696, + "close": 2213.548, + "high": 2215.1924, + "low": 2187.7803, + "vol": 200966406.0 + }, + { + "date": "20241223", + "open": 2205.6903, + "close": 2187.9448, + "high": 2223.6367, + "low": 2185.3528, + "vol": 245538770.0 + }, + { + "date": "20241220", + "open": 2209.3755, + "close": 2209.6616, + "high": 2234.1315, + "low": 2201.9409, + "vol": 230049274.0 + }, + { + "date": "20241219", + "open": 2178.463, + "close": 2213.5429, + "high": 2220.4332, + "low": 2174.9434, + "vol": 221719261.0 + }, + { + "date": "20241218", + "open": 2207.1823, + "close": 2202.1373, + "high": 2216.0033, + "low": 2192.5973, + "vol": 216481475.0 + }, + { + "date": "20241217", + "open": 2197.1259, + "close": 2201.181, + "high": 2233.172, + "low": 2197.1259, + "vol": 248753480.0 + }, + { + "date": "20241216", + "open": 2232.6249, + "close": 2201.5324, + "high": 2234.1863, + "low": 2192.2524, + "vol": 273787933.0 + }, + { + "date": "20241213", + "open": 2272.2664, + "close": 2235.2586, + "high": 2276.1275, + "low": 2234.1701, + "vol": 328537971.0 + } + ] + }, + { + "code": "000016.SH", + "name": "上证50", + "price": 2560.2497, + "change": -0.7492, + "kline_data": [ + { + "date": "20250110", + "open": 2585.1998, + "close": 2560.2497, + "high": 2593.0872, + "low": 2560.2497, + "vol": 36166878.0 + }, + { + "date": "20250109", + "open": 2595.6918, + "close": 2579.5751, + "high": 2597.0557, + "low": 2574.603, + "vol": 34356506.0 + }, + { + "date": "20250108", + "open": 2587.5267, + "close": 2597.4459, + "high": 2611.1528, + "low": 2565.5989, + "vol": 46257463.0 + }, + { + "date": "20250107", + "open": 2576.6772, + "close": 2593.9654, + "high": 2595.6633, + "low": 2566.6785, + "vol": 39218598.0 + }, + { + "date": "20250106", + "open": 2589.3354, + "close": 2579.9632, + "high": 2591.8944, + "low": 2555.9276, + "vol": 44486402.0 + }, + { + "date": "20250103", + "open": 2615.3507, + "close": 2587.1316, + "high": 2620.6463, + "low": 2579.439, + "vol": 50804858.0 + }, + { + "date": "20250102", + "open": 2682.4157, + "close": 2610.342, + "high": 2684.2467, + "low": 2595.3991, + "vol": 61160984.0 + }, + { + "date": "20241231", + "open": 2715.2066, + "close": 2684.7706, + "high": 2725.7876, + "low": 2684.7706, + "vol": 50745554.0 + }, + { + "date": "20241230", + "open": 2700.2245, + "close": 2718.7486, + "high": 2724.7263, + "low": 2700.2245, + "vol": 44994763.0 + }, + { + "date": "20241227", + "open": 2701.2086, + "close": 2701.7031, + "high": 2713.8405, + "low": 2686.5011, + "vol": 44894855.0 + }, + { + "date": "20241226", + "open": 2709.5915, + "close": 2702.5952, + "high": 2709.5915, + "low": 2692.8677, + "vol": 41770736.0 + }, + { + "date": "20241225", + "open": 2706.5666, + "close": 2710.1649, + "high": 2726.1403, + "low": 2700.6812, + "vol": 51010381.0 + }, + { + "date": "20241224", + "open": 2671.9088, + "close": 2702.282, + "high": 2704.5775, + "low": 2666.3412, + "vol": 46469198.0 + }, + { + "date": "20241223", + "open": 2648.9188, + "close": 2671.2025, + "high": 2688.8864, + "low": 2648.9188, + "vol": 56629099.0 + }, + { + "date": "20241220", + "open": 2654.8486, + "close": 2648.4609, + "high": 2670.3177, + "low": 2644.8364, + "vol": 42493510.0 + }, + { + "date": "20241219", + "open": 2652.1949, + "close": 2661.7224, + "high": 2672.5807, + "low": 2642.6831, + "vol": 40214140.0 + }, + { + "date": "20241218", + "open": 2662.0322, + "close": 2671.3461, + "high": 2681.1212, + "low": 2661.8983, + "vol": 44259056.0 + }, + { + "date": "20241217", + "open": 2637.3498, + "close": 2652.3455, + "high": 2672.9552, + "low": 2637.3498, + "vol": 44279234.0 + }, + { + "date": "20241216", + "open": 2637.7401, + "close": 2641.5604, + "high": 2647.6977, + "low": 2635.2467, + "vol": 54098120.0 + }, + { + "date": "20241213", + "open": 2684.2932, + "close": 2638.0266, + "high": 2684.4109, + "low": 2636.9729, + "vol": 61597705.0 + } + ] + }, + { + "code": "000300.SH", + "name": "沪深300", + "price": 3732.4806, + "change": -1.2539, + "kline_data": [ + { + "date": "20250110", + "open": 3778.8928, + "close": 3732.4806, + "high": 3786.8069, + "low": 3732.4806, + "vol": 137523171.0 + }, + { + "date": "20250109", + "open": 3780.7341, + "close": 3779.8773, + "high": 3795.2608, + "low": 3768.681, + "vol": 129794002.0 + }, + { + "date": "20250108", + "open": 3781.261, + "close": 3789.2153, + "high": 3810.7355, + "low": 3731.1887, + "vol": 169166968.0 + }, + { + "date": "20250107", + "open": 3760.866, + "close": 3796.1055, + "high": 3797.602, + "low": 3749.0581, + "vol": 146455779.0 + }, + { + "date": "20250106", + "open": 3775.9902, + "close": 3768.9697, + "high": 3788.8489, + "low": 3743.0727, + "vol": 150946670.0 + }, + { + "date": "20250103", + "open": 3825.2426, + "close": 3775.1648, + "high": 3835.9353, + "low": 3767.6653, + "vol": 175342676.0 + }, + { + "date": "20250102", + "open": 3931.8155, + "close": 3820.3952, + "high": 3934.2034, + "low": 3796.3389, + "vol": 217898724.0 + }, + { + "date": "20241231", + "open": 3995.8705, + "close": 3934.9109, + "high": 4004.3462, + "low": 3934.9109, + "vol": 183928397.0 + }, + { + "date": "20241230", + "open": 3976.7004, + "close": 3999.0549, + "high": 4005.778, + "low": 3976.7004, + "vol": 164612485.0 + }, + { + "date": "20241227", + "open": 3987.0189, + "close": 3981.0307, + "high": 4007.1314, + "low": 3970.9365, + "vol": 169268666.0 + }, + { + "date": "20241226", + "open": 3981.7317, + "close": 3987.4801, + "high": 3991.5331, + "low": 3965.5461, + "vol": 143425286.0 + }, + { + "date": "20241225", + "open": 3987.9557, + "close": 3985.6291, + "high": 4007.7626, + "low": 3969.4766, + "vol": 159390747.0 + }, + { + "date": "20241224", + "open": 3934.9422, + "close": 3983.6882, + "high": 3985.8588, + "low": 3934.7338, + "vol": 161494589.0 + }, + { + "date": "20241223", + "open": 3928.1493, + "close": 3933.5718, + "high": 3965.8502, + "low": 3928.1493, + "vol": 190067362.0 + }, + { + "date": "20241220", + "open": 3937.4722, + "close": 3927.7441, + "high": 3958.6336, + "low": 3923.2562, + "vol": 154986496.0 + }, + { + "date": "20241219", + "open": 3911.8901, + "close": 3945.4635, + "high": 3954.3491, + "low": 3899.7479, + "vol": 161081040.0 + }, + { + "date": "20241218", + "open": 3934.5425, + "close": 3941.89, + "high": 3956.1441, + "low": 3933.9693, + "vol": 157738321.0 + }, + { + "date": "20241217", + "open": 3908.1263, + "close": 3922.0334, + "high": 3955.945, + "low": 3908.1263, + "vol": 160939090.0 + }, + { + "date": "20241216", + "open": 3930.7807, + "close": 3911.8416, + "high": 3938.7595, + "low": 3901.6465, + "vol": 178511251.0 + }, + { + "date": "20241213", + "open": 4000.9854, + "close": 3933.1808, + "high": 4000.9854, + "low": 3931.7392, + "vol": 230353079.0 + } + ] + }, + { + "code": "000905.SH", + "name": "中证500", + "price": 5369.2811, + "change": -1.6, + "kline_data": [ + { + "date": "20250110", + "open": 5449.0014, + "close": 5369.2811, + "high": 5496.3833, + "low": 5369.2811, + "vol": 130179553.0 + }, + { + "date": "20250109", + "open": 5437.8128, + "close": 5456.5863, + "high": 5493.6799, + "low": 5436.7036, + "vol": 129832627.0 + }, + { + "date": "20250108", + "open": 5462.324, + "close": 5461.5861, + "high": 5501.6555, + "low": 5326.9259, + "vol": 163849915.0 + }, + { + "date": "20250107", + "open": 5429.1729, + "close": 5484.6847, + "high": 5485.5848, + "low": 5405.1327, + "vol": 145065278.0 + }, + { + "date": "20250106", + "open": 5421.0653, + "close": 5427.1007, + "high": 5482.6253, + "low": 5384.3326, + "vol": 146265658.0 + }, + { + "date": "20250103", + "open": 5552.75, + "close": 5427.8041, + "high": 5581.3978, + "low": 5419.7429, + "vol": 174077310.0 + }, + { + "date": "20250102", + "open": 5717.9157, + "close": 5545.8174, + "high": 5722.1107, + "low": 5498.8216, + "vol": 193763157.0 + }, + { + "date": "20241231", + "open": 5902.033, + "close": 5725.7324, + "high": 5905.463, + "low": 5725.7324, + "vol": 182158855.0 + }, + { + "date": "20241230", + "open": 5890.4633, + "close": 5898.884, + "high": 5928.5718, + "low": 5867.8538, + "vol": 155565586.0 + }, + { + "date": "20241227", + "open": 5892.6943, + "close": 5899.1665, + "high": 5975.7919, + "low": 5881.7173, + "vol": 179085498.0 + }, + { + "date": "20241226", + "open": 5830.7212, + "close": 5887.0605, + "high": 5897.4827, + "low": 5830.7212, + "vol": 145319444.0 + }, + { + "date": "20241225", + "open": 5896.4295, + "close": 5840.2889, + "high": 5897.5101, + "low": 5796.6475, + "vol": 157279132.0 + }, + { + "date": "20241224", + "open": 5827.1597, + "close": 5895.2595, + "high": 5897.1946, + "low": 5827.1597, + "vol": 167365824.0 + }, + { + "date": "20241223", + "open": 5917.6939, + "close": 5818.551, + "high": 5935.1523, + "low": 5815.3415, + "vol": 177315486.0 + }, + { + "date": "20241220", + "open": 5889.946, + "close": 5917.1751, + "high": 5962.4154, + "low": 5889.946, + "vol": 178090399.0 + }, + { + "date": "20241219", + "open": 5829.2631, + "close": 5901.9155, + "high": 5918.4895, + "low": 5816.4729, + "vol": 196531194.0 + }, + { + "date": "20241218", + "open": 5861.2624, + "close": 5888.6162, + "high": 5921.9798, + "low": 5853.1628, + "vol": 187996290.0 + }, + { + "date": "20241217", + "open": 5896.9676, + "close": 5848.1628, + "high": 5924.6244, + "low": 5837.2514, + "vol": 193108547.0 + }, + { + "date": "20241216", + "open": 5983.1598, + "close": 5912.6014, + "high": 5991.5772, + "low": 5888.0426, + "vol": 214647441.0 + }, + { + "date": "20241213", + "open": 6065.9542, + "close": 5988.292, + "high": 6065.9542, + "low": 5988.292, + "vol": 286328198.0 + } + ] + }, + { + "code": "000852.SH", + "name": "中证1000", + "price": 5544.9158, + "change": -2.254, + "kline_data": [ + { + "date": "20250110", + "open": 5662.7667, + "close": 5544.9158, + "high": 5718.2122, + "low": 5544.9158, + "vol": 198014877.0 + }, + { + "date": "20250109", + "open": 5629.8952, + "close": 5672.7796, + "high": 5714.4606, + "low": 5629.8239, + "vol": 193320421.0 + }, + { + "date": "20250108", + "open": 5664.3121, + "close": 5661.7793, + "high": 5706.6593, + "low": 5501.4918, + "vol": 225800830.0 + }, + { + "date": "20250107", + "open": 5610.9462, + "close": 5692.041, + "high": 5694.0046, + "low": 5580.8851, + "vol": 193912450.0 + }, + { + "date": "20250106", + "open": 5614.0281, + "close": 5608.0344, + "high": 5688.9223, + "low": 5561.898, + "vol": 203047676.0 + }, + { + "date": "20250103", + "open": 5808.5424, + "close": 5625.209, + "high": 5826.0833, + "low": 5609.7663, + "vol": 237093211.0 + }, + { + "date": "20250102", + "open": 5948.6802, + "close": 5797.089, + "high": 5982.5535, + "low": 5743.1785, + "vol": 244125606.0 + }, + { + "date": "20241231", + "open": 6151.0339, + "close": 5957.7172, + "high": 6160.7327, + "low": 5957.7172, + "vol": 237990032.0 + }, + { + "date": "20241230", + "open": 6164.908, + "close": 6145.928, + "high": 6192.5845, + "low": 6097.1031, + "vol": 207712448.0 + }, + { + "date": "20241227", + "open": 6164.67, + "close": 6171.5653, + "high": 6250.2013, + "low": 6135.4787, + "vol": 237841554.0 + }, + { + "date": "20241226", + "open": 6083.8873, + "close": 6160.4518, + "high": 6185.8068, + "low": 6083.8873, + "vol": 206916739.0 + }, + { + "date": "20241225", + "open": 6158.6491, + "close": 6094.6866, + "high": 6159.5964, + "low": 6037.7355, + "vol": 219027032.0 + }, + { + "date": "20241224", + "open": 6110.7669, + "close": 6164.3605, + "high": 6165.2022, + "low": 6066.226, + "vol": 216010918.0 + }, + { + "date": "20241223", + "open": 6271.7634, + "close": 6096.3813, + "high": 6276.9712, + "low": 6086.912, + "vol": 258474975.0 + }, + { + "date": "20241220", + "open": 6195.6436, + "close": 6271.7532, + "high": 6320.9626, + "low": 6191.0814, + "vol": 243659085.0 + }, + { + "date": "20241219", + "open": 6127.2202, + "close": 6207.1862, + "high": 6223.8046, + "low": 6108.3942, + "vol": 251487924.0 + }, + { + "date": "20241218", + "open": 6156.6921, + "close": 6195.8677, + "high": 6242.5165, + "low": 6121.0537, + "vol": 248024122.0 + }, + { + "date": "20241217", + "open": 6262.4928, + "close": 6143.9436, + "high": 6269.1401, + "low": 6133.1291, + "vol": 274793959.0 + }, + { + "date": "20241216", + "open": 6355.6283, + "close": 6277.5162, + "high": 6367.8085, + "low": 6248.1579, + "vol": 310215173.0 + }, + { + "date": "20241213", + "open": 6447.2029, + "close": 6354.9883, + "high": 6460.9385, + "low": 6351.3009, + "vol": 361985513.0 + } + ] + }, + { + "code": "899050.BJ", + "name": "北证50", + "price": 1024.259, + "change": -2.7783, + "kline_data": [ + { + "date": "20250110", + "open": 1050.5655, + "close": 1024.259, + "high": 1068.3807, + "low": 1024.259, + "vol": 8430121.72 + }, + { + "date": "20250109", + "open": 1033.4444, + "close": 1053.529, + "high": 1063.6647, + "low": 1028.7234, + "vol": 10081530.53 + }, + { + "date": "20250108", + "open": 1018.3275, + "close": 1037.3981, + "high": 1047.0022, + "low": 999.111, + "vol": 8772397.56 + }, + { + "date": "20250107", + "open": 1006.3205, + "close": 1024.4156, + "high": 1024.4156, + "low": 988.2998, + "vol": 7687642.52 + }, + { + "date": "20250106", + "open": 1017.0684, + "close": 1003.6427, + "high": 1030.2143, + "low": 995.401, + "vol": 7686193.04 + }, + { + "date": "20250103", + "open": 1026.3682, + "close": 1018.1664, + "high": 1049.3344, + "low": 1011.6511, + "vol": 8352396.17 + }, + { + "date": "20250102", + "open": 1034.7079, + "close": 1019.8776, + "high": 1047.6465, + "low": 1009.8723, + "vol": 7543876.8 + }, + { + "date": "20241231", + "open": 1049.6903, + "close": 1037.8089, + "high": 1072.333, + "low": 1037.2628, + "vol": 7807303.05 + }, + { + "date": "20241230", + "open": 1092.0436, + "close": 1046.1662, + "high": 1092.0436, + "low": 1044.7641, + "vol": 7390755.29 + }, + { + "date": "20241227", + "open": 1108.9413, + "close": 1094.2393, + "high": 1115.3085, + "low": 1090.0353, + "vol": 7700006.02 + }, + { + "date": "20241226", + "open": 1105.5626, + "close": 1107.7839, + "high": 1130.2294, + "low": 1104.3358, + "vol": 7499455.6 + }, + { + "date": "20241225", + "open": 1148.0414, + "close": 1106.1983, + "high": 1148.4815, + "low": 1106.1983, + "vol": 7681377.4 + }, + { + "date": "20241224", + "open": 1148.274, + "close": 1149.776, + "high": 1161.8042, + "low": 1125.8246, + "vol": 7348674.51 + }, + { + "date": "20241223", + "open": 1169.7814, + "close": 1140.8764, + "high": 1185.7544, + "low": 1140.846, + "vol": 8063638.57 + }, + { + "date": "20241220", + "open": 1156.3654, + "close": 1171.7736, + "high": 1197.9436, + "low": 1153.6311, + "vol": 9409865.85 + }, + { + "date": "20241219", + "open": 1116.1291, + "close": 1169.7086, + "high": 1174.2728, + "low": 1091.6927, + "vol": 11979102.42 + }, + { + "date": "20241218", + "open": 1144.941, + "close": 1132.6112, + "high": 1164.2948, + "low": 1127.6974, + "vol": 8281584.21 + }, + { + "date": "20241217", + "open": 1178.1043, + "close": 1139.4107, + "high": 1182.6161, + "low": 1138.1503, + "vol": 8478059.38 + }, + { + "date": "20241216", + "open": 1215.7327, + "close": 1177.3635, + "high": 1222.3319, + "low": 1169.0697, + "vol": 9263746.01 + }, + { + "date": "20241213", + "open": 1222.1633, + "close": 1219.2972, + "high": 1248.5568, + "low": 1211.8177, + "vol": 11252895.68 + } + ] + } + ] + }, + "timestamp": "2025-01-11" + }, + "300009": { + "data": { + "stock_info": { + "code": "300009", + "name": "安科生物", + "market_value": 135.81, + "pe_ratio": 16.03, + "pb_ratio": 3.47, + "ps_ratio": 4.74, + "dividend_yield": 0.0308, + "price": 8.12, + "change_percent": 0.01, + "roe": 0.1537, + "gross_profit_margin": 0.7775, + "net_profit_margin": 0.3122, + "debt_to_assets": 0.172, + "revenue_yoy": 0, + "net_profit_yoy": -0.0989, + "bps": 2.349, + "ocfps": 0.251, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": null, + "max": null + } + } + }, + "timestamp": "2025-01-13" + }, + "300743": { + "data": { + "stock_info": { + "code": "300743", + "name": "天地数码", + "market_value": 21.74, + "pe_ratio": 39.08, + "pb_ratio": 3.57, + "ps_ratio": 3.38, + "dividend_yield": 0.0241, + "price": 14.17, + "change_percent": 0.0093, + "roe": 0.1209, + "gross_profit_margin": 0.3493, + "net_profit_margin": 0.1335, + "debt_to_assets": 0.3786, + "revenue_yoy": 0, + "net_profit_yoy": 0.8549, + "bps": 3.972, + "ocfps": 0.327, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": null, + "max": null + } + } + }, + "timestamp": "2025-01-13" + }, + "603511": { + "data": { + "stock_info": { + "code": "603511", + "name": "爱慕股份", + "market_value": 46.23, + "pe_ratio": 15.12, + "pb_ratio": 1.05, + "ps_ratio": 1.35, + "dividend_yield": 0.0662, + "price": 11.37, + "change_percent": -0.0026, + "roe": 0.0317, + "gross_profit_margin": 0.6597, + "net_profit_margin": 0.0615, + "debt_to_assets": 0.146, + "revenue_yoy": 0, + "net_profit_yoy": -0.3817, + "bps": 10.779, + "ocfps": 0.79, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": null, + "max": null + } + } + }, + "timestamp": "2025-01-13" + }, + "688553": { + "data": { + "stock_info": { + "code": "688553", + "name": "汇宇制药-W", + "market_value": 58.37, + "pe_ratio": 41.76, + "pb_ratio": 1.5, + "ps_ratio": 6.3, + "dividend_yield": 0.0, + "price": 13.78, + "change_percent": 0.0117, + "roe": 0.0594, + "gross_profit_margin": 0.8315, + "net_profit_margin": 0.2592, + "debt_to_assets": 0.2321, + "revenue_yoy": 0, + "net_profit_yoy": 1.5562, + "bps": 9.16, + "ocfps": 0.383, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": null, + "max": null + } + } + }, + "timestamp": "2025-01-13" + }, + "600030": { + "data": { + "stock_info": { + "code": "600030", + "name": "中信证券", + "market_value": 3928.93, + "pe_ratio": 19.92, + "pb_ratio": 1.53, + "ps_ratio": 6.54, + "dividend_yield": 0.027, + "price": 26.51, + "change_percent": 0.0019, + "roe": 0.0605, + "gross_profit_margin": 0, + "net_profit_margin": 0.3782, + "debt_to_assets": 0.8314, + "revenue_yoy": 0, + "net_profit_yoy": 0.0235, + "bps": 17.594, + "ocfps": 7.666, + "from_cache": false + }, + "targets": { + "target_market_value": { + "min": null, + "max": null + } + } + }, + "timestamp": "2025-01-13" + } +} \ No newline at end of file diff --git a/watchlist.json b/watchlist.json new file mode 100644 index 0000000..460325f --- /dev/null +++ b/watchlist.json @@ -0,0 +1,38 @@ +{ + "sh600601": { + "target_market_value": null, + "target_pe": null, + "target_pb": null, + "target_dividend_yield": null + }, + "sz0024515": { + "target_market_value": null, + "target_pe": null, + "target_pb": null, + "target_dividend_yield": null + }, + "sz870199": { + "target_market_value": null, + "target_pe": null, + "target_pb": null, + "target_dividend_yield": null + }, + "601318": { + "target_market_value": null, + "target_pe": null, + "target_pb": null, + "target_dividend_yield": null + }, + "600601": { + "target_market_value": null, + "target_pe": null, + "target_pb": null, + "target_dividend_yield": null + }, + "300059": { + "target_market_value": null, + "target_pe": null, + "target_pb": null, + "target_dividend_yield": null + } +} \ No newline at end of file