469 lines
15 KiB
Python
469 lines
15 KiB
Python
from flask import Blueprint, request, jsonify
|
|
import pandas as pd
|
|
import json
|
|
from datetime import datetime
|
|
|
|
bp = Blueprint('limitanalyse', __name__, url_prefix='/api/limit-analyse')
|
|
|
|
@bp.route('/available-dates', methods=['GET'])
|
|
def get_available_dates():
|
|
"""获取可用日期列表"""
|
|
try:
|
|
# 模拟可用日期
|
|
dates = [
|
|
'2025-07-16',
|
|
'2025-07-15',
|
|
'2025-07-14',
|
|
'2025-07-11',
|
|
'2025-07-10'
|
|
]
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'data': dates
|
|
})
|
|
except Exception as e:
|
|
print(f"Error getting available dates: {e}")
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
|
|
def load_stock_data(datestr):
|
|
"""加载股票数据"""
|
|
try:
|
|
# 模拟股票数据
|
|
data = []
|
|
for i in range(100):
|
|
data.append({
|
|
'code': f'00000{i:03d}',
|
|
'name': f'股票{i}',
|
|
'price': 10.0 + i * 0.1,
|
|
'change': (i % 10 - 5) * 0.5,
|
|
'sector': f'板块{i % 5}',
|
|
'limit_type': '涨停' if i % 10 == 0 else '正常',
|
|
'volume': 1000000 + i * 50000,
|
|
'amount': 10000000 + i * 500000
|
|
})
|
|
|
|
return pd.DataFrame(data)
|
|
except Exception as e:
|
|
print(f"Error loading stock data: {e}")
|
|
return pd.DataFrame()
|
|
|
|
@bp.route('/data', methods=['GET'])
|
|
def get_analysis_data():
|
|
"""获取分析数据"""
|
|
try:
|
|
date = request.args.get('date', '2025-07-16')
|
|
|
|
# 加载数据
|
|
df = load_stock_data(date)
|
|
if df.empty:
|
|
return jsonify({'success': False, 'error': '数据加载失败'}), 500
|
|
|
|
# 统计信息
|
|
total_stocks = len(df)
|
|
limit_up_stocks = len(df[df['limit_type'] == '涨停'])
|
|
limit_down_stocks = len(df[df['limit_type'] == '跌停'])
|
|
|
|
# 板块统计
|
|
sector_stats = df.groupby('sector').agg({
|
|
'code': 'count',
|
|
'change': 'mean',
|
|
'volume': 'sum'
|
|
}).reset_index()
|
|
|
|
sector_data = []
|
|
for _, row in sector_stats.iterrows():
|
|
sector_data.append({
|
|
'sector': row['sector'],
|
|
'stock_count': int(row['code']),
|
|
'avg_change': round(row['change'], 2),
|
|
'total_volume': int(row['volume'])
|
|
})
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'data': {
|
|
'date': date,
|
|
'total_stocks': total_stocks,
|
|
'limit_up_stocks': limit_up_stocks,
|
|
'limit_down_stocks': limit_down_stocks,
|
|
'sector_stats': sector_data
|
|
}
|
|
})
|
|
|
|
except Exception as e:
|
|
print(f"Error getting analysis data: {e}")
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
|
|
@bp.route('/sector-data', methods=['GET'])
|
|
def get_sector_data():
|
|
"""获取板块数据"""
|
|
try:
|
|
date = request.args.get('date', '2025-07-16')
|
|
|
|
# 加载数据
|
|
df = load_stock_data(date)
|
|
if df.empty:
|
|
return jsonify({'success': False, 'error': '数据加载失败'}), 500
|
|
|
|
# 板块统计
|
|
sector_stats = df.groupby('sector').agg({
|
|
'code': 'count',
|
|
'change': 'mean',
|
|
'volume': 'sum',
|
|
'amount': 'sum'
|
|
}).reset_index()
|
|
|
|
sector_data = []
|
|
for _, row in sector_stats.iterrows():
|
|
sector_data.append({
|
|
'sector': row['sector'],
|
|
'stock_count': int(row['code']),
|
|
'avg_change': round(row['change'], 2),
|
|
'total_volume': int(row['volume']),
|
|
'total_amount': int(row['amount'])
|
|
})
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'data': sector_data
|
|
})
|
|
|
|
except Exception as e:
|
|
print(f"Error getting sector data: {e}")
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
|
|
@bp.route('/word-cloud', methods=['GET'])
|
|
def get_word_cloud_data():
|
|
"""获取词云数据"""
|
|
try:
|
|
date = request.args.get('date', '2025-07-16')
|
|
|
|
# 模拟词云数据
|
|
word_data = [
|
|
{'word': '科技', 'value': 100},
|
|
{'word': '新能源', 'value': 85},
|
|
{'word': '医药', 'value': 70},
|
|
{'word': '消费', 'value': 65},
|
|
{'word': '金融', 'value': 50},
|
|
{'word': '地产', 'value': 45},
|
|
{'word': '制造', 'value': 40},
|
|
{'word': '农业', 'value': 35},
|
|
{'word': '传媒', 'value': 30},
|
|
{'word': '环保', 'value': 25}
|
|
]
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'data': word_data
|
|
})
|
|
|
|
except Exception as e:
|
|
print(f"Error getting word cloud data: {e}")
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
|
|
@bp.route('/chart-data', methods=['GET'])
|
|
def get_chart_data():
|
|
"""获取图表数据"""
|
|
try:
|
|
date = request.args.get('date', '2025-07-16')
|
|
|
|
# 模拟图表数据
|
|
chart_data = {
|
|
'limit_up_distribution': [
|
|
{'sector': '科技', 'count': 15},
|
|
{'sector': '新能源', 'count': 12},
|
|
{'sector': '医药', 'count': 10},
|
|
{'sector': '消费', 'count': 8},
|
|
{'sector': '金融', 'count': 6}
|
|
],
|
|
'sector_performance': [
|
|
{'sector': '科技', 'change': 2.5},
|
|
{'sector': '新能源', 'change': 1.8},
|
|
{'sector': '医药', 'change': 1.2},
|
|
{'sector': '消费', 'change': 0.8},
|
|
{'sector': '金融', 'change': 0.5}
|
|
]
|
|
}
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'data': chart_data
|
|
})
|
|
|
|
except Exception as e:
|
|
print(f"Error getting chart data: {e}")
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
|
|
@bp.route('/stock-details', methods=['GET'])
|
|
def get_stock_details():
|
|
"""获取股票详情"""
|
|
try:
|
|
code = request.args.get('code')
|
|
date = request.args.get('date', '2025-07-16')
|
|
|
|
if not code:
|
|
return jsonify({'success': False, 'error': '请提供股票代码'}), 400
|
|
|
|
# 模拟股票详情
|
|
stock_detail = {
|
|
'code': code,
|
|
'name': f'股票{code}',
|
|
'price': 15.50,
|
|
'change': 2.5,
|
|
'sector': '科技',
|
|
'volume': 1500000,
|
|
'amount': 23250000,
|
|
'limit_type': '涨停',
|
|
'turnover_rate': 3.2,
|
|
'market_cap': 15500000000
|
|
}
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'data': stock_detail
|
|
})
|
|
|
|
except Exception as e:
|
|
print(f"Error getting stock details: {e}")
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
|
|
@bp.route('/sector-analysis', methods=['GET'])
|
|
def get_sector_analysis():
|
|
"""获取板块分析"""
|
|
try:
|
|
sector = request.args.get('sector')
|
|
date = request.args.get('date', '2025-07-16')
|
|
|
|
if not sector:
|
|
return jsonify({'success': False, 'error': '请提供板块名称'}), 400
|
|
|
|
# 模拟板块分析数据
|
|
sector_analysis = {
|
|
'sector': sector,
|
|
'stock_count': 25,
|
|
'avg_change': 1.8,
|
|
'limit_up_count': 8,
|
|
'limit_down_count': 2,
|
|
'total_volume': 50000000,
|
|
'total_amount': 750000000,
|
|
'top_stocks': [
|
|
{'code': '000001', 'name': '股票A', 'change': 10.0},
|
|
{'code': '000002', 'name': '股票B', 'change': 9.5},
|
|
{'code': '000003', 'name': '股票C', 'change': 8.8}
|
|
]
|
|
}
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'data': sector_analysis
|
|
})
|
|
|
|
except Exception as e:
|
|
print(f"Error getting sector analysis: {e}")
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
|
|
@bp.route('/trend-analysis', methods=['GET'])
|
|
def get_trend_analysis():
|
|
"""获取趋势分析"""
|
|
try:
|
|
date = request.args.get('date', '2025-07-16')
|
|
|
|
# 模拟趋势分析数据
|
|
trend_data = {
|
|
'limit_up_trend': [
|
|
{'date': '2025-07-10', 'count': 45},
|
|
{'date': '2025-07-11', 'count': 52},
|
|
{'date': '2025-07-14', 'count': 48},
|
|
{'date': '2025-07-15', 'count': 55},
|
|
{'date': '2025-07-16', 'count': 51}
|
|
],
|
|
'sector_trend': [
|
|
{'sector': '科技', 'trend': 'up'},
|
|
{'sector': '新能源', 'trend': 'up'},
|
|
{'sector': '医药', 'trend': 'stable'},
|
|
{'sector': '消费', 'trend': 'down'},
|
|
{'sector': '金融', 'trend': 'stable'}
|
|
]
|
|
}
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'data': trend_data
|
|
})
|
|
|
|
except Exception as e:
|
|
print(f"Error getting trend analysis: {e}")
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
|
|
@bp.route('/heat-map', methods=['GET'])
|
|
def get_heat_map_data():
|
|
"""获取热力图数据"""
|
|
try:
|
|
date = request.args.get('date', '2025-07-16')
|
|
|
|
# 模拟热力图数据
|
|
heat_map_data = []
|
|
sectors = ['科技', '新能源', '医药', '消费', '金融', '地产', '制造', '农业']
|
|
|
|
for i, sector in enumerate(sectors):
|
|
for j in range(8):
|
|
heat_map_data.append({
|
|
'sector': sector,
|
|
'metric': f'指标{j+1}',
|
|
'value': (i + j) % 10 + 1
|
|
})
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'data': heat_map_data
|
|
})
|
|
|
|
except Exception as e:
|
|
print(f"Error getting heat map data: {e}")
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
|
|
@bp.route('/correlation-analysis', methods=['GET'])
|
|
def get_correlation_analysis():
|
|
"""获取相关性分析"""
|
|
try:
|
|
date = request.args.get('date', '2025-07-16')
|
|
|
|
# 模拟相关性分析数据
|
|
correlation_data = {
|
|
'sector_correlations': [
|
|
{'sector1': '科技', 'sector2': '新能源', 'correlation': 0.85},
|
|
{'sector1': '医药', 'sector2': '消费', 'correlation': 0.72},
|
|
{'sector1': '金融', 'sector2': '地产', 'correlation': 0.68},
|
|
{'sector1': '科技', 'sector2': '医药', 'correlation': 0.45},
|
|
{'sector1': '新能源', 'sector2': '制造', 'correlation': 0.78}
|
|
],
|
|
'stock_correlations': [
|
|
{'stock1': '000001', 'stock2': '000002', 'correlation': 0.92},
|
|
{'stock1': '000003', 'stock2': '000004', 'correlation': 0.88},
|
|
{'stock1': '000005', 'stock2': '000006', 'correlation': 0.76}
|
|
]
|
|
}
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'data': correlation_data
|
|
})
|
|
|
|
except Exception as e:
|
|
print(f"Error getting correlation analysis: {e}")
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
|
|
@bp.route('/export-data', methods=['POST'])
|
|
def export_data():
|
|
"""导出数据"""
|
|
try:
|
|
data = request.get_json()
|
|
date = data.get('date', '2025-07-16')
|
|
export_type = data.get('type', 'excel')
|
|
|
|
# 模拟导出
|
|
filename = f'limit_analyse_{date}.{export_type}'
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'message': '数据导出成功',
|
|
'data': {
|
|
'filename': filename,
|
|
'download_url': f'/downloads/{filename}'
|
|
}
|
|
})
|
|
|
|
except Exception as e:
|
|
print(f"Error exporting data: {e}")
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
|
|
@bp.route('/high-position-stocks', methods=['GET'])
|
|
def get_high_position_stocks():
|
|
"""获取高位股统计数据"""
|
|
try:
|
|
date = request.args.get('date', datetime.now().strftime('%Y%m%d'))
|
|
|
|
# 模拟高位股数据 - 实际使用时需要连接真实的数据库
|
|
# 根据用户提供的表结构,查询连续涨停天数较多的股票
|
|
high_position_stocks = [
|
|
{
|
|
'stock_code': '000001',
|
|
'stock_name': '平安银行',
|
|
'price': 15.68,
|
|
'increase_rate': 10.02,
|
|
'limit_up_days': 5,
|
|
'continuous_limit_up': 3,
|
|
'industry': '银行',
|
|
'turnover_rate': 3.45,
|
|
'market_cap': 32000000000
|
|
},
|
|
{
|
|
'stock_code': '000002',
|
|
'stock_name': '万科A',
|
|
'price': 18.92,
|
|
'increase_rate': 9.98,
|
|
'limit_up_days': 4,
|
|
'continuous_limit_up': 2,
|
|
'industry': '房地产',
|
|
'turnover_rate': 5.67,
|
|
'market_cap': 21000000000
|
|
},
|
|
{
|
|
'stock_code': '600036',
|
|
'stock_name': '招商银行',
|
|
'price': 42.15,
|
|
'increase_rate': 8.45,
|
|
'limit_up_days': 6,
|
|
'continuous_limit_up': 4,
|
|
'industry': '银行',
|
|
'turnover_rate': 2.89,
|
|
'market_cap': 105000000000
|
|
},
|
|
{
|
|
'stock_code': '000858',
|
|
'stock_name': '五粮液',
|
|
'price': 168.50,
|
|
'increase_rate': 7.23,
|
|
'limit_up_days': 3,
|
|
'continuous_limit_up': 2,
|
|
'industry': '白酒',
|
|
'turnover_rate': 1.56,
|
|
'market_cap': 650000000000
|
|
},
|
|
{
|
|
'stock_code': '002415',
|
|
'stock_name': '海康威视',
|
|
'price': 35.68,
|
|
'increase_rate': 6.89,
|
|
'limit_up_days': 4,
|
|
'continuous_limit_up': 3,
|
|
'industry': '安防',
|
|
'turnover_rate': 4.12,
|
|
'market_cap': 33000000000
|
|
}
|
|
]
|
|
|
|
# 统计信息
|
|
total_count = len(high_position_stocks)
|
|
avg_continuous_days = sum(stock['continuous_limit_up'] for stock in high_position_stocks) / total_count if total_count > 0 else 0
|
|
|
|
# 按连续涨停天数排序
|
|
high_position_stocks.sort(key=lambda x: x['continuous_limit_up'], reverse=True)
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'data': {
|
|
'stocks': high_position_stocks,
|
|
'statistics': {
|
|
'total_count': total_count,
|
|
'avg_continuous_days': round(avg_continuous_days, 2),
|
|
'max_continuous_days': max([stock['continuous_limit_up'] for stock in high_position_stocks], default=0),
|
|
'industry_distribution': {}
|
|
}
|
|
}
|
|
})
|
|
|
|
except Exception as e:
|
|
print(f"Error getting high position stocks: {e}")
|
|
return jsonify({'success': False, 'error': str(e)}), 500 |