diff --git a/backend/app/config.py b/backend/app/config.py index 6814a3a..8ec6501 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -175,7 +175,7 @@ class Settings(BaseSettings): # 股票智能体配置 stock_symbols_us: str = "" # 美股代码,逗号分隔 - # 港股代码:科技+新能源+芯片+AI+金融+汽车+医药+消费+能源 + # 港股代码:科技+新能源+芯片+AI+金融+汽车+医药+消费+能源(统一格式:去掉前导零) # 科技:腾讯控股/阿里巴巴/美团/小米集团/京东集团/网易/百度/快手/知乎/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_llm_threshold: float = 0.70 # 触发 LLM 分析的置信度阈值 diff --git a/backend/app/services/fundamental_service.py b/backend/app/services/fundamental_service.py index 6d2b662..6ff10d5 100644 --- a/backend/app/services/fundamental_service.py +++ b/backend/app/services/fundamental_service.py @@ -30,6 +30,33 @@ class FundamentalService: 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]]: """ 获取股票的基本面数据 @@ -44,7 +71,10 @@ class FundamentalService: return None try: - ticker = yf.Ticker(symbol) + # 标准化港股代码格式 + normalized_symbol = self._normalize_hk_symbol(symbol) + + ticker = yf.Ticker(normalized_symbol) # 获取股票信息 info = ticker.info diff --git a/backend/app/services/us_stock_service.py b/backend/app/services/us_stock_service.py index d26eaae..8753a75 100644 --- a/backend/app/services/us_stock_service.py +++ b/backend/app/services/us_stock_service.py @@ -9,24 +9,54 @@ from app.utils.logger import logger class USStockService: - """美股数据服务类""" + """美股数据服务类(支持美股和港股)""" def __init__(self): """初始化美股数据服务""" 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]]: """ 获取美股基本信息 Args: - symbol: 股票代码(如 AAPL, TSLA) + symbol: 股票代码(如 AAPL, TSLA 或 0700.HK) Returns: 股票基本信息字典 """ try: - stock = yf.Ticker(symbol) + # 标准化港股代码格式 + normalized_symbol = self._normalize_hk_symbol(symbol) + + stock = yf.Ticker(normalized_symbol) info = stock.info if not info or 'symbol' not in info: @@ -85,7 +115,10 @@ class USStockService: 包含OHLCV数据的DataFrame """ try: - stock = yf.Ticker(symbol) + # 标准化港股代码格式 + normalized_symbol = self._normalize_hk_symbol(symbol) + + stock = yf.Ticker(normalized_symbol) hist = stock.history(period=period, interval=interval) if hist.empty: @@ -110,7 +143,10 @@ class USStockService: 财务数据字典 """ try: - stock = yf.Ticker(symbol) + # 标准化港股代码格式 + normalized_symbol = self._normalize_hk_symbol(symbol) + + stock = yf.Ticker(normalized_symbol) # 获取财务报表 financials = stock.financials diff --git a/backend/app/services/yfinance_service.py b/backend/app/services/yfinance_service.py index 0643c5e..65ea145 100644 --- a/backend/app/services/yfinance_service.py +++ b/backend/app/services/yfinance_service.py @@ -25,18 +25,47 @@ class YFinanceService: logger.error("yfinance 未安装,请运行: pip install yfinance") 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]: """ 获取股票实时行情 Args: - symbol: 股票代码,如 'AAPL' + symbol: 股票代码,如 'AAPL' 或 '0700.HK' Returns: 行情数据字典 """ try: - ticker = self.yf.Ticker(symbol) + # 标准化港股代码格式 + normalized_symbol = self._normalize_hk_symbol(symbol) + + ticker = self.yf.Ticker(normalized_symbol) # 使用 history 方法获取数据(更可靠,避免 429 错误) hist = ticker.history(period="2d", interval="1h") @@ -112,7 +141,9 @@ class YFinanceService: period: str ) -> 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() # 检查缓存 @@ -124,7 +155,7 @@ class YFinanceService: # 获取新数据 try: - ticker = self.yf.Ticker(symbol) + ticker = self.yf.Ticker(normalized_symbol) df = ticker.history(period=period, interval=interval) if df.empty: