""" 豆包AI服务模块 用于股票分析的AI对话(流式输出版本) """ import requests import json from config import Config # API配置 API_KEY = Config.DOUBAO_API_KEY or "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)