Initial commit
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
"""
|
||||
豆包AI服务模块
|
||||
用于股票分析的AI对话(流式输出版本)
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
# API配置
|
||||
API_KEY = "9fd8383f-5776-4366-855d-c6f40e867940"
|
||||
API_URL = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
|
||||
MODEL = "doubao-seed-1-6-251015"
|
||||
|
||||
|
||||
def analyze_stock(stock_code, stock_name, stock_data):
|
||||
"""
|
||||
使用豆包AI分析股票(流式输出,获取思考和结论)
|
||||
"""
|
||||
|
||||
# 构建分析提示词(基于推荐模型v3.0)
|
||||
fund_flow_str = format_fund_flow(stock_data.get('fund_flow_3days', []))
|
||||
|
||||
prompt = f"""分析{stock_name}({stock_code})投资价值。
|
||||
|
||||
数据:价格{stock_data.get('price', 'N/A')}元,PE={stock_data.get('pe', 'N/A')},PB={stock_data.get('pb', 'N/A')},ROE={stock_data.get('roe', 'N/A')}%,市值{format_market_cap(stock_data.get('total_market_cap'))}
|
||||
资金流向:{fund_flow_str}
|
||||
|
||||
评分模型:价格位置(30分,低于50%为低位)、资金流向(25分,主力净流入占比)、趋势(15分)、连续性(15分,≥3天强连续)、涨跌配合(10分)、量能(5分)
|
||||
推荐率:80-100强买/卖,60-79可操作,40-59观望,0-39不建议
|
||||
|
||||
按以下格式输出:
|
||||
## 技术面分析
|
||||
分析资金流向趋势和连续性
|
||||
|
||||
## 基本面分析
|
||||
分析PE/ROE估值和盈利能力
|
||||
|
||||
## 操作建议
|
||||
明确建议买入/持有/卖出,给出预估推荐率(0-100)
|
||||
|
||||
## 风险提示
|
||||
列出2-3个风险点"""
|
||||
|
||||
try:
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {API_KEY}"
|
||||
}
|
||||
|
||||
payload = {
|
||||
"model": MODEL,
|
||||
"max_completion_tokens": 2048,
|
||||
"stream": True, # 启用流式输出
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": prompt
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# 流式请求
|
||||
response = requests.post(API_URL, headers=headers, json=payload, timeout=120, stream=True)
|
||||
|
||||
if response.status_code == 200:
|
||||
reasoning_content = "" # 思考过程
|
||||
content = "" # 最终结论
|
||||
|
||||
for line in response.iter_lines():
|
||||
if line:
|
||||
line_str = line.decode('utf-8')
|
||||
if line_str.startswith('data: '):
|
||||
data_str = line_str[6:]
|
||||
if data_str == '[DONE]':
|
||||
break
|
||||
try:
|
||||
data = json.loads(data_str)
|
||||
if 'choices' in data and len(data['choices']) > 0:
|
||||
delta = data['choices'][0].get('delta', {})
|
||||
# 获取思考过程
|
||||
if 'reasoning_content' in delta and delta['reasoning_content']:
|
||||
reasoning_content += delta['reasoning_content']
|
||||
# 获取最终内容
|
||||
if 'content' in delta and delta['content']:
|
||||
content += delta['content']
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
|
||||
# 组合思考过程和结论
|
||||
full_analysis = ""
|
||||
if reasoning_content:
|
||||
full_analysis += "## 💭 AI思考过程\n" + reasoning_content + "\n\n---\n\n"
|
||||
if content:
|
||||
full_analysis += content
|
||||
|
||||
if full_analysis:
|
||||
return {'success': True, 'analysis': full_analysis}
|
||||
else:
|
||||
return {'success': False, 'error': 'AI返回内容为空'}
|
||||
else:
|
||||
return {'success': False, 'error': f'API请求失败: {response.status_code}'}
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
return {'success': False, 'error': 'AI分析超时,请稍后重试'}
|
||||
except Exception as e:
|
||||
return {'success': False, 'error': f'AI分析失败: {str(e)}'}
|
||||
|
||||
|
||||
def analyze_stock_stream(stock_code, stock_name, stock_data):
|
||||
"""
|
||||
使用豆包AI分析股票(流式生成器,用于SSE)
|
||||
"""
|
||||
|
||||
# 构建分析提示词
|
||||
fund_flow_str = format_fund_flow(stock_data.get('fund_flow_3days', []))
|
||||
|
||||
signal_status = stock_data.get('signal_status', [])
|
||||
indicators = stock_data.get('indicators', {})
|
||||
|
||||
signal_lines = []
|
||||
triggered_names = []
|
||||
for s in signal_status:
|
||||
status = '✅已触发' if s.get('triggered') else '○未触发'
|
||||
signal_lines.append(f" {s.get('name','')}: {status} (胜率{s.get('strength',0)}%) — {s.get('description','')}")
|
||||
if s.get('triggered'):
|
||||
triggered_names.append(s.get('name', ''))
|
||||
signal_text = '\n'.join(signal_lines) if signal_lines else ' 暂无扫描数据'
|
||||
triggered_text = '、'.join(triggered_names) if triggered_names else '无'
|
||||
|
||||
macd = indicators.get('macd', {})
|
||||
skdj = indicators.get('skdj', {})
|
||||
ema = indicators.get('ema', {})
|
||||
indicator_text = f"MACD: DIF={macd.get('dif','N/A')}, DEA={macd.get('dea','N/A')} | SKDJ: K={skdj.get('k','N/A')}, D={skdj.get('d','N/A')} | EMA: EMA3={ema.get('ema3','N/A')}, EMA21={ema.get('ema21','N/A')}"
|
||||
|
||||
prompt = f"""基于"交易信号实战体系"分析{stock_name}({stock_code})。
|
||||
|
||||
【基本面数据】
|
||||
价格{stock_data.get('price', 'N/A')}元,PE={stock_data.get('pe', 'N/A')},PB={stock_data.get('pb', 'N/A')},ROE={stock_data.get('roe', 'N/A')}%,市值{format_market_cap(stock_data.get('total_market_cap'))}
|
||||
资金流向:{fund_flow_str}
|
||||
|
||||
【技术指标】
|
||||
{indicator_text}
|
||||
|
||||
【7大交易信号状态】(当前已触发: {triggered_text})
|
||||
{signal_text}
|
||||
|
||||
【交易信号体系规则】
|
||||
信号胜率排行: ★主升浪85% > 日线底背离80% > 龙抬头75% > 真龙70% > 短底背离65% > 老鼠仓60% > 反弹55%
|
||||
标准牛股启动顺序: 日线底背离→龙抬头→真龙→★主升浪→反弹
|
||||
体系最强战法: 1)日线底背离出现→关注 2)龙抬头出现→买入 3)真龙/★主升浪→持有加仓 4)不见主升浪→不出场
|
||||
|
||||
请按以下格式分析:
|
||||
## 交易信号分析
|
||||
根据7大信号的触发状态,判断该股在"底部→拉升"流程中处于哪个阶段。已触发的信号说明什么?距离下一个关键信号还有多远?
|
||||
|
||||
## 基本面分析
|
||||
简要分析PE/ROE估值水平和盈利能力(2-3句话)
|
||||
|
||||
## 操作建议
|
||||
基于体系最强战法规则,给出明确建议(关注/买入/持有加仓/减仓/观望),并解释理由。给出推荐率(0-100)
|
||||
|
||||
## 风险提示
|
||||
列出2-3个关键风险点"""
|
||||
|
||||
try:
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {API_KEY}"
|
||||
}
|
||||
|
||||
payload = {
|
||||
"model": MODEL,
|
||||
"max_completion_tokens": 2048,
|
||||
"stream": True,
|
||||
"messages": [
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
}
|
||||
|
||||
response = requests.post(API_URL, headers=headers, json=payload, timeout=120, stream=True)
|
||||
|
||||
if response.status_code == 200:
|
||||
in_reasoning = False
|
||||
|
||||
for line in response.iter_lines():
|
||||
if line:
|
||||
line_str = line.decode('utf-8')
|
||||
if line_str.startswith('data: '):
|
||||
data_str = line_str[6:]
|
||||
if data_str == '[DONE]':
|
||||
break
|
||||
try:
|
||||
data = json.loads(data_str)
|
||||
if 'choices' in data and len(data['choices']) > 0:
|
||||
delta = data['choices'][0].get('delta', {})
|
||||
|
||||
# 思考过程
|
||||
if 'reasoning_content' in delta and delta['reasoning_content']:
|
||||
if not in_reasoning:
|
||||
yield {'type': 'reasoning_start'}
|
||||
in_reasoning = True
|
||||
yield {'type': 'reasoning', 'content': delta['reasoning_content']}
|
||||
|
||||
# 最终内容
|
||||
if 'content' in delta and delta['content']:
|
||||
if in_reasoning:
|
||||
yield {'type': 'reasoning_end'}
|
||||
in_reasoning = False
|
||||
yield {'type': 'content', 'content': delta['content']}
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
|
||||
if in_reasoning:
|
||||
yield {'type': 'reasoning_end'}
|
||||
else:
|
||||
yield {'type': 'error', 'content': f'API请求失败: {response.status_code}'}
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
yield {'type': 'error', 'content': 'AI分析超时,请稍后重试'}
|
||||
except Exception as e:
|
||||
yield {'type': 'error', 'content': f'AI分析失败: {str(e)}'}
|
||||
|
||||
|
||||
def format_market_cap(value):
|
||||
"""格式化市值"""
|
||||
if not value:
|
||||
return 'N/A'
|
||||
try:
|
||||
value = float(value)
|
||||
if value >= 100000000000: # 千亿
|
||||
return f"{value/100000000000:.2f}千亿"
|
||||
elif value >= 100000000: # 亿
|
||||
return f"{value/100000000:.2f}亿"
|
||||
else:
|
||||
return f"{value/10000:.2f}万"
|
||||
except:
|
||||
return str(value)
|
||||
|
||||
|
||||
def format_fund_flow(fund_flow_list):
|
||||
"""格式化资金流向数据"""
|
||||
if not fund_flow_list:
|
||||
return "暂无数据"
|
||||
|
||||
lines = []
|
||||
for item in fund_flow_list:
|
||||
date = item.get('date', '')
|
||||
change = item.get('change_pct', 0)
|
||||
main = item.get('main_pct', 0)
|
||||
super_pct = item.get('super_pct', 0)
|
||||
lines.append(f"- {date}: 涨跌{change:+.2f}%, 主力{main:+.2f}%, 超大单{super_pct:+.2f}%")
|
||||
|
||||
return '\n'.join(lines)
|
||||
Reference in New Issue
Block a user