Daily Tech Losers Scan #55
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
| # ────────────────────────────────────────────────────────────── | |
| # Daily Tech Losers Scan | |
| # Scans US Tech stocks for ≥5% daily losses, emails a report. | |
| # Runs Tue–Sat at 00:00 UTC (8:00 AM SGT — after US market close). | |
| # ────────────────────────────────────────────────────────────── | |
| name: Daily Tech Losers Scan | |
| on: | |
| # Manual trigger from Actions tab | |
| workflow_dispatch: | |
| # Automated daily schedule (must be on default branch to fire) | |
| schedule: | |
| # 00:00 UTC = 08:00 SGT — well after US market close | |
| - cron: "0 0 * * 2-6" # Tue–Sat SGT (Mon–Fri US) | |
| # Cancel any in-progress run if a new one triggers (idempotency guard) | |
| concurrency: | |
| group: tech-losers-scan | |
| cancel-in-progress: true | |
| jobs: | |
| scan: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| # ── All secrets & config as env vars (job level) ────────── | |
| env: | |
| # Secrets (stored in Settings → Secrets → Actions) | |
| FMP_API_KEY: ${{ secrets.FMP_API_KEY }} | |
| TWELVE_DATA_API_KEYS_CSV: ${{ secrets.TWELVE_DATA_API_KEYS_CSV }} | |
| TWELVE_DATA_API_KEY: ${{ secrets.TWELVE_DATA_API_KEY }} | |
| GMAIL_SENDER: ${{ secrets.GMAIL_SENDER }} | |
| EMAIL_RECIPIENTS: ${{ secrets.EMAIL_RECIPIENTS }} | |
| SMTP_USER: ${{ secrets.SMTP_USER }} | |
| SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }} | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| # Non-secret config (safe to hardcode) | |
| SMTP_HOST: smtp.gmail.com | |
| SMTP_PORT: "587" | |
| SMTP_USE_TLS: "true" | |
| NVIDIA_MODEL: stockmark/stockmark-2-100b-instruct | |
| NVIDIA_API_BASE_URL: https://integrate.api.nvidia.com | |
| AI_SUMMARY_ENABLED: "true" | |
| AI_SUMMARY_MAX_TOKENS: "220" | |
| AI_SUMMARY_TEMPERATURE: "0.2" | |
| PROFILE_CACHE_DAYS: "7" | |
| TWELVE_DATA_DAILY_LIMIT: "800" | |
| TWELVE_DATA_REQUEST_TIMEOUT: "8" | |
| TWELVE_DATA_MAX_RETRIES: "2" | |
| TWELVE_DATA_WORKERS_PER_KEY: "2" | |
| INCLUDE_INDUSTRY_KEYWORDS: software | |
| EXTRA_SYMBOLS: IREN | |
| FORCE_INCLUDE_SYMBOLS: IREN | |
| steps: | |
| # ── 1. Checkout code ──────────────────────────────────── | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| # ── 2. Set up Python ──────────────────────────────────── | |
| - name: Set up Python 3.11 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| cache: pip # built-in pip caching | |
| # ── 3. Install dependencies ───────────────────────────── | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| # ── 4. Verify critical secrets are set ────────────────── | |
| - name: Verify secrets | |
| run: | | |
| missing=0 | |
| for secret_name in FMP_API_KEY SMTP_USER SMTP_PASSWORD GMAIL_SENDER EMAIL_RECIPIENTS; do | |
| val=$(eval echo "\$$secret_name") | |
| if [ -z "$val" ]; then | |
| echo "::error::Missing required secret: $secret_name" | |
| missing=1 | |
| else | |
| echo "✓ $secret_name is set" | |
| fi | |
| done | |
| if [ "$missing" -eq 1 ]; then | |
| echo "::error::One or more required secrets are missing. Go to Settings → Secrets → Actions to add them." | |
| exit 1 | |
| fi | |
| # ── 5. Create data directory ──────────────────────────── | |
| - name: Prepare data directory | |
| run: mkdir -p data | |
| # ── 6. Run the bot ────────────────────────────────────── | |
| - name: Run Tech Losers Scan | |
| run: python main.py --no-wait --scan-source td | |
| # ── 7. Upload report artifacts (optional, for debugging) ─ | |
| - name: Upload reports | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: scan-reports-${{ github.run_id }} | |
| path: data/ | |
| retention-days: 14 | |
| if-no-files-found: ignore |