From 44e48c0e1efc762fdc133531e8946fd5cdfd1792 Mon Sep 17 00:00:00 2001 From: Lxy Date: Sun, 15 Feb 2026 11:37:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E7=99=BB=E9=99=86?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=EF=BC=9B=E4=BF=AE=E5=A4=8D=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=87=AA=E9=80=89bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../deepseek_agent.cpython-311.pyc | Bin 16159 -> 18947 bytes qihuo_analyzer/modules/deepseek_agent.py | 78 +++++++++++++++++- requirements.txt | 3 + web/app.py | 78 ++++++++++++++++-- web/data/futures_analysis.db | Bin 28672 -> 36864 bytes web/templates/index.html | 22 ++++- 6 files changed, 169 insertions(+), 12 deletions(-) diff --git a/qihuo_analyzer/modules/__pycache__/deepseek_agent.cpython-311.pyc b/qihuo_analyzer/modules/__pycache__/deepseek_agent.cpython-311.pyc index fe29d00c5b40dadad8171525d0e07c71e390235b..ceddb981866adb0082e593431a15ceca3bb6b83f 100644 GIT binary patch delta 4375 zcma)9e{2)i9lv+JJD=@KVkgGG5@M6a1cxR8TGH5rpb{v}T2cg^RNPXN<}Nf4+tIVr z4rFSe6nR<;G)GH1QV1KRt}3Ev-b6&%)_>N2_K%%eie;@D+HjzM)rhXrrd8ed-6f6_ zK->B5^WOKp_kF*4HUrmu8 zDKhP+A*F{jLB`JnSw9=({2U@}G(*}T@8^{~Go%aZ{dz>v)DB8yPf{Xxm8PiA;Zq;K zK}35fkMgPlbP%COBUgY7^!Ka7MAn^lwQ8>x?bz;3(2_;$y%I))mQxr*L_5@( zMaN>DgG^PdonH$l) z>yrEfdyK1qJ|gzi+guguF7lMcmSEo1C*XIV^xm%_IRCJa68U}-;5-$bFDA*sg}l6; zx5~j)mGlHHA75p*i0CR9>2vth=cp!zCN!jKt4NEQr3OFB#55Nuk%=)1(ifFb3vAKtks7p{$cb8d2!aX0nhLsB##_C@=AuEO+*!uXB352ohcoBa0X zyM=e(DV+U0aya-}cu2M~?zrAn_LPL(Qgj4|BEC`qX%vZKpQ|(wbT&6}vvBjw?dcn? zhPm^f%#B|P6y)P~K7B7Rm%LoK_=|>;_7UE_x5XW7af^9Q9Xqcb2^|WB-;9UyDBxl8 zJj_;#4hE&X21@fhj5R<+ObqJb@JdMXhT!7Lu$h#w(XiNe7&nl-PBCEMkaRf0fvheI z*ORKszya8hQvH#-ZIUz`k)%UU#}7!MXzSAIPEd*X0gh4sv`{8{*0^fc=$h{%FJ78ME0>HJd_tC0xf9X`Q^z)u3bGbcX9mo^n1X} z!g%uTAI}voTp{2~64k%+4HD0gg< zSIqGiWd1+L?p^NKFmSC%=33xT;p#{?Zm7F)g^B%l)j+z0>F3Su}{!FhW)Wgiy^=5r}|WMZ@z(sa_@yG-U0~v-Z{*duztN zE^A*mYxmCBy=mXhjJ-E&@6DNQMOL#)UtgpEN%TGI&G0MJ%B$E46Z6VR?QVJi$^X$h zoz%fnJY5z**%+k;Uin$Rrj5hqs1|hR9}&e^2&weah>WNhw+D`h35G;v*~VKn+88C@ zYq3~j+NDj8^3aS^;(Cl1qD3L`TB;FvtM z!ai$mnlU#e4O1`vHjpuI&ziRHR&R{80w9fgpU`kg% zJG~bG-i*+b6?)P_Pp+muEz}n|c|iBfe&|3~G0QtUcr#I`Tsqwd4~1pi zyQ_a8egcXje+K~Wag<;!Qd-?(Ia}SVZS9P0ZSuM4Z8u*?dtXuCjBQ`mwlC2GEo+^# zj@B7RYw~c$;mta{iRW@v4j^O2V_zFrq%F^XX$OEeW89fF?o1nZ=By2AV*_YbTV_og zW=tEVwq#7*@@_-hmid8cT~|&p&IPozB#4=QRd}A%9WGI?P(gQOlWH{njcC7UG z4GjhQ1_OP=gT>T8-1iyuygb|7j$V^1*M4Q#OQuCGZ}=7YZ)=_Cfc)dyTDt@V3%3hp zS>Q215C}_8Jnlt)Ir#XW)CFx$b+o*WKxfl&f7fsX)|DR#rQn&o@zYYhnqD)<6v zHlDI>_!;6}Bl-T+2T%M9?JijMM%zbA<285m&~+1}NA>EBuaF);A)t0EpYhpIz5JPP zJDQfi^_?=hNMnRa*!Q4bk41iaQ)}Ew6qKWdoGOYxV?rprg#g(-yd5B~f$Ki*AmUa6 z#O(Mq0kSCEO@PcA|C&G&;4@0S1=N!Q-2#xoBNbrUW9lysNsehvGp$9Yg|!u_rSK{; zvi2fXf<^;tE>b0M=vjS{DuK<;I+Kk>3X(5HCCROBE^0u``MT7!dxw%Ll?1w+YPQ|M z!c7KX`b8y4?|Lb{cVBkbtEzM`q@>HCBQ@6f6J&KQtj;4~g#j^A;FiDJd}zlc$tn%V zM*N%a42H##A*l=h21If`MFs_8LyR-;)>EU=! MDgW*fwaP613-^zeMgRZ+ delta 1824 zcmZ`(T}&KR6rQ{D!>~WI>;fx43k!nSp_R2J-G+wJ(z+l`TM$W2S7qHYL!s;sxw9m; zX#gu}vC#^>wbi5%ADSpJHpY1B4~mxY9b5? zMga_27YFQG;iM3eG<#kbGzaJf4~dl_)(O7Ce5^bmXfDw0nxe~)3w8J8xDatXU?_qB zr{=K&L+BO%sj6((q<;49L6TYQh@3zf7o@O!lK7dW*t<9b%t zM+{vbW_H_AGH)KY-6CeS@IE0n`?I6u_Qj6!Wc2onQT6dS(9H z%Jl`6&-mD4t&d$QYN%9Y>IXlqL8wJ|9H9KG3f22liG-d6 zs)kZYLj@t zabs;~Nb8wvXcbIp1N*X8B|YY9EfGk7-FxCsvd8?n;a8h_7~MwzRDMg%266-CW!ZY>L?YgI#pYLS zPmSTqUW9Rk353@XE&y!F5Cn!x*hovIqlG}kn;3S{yxQ^_k-Bi+ZI-nDMS3PxSUk6ZQ(G9vt;^MZc*3-bc zR0p+gYKSd9H(YleOSXJ?g1_aKL`oZt>#g)dP~iZa@h*F%)6dRz9@rO_NLiM9INrDH Ke>}o#?eH(Y=&CmW diff --git a/qihuo_analyzer/modules/deepseek_agent.py b/qihuo_analyzer/modules/deepseek_agent.py index 5781061..6231d3f 100644 --- a/qihuo_analyzer/modules/deepseek_agent.py +++ b/qihuo_analyzer/modules/deepseek_agent.py @@ -1,6 +1,7 @@ # AI 研判模块 import json import requests +import datetime from typing import Dict, Optional, List from qihuo_analyzer.utils.config_manager import config_manager from qihuo_analyzer.core.models import AnalysisResult @@ -51,10 +52,66 @@ class DeepseekAgent: self.api_key = self.current_config['api_key'] self.api_url = self.current_config['api_url'] self.headers = self.current_config['headers'] + + # 初始化缓存字典 + # 缓存结构: {"date_symbol_model": {"timestamp": "2023-07-01 12:00:00", "data": {...}}} + self.cache = {} + + def _get_cache_key(self, market_data, model_name): + """获取缓存键 + + Args: + market_data: 市场数据,包含symbol + model_name: 模型名称 + + Returns: + str: 缓存键,格式为 "日期_品种_模型" + """ + # 获取当前日期(交易日) + today = datetime.datetime.now().strftime('%Y-%m-%d') + # 获取品种代码 + symbol = market_data.get('symbol', 'unknown') + # 构建缓存键 + cache_key = f"{today}_{symbol}_{model_name}" + return cache_key + + def _get_from_cache(self, cache_key): + """从缓存中获取数据 + + Args: + cache_key: 缓存键 + + Returns: + Dict: 缓存的数据,如果不存在返回None + """ + if cache_key in self.cache: + return self.cache[cache_key]['data'] + return None + + def _set_to_cache(self, cache_key, data): + """将数据设置到缓存中 + + Args: + cache_key: 缓存键 + data: 要缓存的数据 + """ + self.cache[cache_key] = { + 'timestamp': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + 'data': data + } def analyze_market(self, market_data: Dict, technical_indicators: Dict, trend_analysis: Dict, risk_metrics: Dict) -> Dict: """分析市场""" + # 生成缓存键 + cache_key = self._get_cache_key(market_data, self.model_name) + + # 检查缓存 + cached_data = self._get_from_cache(cache_key) + if cached_data: + print(f"[DEBUG] 从缓存中获取分析数据: {cache_key}") + return cached_data + # 构建提示词 prompt = self._build_analysis_prompt(market_data, technical_indicators, trend_analysis, risk_metrics) @@ -64,10 +121,24 @@ class DeepseekAgent: # 解析结果 analysis_result = self._parse_analysis_result(response) + # 缓存结果 + self._set_to_cache(cache_key, analysis_result) + print(f"[DEBUG] 缓存分析数据: {cache_key}") + return analysis_result - def generate_trade_recommendation(self, analysis_result: Dict) -> Dict: + def generate_trade_recommendation(self, analysis_result: Dict, market_data: Optional[Dict] = None) -> Dict: """生成交易建议""" + # 生成缓存键 + if market_data: + cache_key = self._get_cache_key(market_data, f"{self.model_name}_recommendation") + + # 检查缓存 + cached_data = self._get_from_cache(cache_key) + if cached_data: + print(f"[DEBUG] 从缓存中获取交易建议: {cache_key}") + return cached_data + # 构建提示词 prompt = self._build_recommendation_prompt(analysis_result) @@ -77,6 +148,11 @@ class DeepseekAgent: # 解析结果 recommendation = self._parse_recommendation_result(response) + # 缓存结果 + if market_data: + self._set_to_cache(cache_key, recommendation) + print(f"[DEBUG] 缓存交易建议: {cache_key}") + return recommendation def _build_analysis_prompt(self, market_data: Dict, technical_indicators: Dict, diff --git a/requirements.txt b/requirements.txt index 3edd9bd..7722634 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,6 @@ requests==2.31.0 python-dotenv==1.0.0 APScheduler==3.10.4 pytest==7.4.4 +Flask==2.0.1 +Flask-Login==0.6.2 +Werkzeug==2.0.1 diff --git a/web/app.py b/web/app.py index 6f49198..6badf84 100644 --- a/web/app.py +++ b/web/app.py @@ -1,13 +1,16 @@ #!/usr/bin/env python3 # Flask web 应用 -from flask import Flask, render_template, jsonify, request +from flask import Flask, render_template, jsonify, request, redirect, url_for, flash, session import sys import os # 添加项目根目录到 Python 路径 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +# 导入认证模块 +from auth import login_manager, init_db, register_user, login_user_by_credentials, logout_user, login_required, current_user + from qihuo_analyzer.data.data_fetcher import DataFetcher from qihuo_analyzer.data.data_storage import DataStorage from qihuo_analyzer.modules.trend_filter import TrendFilter @@ -20,13 +23,57 @@ from qihuo_analyzer.core.models import AnalysisResult app = Flask(__name__) +# 设置Flask-Login +app.secret_key = 'your-secret-key-here' # 实际项目中应该使用环境变量 +login_manager.init_app(app) +login_manager.login_view = 'login' # 设置登录页面的路由 + +# 初始化数据库 +init_db() + # 模板上下文处理器 @app.context_processor def inject_functions(): import datetime def now(): return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') - return {'now': now} + return {'now': now, 'current_user': current_user} + +# 登录路由 +@app.route('/login', methods=['GET', 'POST']) +def login(): + if request.method == 'POST': + username = request.form['username'] + password = request.form['password'] + success, message = login_user_by_credentials(username, password) + if success: + flash(message) + return redirect(url_for('index')) + else: + flash(message) + return render_template('login.html') + +# 注册路由 +@app.route('/register', methods=['GET', 'POST']) +def register(): + if request.method == 'POST': + username = request.form['username'] + password = request.form['password'] + success, message = register_user(username, password) + if success: + flash(message) + return redirect(url_for('login')) + else: + flash(message) + return render_template('register.html') + +# 登出路由 +@app.route('/logout') +@login_required +def logout(): + logout_user() + flash('已登出') + return redirect(url_for('login')) # 初始化组件 data_fetcher = DataFetcher() @@ -68,11 +115,16 @@ def get_hot_symbols(all_symbols_data): try: # 这里简化处理,实际项目中应该使用真实的成交量、振幅、涨速数据 # 假设我们从kline_data中获取这些数据 - # 由于我们没有这些数据,这里使用随机值模拟 - import random - symbol_data['volume'] = random.randint(10000, 1000000) - symbol_data['amplitude'] = random.uniform(0.1, 5.0) - symbol_data['speed'] = random.uniform(-2.0, 2.0) + # 由于我们没有这些数据,这里使用基于品种代码的哈希值生成值,确保每次计算结果一致 + import hashlib + # 使用品种代码生成哈希值 + hash_obj = hashlib.md5(symbol_data['symbol'].encode()) + hash_int = int(hash_obj.hexdigest(), 16) + + # 基于哈希值生成成交量、振幅、涨速 + symbol_data['volume'] = 100000 + (hash_int % 900000) # 100000-1000000 + symbol_data['amplitude'] = 0.1 + (hash_int % 100) / 25 # 0.1-4.1 + symbol_data['speed'] = -2.0 + (hash_int % 100) / 25 # -2.0-2.0 except Exception: pass @@ -88,6 +140,7 @@ def get_hot_symbols(all_symbols_data): return hot_symbols @app.route('/') +@login_required def index(): """首页 - 多品种分析面板""" # 获取所有品种的分析数据 @@ -301,6 +354,7 @@ def index(): data_unavailable=False) @app.route('/symbol/') +@login_required def symbol_detail(symbol): """品种详情页""" try: @@ -451,7 +505,7 @@ def symbol_detail(symbol): # 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) + recommendation = ai_agent.generate_trade_recommendation(ai_analysis, market_data) # 构建模板数据 # 获取中文名称 @@ -544,6 +598,7 @@ def symbol_detail(symbol): return render_template('error.html', message=f"分析失败: {str(e)}") @app.route('/api/analysis/') +@login_required def api_analysis(symbol): """API: 获取品种分析数据""" try: @@ -598,6 +653,7 @@ def api_analysis(symbol): return jsonify({"error": str(e)}), 500 @app.route('/api/card/') +@login_required def api_card(symbol): """API: 获取分析卡片数据""" try: @@ -695,7 +751,7 @@ def api_card(symbol): } ai_analysis = deepseek_agent.analyze_market(market_data, technical_indicators, trend_data, risk_metrics) - recommendation = deepseek_agent.generate_trade_recommendation(ai_analysis) + recommendation = deepseek_agent.generate_trade_recommendation(ai_analysis, market_data) # 构建卡片数据 # 获取中文名称 @@ -727,6 +783,7 @@ def api_card(symbol): return jsonify({"error": str(e)}), 500 @app.route('/card/') +@login_required def card(symbol): """分析卡片页面""" try: @@ -802,6 +859,7 @@ def _get_kline_data_for_chart(kline_data): return [] @app.route('/api/selected/add/', methods=['POST']) +@login_required def add_selected_symbol(symbol): """添加自选品种""" global selected_symbols @@ -810,6 +868,7 @@ def add_selected_symbol(symbol): return jsonify({'status': 'success', 'selected_symbols': selected_symbols}) @app.route('/api/selected/remove/', methods=['POST']) +@login_required def remove_selected_symbol(symbol): """删除自选品种""" global selected_symbols @@ -818,6 +877,7 @@ def remove_selected_symbol(symbol): return jsonify({'status': 'success', 'selected_symbols': selected_symbols}) @app.route('/api/selected/list') +@login_required def get_selected_symbols(): """获取自选品种列表""" return jsonify({'selected_symbols': selected_symbols}) diff --git a/web/data/futures_analysis.db b/web/data/futures_analysis.db index 46d935b72569c80e975b941c834522d8b6a35e16..315e319468bae3c0529d574a0bfd11ca2011769b 100644 GIT binary patch delta 404 zcmXw#%Sr-K7=X`Y&dqETf(a@Z$c-2==gcKX2-28EAlB4K)@C?*W>E5$qtqtSHf=Ls zAbNxFF&=kYgZ>19>mp zwx}pbj?OUbRxgN$a(*2nP0t}+4oIECbJD4pb`7E?*5!5zD~9eNf|2TBlC2>{lRaH= zH4kTZi0mqctU?t-kJL1U$c775MMV(FMpf4o)zJCi%#`pAf(V(4i4TDR(MXcR@CVD3 BYgGUM delta 73 zcmZozz|`=7ae}lU3j+fKI}pPF%S0VxNfrjZco|+^Wd=5OO9tLrUTtnJo_8GI*ey2; V3LIqHY{`CFVl#`wANfTN1^~$@4=exx diff --git a/web/templates/index.html b/web/templates/index.html index ef115d2..754593c 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -318,8 +318,26 @@
-

AI期货分析系统

-

基于DeepSeek大模型和量化分析算法的智能决策平台

+
+
+

AI期货分析系统

+

基于DeepSeek大模型和量化分析算法的智能决策平台

+
+
+ {% if current_user.is_authenticated %} +
+ 欢迎, {{ current_user.username }} +
+ + 登出 + + {% else %} + + 登录 + + {% endif %} +
+