|
|
|
|
@ -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/<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/<symbol>')
|
|
|
|
|
@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/<symbol>')
|
|
|
|
|
@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/<symbol>')
|
|
|
|
|
@login_required
|
|
|
|
|
def card(symbol):
|
|
|
|
|
"""分析卡片页面"""
|
|
|
|
|
try:
|
|
|
|
|
@ -802,6 +859,7 @@ def _get_kline_data_for_chart(kline_data):
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
@app.route('/api/selected/add/<symbol>', 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/<symbol>', 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})
|
|
|
|
|
|