commit;
This commit is contained in:
		
							parent
							
								
									8f73099e18
								
							
						
					
					
						commit
						03450116ce
					
				
							
								
								
									
										23
									
								
								src/app.py
								
								
								
								
							
							
						
						
									
										23
									
								
								src/app.py
								
								
								
								
							|  | @ -241,7 +241,7 @@ def initialize_stock_price_schedule(): | ||||||
|         scheduler.add_job( |         scheduler.add_job( | ||||||
|             func=update_stock_price, |             func=update_stock_price, | ||||||
|             trigger='interval', |             trigger='interval', | ||||||
|             minutes=5, |             minutes=60, | ||||||
|             id='stock_price_update', |             id='stock_price_update', | ||||||
|             name='实时股价数据采集', |             name='实时股价数据采集', | ||||||
|             replace_existing=True |             replace_existing=True | ||||||
|  | @ -249,7 +249,7 @@ def initialize_stock_price_schedule(): | ||||||
|          |          | ||||||
|         # 启动调度器 |         # 启动调度器 | ||||||
|         scheduler.start() |         scheduler.start() | ||||||
|         logger.info("实时股价数据采集定时任务已初始化,将在交易时间内每5分钟执行一次") |         logger.info("实时股价数据采集定时任务已初始化,将在交易时间内每60分钟执行一次") | ||||||
|         return scheduler |         return scheduler | ||||||
|          |          | ||||||
|     except Exception as e: |     except Exception as e: | ||||||
|  | @ -1452,6 +1452,22 @@ def comprehensive_analysis(): | ||||||
|                     if 'db_session2' in locals() and db_session2 is not None:  # 确保 db_session 已定义 |                     if 'db_session2' in locals() and db_session2 is not None:  # 确保 db_session 已定义 | ||||||
|                         db_session2.close()  # <--- 关闭会话 |                         db_session2.close()  # <--- 关闭会话 | ||||||
| 
 | 
 | ||||||
|  |                 # 获取企业市值信息 | ||||||
|  |                 market_value_results = {} | ||||||
|  |                 try: | ||||||
|  |                     from src.valuation_analysis.stock_price_collector import StockPriceCollector | ||||||
|  |                     price_collector = StockPriceCollector() | ||||||
|  |                      | ||||||
|  |                     for stock_code in input_stock_codes: | ||||||
|  |                         price_data = price_collector.get_stock_price_data(stock_code) | ||||||
|  |                         if price_data and 'total_market_value' in price_data: | ||||||
|  |                             market_value_results[stock_code] = price_data['total_market_value'] | ||||||
|  |                         else: | ||||||
|  |                             market_value_results[stock_code] = None | ||||||
|  |                 except Exception as e: | ||||||
|  |                     logger.error(f"获取企业市值信息失败: {str(e)}") | ||||||
|  |                     market_value_results = {} | ||||||
|  | 
 | ||||||
|                 db_session = next(get_db()) |                 db_session = next(get_db()) | ||||||
|                 # 筛选出传入列表中符合条件的股票 |                 # 筛选出传入列表中符合条件的股票 | ||||||
|                 for code, name in all_stocks: |                 for code, name in all_stocks: | ||||||
|  | @ -1475,7 +1491,8 @@ def comprehensive_analysis(): | ||||||
|                             "industry_space": industry_space,  # 行业发展空间(2:高速增长, 1:稳定经营, 0:不确定性大, -1:不利经营) |                             "industry_space": industry_space,  # 行业发展空间(2:高速增长, 1:稳定经营, 0:不确定性大, -1:不利经营) | ||||||
|                             "financial_report_level": financial_report_level,  # 经营质量(2:优秀, 1:较好, 0:一般, -1:存在隐患,-2:较大隐患) |                             "financial_report_level": financial_report_level,  # 经营质量(2:优秀, 1:较好, 0:一般, -1:存在隐患,-2:较大隐患) | ||||||
|                             "pe_industry": pe_industry,  # 个股在行业的PE水平(-1:高于行业, 0:接近行业, 1:低于行业) |                             "pe_industry": pe_industry,  # 个股在行业的PE水平(-1:高于行业, 0:接近行业, 1:低于行业) | ||||||
|                             "tracks": track_results.get(code, [])  # 添加赛道信息 |                             "tracks": track_results.get(code, []),  # 添加赛道信息 | ||||||
|  |                             "market_value": market_value_results.get(code)  # 添加企业市值信息 | ||||||
|                         }) |                         }) | ||||||
|                  |                  | ||||||
|                 logger.info(f"筛选出 {len(filtered_stocks)} 个符合条件的股票") |                 logger.info(f"筛选出 {len(filtered_stocks)} 个符合条件的股票") | ||||||
|  |  | ||||||
|  | @ -1477,6 +1477,13 @@ class FundamentalAnalyzer: | ||||||
|                - 长期持有:公司具备长期稳定的盈利能力、行业地位稳固、长期成长性好 |                - 长期持有:公司具备长期稳定的盈利能力、行业地位稳固、长期成长性好 | ||||||
|                - 不建议投资:存在明显风险因素、基本面恶化、估值过高、行业前景不佳或者存在退市风险 |                - 不建议投资:存在明显风险因素、基本面恶化、估值过高、行业前景不佳或者存在退市风险 | ||||||
|              |              | ||||||
|  |             请注意: | ||||||
|  |             1. 请完全基于提供的分析结果中的最新数据进行分析,不要使用任何历史数据或过时信息 | ||||||
|  |             2. 如果分析结果中包含2024年或2025年的数据,请优先使用这些最新数据 | ||||||
|  |             3. 避免使用"2023年"等历史时间点的数据,除非分析结果中明确提供了这些数据 | ||||||
|  |             4. 重点关注公司最新的业务发展、财务表现和市场定位 | ||||||
|  |             5. 在分析行业环境时,请使用最新的行业数据和竞争格局信息 | ||||||
|  |              | ||||||
|             请提供专业、客观的分析,突出关键信息,避免冗长描述。重点关注投资价值和风险。在输出投资建议时,请明确指出是短期持有、中期持有、长期持有还是不建议投资。 |             请提供专业、客观的分析,突出关键信息,避免冗长描述。重点关注投资价值和风险。在输出投资建议时,请明确指出是短期持有、中期持有、长期持有还是不建议投资。 | ||||||
|              |              | ||||||
|             各维度分析结果: |             各维度分析结果: | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ XUEQIU_HEADERS = { | ||||||
|     'Accept-Encoding': 'gzip, deflate, br, zstd', |     'Accept-Encoding': 'gzip, deflate, br, zstd', | ||||||
|     'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', |     'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', | ||||||
|     'Client-Version': 'v2.44.75', |     'Client-Version': 'v2.44.75', | ||||||
|     'Cookie': 'cookiesu=811743062689927; device_id=33fa3c7fca4a65f8f4354e10ed6b7470; HMACCOUNT=8B64A2E3C307C8C0; s=c611ttmqlj; xq_is_login=1; u=8493411634; bid=4065a77ca57a69c83405d6e591ab5449_m8r2nhs8; Hm_lvt_1db88642e346389874251b5a1eded6e3=1746410725; xq_a_token=660fb18cf1d15162da76deedc46b649370124dca; xqat=660fb18cf1d15162da76deedc46b649370124dca; xq_id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1aWQiOjg0OTM0MTE2MzQsImlzcyI6InVjIiwiZXhwIjoxNzQ5ODYxNjY5LCJjdG0iOjE3NDcyNjk2Njk0NDgsImNpZCI6ImQ5ZDBuNEFadXAifQ.jc_E9qvguLwBDASn1Z-KjGtU89pNJRwJq_hIaiR3r2re7-_xiXH8qhuhC3Se8rlfKGZ8sHsb3rSND_vnF7yMp90QQHdK_brSmlgd6_ltHmJfWSFNJvMk7F3s0yPjcpeMqeUTPFnZwKmoWwZVKEwdVBN8f25z6e9M2JjtSTZ2huADH_FdEn1rb9IU-H35z_MLWW1M7vB5xc2rh57yFIBnQoxu9OLfeETpeIpASP1UBeZXoQZ_v1gIWiFYItwuudIz0tPYzB-o2duRe31G0S_hNvEGl3HH4M5FjTyaPAq2PRuiZCyRF-25gHXBZnLcxyavZ1VAURfHng_377_IJNSXsw; xq_r_token=8a5dec9c93caf88d0e1f98f1d23ea1bb60eb6225; is_overseas=0; Hm_lpvt_1db88642e346389874251b5a1eded6e3=1747356850; ssxmod_itna=eqGxBDnGKYuxcD4kDRgxYq7ueYKS8DBP01Dp2xQyP08D60DB40QuHhqDyGGdVmpmghQehYtDDsqze4GzDiLPGhDBWAFdYCdqt4NKWooqCWKCwdUme9Ill25QAClcymm=0Iil4OAe8oGLDY=DCTKK420iDYAEDBYD74G+DDeDiO3Dj4GmDGY=aeDFIQutVCRKdxDwDB=DmqG23ObDm4DfDDLorBD4Il2YDDtDAkaGNPDADA3doDDlYD84edb4DYpogQ0FdgahphuXIeDMixGXzAlzx9CnoiWtV/LfNf2aHPGuDG=OcC0Hh2bmRT3f8hGxYBY5QeOhx+BxorKq0DW7HRYqexx=CD=WKK7oQ7YBGxPG4KiKy7hAQd5dpOodYYrcqsMkbZMshieygdyhxogYO2deGd46DAQ5MA5VBxiT5/h4WB++l=Eet4D; ssxmod_itna2=eqGxBDnGKYuxcD4kDRgxYq7ueYKS8DBP01Dp2xQyP08D60DB40QuHhqDyGGdVmpmghQehY4Dfie4pCoTp35CT5NsKziGGtvkoYD', |     'Cookie': 'cookiesu=811743062689927; device_id=33fa3c7fca4a65f8f4354e10ed6b7470; HMACCOUNT=8B64A2E3C307C8C0; s=c611ttmqlj; xq_is_login=1; u=8493411634; bid=4065a77ca57a69c83405d6e591ab5449_m8r2nhs8; Hm_lvt_1db88642e346389874251b5a1eded6e3=1746410725; xq_a_token=660fb18cf1d15162da76deedc46b649370124dca; xqat=660fb18cf1d15162da76deedc46b649370124dca; xq_id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1aWQiOjg0OTM0MTE2MzQsImlzcyI6InVjIiwiZXhwIjoxNzQ5ODYxNjY5LCJjdG0iOjE3NDcyNjk2Njk0NDgsImNpZCI6ImQ5ZDBuNEFadXAifQ.jc_E9qvguLwBDASn1Z-KjGtU89pNJRwJq_hIaiR3r2re7-_xiXH8qhuhC3Se8rlfKGZ8sHsb3rSND_vnF7yMp90QQHdK_brSmlgd6_ltHmJfWSFNJvMk7F3s0yPjcpeMqeUTPFnZwKmoWwZVKEwdVBN8f25z6e9M2JjtSTZ2huADH_FdEn1rb9IU-H35z_MLWW1M7vB5xc2rh57yFIBnQoxu9OLfeETpeIpASP1UBeZXoQZ_v1gIWiFYItwuudIz0tPYzB-o2duRe31G0S_hNvEGl3HH4M5FjTyaPAq2PRuiZCyRF-25gHXBZnLcxyavZ1VAURfHng_377_IJNSXsw; xq_r_token=8a5dec9c93caf88d0e1f98f1d23ea1bb60eb6225; snbim_minify=true; is_overseas=0; Hm_lpvt_1db88642e346389874251b5a1eded6e3=1747905410; ssxmod_itna=eqGxBDnGKYuxcD4kDRgxYq7ueYKS8DBP01Dp2xQyP08D60DB40QuHhqDyGGTrlGiGbtOh01qDsqze4GzDiLPGhDBWAFdYCdqtsqfmmxXxyB+doh6odserKO5sg=EiqfqztqpiexCPGnD0=O77N4xYAEDBYD74G+DDeDiO3Dj4GmDGYd=eDFzjRQyl2edxDwDB=DmqG23grDm4DfDDL5xRD4zC2YDDtDAMWz5PDADA3ooDDlYGO44Lr4DYp52nXWdOaspxTXzeDMixGXzYlCgaCRo0TQy9LAN32TNPGuDG=H6e0ahrbicn0AP4KGGwQ0imPKY+5meOQDqixGYwQGGiGGetGe3qqjeKYw10G4ixqim2mpbK+h1iaIPeQAieNS1X5pXZP4rQ04Iv4zmQWvplG40P4Gw4CqRjwzlwGjPwlD3iho+qKlD4hi3YD; ssxmod_itna2=eqGxBDnGKYuxcD4kDRgxYq7ueYKS8DBP01Dp2xQyP08D60DB40QuHhqDyGGTrlGiGbtOh0P4DWhYebouIdHtBItz/DboqtwisfWD', | ||||||
|     'Referer': 'https://weibo.com/u/7735765253', |     'Referer': 'https://weibo.com/u/7735765253', | ||||||
|     'Sec-Ch-Ua': '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"', |     'Sec-Ch-Ua': '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"', | ||||||
|     'Sec-Ch-Ua-Mobile': '?0', |     'Sec-Ch-Ua-Mobile': '?0', | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ from pymongo import MongoClient | ||||||
| import logging | import logging | ||||||
| from typing import Dict, List, Optional, Union, Tuple | from typing import Dict, List, Optional, Union, Tuple | ||||||
| import json | import json | ||||||
|  | import requests | ||||||
| 
 | 
 | ||||||
| from .config import DB_URL, MONGO_CONFIG, LOG_FILE | from .config import DB_URL, MONGO_CONFIG, LOG_FILE | ||||||
| from .stock_price_collector import StockPriceCollector | from .stock_price_collector import StockPriceCollector | ||||||
|  | @ -394,6 +395,69 @@ class FinancialAnalyzer: | ||||||
|                 'message': f'查询失败: {str(e)}' |                 'message': f'查询失败: {str(e)}' | ||||||
|             } |             } | ||||||
|      |      | ||||||
|  |     def get_momentum_indicators(self, stock_code: str, industry_codes: List[str]) -> Dict: | ||||||
|  |         """ | ||||||
|  |         获取动量指标数据 | ||||||
|  |          | ||||||
|  |         Args: | ||||||
|  |             stock_code: 目标股票代码 | ||||||
|  |             industry_codes: 行业股票代码列表 | ||||||
|  |              | ||||||
|  |         Returns: | ||||||
|  |             动量指标数据字典 | ||||||
|  |         """ | ||||||
|  |         try: | ||||||
|  |             url = "http://192.168.18.42:5000/api/dify/getStockMomentumIndex" | ||||||
|  |             payload = { | ||||||
|  |                 "code_list": industry_codes, | ||||||
|  |                 "target_code": stock_code | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             response = requests.post(url, json=payload) | ||||||
|  |             if response.status_code != 200: | ||||||
|  |                 return { | ||||||
|  |                     'success': False, | ||||||
|  |                     'message': f'获取动量指标失败: HTTP {response.status_code}' | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|  |             data = response.json() | ||||||
|  |              | ||||||
|  |             # 计算OBV和NATR的rank_score | ||||||
|  |             obv_rank_score = round((1 - data['obv_rank'] / len(industry_codes)) * 10, 3) | ||||||
|  |             natr_rank_score = round((1 - data['natr_rank'] / len(industry_codes)) * 10, 3) | ||||||
|  |              | ||||||
|  |             return { | ||||||
|  |                 'success': True, | ||||||
|  |                 'indicators': [ | ||||||
|  |                     { | ||||||
|  |                         'key': 'obv', | ||||||
|  |                         'desc': '能量', | ||||||
|  |                         'value': round(data['OBV'], 3), | ||||||
|  |                         'rank_score': obv_rank_score | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         'key': 'form', | ||||||
|  |                         'desc': f'技术形态-{data["form"]}', | ||||||
|  |                         'value': round(data['form_probability'], 3), | ||||||
|  |                         'rank_score': round(data['form_probability'] * 10, 3)  # 将概率转换为0-10的分数 | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         'key': 'natr', | ||||||
|  |                         'desc': '阶段位置', | ||||||
|  |                         'value': round(data['NATR'], 3), | ||||||
|  |                         'rank_score': natr_rank_score | ||||||
|  |                     } | ||||||
|  |                 ], | ||||||
|  |                 'avg_score': round((obv_rank_score + natr_rank_score + round(data['form_probability'] * 10, 3)) / 3, 3) | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |         except Exception as e: | ||||||
|  |             logger.error(f"获取动量指标失败: {str(e)}") | ||||||
|  |             return { | ||||||
|  |                 'success': False, | ||||||
|  |                 'message': f'获取动量指标失败: {str(e)}' | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|     def analyze_financial_data(self, stock_code: str) -> Dict: |     def analyze_financial_data(self, stock_code: str) -> Dict: | ||||||
|         """ |         """ | ||||||
|         分析财务数据 |         分析财务数据 | ||||||
|  | @ -413,6 +477,12 @@ class FinancialAnalyzer: | ||||||
|             industry_analyzer = IndustryAnalyzer() |             industry_analyzer = IndustryAnalyzer() | ||||||
|             concepts = industry_analyzer.get_stock_concepts(stock_code) |             concepts = industry_analyzer.get_stock_concepts(stock_code) | ||||||
|              |              | ||||||
|  |             # 获取同行业股票列表 | ||||||
|  |             industry_stocks = self.get_industry_stocks(stock_code) | ||||||
|  |              | ||||||
|  |             # 获取动量指标数据 | ||||||
|  |             momentum_result = self.get_momentum_indicators(stock_code, industry_stocks) | ||||||
|  |              | ||||||
|             # 获取基础财务指标 |             # 获取基础财务指标 | ||||||
|             base_result = self.extract_financial_indicators(stock_code) |             base_result = self.extract_financial_indicators(stock_code) | ||||||
|             if not base_result.get('success'): |             if not base_result.get('success'): | ||||||
|  | @ -578,6 +648,7 @@ class FinancialAnalyzer: | ||||||
|                 'growth': growth_data, |                 'growth': growth_data, | ||||||
|                 'value_rating': process_indicators(value_rating_indicators), |                 'value_rating': process_indicators(value_rating_indicators), | ||||||
|                 'liquidity': process_indicators(liquidity_indicators), |                 'liquidity': process_indicators(liquidity_indicators), | ||||||
|  |                 'momentum': momentum_result.get('indicators', []),  # 添加动量指标数据 | ||||||
|                 'concepts': concepts,  # 添加概念板块数据 |                 'concepts': concepts,  # 添加概念板块数据 | ||||||
|                 'price_data': price_data  # 添加实时股价数据 |                 'price_data': price_data  # 添加实时股价数据 | ||||||
|             } |             } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue