From fd99430c6f918920343d0abaa888156ff5ba428d Mon Sep 17 00:00:00 2001 From: Lxy Date: Sat, 14 Feb 2026 18:20:22 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5=E8=87=AA=E9=80=89?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=AF=A6=E6=83=85=E5=92=8C=E7=B2=BE=E7=AE=80?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 11 +- .../deepseek_agent.cpython-311.pyc | Bin 14203 -> 16152 bytes qihuo_analyzer/modules/deepseek_agent.py | 163 +++++++++--- web/app.py | 32 ++- web/templates/index.html | 92 ++++++- web/templates/symbol_detail.html | 245 ++++++++++++++---- 6 files changed, 448 insertions(+), 95 deletions(-) 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 b5d63b43c22b9abdef85727a1c3a4070c69da1a5..72103a45b3a18d33e8b8242e1f7a0f9206daad4b 100644 GIT binary patch delta 4392 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 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 %}