mindspore/utils.py

400 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from dataclasses import dataclass, field
from typing import List, Dict, Optional
import pandas as pd
import asyncio
import requests
from bs4 import BeautifulSoup
from datetime import datetime, timedelta
import tushare as ts
import json
# 使用你的Tushare API Token
ts.set_token('')
pro = ts.pro_api()
# 加载配置文件
class Config:
with open('config.json', 'r') as f:
config_data = json.load(f)
LLM_API_KEY = '' # 替换为你的API Key
LLM_URL = 'https://api.deepseek.com/chat/completions'
LLM_MODEL = 'deepseek-chat'
@dataclass
class TickerClass:
name: str # 股票代码
display_name: str = field(default="") # 股票名称
hist_data: Optional[pd.DataFrame] = field(default=None) # 历史数据
balance_sheet: Optional[pd.DataFrame] = field(default=None) # 资产负债表
financials: Optional[pd.DataFrame] = field(default=None) # 财务数据
news: Optional[Dict] = field(default=None) # 新闻数据
analyst_ratings: str = field(default=None) # 分析师评级
price: float = field(default=None) # 股票当前价格
sentiment_analysis: str = field(default=None) # 情感分析
industry_analysis: str = field(default=None) # 行业分析
final_analysis: str = field(default=None) # 最终的分析
# 获取文章的内容输入是文章的URL输出是文章的文本
def get_article_text(url: str) -> str:
try:
response = requests.get(url)
soup = BeautifulSoup(response.content, 'html.parser')
article_text = ' '.join([p.get_text() for p in soup.find_all('p')])
return article_text
except:
return "Error retrieving article text."
# 获取股票数据
def get_stock_data(ticker: str, days: int = 30) -> tuple:
end_date = datetime.now().strftime('%Y%m%d')
start_date = (datetime.now() - timedelta(days=days)).strftime('%Y%m%d')
# 获取日线行情数据
hist_data = pro.daily(ts_code=ticker, start_date=start_date, end_date=end_date)
# 获取资产负债表
balance_sheet = pro.balancesheet(ts_code=ticker)
# 获取利润表
financials = pro.income(ts_code=ticker)
# Tushare 不提供类似 yfinance 的新闻接口,这里返回空字典
news = {}
return hist_data, balance_sheet, financials, news
# 获取股票名称
def get_stock_name(ticker: str) -> str:
stock_info = pro.stock_basic(ts_code=ticker, fields='name')
if not stock_info.empty:
return stock_info.iloc[0]['name']
return "Unknown"
# 获取分析师预测数据
def get_analyst_ratings(ticker: str) -> str:
forecast = pro.forecast(ts_code=ticker)
if forecast.empty:
return "No earnings forecast available."
latest_forecast = forecast.iloc[0]
forecast_summary = f"Latest earnings forecast for {ticker}:\n {str(latest_forecast.to_dict())}"
return forecast_summary
# 获取当前股票价格
def get_current_price(ticker: str) -> float:
df = pro.daily(ts_code=ticker)
if df.empty:
return None
return df.iloc[0]['close']
import requests
# 分析情感数据,结合新闻、历史数据、财务数据等,并让大模型给出情绪分数
async def get_sentiment_analysis(news_title: str, news_content: str, related_stocks: List[str]) -> Dict[str, Dict]:
print(f"Analyzing sentiment for the news: {news_title}")
news_analysis_results = {}
for stock in related_stocks:
hist_data, balance_sheet, financials, _ = get_stock_data(stock)
# 构建要发送给大模型的 prompt
messages = [
{
"role": "system",
"content": (
"你是一名专业的金融分析师,专注于金融新闻和财务数据的情感分析。"
"你将根据新闻内容、股票历史数据、财务数据和资产负债表等信息,分析该新闻对相关股票的情绪影响,"
"并给出情绪分数0到1之间1表示非常积极0表示非常消极以及投资建议例如买入、持有、卖出"
"请详细考虑市场反应、公司财务健康状况和未来前景。"
)
},
{
"role": "user",
"content": (
f"以下是关于股票 {stock} 的分析请求。\n\n"
f"新闻标题: {news_title}\n"
f"新闻内容: {news_content}\n\n"
f"股票历史数据 (过去一年): {hist_data.to_dict()}\n\n"
f"财务数据: {financials.to_dict()}\n\n"
f"资产负债表: {balance_sheet.to_dict()}\n\n"
f"请基于以上信息给出以下分析:\n"
f"1. 该新闻对股票 {stock} 的情绪分数0-11 表示非常积极)\n"
f"2. 对股票的投资建议(买入、持有或卖出),并详细说明原因。"
)
}
]
headers = {
"Authorization": f"Bearer {Config.LLM_API_KEY}",
"Content-Type": "application/json"
}
# 请求大模型 API 进行分析
response = requests.post(Config.LLM_URL, json={"model": Config.LLM_MODEL, "messages": messages}, headers=headers)
if response.status_code == 200:
sentiment_result = response.json()['choices'][0]['message']['content']
sentiment_score = extract_sentiment_score(sentiment_result)
investment_suggestion = extract_investment_suggestion(sentiment_result)
news_analysis_results[stock] = {
"情绪分数": sentiment_score,
"投资建议": investment_suggestion,
"依据": sentiment_result
}
else:
news_analysis_results[stock] = {
"情绪分数": "无法获取",
"投资建议": "错误",
"依据": "分析过程中出错"
}
return news_analysis_results
# 提取情感分数的辅助函数
def extract_sentiment_score(sentiment_text: str) -> float:
import re
match = re.search(r"情绪分数[:]\s*(\d*\.?\d+)", sentiment_text)
if match:
return float(match.group(1))
return 0.5
# 提取投资建议的辅助函数
def extract_investment_suggestion(sentiment_text: str) -> str:
import re
match = re.search(r"投资建议[:]\s*(买入|持有|卖出)", sentiment_text)
if match:
return match.group(1)
return "持有"
# 调用大模型进行行业分析
async def get_industry_analysis(ticker: TickerClass) -> None:
print(f"Industry analysis for {ticker.name}")
stock_info = pro.stock_basic(ts_code=ticker.name, fields='industry')
if stock_info.empty:
industry = "未知"
else:
industry = stock_info.iloc[0]['industry']
payload = {
"model": Config.LLM_MODEL,
"messages": [
{"role": "system", "content": f"你是一个行业分析助手。请为 {ticker.name} 提供行业分析。"},
{"role": "user", "content": f"行业: {industry}\n请分析该行业的趋势、增长前景、监管变化和竞争格局。"}
]
}
headers = {
"Authorization": f"Bearer {Config.LLM_API_KEY}",
"Content-Type": "application/json"
}
response = requests.post(Config.LLM_URL, json=payload, headers=headers)
if response.status_code == 200:
ticker.industry_analysis = response.json()['choices'][0]['message']['content']
else:
ticker.industry_analysis = "行业分析出错"
# 调用大模型进行最终分析
async def get_final_analysis(ticker: TickerClass) -> None:
print(f"Final analysis for {ticker.name}")
# 在构建的prompt中明确要求模型引用情绪分析、财务数据和历史数据并提供详细的依据
payload = {
"model": Config.LLM_MODEL,
"messages": [
{
"role": "system",
"content": (
f"你是一位专业的金融分析师,负责提供股票 {ticker.name} 的最终投资建议。"
"请基于以下内容,给出详细的建议并明确引用数据:\n"
f"1. 情绪分析: {ticker.sentiment_analysis}\n"
f"2. 最新分析师评级: {ticker.analyst_ratings}\n"
f"3. 行业分析: {ticker.industry_analysis}\n"
"你需要综合公司财务健康状况、市场表现、行业前景和情绪分析,"
"并明确说明你的建议是基于哪些具体数据、分析或情绪影响,"
"最终给出是否建议买入、持有或卖出股票。"
)
},
{
"role": "user",
"content": (
f"股票: {ticker.name}\n\n"
f"请基于以上内容提供详细的投资建议,并引用财务数据、市场趋势、竞争地位和潜在风险等作为依据。"
)
}
]
}
headers = {
"Authorization": f"Bearer {Config.LLM_API_KEY}",
"Content-Type": "application/json"
}
response = requests.post(Config.LLM_URL, json=payload, headers=headers)
if response.status_code == 200:
ticker.final_analysis = response.json()['choices'][0]['message']['content']
else:
ticker.final_analysis = "生成最终分析出错"
# 根据所有股票的分析,进行股票排名
def rank_companies(ticker_info_list: List[TickerClass], industry: str) -> str:
print(f"Ranking ...")
analysis_text = "\n\n".join(
f"股票: {ticker.name} - {ticker.display_name}\n"
f"当前价格: {ticker.price}\n"
f"新闻情绪分析: {ticker.sentiment_analysis}\n"
f"最新分析师评级: {ticker.analyst_ratings}\n"
f"行业分析: {ticker.industry_analysis}\n"
f"最终投资建议: {ticker.final_analysis}"
for ticker in ticker_info_list
)
payload = {
"model": Config.LLM_MODEL,
"messages": [
{
"role": "system",
"content": (
"你是一位资深的金融分析师,负责为房地产行业中的公司按投资潜力进行排名。"
"在这次分析中,市场新闻的情绪分析是至关重要的判断依据,"
"请务必将新闻情绪分析的结果作为每个股票排名和投资建议的核心因素之一,"
"并结合公司的财务状况、市场趋势和竞争地位进行综合分析。"
"请确保分析结果按照以下格式输出,并且每次都严格遵循以下示例格式:\n"
"### 投资吸引力排名\n"
"1. **000560.SZ** - **股票名称**\n"
" **理由**:\n"
" **新闻情绪分析**: 新闻情绪的详细分析结果及对市场反应的预期。\n"
" **财务健康状况**: 描述财务状况。\n"
" **市场趋势**: 描述市场趋势。\n"
" **竞争地位**: 描述竞争地位。\n"
" **潜在风险**: 描述潜在风险。\n"
" **建议**: 买入或卖出。\n"
"2. **002016.SZ** - **股票名称**\n"
" **理由**:\n"
" **新闻情绪分析**: 新闻情绪的详细分析结果及对市场反应的预期。\n"
" **财务健康状况**: 描述财务状况。\n"
" **市场趋势**: 描述市场趋势。\n"
" **竞争地位**: 描述竞争地位。\n"
" **潜在风险**: 描述潜在风险。\n"
" **建议**: 买入或卖出。\n"
"3. **000736.SZ** - **股票名称**\n"
" **理由**:\n"
" **新闻情绪分析**: 新闻情绪的详细分析结果及对市场反应的预期。\n"
" **财务健康状况**: 描述财务状况。\n"
" **市场趋势**: 描述市场趋势。\n"
" **竞争地位**: 描述竞争地位。\n"
" **潜在风险**: 描述潜在风险。\n"
" **建议**: 买入或卖出。\n"
"# 总结\n"
"最后进行总结,说明哪个股票最具投资吸引力,并解释新闻情绪分析对其的影响。"
)
},
{
"role": "user",
"content": (
f"行业: {industry}\n\n"
f"公司分析:\n{analysis_text}\n\n"
f"请根据提供的分析,将这些公司按投资吸引力进行排名,并提供详细的理由。"
"请务必明确引用新闻情绪分析结果,结合财务健康状况和市场趋势等作为排名依据。"
)
}
]
}
headers = {
"Authorization": f"Bearer {Config.LLM_API_KEY}",
"Content-Type": "application/json"
}
response = requests.post(Config.LLM_URL, json=payload, headers=headers)
if response.status_code == 200:
return response.json()['choices'][0]['message']['content']
else:
return "公司排名出错"
async def get_openai_verdict_2(news_title: str, news_content: str, tickers: List[str], industry: str):
ticker_info_list = []
for ticker in tickers:
stock_name = get_stock_name(ticker)
temp_ticker_info = TickerClass(name=ticker, display_name=stock_name)
temp_ticker_info.hist_data, temp_ticker_info.balance_sheet, temp_ticker_info.financials, temp_ticker_info.news = get_stock_data(ticker, days=30)
temp_ticker_info.analyst_ratings = get_analyst_ratings(ticker)
temp_ticker_info.price = get_current_price(ticker)
ticker_info_list.append(temp_ticker_info)
sentiment_results = await get_sentiment_analysis(news_title, news_content, tickers)
for ticker_object in ticker_info_list:
if ticker_object.name in sentiment_results:
ticker_object.sentiment_analysis = sentiment_results[ticker_object.name]["依据"]
# 异步调用行业分析
tasks2 = [get_industry_analysis(ticker_object) for ticker_object in ticker_info_list]
await asyncio.gather(*tasks2)
# 异步调用最终分析
tasks3 = [get_final_analysis(ticker_object) for ticker_object in ticker_info_list]
await asyncio.gather(*tasks3)
# 最后对所有股票进行排名并返回
final_ranking = rank_companies(ticker_info_list, industry)
return final_ranking
'''
async def main():
news_title = "央行降低存量房贷利率,多重政策推动房地产市场回暖"
news_content = (
"2024年9月24日央行宣布多项措施以提振房地产市场包括降低存量房贷利率、统一房贷最低首付比例、"
"引导LPR贷款市场报价利率下行等。这些政策旨在减轻购房者负担增加市场需求并提振整体经济。"
"消息公布后房地产股大幅上涨多只港股内房股涨幅超过10%"
)
tickers = ['000560.SZ', '000736.SZ', '002016.SZ']
industry = "房地产"
final_rankings = await get_openai_verdict_2(news_title, news_content, tickers=tickers, industry=industry)
print(final_rankings)
if __name__ == "__main__":
asyncio.run(main())
'''