From 9a3d87ed561454eec48496cef3f374c3b8f1ccaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=B0=E5=BF=97=E5=AE=8F?= Date: Sat, 1 Mar 2025 17:03:25 +0800 Subject: [PATCH] Add files via upload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 增加了港股支持 2. 补全Dockerfile 3. 支持HTML在线访问 --- Dockerfile | 27 ++++++++++++++++++++++++ requirements.txt | 5 +---- stock_analyzer.py | 53 ++++++++++++++++++++++++++++++++++------------- web_server.py | 46 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 18 deletions(-) create mode 100644 Dockerfile create mode 100644 web_server.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ed99415 --- /dev/null +++ b/Dockerfile @@ -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"] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 2f7f9d7..29743f8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 diff --git a/stock_analyzer.py b/stock_analyzer.py index db8dac1..f3fe3c4 100644 --- a/stock_analyzer.py +++ b/stock_analyzer.py @@ -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') # 配置参数 @@ -30,8 +30,15 @@ class StockAnalyzer: 'volume_ma_period': 20, 'atr_period': 14 } + + # 添加市场类型枚举 + self.MARKET_TYPES = { + 'A': 'A股', + 'HK': '港股', + 'CRYPTO': '加密货币' + } - def get_stock_data(self, stock_code, start_date=None, end_date=None): + 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, - start_date=start_date, - end_date=end_date, - adjust="qfq") + # 根据市场类型获取数据 + if market_type == 'A': + df = ak.stock_zh_a_hist( + symbol=stock_code, + start_date=start_date, + end_date=end_date, + 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,9 +255,12 @@ 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'] else: @@ -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: diff --git a/web_server.py b/web_server.py new file mode 100644 index 0000000..25ee564 --- /dev/null +++ b/web_server.py @@ -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) + + \ No newline at end of file