Merge branch 'main' into main #71
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: 每日股票分析 | ||
| on: | ||
| # 定时触发 - 每天北京时间 18:00 (UTC 10:00) | ||
| schedule: | ||
| - cron: '5 7 * * 1-5' # 周一到周五,UTC 10:00 = 北京时间 18:00 | ||
| # 手动触发 | ||
| workflow_dispatch: | ||
| inputs: | ||
| mode: | ||
| description: '运行模式' | ||
| required: true | ||
| default: 'full' | ||
| type: choice | ||
| options: | ||
| - full # 完整分析(股票+大盘) | ||
| - market-only # 仅大盘复盘 | ||
| - stocks-only # 仅股票分析 | ||
| force_run: | ||
| description: '强制运行(跳过交易日检查)' | ||
| required: false | ||
| default: false | ||
| type: boolean | ||
| # 并发控制:同一时间只运行一个分析任务 | ||
| concurrency: | ||
| group: stock-analysis | ||
| cancel-in-progress: false | ||
| jobs: | ||
| analyze: | ||
| runs-on: ubuntu-latest | ||
| # 添加超时限制,防止任务卡死 | ||
| timeout-minutes: ${{ fromJSON(vars.ANALYSIS_TIMEOUT_MINUTES || '30') }} | ||
| steps: | ||
| - name: 随机延迟(避免固定时间访问) | ||
| run: sleep $((RANDOM % 60)) # 随机延迟0-60秒启动 | ||
| - name: 检出代码 | ||
| uses: actions/checkout@v5 | ||
| - name: 设置 Python 环境 | ||
| uses: actions/setup-python@v6 | ||
| with: | ||
| python-version: '3.11' | ||
| cache: 'pip' | ||
| - name: 安装依赖 | ||
| run: | | ||
| pip install --upgrade pip | ||
| pip install -r requirements.txt | ||
| - name: 创建必要目录 | ||
| run: | | ||
| mkdir -p data logs reports | ||
| - name: 执行股票分析 | ||
| env: | ||
| # ========================================== | ||
| # AI 配置 | ||
| # ========================================== | ||
| # LITELLM_CONFIG | ||
| LITELLM_CONFIG: ${{ vars.LITELLM_CONFIG || secrets.LITELLM_CONFIG }} | ||
| LITELLM_CONFIG_YAML: ${{ vars.LITELLM_CONFIG_YAML || secrets.LITELLM_CONFIG_YAML }} | ||
| LITELLM_API_KEY: ${{ secrets.LITELLM_API_KEY }} | ||
| LITELLM_MODEL: ${{ vars.LITELLM_MODEL || secrets.LITELLM_MODEL }} | ||
| LITELLM_FALLBACK_MODELS: ${{ vars.LITELLM_FALLBACK_MODELS || secrets.LITELLM_FALLBACK_MODELS }} | ||
| AGENT_LITELLM_MODEL: ${{ vars.AGENT_LITELLM_MODEL || secrets.AGENT_LITELLM_MODEL }} | ||
| VISION_MODEL: ${{ vars.VISION_MODEL || secrets.VISION_MODEL }} | ||
| OPENAI_VISION_MODEL: ${{ vars.OPENAI_VISION_MODEL || secrets.OPENAI_VISION_MODEL }} | ||
| VISION_PROVIDER_PRIORITY: ${{ vars.VISION_PROVIDER_PRIORITY || secrets.VISION_PROVIDER_PRIORITY }} | ||
| LLM_TEMPERATURE: ${{ vars.LLM_TEMPERATURE || secrets.LLM_TEMPERATURE }} | ||
| LLM_CHANNELS: ${{ vars.LLM_CHANNELS || secrets.LLM_CHANNELS }} | ||
| # Gemini AI(主选) | ||
| GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} | ||
| GEMINI_API_KEYS: ${{ secrets.GEMINI_API_KEYS }} | ||
| GEMINI_MODEL: ${{ vars.GEMINI_MODEL || secrets.GEMINI_MODEL || 'gemini-3-flash-preview' }} | ||
| GEMINI_MODEL_FALLBACK: ${{ vars.GEMINI_MODEL_FALLBACK || secrets.GEMINI_MODEL_FALLBACK || 'gemini-2.5-flash' }} | ||
| GEMINI_REQUEST_DELAY: '3.0' | ||
| # AIHubMix(优先于 OPENAI_API_KEY,自动适配 base_url,推荐国内用户) | ||
| AIHUBMIX_KEY: ${{ secrets.AIHUBMIX_KEY }} | ||
| # OpenAI 兼容 API(备选) | ||
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | ||
| OPENAI_API_KEYS: ${{ secrets.OPENAI_API_KEYS }} | ||
| OPENAI_BASE_URL: ${{ vars.OPENAI_BASE_URL || secrets.OPENAI_BASE_URL }} | ||
| OPENAI_MODEL: ${{ vars.OPENAI_MODEL || secrets.OPENAI_MODEL }} | ||
| # 自定义 API: | ||
| LLM_CHANNELS: ${{ secrets.LLM_CHANNELS }} | ||
| LLM_MY_PROXY_API_KEY: ${{ secrets.LLM_MY_PROXY_API_KEY }} | ||
| LLM_MY_PROXY_BASE_URL: ${{ secrets.LLM_MY_PROXY_BASE_URL }} | ||
| LLM_MY_PROXY_MODELS: ${{ secrets.LLM_MY_PROXY_MODELS }} | ||
| LLM_MY_PROXY_PROTOCOL: ${{ secrets.LLM_MY_PROXY_PROTOCOL }} | ||
| # Anthropic / Claude | ||
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | ||
| ANTHROPIC_API_KEYS: ${{ secrets.ANTHROPIC_API_KEYS }} | ||
| ANTHROPIC_MODEL: ${{ vars.ANTHROPIC_MODEL || secrets.ANTHROPIC_MODEL || 'claude-3-5-sonnet-20241022' }} | ||
| # LLM 渠道模式(常见渠道名需显式映射,GitHub Actions 不会自动导入任意 Secret/Variable) | ||
| LLM_PRIMARY_PROTOCOL: ${{ vars.LLM_PRIMARY_PROTOCOL || secrets.LLM_PRIMARY_PROTOCOL }} | ||
| LLM_PRIMARY_BASE_URL: ${{ vars.LLM_PRIMARY_BASE_URL || secrets.LLM_PRIMARY_BASE_URL }} | ||
| LLM_PRIMARY_API_KEY: ${{ secrets.LLM_PRIMARY_API_KEY }} | ||
| LLM_PRIMARY_API_KEYS: ${{ secrets.LLM_PRIMARY_API_KEYS }} | ||
| LLM_PRIMARY_MODELS: ${{ vars.LLM_PRIMARY_MODELS || secrets.LLM_PRIMARY_MODELS }} | ||
| LLM_PRIMARY_ENABLED: ${{ vars.LLM_PRIMARY_ENABLED || secrets.LLM_PRIMARY_ENABLED }} | ||
| LLM_PRIMARY_EXTRA_HEADERS: ${{ vars.LLM_PRIMARY_EXTRA_HEADERS || secrets.LLM_PRIMARY_EXTRA_HEADERS }} | ||
| LLM_SECONDARY_PROTOCOL: ${{ vars.LLM_SECONDARY_PROTOCOL || secrets.LLM_SECONDARY_PROTOCOL }} | ||
| LLM_SECONDARY_BASE_URL: ${{ vars.LLM_SECONDARY_BASE_URL || secrets.LLM_SECONDARY_BASE_URL }} | ||
| LLM_SECONDARY_API_KEY: ${{ secrets.LLM_SECONDARY_API_KEY }} | ||
| LLM_SECONDARY_API_KEYS: ${{ secrets.LLM_SECONDARY_API_KEYS }} | ||
| LLM_SECONDARY_MODELS: ${{ vars.LLM_SECONDARY_MODELS || secrets.LLM_SECONDARY_MODELS }} | ||
| LLM_SECONDARY_ENABLED: ${{ vars.LLM_SECONDARY_ENABLED || secrets.LLM_SECONDARY_ENABLED }} | ||
| LLM_SECONDARY_EXTRA_HEADERS: ${{ vars.LLM_SECONDARY_EXTRA_HEADERS || secrets.LLM_SECONDARY_EXTRA_HEADERS }} | ||
| LLM_GEMINI_PROTOCOL: ${{ vars.LLM_GEMINI_PROTOCOL || secrets.LLM_GEMINI_PROTOCOL }} | ||
| LLM_GEMINI_BASE_URL: ${{ vars.LLM_GEMINI_BASE_URL || secrets.LLM_GEMINI_BASE_URL }} | ||
| LLM_GEMINI_API_KEY: ${{ secrets.LLM_GEMINI_API_KEY }} | ||
| LLM_GEMINI_API_KEYS: ${{ secrets.LLM_GEMINI_API_KEYS }} | ||
| LLM_GEMINI_MODELS: ${{ vars.LLM_GEMINI_MODELS || secrets.LLM_GEMINI_MODELS }} | ||
| LLM_GEMINI_ENABLED: ${{ vars.LLM_GEMINI_ENABLED || secrets.LLM_GEMINI_ENABLED }} | ||
| LLM_GEMINI_EXTRA_HEADERS: ${{ vars.LLM_GEMINI_EXTRA_HEADERS || secrets.LLM_GEMINI_EXTRA_HEADERS }} | ||
| LLM_DEEPSEEK_PROTOCOL: ${{ vars.LLM_DEEPSEEK_PROTOCOL || secrets.LLM_DEEPSEEK_PROTOCOL }} | ||
| LLM_DEEPSEEK_BASE_URL: ${{ vars.LLM_DEEPSEEK_BASE_URL || secrets.LLM_DEEPSEEK_BASE_URL }} | ||
| LLM_DEEPSEEK_API_KEY: ${{ secrets.LLM_DEEPSEEK_API_KEY }} | ||
| LLM_DEEPSEEK_API_KEYS: ${{ secrets.LLM_DEEPSEEK_API_KEYS }} | ||
| LLM_DEEPSEEK_MODELS: ${{ vars.LLM_DEEPSEEK_MODELS || secrets.LLM_DEEPSEEK_MODELS }} | ||
| LLM_DEEPSEEK_ENABLED: ${{ vars.LLM_DEEPSEEK_ENABLED || secrets.LLM_DEEPSEEK_ENABLED }} | ||
| LLM_DEEPSEEK_EXTRA_HEADERS: ${{ vars.LLM_DEEPSEEK_EXTRA_HEADERS || secrets.LLM_DEEPSEEK_EXTRA_HEADERS }} | ||
| LLM_AIHUBMIX_PROTOCOL: ${{ vars.LLM_AIHUBMIX_PROTOCOL || secrets.LLM_AIHUBMIX_PROTOCOL }} | ||
| LLM_AIHUBMIX_BASE_URL: ${{ vars.LLM_AIHUBMIX_BASE_URL || secrets.LLM_AIHUBMIX_BASE_URL }} | ||
| LLM_AIHUBMIX_API_KEY: ${{ secrets.LLM_AIHUBMIX_API_KEY }} | ||
| LLM_AIHUBMIX_API_KEYS: ${{ secrets.LLM_AIHUBMIX_API_KEYS }} | ||
| LLM_AIHUBMIX_MODELS: ${{ vars.LLM_AIHUBMIX_MODELS || secrets.LLM_AIHUBMIX_MODELS }} | ||
| LLM_AIHUBMIX_ENABLED: ${{ vars.LLM_AIHUBMIX_ENABLED || secrets.LLM_AIHUBMIX_ENABLED }} | ||
| LLM_AIHUBMIX_EXTRA_HEADERS: ${{ vars.LLM_AIHUBMIX_EXTRA_HEADERS || secrets.LLM_AIHUBMIX_EXTRA_HEADERS }} | ||
| LLM_ANSPIRE_PROTOCOL: ${{ vars.LLM_ANSPIRE_PROTOCOL || secrets.LLM_ANSPIRE_PROTOCOL }} | ||
| LLM_ANSPIRE_BASE_URL: ${{ vars.LLM_ANSPIRE_BASE_URL || secrets.LLM_ANSPIRE_BASE_URL }} | ||
| LLM_ANSPIRE_API_KEY: ${{ secrets.LLM_ANSPIRE_API_KEY }} | ||
| LLM_ANSPIRE_API_KEYS: ${{ secrets.LLM_ANSPIRE_API_KEYS }} | ||
| LLM_ANSPIRE_MODELS: ${{ vars.LLM_ANSPIRE_MODELS || secrets.LLM_ANSPIRE_MODELS }} | ||
| LLM_ANSPIRE_ENABLED: ${{ vars.LLM_ANSPIRE_ENABLED || secrets.LLM_ANSPIRE_ENABLED }} | ||
| LLM_ANSPIRE_EXTRA_HEADERS: ${{ vars.LLM_ANSPIRE_EXTRA_HEADERS || secrets.LLM_ANSPIRE_EXTRA_HEADERS }} | ||
| LLM_OPENAI_PROTOCOL: ${{ vars.LLM_OPENAI_PROTOCOL || secrets.LLM_OPENAI_PROTOCOL }} | ||
| LLM_OPENAI_BASE_URL: ${{ vars.LLM_OPENAI_BASE_URL || secrets.LLM_OPENAI_BASE_URL }} | ||
| LLM_OPENAI_API_KEY: ${{ secrets.LLM_OPENAI_API_KEY }} | ||
| LLM_OPENAI_API_KEYS: ${{ secrets.LLM_OPENAI_API_KEYS }} | ||
| LLM_OPENAI_MODELS: ${{ vars.LLM_OPENAI_MODELS || secrets.LLM_OPENAI_MODELS }} | ||
| LLM_OPENAI_ENABLED: ${{ vars.LLM_OPENAI_ENABLED || secrets.LLM_OPENAI_ENABLED }} | ||
| LLM_OPENAI_EXTRA_HEADERS: ${{ vars.LLM_OPENAI_EXTRA_HEADERS || secrets.LLM_OPENAI_EXTRA_HEADERS }} | ||
| LLM_ANTHROPIC_PROTOCOL: ${{ vars.LLM_ANTHROPIC_PROTOCOL || secrets.LLM_ANTHROPIC_PROTOCOL }} | ||
| LLM_ANTHROPIC_BASE_URL: ${{ vars.LLM_ANTHROPIC_BASE_URL || secrets.LLM_ANTHROPIC_BASE_URL }} | ||
| LLM_ANTHROPIC_API_KEY: ${{ secrets.LLM_ANTHROPIC_API_KEY }} | ||
| LLM_ANTHROPIC_API_KEYS: ${{ secrets.LLM_ANTHROPIC_API_KEYS }} | ||
| LLM_ANTHROPIC_MODELS: ${{ vars.LLM_ANTHROPIC_MODELS || secrets.LLM_ANTHROPIC_MODELS }} | ||
| LLM_ANTHROPIC_ENABLED: ${{ vars.LLM_ANTHROPIC_ENABLED || secrets.LLM_ANTHROPIC_ENABLED }} | ||
| LLM_ANTHROPIC_EXTRA_HEADERS: ${{ vars.LLM_ANTHROPIC_EXTRA_HEADERS || secrets.LLM_ANTHROPIC_EXTRA_HEADERS }} | ||
| LLM_MOONSHOT_PROTOCOL: ${{ vars.LLM_MOONSHOT_PROTOCOL || secrets.LLM_MOONSHOT_PROTOCOL }} | ||
| LLM_MOONSHOT_BASE_URL: ${{ vars.LLM_MOONSHOT_BASE_URL || secrets.LLM_MOONSHOT_BASE_URL }} | ||
| LLM_MOONSHOT_API_KEY: ${{ secrets.LLM_MOONSHOT_API_KEY }} | ||
| LLM_MOONSHOT_API_KEYS: ${{ secrets.LLM_MOONSHOT_API_KEYS }} | ||
| LLM_MOONSHOT_MODELS: ${{ vars.LLM_MOONSHOT_MODELS || secrets.LLM_MOONSHOT_MODELS }} | ||
| LLM_MOONSHOT_ENABLED: ${{ vars.LLM_MOONSHOT_ENABLED || secrets.LLM_MOONSHOT_ENABLED }} | ||
| LLM_MOONSHOT_EXTRA_HEADERS: ${{ vars.LLM_MOONSHOT_EXTRA_HEADERS || secrets.LLM_MOONSHOT_EXTRA_HEADERS }} | ||
| LLM_DASHSCOPE_PROTOCOL: ${{ vars.LLM_DASHSCOPE_PROTOCOL || secrets.LLM_DASHSCOPE_PROTOCOL }} | ||
| LLM_DASHSCOPE_BASE_URL: ${{ vars.LLM_DASHSCOPE_BASE_URL || secrets.LLM_DASHSCOPE_BASE_URL }} | ||
| LLM_DASHSCOPE_API_KEY: ${{ secrets.LLM_DASHSCOPE_API_KEY }} | ||
| LLM_DASHSCOPE_API_KEYS: ${{ secrets.LLM_DASHSCOPE_API_KEYS }} | ||
| LLM_DASHSCOPE_MODELS: ${{ vars.LLM_DASHSCOPE_MODELS || secrets.LLM_DASHSCOPE_MODELS }} | ||
| LLM_DASHSCOPE_ENABLED: ${{ vars.LLM_DASHSCOPE_ENABLED || secrets.LLM_DASHSCOPE_ENABLED }} | ||
| LLM_DASHSCOPE_EXTRA_HEADERS: ${{ vars.LLM_DASHSCOPE_EXTRA_HEADERS || secrets.LLM_DASHSCOPE_EXTRA_HEADERS }} | ||
| LLM_ZHIPU_PROTOCOL: ${{ vars.LLM_ZHIPU_PROTOCOL || secrets.LLM_ZHIPU_PROTOCOL }} | ||
| LLM_ZHIPU_BASE_URL: ${{ vars.LLM_ZHIPU_BASE_URL || secrets.LLM_ZHIPU_BASE_URL }} | ||
| LLM_ZHIPU_API_KEY: ${{ secrets.LLM_ZHIPU_API_KEY }} | ||
| LLM_ZHIPU_API_KEYS: ${{ secrets.LLM_ZHIPU_API_KEYS }} | ||
| LLM_ZHIPU_MODELS: ${{ vars.LLM_ZHIPU_MODELS || secrets.LLM_ZHIPU_MODELS }} | ||
| LLM_ZHIPU_ENABLED: ${{ vars.LLM_ZHIPU_ENABLED || secrets.LLM_ZHIPU_ENABLED }} | ||
| LLM_ZHIPU_EXTRA_HEADERS: ${{ vars.LLM_ZHIPU_EXTRA_HEADERS || secrets.LLM_ZHIPU_EXTRA_HEADERS }} | ||
| LLM_MINIMAX_PROTOCOL: ${{ vars.LLM_MINIMAX_PROTOCOL || secrets.LLM_MINIMAX_PROTOCOL }} | ||
| LLM_MINIMAX_BASE_URL: ${{ vars.LLM_MINIMAX_BASE_URL || secrets.LLM_MINIMAX_BASE_URL }} | ||
| LLM_MINIMAX_API_KEY: ${{ secrets.LLM_MINIMAX_API_KEY }} | ||
| LLM_MINIMAX_API_KEYS: ${{ secrets.LLM_MINIMAX_API_KEYS }} | ||
| LLM_MINIMAX_MODELS: ${{ vars.LLM_MINIMAX_MODELS || secrets.LLM_MINIMAX_MODELS }} | ||
| LLM_MINIMAX_ENABLED: ${{ vars.LLM_MINIMAX_ENABLED || secrets.LLM_MINIMAX_ENABLED }} | ||
| LLM_MINIMAX_EXTRA_HEADERS: ${{ vars.LLM_MINIMAX_EXTRA_HEADERS || secrets.LLM_MINIMAX_EXTRA_HEADERS }} | ||
| LLM_VOLCENGINE_PROTOCOL: ${{ vars.LLM_VOLCENGINE_PROTOCOL || secrets.LLM_VOLCENGINE_PROTOCOL }} | ||
| LLM_VOLCENGINE_BASE_URL: ${{ vars.LLM_VOLCENGINE_BASE_URL || secrets.LLM_VOLCENGINE_BASE_URL }} | ||
| LLM_VOLCENGINE_API_KEY: ${{ secrets.LLM_VOLCENGINE_API_KEY }} | ||
| LLM_VOLCENGINE_API_KEYS: ${{ secrets.LLM_VOLCENGINE_API_KEYS }} | ||
| LLM_VOLCENGINE_MODELS: ${{ vars.LLM_VOLCENGINE_MODELS || secrets.LLM_VOLCENGINE_MODELS }} | ||
| LLM_VOLCENGINE_ENABLED: ${{ vars.LLM_VOLCENGINE_ENABLED || secrets.LLM_VOLCENGINE_ENABLED }} | ||
| LLM_VOLCENGINE_EXTRA_HEADERS: ${{ vars.LLM_VOLCENGINE_EXTRA_HEADERS || secrets.LLM_VOLCENGINE_EXTRA_HEADERS }} | ||
| LLM_SILICONFLOW_PROTOCOL: ${{ vars.LLM_SILICONFLOW_PROTOCOL || secrets.LLM_SILICONFLOW_PROTOCOL }} | ||
| LLM_SILICONFLOW_BASE_URL: ${{ vars.LLM_SILICONFLOW_BASE_URL || secrets.LLM_SILICONFLOW_BASE_URL }} | ||
| LLM_SILICONFLOW_API_KEY: ${{ secrets.LLM_SILICONFLOW_API_KEY }} | ||
| LLM_SILICONFLOW_API_KEYS: ${{ secrets.LLM_SILICONFLOW_API_KEYS }} | ||
| LLM_SILICONFLOW_MODELS: ${{ vars.LLM_SILICONFLOW_MODELS || secrets.LLM_SILICONFLOW_MODELS }} | ||
| LLM_SILICONFLOW_ENABLED: ${{ vars.LLM_SILICONFLOW_ENABLED || secrets.LLM_SILICONFLOW_ENABLED }} | ||
| LLM_SILICONFLOW_EXTRA_HEADERS: ${{ vars.LLM_SILICONFLOW_EXTRA_HEADERS || secrets.LLM_SILICONFLOW_EXTRA_HEADERS }} | ||
| LLM_OPENROUTER_PROTOCOL: ${{ vars.LLM_OPENROUTER_PROTOCOL || secrets.LLM_OPENROUTER_PROTOCOL }} | ||
| LLM_OPENROUTER_BASE_URL: ${{ vars.LLM_OPENROUTER_BASE_URL || secrets.LLM_OPENROUTER_BASE_URL }} | ||
| LLM_OPENROUTER_API_KEY: ${{ secrets.LLM_OPENROUTER_API_KEY }} | ||
| LLM_OPENROUTER_API_KEYS: ${{ secrets.LLM_OPENROUTER_API_KEYS }} | ||
| LLM_OPENROUTER_MODELS: ${{ vars.LLM_OPENROUTER_MODELS || secrets.LLM_OPENROUTER_MODELS }} | ||
| LLM_OPENROUTER_ENABLED: ${{ vars.LLM_OPENROUTER_ENABLED || secrets.LLM_OPENROUTER_ENABLED }} | ||
| LLM_OPENROUTER_EXTRA_HEADERS: ${{ vars.LLM_OPENROUTER_EXTRA_HEADERS || secrets.LLM_OPENROUTER_EXTRA_HEADERS }} | ||
| LLM_OLLAMA_PROTOCOL: ${{ vars.LLM_OLLAMA_PROTOCOL || secrets.LLM_OLLAMA_PROTOCOL }} | ||
| LLM_OLLAMA_BASE_URL: ${{ vars.LLM_OLLAMA_BASE_URL || secrets.LLM_OLLAMA_BASE_URL }} | ||
| LLM_OLLAMA_API_KEY: ${{ secrets.LLM_OLLAMA_API_KEY }} | ||
| LLM_OLLAMA_API_KEYS: ${{ secrets.LLM_OLLAMA_API_KEYS }} | ||
| LLM_OLLAMA_MODELS: ${{ vars.LLM_OLLAMA_MODELS || secrets.LLM_OLLAMA_MODELS }} | ||
| LLM_OLLAMA_ENABLED: ${{ vars.LLM_OLLAMA_ENABLED || secrets.LLM_OLLAMA_ENABLED }} | ||
| LLM_OLLAMA_EXTRA_HEADERS: ${{ vars.LLM_OLLAMA_EXTRA_HEADERS || secrets.LLM_OLLAMA_EXTRA_HEADERS }} | ||
| # ========================================== | ||
| # 数据源 | ||
| # ========================================== | ||
| TUSHARE_TOKEN: ${{ secrets.TUSHARE_TOKEN }} | ||
| # Longbridge OpenAPI(美股/港股实时行情字段补充;不设则仅 YFinance/AkShare,无 [Longbridge] 调用) | ||
| LONGBRIDGE_APP_KEY: ${{ secrets.LONGBRIDGE_APP_KEY }} | ||
| LONGBRIDGE_APP_SECRET: ${{ secrets.LONGBRIDGE_APP_SECRET }} | ||
| LONGBRIDGE_ACCESS_TOKEN: ${{ secrets.LONGBRIDGE_ACCESS_TOKEN }} | ||
| LONGBRIDGE_REGION: ${{ vars.LONGBRIDGE_REGION || secrets.LONGBRIDGE_REGION }} | ||
| LONGBRIDGE_HTTP_URL: ${{ vars.LONGBRIDGE_HTTP_URL || secrets.LONGBRIDGE_HTTP_URL }} | ||
| LONGBRIDGE_QUOTE_WS_URL: ${{ vars.LONGBRIDGE_QUOTE_WS_URL || secrets.LONGBRIDGE_QUOTE_WS_URL }} | ||
| LONGBRIDGE_TRADE_WS_URL: ${{ vars.LONGBRIDGE_TRADE_WS_URL || secrets.LONGBRIDGE_TRADE_WS_URL }} | ||
| LONGBRIDGE_STATIC_INFO_TTL_SECONDS: ${{ vars.LONGBRIDGE_STATIC_INFO_TTL_SECONDS || secrets.LONGBRIDGE_STATIC_INFO_TTL_SECONDS }} | ||
| LONGBRIDGE_ENABLE_OVERNIGHT: ${{ vars.LONGBRIDGE_ENABLE_OVERNIGHT || secrets.LONGBRIDGE_ENABLE_OVERNIGHT }} | ||
| LONGBRIDGE_PUSH_CANDLESTICK_MODE: ${{ vars.LONGBRIDGE_PUSH_CANDLESTICK_MODE || secrets.LONGBRIDGE_PUSH_CANDLESTICK_MODE }} | ||
| LONGBRIDGE_PRINT_QUOTE_PACKAGES: ${{ vars.LONGBRIDGE_PRINT_QUOTE_PACKAGES || secrets.LONGBRIDGE_PRINT_QUOTE_PACKAGES }} | ||
| # ========================================== | ||
| # 搜索服务 | ||
| # ========================================== | ||
| ANSPIRE_API_KEYS: ${{ secrets.ANSPIRE_API_KEYS }} | ||
| ANSPIRE_LLM_MODEL: ${{ vars.ANSPIRE_LLM_MODEL || secrets.ANSPIRE_LLM_MODEL }} | ||
| ANSPIRE_LLM_BASE_URL: ${{ vars.ANSPIRE_LLM_BASE_URL || secrets.ANSPIRE_LLM_BASE_URL }} | ||
| ANSPIRE_LLM_ENABLED: ${{ vars.ANSPIRE_LLM_ENABLED || secrets.ANSPIRE_LLM_ENABLED }} | ||
| BOCHA_API_KEYS: ${{ secrets.BOCHA_API_KEYS }} | ||
| TAVILY_API_KEYS: ${{ secrets.TAVILY_API_KEYS }} | ||
| SERPAPI_API_KEYS: ${{ secrets.SERPAPI_API_KEYS }} | ||
| MINIMAX_API_KEYS: ${{ secrets.MINIMAX_API_KEYS }} | ||
| BRAVE_API_KEYS: ${{ secrets.BRAVE_API_KEYS }} | ||
| SEARXNG_BASE_URLS: ${{ secrets.SEARXNG_BASE_URLS }} | ||
| SEARXNG_PUBLIC_INSTANCES_ENABLED: ${{ vars.SEARXNG_PUBLIC_INSTANCES_ENABLED || secrets.SEARXNG_PUBLIC_INSTANCES_ENABLED }} | ||
| # ========================================== | ||
| # 通知渠道(可同时配置多个,全部推送) | ||
| # ========================================== | ||
| # 方式一:企业微信 Webhook | ||
| WECHAT_WEBHOOK_URL: ${{ secrets.WECHAT_WEBHOOK_URL }} | ||
| WECHAT_MSG_TYPE: ${{ vars.WECHAT_MSG_TYPE || secrets.WECHAT_MSG_TYPE || 'markdown' }} | ||
| # 方式二:飞书 Webhook | ||
| FEISHU_WEBHOOK_URL: ${{ secrets.FEISHU_WEBHOOK_URL }} | ||
| FEISHU_WEBHOOK_SECRET: ${{ secrets.FEISHU_WEBHOOK_SECRET }} | ||
| FEISHU_WEBHOOK_KEYWORD: ${{ vars.FEISHU_WEBHOOK_KEYWORD || secrets.FEISHU_WEBHOOK_KEYWORD }} | ||
| # 方式三:Telegram | ||
| TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} | ||
| TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} | ||
| TELEGRAM_MESSAGE_THREAD_ID: ${{ secrets.TELEGRAM_MESSAGE_THREAD_ID }} | ||
| # 方式四:邮件 | ||
| EMAIL_SENDER: ${{ vars.EMAIL_SENDER || secrets.EMAIL_SENDER }} | ||
| EMAIL_PASSWORD: ${{ secrets.EMAIL_PASSWORD }} | ||
| EMAIL_RECEIVERS: ${{ vars.EMAIL_RECEIVERS || secrets.EMAIL_RECEIVERS }} | ||
| EMAIL_SENDER_NAME: ${{ vars.EMAIL_SENDER_NAME || secrets.EMAIL_SENDER_NAME || 'daily_stock_analysis股票分析助手' }} | ||
| # 方式五:Pushover | ||
| PUSHOVER_USER_KEY: ${{ secrets.PUSHOVER_USER_KEY }} | ||
| PUSHOVER_API_TOKEN: ${{ secrets.PUSHOVER_API_TOKEN }} | ||
| # 方式六:PushPlus ⬅️ 新增! | ||
| PUSHPLUS_TOKEN: ${{ secrets.PUSHPLUS_TOKEN }} | ||
| PUSHPLUS_TOPIC: ${{ vars.PUSHPLUS_TOPIC || secrets.PUSHPLUS_TOPIC }} | ||
| # 方式七:自定义 Webhook(钉钉、Bark、自建服务等) | ||
| CUSTOM_WEBHOOK_URLS: ${{ secrets.CUSTOM_WEBHOOK_URLS }} | ||
| CUSTOM_WEBHOOK_BEARER_TOKEN: ${{ secrets.CUSTOM_WEBHOOK_BEARER_TOKEN }} | ||
| CUSTOM_WEBHOOK_BODY_TEMPLATE: ${{ vars.CUSTOM_WEBHOOK_BODY_TEMPLATE || secrets.CUSTOM_WEBHOOK_BODY_TEMPLATE }} | ||
| WEBHOOK_VERIFY_SSL: ${{ vars.WEBHOOK_VERIFY_SSL || secrets.WEBHOOK_VERIFY_SSL || 'true' }} | ||
| # 方式八:Discord | ||
| DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} | ||
| DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }} | ||
| DISCORD_MAIN_CHANNEL_ID: ${{ secrets.DISCORD_MAIN_CHANNEL_ID }} | ||
| # 方式九:飞书云文档 | ||
| FEISHU_APP_ID: ${{ secrets.FEISHU_APP_ID }} | ||
| FEISHU_APP_SECRET: ${{ secrets.FEISHU_APP_SECRET }} | ||
| FEISHU_FOLDER_TOKEN: ${{ secrets.FEISHU_FOLDER_TOKEN }} | ||
| # 方式十:AstrBot | ||
| ASTRBOT_URL: ${{ secrets.ASTRBOT_URL }} | ||
| ASTRBOT_TOKEN: ${{ secrets.ASTRBOT_TOKEN }} | ||
| # 方式十一:Server酱3 | ||
| SERVERCHAN3_SENDKEY: ${{ secrets.SERVERCHAN3_SENDKEY }} | ||
| # 方式十二:Slack | ||
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} | ||
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | ||
| SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} | ||
| # ========================================== | ||
| # 自选股配置 | ||
| # ========================================== | ||
| STOCK_LIST: ${{ vars.STOCK_LIST || secrets.STOCK_LIST || '600519' }} | ||
| MARKET_REVIEW_REGION: ${{ vars.MARKET_REVIEW_REGION || secrets.MARKET_REVIEW_REGION || 'cn' }} | ||
| # ========================================== | ||
| # 运行配置 ⬅️ 新增! | ||
| # ========================================== | ||
| REPORT_TYPE: ${{ vars.REPORT_TYPE || secrets.REPORT_TYPE || 'simple' }} | ||
| REPORT_LANGUAGE: ${{ vars.REPORT_LANGUAGE || secrets.REPORT_LANGUAGE || '' }} | ||
| SINGLE_STOCK_NOTIFY: ${{ vars.SINGLE_STOCK_NOTIFY || secrets.SINGLE_STOCK_NOTIFY || 'false' }} | ||
| MARKET_REVIEW_ENABLED: ${{ vars.MARKET_REVIEW_ENABLED || secrets.MARKET_REVIEW_ENABLED || 'true' }} | ||
| ANALYSIS_DELAY: ${{ vars.ANALYSIS_DELAY || secrets.ANALYSIS_DELAY || '0' }} | ||
| TRADING_DAY_CHECK_ENABLED: ${{ vars.TRADING_DAY_CHECK_ENABLED || secrets.TRADING_DAY_CHECK_ENABLED || 'true' }} | ||
| # ========================================== | ||
| # 系统配置 | ||
| # ========================================== | ||
| LOG_LEVEL: INFO | ||
| MAX_WORKERS: ${{ vars.MAX_WORKERS || secrets.MAX_WORKERS || '1' }} | ||
| # 筹码分布:默认 false(云端接口不稳定);需启用时在 Settings → Variables 添加 ENABLE_CHIP_DISTRIBUTION=true | ||
| ENABLE_CHIP_DISTRIBUTION: ${{ vars.ENABLE_CHIP_DISTRIBUTION || secrets.ENABLE_CHIP_DISTRIBUTION || 'false' }} | ||
| # 实时行情数据源优先级(腾讯有量比且稳定,推荐首选) | ||
| # 可选值: tencent, akshare_sina, efinance, akshare_em, tushare | ||
| # 如果有 Tushare Pro 高积分账号,可将 tushare 放在首位 | ||
| REALTIME_SOURCE_PRIORITY: ${{ vars.REALTIME_SOURCE_PRIORITY || 'tencent,akshare_sina,efinance,akshare_em' }} | ||
| run: | | ||
| # 处理 LITELLM YAML 配置文件 | ||
| if [ -n "$LITELLM_CONFIG_YAML" ] && [ -n "$LITELLM_CONFIG" ]; then | ||
| echo "📝 使用 GitHub Actions Secrets/Variables 中的 LITELLM_CONFIG_YAML 配置" | ||
| # 确保目录存在 | ||
| mkdir -p "$(dirname "$LITELLM_CONFIG")" | ||
| # 写入配置文件(覆盖已有文件) | ||
| echo "$LITELLM_CONFIG_YAML" > "$LITELLM_CONFIG" | ||
| echo "✅ LITELLM 配置文件已写入: $LITELLM_CONFIG" | ||
| fi | ||
| # 判断运行模式 | ||
| MODE="${{ github.event.inputs.mode || 'full' }}" | ||
| echo "==========================================" | ||
| echo "🚀 A股自选股智能分析系统" | ||
| echo "==========================================" | ||
| echo "⏰ 时间: $(TZ='Asia/Shanghai' date '+%Y-%m-%d %H:%M:%S')" | ||
| echo "🎯 运行模式: $MODE" | ||
| echo "📊 自选股: $STOCK_LIST" | ||
| echo "📝 报告类型: $REPORT_TYPE" | ||
| echo "" | ||
| echo "==========================================" | ||
| echo "📋 配置检查" | ||
| echo "==========================================" | ||
| echo "【AI 配置】" | ||
| if [ -n "$LITELLM_CONFIG" ] || [ -n "$LITELLM_API_KEY" ] || [ -n "$LITELLM_MODEL" ] || [ -n "$ANSPIRE_API_KEYS" ]; then | ||
| echo " LiteLLM: ✅ 已配置" | ||
| else | ||
| echo " LiteLLM: ❌ 未配置" | ||
| fi | ||
| echo " Gemini API Key: $([ -n "$GEMINI_API_KEY" ] && echo '✅ 已配置' || echo '❌ 未配置')" | ||
| echo " DeepSeek Key: $([ -n "$DEEPSEEK_API_KEY" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " Anspire Key: $([ -n "$ANSPIRE_API_KEYS" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " AIHubMix Key: $([ -n "$AIHUBMIX_KEY" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " OpenAI API Key: $([ -n "$OPENAI_API_KEY" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " Anthropic Key: $([ -n "$ANTHROPIC_API_KEY" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo "" | ||
| echo "【数据源】" | ||
| echo " Tushare Token: $([ -n "$TUSHARE_TOKEN" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " Longbridge: $([ -n "$LONGBRIDGE_APP_KEY" ] && [ -n "$LONGBRIDGE_APP_SECRET" ] && [ -n "$LONGBRIDGE_ACCESS_TOKEN" ] && echo '✅ 已配置(美股/港股行情补充)' || echo '⚪ 未配置(仅 YFinance 等,无长桥调用)')" | ||
| echo "" | ||
| echo "【搜索引擎】" | ||
| echo " Bocha API Keys: $([ -n "$BOCHA_API_KEYS" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " Tavily API Keys: $([ -n "$TAVILY_API_KEYS" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " SerpAPI Keys: $([ -n "$SERPAPI_API_KEYS" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " MiniMax API Keys: $([ -n "$MINIMAX_API_KEYS" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " Brave API Keys: $([ -n "$BRAVE_API_KEYS" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " SearXNG Base URLs: $([ -n "$SEARXNG_BASE_URLS" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| case "${SEARXNG_PUBLIC_INSTANCES_ENABLED,,}" in | ||
| 0|false|no|off) | ||
| echo " SearXNG Public Instances: ❌ 已禁用" | ||
| ;; | ||
| "") | ||
| echo " SearXNG Public Instances: ✅ 默认开启" | ||
| ;; | ||
| *) | ||
| echo " SearXNG Public Instances: ✅ 已启用" | ||
| ;; | ||
| esac | ||
| echo "" | ||
| echo "【通知渠道】" | ||
| echo " PushPlus: $([ -n "$PUSHPLUS_TOKEN" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " 企业微信: $([ -n "$WECHAT_WEBHOOK_URL" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " 飞书: $([ -n "$FEISHU_WEBHOOK_URL" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " Telegram: $([ -n "$TELEGRAM_BOT_TOKEN" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " Discord: $([ -n "$DISCORD_WEBHOOK_URL" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " AstrBot: $([ -n "$ASTRBOT_URL" ] && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo " Slack: $(([ -n "$SLACK_WEBHOOK_URL" ] || ([ -n "$SLACK_BOT_TOKEN" ] && [ -n "$SLACK_CHANNEL_ID" ])) && echo '✅ 已配置' || echo '⚪ 未配置')" | ||
| echo "==========================================" | ||
| echo "" | ||
| # 构建命令行参数 | ||
| FORCE_RUN_ARG="" | ||
| if [ "${{ github.event.inputs.force_run }}" = "true" ]; then | ||
| FORCE_RUN_ARG="--force-run" | ||
| echo "⚡ 已启用强制运行模式(跳过交易日检查)" | ||
| fi | ||
| # 执行分析 | ||
| if [ "$MODE" = "market-only" ]; then | ||
| python main.py --market-review $FORCE_RUN_ARG | ||
| elif [ "$MODE" = "stocks-only" ]; then | ||
| python main.py --no-market-review $FORCE_RUN_ARG | ||
| else | ||
| python main.py $FORCE_RUN_ARG | ||
| fi | ||
| - name: 上传分析报告 | ||
| uses: actions/upload-artifact@v6 | ||
| if: always() | ||
| with: | ||
| name: analysis-reports-${{ github.run_number }} | ||
| path: | | ||
| reports/ | ||
| logs/ | ||
| retention-days: 30 | ||
| - name: 显示运行结果 | ||
| if: always() | ||
| run: | | ||
| echo "" | ||
| echo "==========================================" | ||
| echo "📊 分析完成" | ||
| echo "==========================================" | ||
| if [ -d "reports" ] && [ "$(ls -A reports 2>/dev/null)" ]; then | ||
| echo "生成的报告:" | ||
| ls -la reports/ | ||
| else | ||
| echo "⚠️ 未生成报告文件" | ||
| fi | ||
| echo "" | ||
| if [ -f "logs/stock_analysis_$(date +%Y%m%d).log" ]; then | ||
| echo "📜 最近日志(最后 30 行):" | ||
| tail -30 logs/stock_analysis_*.log 2>/dev/null || echo "无日志" | ||
| fi | ||