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 1/4] 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 From e1d59eeb7de279f287b72c0c192649332ed50b0f 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 21:06:20 +0800 Subject: [PATCH 2/4] Update .env MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新env --- .env | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.env b/.env index bbb209d..1664e9d 100644 --- a/.env +++ b/.env @@ -1 +1,3 @@ -GEMINI_API_KEY= xxxxxx +GEMINI_API_KEY= +GEMINI_API_URL= +GEMINI_API_MODEL= From ae5e16f85591fdf291b279116eeb9d04f61a28b8 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 21:09:58 +0800 Subject: [PATCH 3/4] Create docker-image.yml Create docker-image.yml --- .github/workflows/docker-image.yml | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..ca21532 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,32 @@ +name: Docker Build and Push + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/stock-scanner:latest + ${{ secrets.DOCKERHUB_USERNAME }}/stock-scanner:${{ github.sha }} From 452c9e491b43777a6c23476aff935cf0a8209902 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 21:28:03 +0800 Subject: [PATCH 4/4] Update requirements.txt Update requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 29743f8..a79b0b4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,6 +11,7 @@ tqdm==4.67.1 # 网络和API请求 requests==2.32.3 python-dotenv==1.0.1 +flask==3.1.0 # 日志和系统工具 loguru==0.7.2