diff --git a/.env b/.env index 0af9986..deb3ca8 100644 --- a/.env +++ b/.env @@ -1,8 +1,13 @@ -# API配置 -OPENAI_API_KEY= -DEEPSEEK_API_KEY= +# DeepSeek API 配置 +DEEPSEEK_API_KEY=sk-49dccf9e8a754d3abb36ce396cb8f189 DEEPSEEK_API_URL=https://api.deepseek.com/v1/chat/completions +# OpenAI API 配置 +OPENAI_API_KEY=your_openai_api_key + +# Gemini API 配置 +GEMINI_API_KEY=your_gemini_api_key + # 天勤TQSDK配置 # 填写你的TQSDK账号密码以使用真实数据 TQSDK_USERNAME=windsdreamer diff --git a/qihuo_analyzer/modules/__pycache__/deepseek_agent.cpython-311.pyc b/qihuo_analyzer/modules/__pycache__/deepseek_agent.cpython-311.pyc index b5d63b4..72103a4 100644 Binary files a/qihuo_analyzer/modules/__pycache__/deepseek_agent.cpython-311.pyc and b/qihuo_analyzer/modules/__pycache__/deepseek_agent.cpython-311.pyc differ diff --git a/qihuo_analyzer/modules/deepseek_agent.py b/qihuo_analyzer/modules/deepseek_agent.py index 45edc52..5781061 100644 --- a/qihuo_analyzer/modules/deepseek_agent.py +++ b/qihuo_analyzer/modules/deepseek_agent.py @@ -7,15 +7,50 @@ from qihuo_analyzer.core.models import AnalysisResult class DeepseekAgent: - """DeepSeek AI 研判代理""" + """AI 研判代理,支持多种模型""" - def __init__(self): - self.api_key = config_manager.deepseek_api_key - self.api_url = config_manager.deepseek_api_url - self.headers = { - 'Content-Type': 'application/json', - 'Authorization': f'Bearer {self.api_key}' + def __init__(self, model_name='deepseek'): + """初始化AI代理 + + Args: + model_name: 模型名称,支持 'deepseek', 'gpt', 'gemini' 等 + """ + self.model_name = model_name + # 安全获取API密钥,避免访问不存在的属性 + gemini_api_key = getattr(config_manager, 'gemini_api_key', '') + + self.api_configs = { + 'deepseek': { + 'api_key': config_manager.deepseek_api_key, + 'api_url': config_manager.deepseek_api_url, + 'headers': { + 'Content-Type': 'application/json', + 'Authorization': f'Bearer {config_manager.deepseek_api_key}' + } + }, + 'gpt': { + 'api_key': config_manager.openai_api_key, + 'api_url': 'https://api.openai.com/v1/chat/completions', + 'headers': { + 'Content-Type': 'application/json', + 'Authorization': f'Bearer {config_manager.openai_api_key}' + } + }, + 'gemini': { + 'api_key': gemini_api_key, + 'api_url': 'https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent', + 'headers': { + 'Content-Type': 'application/json', + 'Authorization': f'Bearer {gemini_api_key}' + } + } } + + # 获取当前模型的配置 + self.current_config = self.api_configs.get(model_name, self.api_configs['deepseek']) + self.api_key = self.current_config['api_key'] + self.api_url = self.current_config['api_url'] + self.headers = self.current_config['headers'] def analyze_market(self, market_data: Dict, technical_indicators: Dict, trend_analysis: Dict, risk_metrics: Dict) -> Dict: @@ -24,7 +59,7 @@ class DeepseekAgent: prompt = self._build_analysis_prompt(market_data, technical_indicators, trend_analysis, risk_metrics) # 调用API - response = self._call_deepseek_api(prompt) + response = self._call_ai_api(prompt) # 解析结果 analysis_result = self._parse_analysis_result(response) @@ -37,7 +72,7 @@ class DeepseekAgent: prompt = self._build_recommendation_prompt(analysis_result) # 调用API - response = self._call_deepseek_api(prompt) + response = self._call_ai_api(prompt) # 解析结果 recommendation = self._parse_recommendation_result(response) @@ -126,28 +161,89 @@ class DeepseekAgent: """ return prompt - def _call_deepseek_api(self, prompt: str) -> str: - """调用DeepSeek API""" - # 如果没有API密钥,返回模拟结果 + def _call_ai_api(self, prompt: str) -> str: + """调用AI API""" + # 如果没有API密钥,返回错误提示 if not self.api_key: - return self._get_mock_response(prompt) + return json.dumps({'error': 'API密钥未配置'}) - payload = { - 'model': 'deepseek-chat', - 'messages': [ - { - 'role': 'system', - 'content': '你是一位专业的期货市场分析师,精通技术分析和基本面分析,能够基于多维度数据提供准确的市场研判和交易建议。' - }, - { - 'role': 'user', - 'content': prompt + # 根据模型类型构建不同的payload + if self.model_name == 'deepseek': + payload = { + 'model': 'deepseek-chat', + 'messages': [ + { + 'role': 'system', + 'content': '你是一位专业的期货市场分析师,精通技术分析和基本面分析,能够基于多维度数据提供准确的市场研判和交易建议。' + }, + { + 'role': 'user', + 'content': prompt + } + ], + 'temperature': 0.3, + 'max_tokens': 2000, + 'top_p': 0.9 + } + elif self.model_name == 'gpt': + payload = { + 'model': 'gpt-3.5-turbo', + 'messages': [ + { + 'role': 'system', + 'content': '你是一位专业的期货市场分析师,精通技术分析和基本面分析,能够基于多维度数据提供准确的市场研判和交易建议。' + }, + { + 'role': 'user', + 'content': prompt + } + ], + 'temperature': 0.3, + 'max_tokens': 2000, + 'top_p': 0.9 + } + elif self.model_name == 'gemini': + payload = { + 'contents': [ + { + 'parts': [ + { + 'text': '你是一位专业的期货市场分析师,精通技术分析和基本面分析,能够基于多维度数据提供准确的市场研判和交易建议。' + } + ] + }, + { + 'parts': [ + { + 'text': prompt + } + ] + } + ], + 'generationConfig': { + 'temperature': 0.3, + 'maxOutputTokens': 2000, + 'topP': 0.9 } - ], - 'temperature': 0.3, - 'max_tokens': 2000, - 'top_p': 0.9 - } + } + else: + # 默认使用deepseek格式 + payload = { + 'model': 'deepseek-chat', + 'messages': [ + { + 'role': 'system', + 'content': '你是一位专业的期货市场分析师,精通技术分析和基本面分析,能够基于多维度数据提供准确的市场研判和交易建议。' + }, + { + 'role': 'user', + 'content': prompt + } + ], + 'temperature': 0.3, + 'max_tokens': 2000, + 'top_p': 0.9 + } try: response = requests.post( @@ -159,10 +255,17 @@ class DeepseekAgent: response.raise_for_status() result = response.json() - return result['choices'][0]['message']['content'] + + # 根据模型类型解析不同的响应格式 + if self.model_name == 'deepseek' or self.model_name == 'gpt': + return result['choices'][0]['message']['content'] + elif self.model_name == 'gemini': + return result['candidates'][0]['content']['parts'][0]['text'] + else: + return result['choices'][0]['message']['content'] except Exception as e: print(f"API调用失败:{e}") - return self._get_mock_response(prompt) + return json.dumps({'error': 'API调用失败'}) def _get_mock_response(self, prompt: str) -> str: """获取模拟响应""" diff --git a/web/app.py b/web/app.py index 3a6ab4e..6f49198 100644 --- a/web/app.py +++ b/web/app.py @@ -254,12 +254,28 @@ def index(): except (ValueError, TypeError): safe_adx = 0 + # 计算胜率(模拟数据,实际项目中应该使用真实的胜率计算) + win_rate = round(win_rate, 1) if win_rate is not None else 0 + + # 计算换月预警(模拟数据,实际项目中应该使用真实的距离交割天数) + rollover_warning = _get_rollover_warning(rollover_analysis) + + # 计算主力资金流向(模拟数据,实际项目中应该使用真实的资金流向数据) + fund_flow = fund_flow_to_cn(fund_flow_analysis.get('fund_signal', 'neutral')) + + # 计算周期(模拟数据,实际项目中应该使用真实的周期判断) + cycle = cycle_to_cn(cycle) + symbol_data = { 'symbol': symbol, 'name': f"{product_name_cn}({symbol})".upper(), 'current_price': safe_current_price, 'direction': trend_analysis.get('overall_trend', 'sideways'), - 'trend_strength': _get_trend_strength_display(safe_adx) + 'win_rate': win_rate, + 'trend_strength': _get_trend_strength_display(safe_adx), + 'cycle': cycle, + 'rollover_warning': rollover_warning, + 'fund_flow': fund_flow } selected_symbols_data.append(symbol_data) except Exception as e: @@ -288,6 +304,9 @@ def index(): def symbol_detail(symbol): """品种详情页""" try: + # 获取模型选择参数,默认为deepseek + model_name = request.args.get('model', 'deepseek') + # 辅助函数:安全地四舍五入数字 def safe_round(value, decimals=2, context=''): try: @@ -429,9 +448,10 @@ def symbol_detail(symbol): 'risk_ratio': safe_round(position_info.get('actual_risk_percent', 0) * 100, 2, context='risk_metrics_risk_ratio') } - # AI 分析 - ai_analysis = deepseek_agent.analyze_market(market_data, technical_indicators, trend_data, risk_metrics) - recommendation = deepseek_agent.generate_trade_recommendation(ai_analysis) + # AI 分析 - 使用指定的模型 + ai_agent = DeepseekAgent(model_name=model_name) + ai_analysis = ai_agent.analyze_market(market_data, technical_indicators, trend_data, risk_metrics) + recommendation = ai_agent.generate_trade_recommendation(ai_analysis) # 构建模板数据 # 获取中文名称 @@ -512,7 +532,9 @@ def symbol_detail(symbol): 'ai_analysis': ai_analysis, 'recommendation': recommendation, 'kline_data': _get_kline_data_for_chart(kline_data), - 'is_favorite': symbol in selected_symbols + 'is_favorite': symbol in selected_symbols, + 'model_name': model_name, + 'available_models': ['deepseek', 'gpt', 'gemini'] } print("[DEBUG] context built successfully") diff --git a/web/templates/index.html b/web/templates/index.html index c4e1400..1eb51f9 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -291,13 +291,23 @@
自选关注 - {{ selected_symbols_data | length }} 个品种 +
+ {{ selected_symbols_data | length }} 个品种 +
+ 显示模式: +
+ + +
+
+
{% if selected_symbols_data %}
{% for symbol_data in selected_symbols_data %} -
+ +
@@ -328,6 +338,50 @@
+ + + {% endfor %}
{% else %} @@ -542,6 +596,40 @@ }); } + // 切换显示模式 + function switchDisplayMode(mode) { + const simpleCards = document.querySelectorAll('.simple-mode'); + const detailCards = document.querySelectorAll('.detail-mode'); + const simpleBtn = document.getElementById('simpleModeBtn'); + const detailBtn = document.getElementById('detailModeBtn'); + + if (mode === 'simple') { + // 显示简约模式卡片,隐藏详细模式卡片 + simpleCards.forEach(card => { + card.style.display = 'block'; + }); + detailCards.forEach(card => { + card.style.display = 'none'; + }); + + // 更新按钮样式 + simpleBtn.style.background = '#f0f0f0'; + detailBtn.style.background = 'white'; + } else if (mode === 'detail') { + // 显示详细模式卡片,隐藏简约模式卡片 + simpleCards.forEach(card => { + card.style.display = 'none'; + }); + detailCards.forEach(card => { + card.style.display = 'block'; + }); + + // 更新按钮样式 + simpleBtn.style.background = 'white'; + detailBtn.style.background = '#f0f0f0'; + } + } + // 搜索功能 document.addEventListener('DOMContentLoaded', function() { const searchInput = document.getElementById('symbolSearch'); diff --git a/web/templates/symbol_detail.html b/web/templates/symbol_detail.html index 91faba2..fe43318 100644 --- a/web/templates/symbol_detail.html +++ b/web/templates/symbol_detail.html @@ -468,98 +468,233 @@
AI
AI智能分析 +
+ 模型选择: +
+ +
+
+
+ + {% if ai_analysis.error %} + +
+
⚠️
+

AI分析不可用

+

+ {% if ai_analysis.error == 'API密钥未配置' %} + 当前模型的API密钥未配置,请在.env文件中添加相应的API密钥 + {% elif ai_analysis.error == 'API调用失败' %} + API调用失败,请检查网络连接或API密钥是否正确 + {% else %} + {{ ai_analysis.error }} + {% endif %} +

+
+ 切换模型 +
+ {% else %} +
-
- -
-
趋势判断
-
-
趋势方向
+ +
+
+
+
+ 趋势判断 +
+
+
+
+
趋势方向
+ {% if ai_analysis.trend_judgment and ('多' in ai_analysis.trend_judgment) %}direction-up{% elif ai_analysis.trend_judgment and ('空' in ai_analysis.trend_judgment) %}direction-down{% endif %}" + style="font-size: 16px; font-weight: 600;"> {{ ai_analysis.trend_judgment | default('未知') }}
-
-
胜率评估
-
{{ ai_analysis.win_rate_assessment | default('未知') }}
-
-
-
风险预警
-
{{ ai_analysis.risk_warning | default('无') }}
+
+
胜率评估
+
{{ ai_analysis.win_rate_assessment | default('未知') }}
-
-
交易建议
+
+
交易建议
+ {% if ai_analysis.trade_recommendation and ('多' in ai_analysis.trade_recommendation) %}direction-up{% elif ai_analysis.trade_recommendation and ('空' in ai_analysis.trade_recommendation) %}direction-down{% endif %}" + style="font-size: 16px; font-weight: 600;"> {{ ai_analysis.trade_recommendation | default('未知') }}
- -
-
分析逻辑
-
- {{ ai_analysis.analysis_logic | default('无详细分析') }} + + {% if ai_analysis.risk_warning and ai_analysis.risk_warning != '无' %} +
+
⚠️ 风险预警
+
{{ ai_analysis.risk_warning }}
+
+ {% endif %} +
+ + + {% if ai_analysis.analysis_logic %} +
+
+
+
+ 分析逻辑
+
+ {% if ai_analysis.analysis_logic is string %} + + {% if '1.' in ai_analysis.analysis_logic or '2.' in ai_analysis.analysis_logic or '3.' in ai_analysis.analysis_logic %} + +
    + {% for item in ai_analysis.analysis_logic.split('\n') %} + {% if item.strip() %} +
  • +
    +
    + {% if loop.index <= 9 %}{{ loop.index }}{% else %}{{ loop.index }}{% endif %} +
    +
    {{ item.strip() }}
    +
    +
  • + {% endif %} + {% endfor %} +
+ {% else %} + +

{{ ai_analysis.analysis_logic }}

+ {% endif %} + {% else %} + +

{{ ai_analysis.analysis_logic | default('无详细分析') }}

+ {% endif %} +
+ {% endif %}
-
详细交易建议
-
+
+
+
+ 详细交易建议 +
+
+
-
+
交易参数
-
-
交易方向
-
- {% if recommendation.direction == 'long' %}多头 - {% elif recommendation.direction == 'short' %}空头 - {% else %}{{ recommendation.direction | default('未知') }} - {% endif %} +
+
+
交易方向
+
+ {% if recommendation.direction == 'long' %}多头 + {% elif recommendation.direction == 'short' %}空头 + {% else %}{{ recommendation.direction | default('未知') }} + {% endif %} +
+
+
+
入场价格
+
{{ recommendation.entry_price | default('未知') }}
+
+
+
止损价格
+
{{ recommendation.stop_loss | default('未知') }}
+
+
+
目标价格
+
{{ recommendation.target_price | default('未知') }}
+
+
+
仓位大小
+
{{ recommendation.position_size | default('未知') }}手
-
-
-
入场价格
-
{{ recommendation.entry_price | default('未知') }}
-
-
-
止损价格
-
{{ recommendation.stop_loss | default('未知') }}
-
-
-
目标价格
-
{{ recommendation.target_price | default('未知') }}
-
-
-
仓位大小
-
{{ recommendation.position_size | default('未知') }}手
-
+
执行计划
-
- {{ recommendation.execution_plan | default('无详细计划') }} +
+ {% if recommendation.execution_plan %} + {% if '1.' in recommendation.execution_plan or '2.' in recommendation.execution_plan or '3.' in recommendation.execution_plan %} + +
    + {% for item in recommendation.execution_plan.split('\n') %} + {% if item.strip() %} +
  • +
    +
    + {% if loop.index <= 9 %}{{ loop.index }}{% else %}{{ loop.index }}{% endif %} +
    +
    {{ item.strip() }}
    +
    +
  • + {% endif %} + {% endfor %} +
+ {% else %} + +

{{ recommendation.execution_plan }}

+ {% endif %} + {% else %} +

无详细计划

+ {% endif %}
-
+
风险提示
-
- {{ recommendation.risk_tips | default('无风险提示') }} +
+ {% if recommendation.risk_tips %} + {% if '1.' in recommendation.risk_tips or '2.' in recommendation.risk_tips or '3.' in recommendation.risk_tips %} + +
    + {% for item in recommendation.risk_tips.split('\n') %} + {% if item.strip() %} +
  • +
    +
    + ⚠️ +
    +
    {{ item.strip() }}
    +
    +
  • + {% endif %} + {% endfor %} +
+ {% else %} + +

{{ recommendation.risk_tips }}

+ {% endif %} + {% else %} +

无风险提示

+ {% endif %}
+ {% endif %}