This commit is contained in:
aaron 2026-02-27 11:27:27 +08:00
parent fcb9e8d659
commit 96e8eddea5
4 changed files with 109 additions and 12 deletions

View File

@ -175,7 +175,7 @@ class Settings(BaseSettings):
# 股票智能体配置 # 股票智能体配置
stock_symbols_us: str = "" # 美股代码,逗号分隔 stock_symbols_us: str = "" # 美股代码,逗号分隔
# 港股代码:科技+新能源+芯片+AI+金融+汽车+医药+消费+能源 # 港股代码:科技+新能源+芯片+AI+金融+汽车+医药+消费+能源(统一格式:去掉前导零)
# 科技:腾讯控股/阿里巴巴/美团/小米集团/京东集团/网易/百度/快手/知乎/B站 # 科技:腾讯控股/阿里巴巴/美团/小米集团/京东集团/网易/百度/快手/知乎/B站
# 新能源:比亚迪/理想汽车/小鹏汽车/赣锋锂业/龙源电力/信义能源 # 新能源:比亚迪/理想汽车/小鹏汽车/赣锋锂业/龙源电力/信义能源
# 芯片:中芯国际/华虹半导体/上海复旦 # 芯片:中芯国际/华虹半导体/上海复旦
@ -185,7 +185,7 @@ class Settings(BaseSettings):
# 医药:药明康德/药明生物/百济神州/信达生物/石药集团 # 医药:药明康德/药明生物/百济神州/信达生物/石药集团
# 消费:名创优品/泡泡玛特/安踏体育 # 消费:名创优品/泡泡玛特/安踏体育
# 能源:中海油/中石油/中国神华 # 能源:中海油/中石油/中国神华
stock_symbols_hk: str = "00700.HK,09988.HK,03690.HK,01810.HK,09618.HK,09999.HK,09888.HK,01024.HK,02390.HK,09626.HK,01211.HK,02015.HK,09868.HK,01772.HK,00916.HK,03868.HK,00981.HK,01347.HK,01385.HK,00020.HK,06682.HK,02121.HK,01357.HK,09959.HK,06608.HK,00005.HK,00939.HK,01398.HK,01288.HK,03988.HK,01299.HK,02318.HK,02628.HK,03908.HK,06030.HK,09866.HK,02333.HK,00175.HK,02359.HK,02269.HK,06160.HK,01801.HK,01093.HK,09896.HK,09992.HK,02020.HK,00883.HK,00857.HK,01088.HK" stock_symbols_hk: str = "700.HK,9988.HK,3690.HK,1810.HK,9618.HK,9999.HK,9888.HK,1024.HK,2390.HK,9626.HK,1211.HK,2015.HK,9868.HK,1772.HK,916.HK,3868.HK,981.HK,1347.HK,1385.HK,20.HK,6682.HK,2121.HK,1357.HK,9959.HK,6608.HK,5.HK,939.HK,1398.HK,1288.HK,3988.HK,1299.HK,2318.HK,2628.HK,3908.HK,6030.HK,9866.HK,2333.HK,175.HK,2359.HK,2269.HK,6160.HK,1801.HK,1093.HK,9896.HK,9992.HK,2020.HK,883.HK,857.HK,1088.HK"
stock_analysis_interval: int = 300 # 分析间隔默认5分钟 stock_analysis_interval: int = 300 # 分析间隔默认5分钟
stock_llm_threshold: float = 0.70 # 触发 LLM 分析的置信度阈值 stock_llm_threshold: float = 0.70 # 触发 LLM 分析的置信度阈值

View File

@ -30,6 +30,33 @@ class FundamentalService:
logger.info("基本面数据服务初始化成功") logger.info("基本面数据服务初始化成功")
@staticmethod
def _normalize_hk_symbol(symbol: str) -> str:
"""
标准化港股代码格式为 yfinance 要求的格式
- 4位及以下左侧补零到4位 700.HK 0700.HK, 5.HK 0005.HK
- 5位及以上去掉前导零 09618.HK 9618.HK
"""
if not symbol.endswith('.HK'):
return symbol
# 分离代码和后缀
code_part = symbol[:-3] # 去掉 .HK
suffix = '.HK'
# 如果是纯数字代码
if code_part.isdigit():
# 4位及以下补零到4位
if len(code_part) <= 4:
normalized_code = code_part.zfill(4)
# 5位及以上去掉前导零
else:
normalized_code = code_part.lstrip('0') or '0'
else:
normalized_code = code_part
return normalized_code + suffix
def get_fundamental_data(self, symbol: str) -> Optional[Dict[str, Any]]: def get_fundamental_data(self, symbol: str) -> Optional[Dict[str, Any]]:
""" """
获取股票的基本面数据 获取股票的基本面数据
@ -44,7 +71,10 @@ class FundamentalService:
return None return None
try: try:
ticker = yf.Ticker(symbol) # 标准化港股代码格式
normalized_symbol = self._normalize_hk_symbol(symbol)
ticker = yf.Ticker(normalized_symbol)
# 获取股票信息 # 获取股票信息
info = ticker.info info = ticker.info

View File

@ -9,24 +9,54 @@ from app.utils.logger import logger
class USStockService: class USStockService:
"""美股数据服务类""" """美股数据服务类(支持美股和港股)"""
def __init__(self): def __init__(self):
"""初始化美股数据服务""" """初始化美股数据服务"""
self.cache = {} # 简单的内存缓存 self.cache = {} # 简单的内存缓存
@staticmethod
def _normalize_hk_symbol(symbol: str) -> str:
"""
标准化港股代码格式为 yfinance 要求的格式
- 4位及以下左侧补零到4位 700.HK 0700.HK, 5.HK 0005.HK
- 5位及以上去掉前导零 09618.HK 9618.HK
"""
if not symbol.endswith('.HK'):
return symbol
# 分离代码和后缀
code_part = symbol[:-3] # 去掉 .HK
suffix = '.HK'
# 如果是纯数字代码
if code_part.isdigit():
# 4位及以下补零到4位
if len(code_part) <= 4:
normalized_code = code_part.zfill(4)
# 5位及以上去掉前导零
else:
normalized_code = code_part.lstrip('0') or '0'
else:
normalized_code = code_part
return normalized_code + suffix
def get_stock_info(self, symbol: str) -> Optional[Dict[str, Any]]: def get_stock_info(self, symbol: str) -> Optional[Dict[str, Any]]:
""" """
获取美股基本信息 获取美股基本信息
Args: Args:
symbol: 股票代码 AAPL, TSLA symbol: 股票代码 AAPL, TSLA 0700.HK
Returns: Returns:
股票基本信息字典 股票基本信息字典
""" """
try: try:
stock = yf.Ticker(symbol) # 标准化港股代码格式
normalized_symbol = self._normalize_hk_symbol(symbol)
stock = yf.Ticker(normalized_symbol)
info = stock.info info = stock.info
if not info or 'symbol' not in info: if not info or 'symbol' not in info:
@ -85,7 +115,10 @@ class USStockService:
包含OHLCV数据的DataFrame 包含OHLCV数据的DataFrame
""" """
try: try:
stock = yf.Ticker(symbol) # 标准化港股代码格式
normalized_symbol = self._normalize_hk_symbol(symbol)
stock = yf.Ticker(normalized_symbol)
hist = stock.history(period=period, interval=interval) hist = stock.history(period=period, interval=interval)
if hist.empty: if hist.empty:
@ -110,7 +143,10 @@ class USStockService:
财务数据字典 财务数据字典
""" """
try: try:
stock = yf.Ticker(symbol) # 标准化港股代码格式
normalized_symbol = self._normalize_hk_symbol(symbol)
stock = yf.Ticker(normalized_symbol)
# 获取财务报表 # 获取财务报表
financials = stock.financials financials = stock.financials

View File

@ -25,18 +25,47 @@ class YFinanceService:
logger.error("yfinance 未安装,请运行: pip install yfinance") logger.error("yfinance 未安装,请运行: pip install yfinance")
raise raise
def _normalize_hk_symbol(self, symbol: str) -> str:
"""
标准化港股代码格式为 yfinance 要求的格式
- 4位及以下左侧补零到4位 700.HK 0700.HK, 5.HK 0005.HK
- 5位及以上去掉前导零 09618.HK 9618.HK
"""
if not symbol.endswith('.HK'):
return symbol
# 分离代码和后缀
code_part = symbol[:-3] # 去掉 .HK
suffix = '.HK'
# 如果是纯数字代码
if code_part.isdigit():
# 4位及以下补零到4位
if len(code_part) <= 4:
normalized_code = code_part.zfill(4)
# 5位及以上去掉前导零
else:
normalized_code = code_part.lstrip('0') or '0'
else:
normalized_code = code_part
return normalized_code + suffix
def get_ticker(self, symbol: str) -> Optional[Dict]: def get_ticker(self, symbol: str) -> Optional[Dict]:
""" """
获取股票实时行情 获取股票实时行情
Args: Args:
symbol: 股票代码 'AAPL' symbol: 股票代码 'AAPL' '0700.HK'
Returns: Returns:
行情数据字典 行情数据字典
""" """
try: try:
ticker = self.yf.Ticker(symbol) # 标准化港股代码格式
normalized_symbol = self._normalize_hk_symbol(symbol)
ticker = self.yf.Ticker(normalized_symbol)
# 使用 history 方法获取数据(更可靠,避免 429 错误) # 使用 history 方法获取数据(更可靠,避免 429 错误)
hist = ticker.history(period="2d", interval="1h") hist = ticker.history(period="2d", interval="1h")
@ -112,7 +141,9 @@ class YFinanceService:
period: str period: str
) -> Optional[pd.DataFrame]: ) -> Optional[pd.DataFrame]:
"""获取带缓存的数据""" """获取带缓存的数据"""
cache_key = f"{symbol}_{interval}_{period}" # 标准化港股代码格式
normalized_symbol = self._normalize_hk_symbol(symbol)
cache_key = f"{normalized_symbol}_{interval}_{period}"
now = datetime.now() now = datetime.now()
# 检查缓存 # 检查缓存
@ -124,7 +155,7 @@ class YFinanceService:
# 获取新数据 # 获取新数据
try: try:
ticker = self.yf.Ticker(symbol) ticker = self.yf.Ticker(normalized_symbol)
df = ticker.history(period=period, interval=interval) df = ticker.history(period=period, interval=interval)
if df.empty: if df.empty: