|
|
|
|
# Service main application
|
|
|
|
|
from flask import Flask, request, jsonify
|
|
|
|
|
import sys
|
|
|
|
|
import os
|
|
|
|
|
import pandas as pd
|
|
|
|
|
|
|
|
|
|
# 添加项目根目录到 Python 路径
|
|
|
|
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
|
|
|
|
|
|
from qihuo_analyzer.data.data_fetcher import DataFetcher
|
|
|
|
|
from qihuo_analyzer.data.data_storage import DataStorage
|
|
|
|
|
from qihuo_analyzer.modules.deepseek_agent import DeepseekAgent
|
|
|
|
|
from qihuo_analyzer.utils.config_manager import config_manager
|
|
|
|
|
|
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
|
|
|
|
# 初始化组件
|
|
|
|
|
data_fetcher = DataFetcher()
|
|
|
|
|
data_storage = DataStorage()
|
|
|
|
|
deepseek_agent = DeepseekAgent()
|
|
|
|
|
|
|
|
|
|
# 连接 API
|
|
|
|
|
print("正在连接 API...")
|
|
|
|
|
connect_success = data_fetcher.connect()
|
|
|
|
|
if connect_success:
|
|
|
|
|
print("API 连接成功,可以获取真实数据")
|
|
|
|
|
else:
|
|
|
|
|
print("API 连接失败,将使用模拟数据")
|
|
|
|
|
|
|
|
|
|
# 健康检查接口
|
|
|
|
|
@app.route('/health', methods=['GET'])
|
|
|
|
|
def health_check():
|
|
|
|
|
return jsonify({'status': 'ok', 'message': 'Service is running'})
|
|
|
|
|
|
|
|
|
|
# 合约数据获取接口
|
|
|
|
|
@app.route('/api/contracts', methods=['GET'])
|
|
|
|
|
def get_contracts():
|
|
|
|
|
try:
|
|
|
|
|
exchange = request.args.get('exchange', '')
|
|
|
|
|
symbol = request.args.get('symbol', '')
|
|
|
|
|
|
|
|
|
|
print("正在获取合约数据..., exchange:", exchange, "symbol:", symbol)
|
|
|
|
|
contracts = data_fetcher.get_contracts(exchange=exchange, symbol=symbol)
|
|
|
|
|
print(f"获取到 {contracts} 合约")
|
|
|
|
|
return jsonify({'status': 'success', 'data': contracts})
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return jsonify({'status': 'error', 'message': str(e)}), 500
|
|
|
|
|
|
|
|
|
|
# 主力合约获取接口
|
|
|
|
|
@app.route('/api/main-contracts', methods=['GET'])
|
|
|
|
|
def get_main_contracts():
|
|
|
|
|
try:
|
|
|
|
|
print("正在获取主力合约数据...")
|
|
|
|
|
main_contracts = data_fetcher.get_main_contracts()
|
|
|
|
|
print(f"获取到主力合约:{main_contracts}")
|
|
|
|
|
|
|
|
|
|
# 转换为主力合约列表格式,包含品种名称
|
|
|
|
|
main_contracts_list = []
|
|
|
|
|
for product, contract in main_contracts.items():
|
|
|
|
|
product_name = data_fetcher.get_product_name_cn(product)
|
|
|
|
|
main_contracts_list.append({
|
|
|
|
|
'product': product,
|
|
|
|
|
'product_name': product_name,
|
|
|
|
|
'main_contract': contract
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return jsonify({'status': 'success', 'data': main_contracts_list})
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return jsonify({'status': 'error', 'message': str(e)}), 500
|
|
|
|
|
|
|
|
|
|
# 最后交易日获取接口
|
|
|
|
|
@app.route('/api/last-trading-day', methods=['GET'])
|
|
|
|
|
def get_last_trading_day():
|
|
|
|
|
try:
|
|
|
|
|
symbol = request.args.get('symbol', 'CU2603') # 默认使用CU2603合约
|
|
|
|
|
print(f"正在获取最后交易日,合约:{symbol}")
|
|
|
|
|
last_trading_day = data_fetcher.get_last_trading_day(symbol)
|
|
|
|
|
print(f"获取到最后交易日:{last_trading_day}")
|
|
|
|
|
|
|
|
|
|
return jsonify({'status': 'success', 'data': {
|
|
|
|
|
'symbol': symbol,
|
|
|
|
|
'last_trading_day': last_trading_day
|
|
|
|
|
}})
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return jsonify({'status': 'error', 'message': str(e)}), 500
|
|
|
|
|
|
|
|
|
|
# K线数据获取接口
|
|
|
|
|
@app.route('/api/kline', methods=['GET'])
|
|
|
|
|
def get_kline():
|
|
|
|
|
try:
|
|
|
|
|
symbol = request.args.get('symbol', '')
|
|
|
|
|
duration = request.args.get('duration', '1m')
|
|
|
|
|
limit = int(request.args.get('limit', 100))
|
|
|
|
|
|
|
|
|
|
if not symbol:
|
|
|
|
|
return jsonify({'status': 'error', 'message': 'Symbol is required'}), 400
|
|
|
|
|
|
|
|
|
|
# 尝试从数据库获取,如果没有则从数据源获取
|
|
|
|
|
df = data_storage.get_kline_data(symbol, duration, limit)
|
|
|
|
|
|
|
|
|
|
if df.empty:
|
|
|
|
|
# 从数据源获取
|
|
|
|
|
df = data_fetcher.get_kline_data(symbol, duration, limit)
|
|
|
|
|
# 保存到数据库
|
|
|
|
|
data_storage.save_kline_data(symbol, duration, df)
|
|
|
|
|
|
|
|
|
|
# 转换为字典格式
|
|
|
|
|
kline_data = []
|
|
|
|
|
for idx, row in df.iterrows():
|
|
|
|
|
kline_data.append({
|
|
|
|
|
'datetime': idx.isoformat(),
|
|
|
|
|
'open': float(row['open']),
|
|
|
|
|
'high': float(row['high']),
|
|
|
|
|
'low': float(row['low']),
|
|
|
|
|
'close': float(row['close']),
|
|
|
|
|
'volume': int(row['volume']),
|
|
|
|
|
'open_interest': int(row['open_interest'])
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return jsonify({'status': 'success', 'data': kline_data})
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return jsonify({'status': 'error', 'message': str(e)}), 500
|
|
|
|
|
|
|
|
|
|
# DeepSeek 分析接口
|
|
|
|
|
@app.route('/api/analyze', methods=['POST'])
|
|
|
|
|
def analyze():
|
|
|
|
|
try:
|
|
|
|
|
data = request.get_json()
|
|
|
|
|
symbol = data.get('symbol', '')
|
|
|
|
|
duration = data.get('duration', '1m')
|
|
|
|
|
analysis_type = data.get('analysis_type', 'technical')
|
|
|
|
|
|
|
|
|
|
if not symbol:
|
|
|
|
|
return jsonify({'status': 'error', 'message': 'Symbol is required'}), 400
|
|
|
|
|
|
|
|
|
|
# 获取K线数据
|
|
|
|
|
df = data_fetcher.get_kline_data(symbol, duration, 1000)
|
|
|
|
|
|
|
|
|
|
# 保存到数据库
|
|
|
|
|
data_storage.save_kline_data(symbol, duration, df)
|
|
|
|
|
|
|
|
|
|
# 准备分析所需的数据
|
|
|
|
|
market_data = {
|
|
|
|
|
'symbol': symbol,
|
|
|
|
|
'latest_price': float(df['close'].iloc[-1]) if not df.empty else 0,
|
|
|
|
|
'volume': int(df['volume'].iloc[-1]) if not df.empty else 0,
|
|
|
|
|
'timeframe': duration
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 模拟技术指标数据
|
|
|
|
|
technical_indicators = {
|
|
|
|
|
'macd': {'status': '金叉'},
|
|
|
|
|
'rsi': 50,
|
|
|
|
|
'bollinger': {'status': '中轨附近'},
|
|
|
|
|
'kdj': {'status': '金叉'},
|
|
|
|
|
'atr': 10
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 模拟趋势分析数据
|
|
|
|
|
trend_analysis = {
|
|
|
|
|
'adx': 30,
|
|
|
|
|
'trend_strength': '中等',
|
|
|
|
|
'trend_direction': '震荡偏多',
|
|
|
|
|
'ma_relationship': '金叉',
|
|
|
|
|
'multi_period_analysis': {'1m': '震荡', '5m': '偏多', '15m': '偏多'},
|
|
|
|
|
'overall_trend': '震荡偏多',
|
|
|
|
|
'win_rate': 60
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 模拟风险指标数据
|
|
|
|
|
risk_metrics = {
|
|
|
|
|
'stop_loss': market_data['latest_price'] * 0.98,
|
|
|
|
|
'target_price': market_data['latest_price'] * 1.02,
|
|
|
|
|
'profit_loss_ratio': 2,
|
|
|
|
|
'position_size': 2,
|
|
|
|
|
'risk_ratio': 2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 执行分析
|
|
|
|
|
analysis_result = deepseek_agent.analyze_market(
|
|
|
|
|
market_data,
|
|
|
|
|
technical_indicators,
|
|
|
|
|
trend_analysis,
|
|
|
|
|
risk_metrics
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 保存分析结果
|
|
|
|
|
data_storage.save_analysis_result(analysis_result)
|
|
|
|
|
|
|
|
|
|
return jsonify({'status': 'success', 'data': analysis_result})
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return jsonify({'status': 'error', 'message': str(e)}), 500
|
|
|
|
|
|
|
|
|
|
# 交易建议接口
|
|
|
|
|
@app.route('/api/recommendations', methods=['GET'])
|
|
|
|
|
def get_recommendations():
|
|
|
|
|
try:
|
|
|
|
|
symbol = request.args.get('symbol', '')
|
|
|
|
|
status = request.args.get('status', '')
|
|
|
|
|
|
|
|
|
|
if not symbol:
|
|
|
|
|
return jsonify({'status': 'error', 'message': 'Symbol is required'}), 400
|
|
|
|
|
|
|
|
|
|
df = data_storage.get_trade_recommendations(symbol, status)
|
|
|
|
|
|
|
|
|
|
# 转换为字典格式
|
|
|
|
|
recommendations = []
|
|
|
|
|
for _, row in df.iterrows():
|
|
|
|
|
recommendations.append({
|
|
|
|
|
'id': int(row['id']),
|
|
|
|
|
'symbol': row['symbol'],
|
|
|
|
|
'timestamp': row['timestamp'],
|
|
|
|
|
'direction': row['direction'],
|
|
|
|
|
'entry_price': float(row['entry_price']) if not pd.isna(row['entry_price']) else None,
|
|
|
|
|
'stop_loss': float(row['stop_loss']) if not pd.isna(row['stop_loss']) else None,
|
|
|
|
|
'target_price': float(row['target_price']) if not pd.isna(row['target_price']) else None,
|
|
|
|
|
'position_size': float(row['position_size']) if not pd.isna(row['position_size']) else None,
|
|
|
|
|
'execution_plan': row['execution_plan'],
|
|
|
|
|
'risk_tips': row['risk_tips'],
|
|
|
|
|
'status': row['status'],
|
|
|
|
|
'created_at': row['created_at']
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return jsonify({'status': 'success', 'data': recommendations})
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return jsonify({'status': 'error', 'message': str(e)}), 500
|
|
|
|
|
|
|
|
|
|
# 风险监控接口
|
|
|
|
|
@app.route('/api/risk', methods=['POST'])
|
|
|
|
|
def monitor_risk():
|
|
|
|
|
try:
|
|
|
|
|
data = request.get_json()
|
|
|
|
|
symbol = data.get('symbol', '')
|
|
|
|
|
current_price = data.get('current_price', 0)
|
|
|
|
|
entry_price = data.get('entry_price', 0)
|
|
|
|
|
stop_loss = data.get('stop_loss', 0)
|
|
|
|
|
target_price = data.get('target_price', 0)
|
|
|
|
|
|
|
|
|
|
if not symbol:
|
|
|
|
|
return jsonify({'status': 'error', 'message': 'Symbol is required'}), 400
|
|
|
|
|
|
|
|
|
|
# 计算当前利润
|
|
|
|
|
current_profit = current_price - entry_price
|
|
|
|
|
|
|
|
|
|
# 评估风险状态
|
|
|
|
|
risk_status = 'normal'
|
|
|
|
|
if abs(current_profit) > (entry_price * 0.05):
|
|
|
|
|
risk_status = 'high'
|
|
|
|
|
|
|
|
|
|
# 保存风险监控数据
|
|
|
|
|
risk_data = {
|
|
|
|
|
'symbol': symbol,
|
|
|
|
|
'current_price': current_price,
|
|
|
|
|
'entry_price': entry_price,
|
|
|
|
|
'stop_loss': stop_loss,
|
|
|
|
|
'target_price': target_price,
|
|
|
|
|
'current_profit': current_profit,
|
|
|
|
|
'risk_status': risk_status
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data_storage.save_risk_monitoring(risk_data)
|
|
|
|
|
|
|
|
|
|
return jsonify({'status': 'success', 'data': risk_data})
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return jsonify({'status': 'error', 'message': str(e)}), 500
|
|
|
|
|
|
|
|
|
|
# 分析历史接口
|
|
|
|
|
@app.route('/api/analysis/history', methods=['GET'])
|
|
|
|
|
def get_analysis_history():
|
|
|
|
|
try:
|
|
|
|
|
symbol = request.args.get('symbol', '')
|
|
|
|
|
limit = int(request.args.get('limit', 100))
|
|
|
|
|
|
|
|
|
|
if not symbol:
|
|
|
|
|
return jsonify({'status': 'error', 'message': 'Symbol is required'}), 400
|
|
|
|
|
|
|
|
|
|
df = data_storage.get_analysis_results(symbol, limit)
|
|
|
|
|
|
|
|
|
|
# 转换为字典格式
|
|
|
|
|
history = []
|
|
|
|
|
for _, row in df.iterrows():
|
|
|
|
|
history.append({
|
|
|
|
|
'id': int(row['id']),
|
|
|
|
|
'symbol': row['symbol'],
|
|
|
|
|
'timestamp': row['timestamp'],
|
|
|
|
|
'trend': row['trend'],
|
|
|
|
|
'probability': float(row['probability']) if not pd.isna(row['probability']) else None,
|
|
|
|
|
'direction': row['direction'],
|
|
|
|
|
'cycle': row['cycle'],
|
|
|
|
|
'atr': float(row['atr']) if not pd.isna(row['atr']) else None,
|
|
|
|
|
'adx': float(row['adx']) if not pd.isna(row['adx']) else None,
|
|
|
|
|
'support': float(row['support']) if not pd.isna(row['support']) else None,
|
|
|
|
|
'resistance': float(row['resistance']) if not pd.isna(row['resistance']) else None,
|
|
|
|
|
'stop_loss': float(row['stop_loss']) if not pd.isna(row['stop_loss']) else None,
|
|
|
|
|
'target_price': float(row['target_price']) if not pd.isna(row['target_price']) else None,
|
|
|
|
|
'position_size': float(row['position_size']) if not pd.isna(row['position_size']) else None,
|
|
|
|
|
'risk_ratio': float(row['risk_ratio']) if not pd.isna(row['risk_ratio']) else None,
|
|
|
|
|
'fund_flow': row['fund_flow'],
|
|
|
|
|
'signals': row['signals'],
|
|
|
|
|
'created_at': row['created_at']
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return jsonify({'status': 'success', 'data': history})
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return jsonify({'status': 'error', 'message': str(e)}), 500
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
app.run(host='0.0.0.0', port=5000, debug=True)
|