Add files via upload
1. 增加了港股支持 2. 补全Dockerfile 3. 支持HTML在线访问
This commit is contained in:
27
Dockerfile
Normal file
27
Dockerfile
Normal file
@@ -0,0 +1,27 @@
|
||||
# 使用 Python 3.9 作为基础镜像
|
||||
FROM python:3.10-slim
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 安装系统依赖
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libgl1-mesa-glx \
|
||||
ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 复制项目文件
|
||||
COPY . /app/
|
||||
|
||||
# 安装 Python 依赖
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
RUN pip install akshare --upgrade -i https://pypi.org/simple
|
||||
|
||||
# 设置环境变量
|
||||
ENV PYTHONPATH=/app
|
||||
|
||||
# 暴露端口(如果需要)
|
||||
EXPOSE 8888
|
||||
|
||||
# 启动命令
|
||||
CMD ["python", "web_server.py"]
|
||||
@@ -1,5 +1,5 @@
|
||||
# 基础科学计算和数据处理库
|
||||
numpy==2.0.0
|
||||
numpy==2.1.2
|
||||
pandas==2.2.2
|
||||
scipy==1.15.1
|
||||
|
||||
@@ -7,9 +7,6 @@ scipy==1.15.1
|
||||
akshare==1.15.87
|
||||
tqdm==4.67.1
|
||||
|
||||
# GUI库
|
||||
PyQt6==6.8.1
|
||||
markdown2==2.5.3
|
||||
|
||||
# 网络和API请求
|
||||
requests==2.32.3
|
||||
|
||||
@@ -18,7 +18,7 @@ class StockAnalyzer:
|
||||
load_dotenv()
|
||||
|
||||
# 设置 Gemini API
|
||||
self.gemini_api_url = "https://api.xxx.xxx"
|
||||
self.gemini_api_url = os.getenv('GEMINI_API_URL')
|
||||
self.gemini_api_key = os.getenv('GEMINI_API_KEY')
|
||||
|
||||
# 配置参数
|
||||
@@ -31,7 +31,14 @@ class StockAnalyzer:
|
||||
'atr_period': 14
|
||||
}
|
||||
|
||||
def get_stock_data(self, stock_code, start_date=None, end_date=None):
|
||||
# 添加市场类型枚举
|
||||
self.MARKET_TYPES = {
|
||||
'A': 'A股',
|
||||
'HK': '港股',
|
||||
'CRYPTO': '加密货币'
|
||||
}
|
||||
|
||||
def get_stock_data(self, stock_code, market_type='A', start_date=None, end_date=None, ):
|
||||
"""获取股票数据"""
|
||||
import akshare as ak
|
||||
|
||||
@@ -41,11 +48,26 @@ class StockAnalyzer:
|
||||
end_date = datetime.now().strftime('%Y%m%d')
|
||||
|
||||
try:
|
||||
# 使用 akshare 获取股票数据
|
||||
df = ak.stock_zh_a_hist(symbol=stock_code,
|
||||
# 根据市场类型获取数据
|
||||
if market_type == 'A':
|
||||
df = ak.stock_zh_a_hist(
|
||||
symbol=stock_code,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
adjust="qfq")
|
||||
adjust="qfq"
|
||||
)
|
||||
# A股数据列名映射
|
||||
elif market_type == 'HK':
|
||||
df = ak.stock_hk_daily(
|
||||
symbol=stock_code,
|
||||
adjust="qfq"
|
||||
)
|
||||
elif market_type == 'CRYPTO':
|
||||
df = ak.crypto_js_spot(
|
||||
symbol=stock_code
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"不支持的市场类型: {market_type}")
|
||||
|
||||
# 重命名列名以匹配分析需求
|
||||
df = df.rename(columns={
|
||||
@@ -225,7 +247,7 @@ class StockAnalyzer:
|
||||
}
|
||||
|
||||
data = {
|
||||
"model": "gemini-1.5-flash",
|
||||
"model": os.getenv('GEMINI_API_MODEL'),
|
||||
"messages": [{"role": "user", "content": prompt}]
|
||||
}
|
||||
|
||||
@@ -233,8 +255,11 @@ class StockAnalyzer:
|
||||
f"{self.gemini_api_url}/v1/chat/completions",
|
||||
headers=headers,
|
||||
json=data,
|
||||
timeout=10
|
||||
timeout=30
|
||||
)
|
||||
print(headers)
|
||||
print(data)
|
||||
print(response.json())
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()['choices'][0]['message']['content']
|
||||
@@ -258,11 +283,11 @@ class StockAnalyzer:
|
||||
else:
|
||||
return '强烈建议卖出'
|
||||
|
||||
def analyze_stock(self, stock_code):
|
||||
def analyze_stock(self, stock_code, market_type='A'):
|
||||
"""分析单个股票"""
|
||||
try:
|
||||
# 获取股票数据
|
||||
df = self.get_stock_data(stock_code)
|
||||
df = self.get_stock_data(stock_code, market_type)
|
||||
|
||||
# 计算技术指标
|
||||
df = self.calculate_indicators(df)
|
||||
@@ -295,13 +320,13 @@ class StockAnalyzer:
|
||||
self.logger.error(f"分析股票时出错: {str(e)}")
|
||||
raise
|
||||
|
||||
def scan_market(self, stock_list, min_score=60):
|
||||
def scan_market(self, stock_list, min_score=60, market_type='A'):
|
||||
"""扫描市场,寻找符合条件的股票"""
|
||||
recommendations = []
|
||||
|
||||
for stock_code in stock_list:
|
||||
try:
|
||||
report = self.analyze_stock(stock_code)
|
||||
report = self.analyze_stock(stock_code, market_type)
|
||||
if report['score'] >= min_score:
|
||||
recommendations.append(report)
|
||||
except Exception as e:
|
||||
|
||||
46
web_server.py
Normal file
46
web_server.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from flask import Flask, render_template, request, jsonify
|
||||
from stock_analyzer import StockAnalyzer
|
||||
import threading
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
import traceback
|
||||
|
||||
app = Flask(__name__)
|
||||
analyzer = StockAnalyzer()
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
handler = RotatingFileHandler('flask_app.log', maxBytes=10000000, backupCount=5)
|
||||
handler.setFormatter(logging.Formatter(
|
||||
'[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
|
||||
))
|
||||
app.logger.addHandler(handler)
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/analyze', methods=['POST'])
|
||||
def analyze():
|
||||
try:
|
||||
data = request.json
|
||||
stock_codes = data.get('stock_codes', [])
|
||||
market_type = data.get('market_type', 'A')
|
||||
|
||||
if not stock_codes:
|
||||
return jsonify({'error': '请输入代码'}), 400
|
||||
|
||||
results = []
|
||||
for stock_code in stock_codes:
|
||||
result = analyzer.analyze_stock(stock_code.strip(), market_type)
|
||||
results.append(result)
|
||||
|
||||
return jsonify({'results': results})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 将 host 设置为 '0.0.0.0' 使其支持所有网络接口访问
|
||||
app.run(host='0.0.0.0', port=8888, debug=True)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user