Skip to content

Daily Trading Pipeline #214

Daily Trading Pipeline

Daily Trading Pipeline #214

Workflow file for this run

name: Daily Trading Pipeline
on:
schedule:
# US market hours — run twice daily Mon-Fri
# 6:45 AM PT = 14:45 UTC (PST, Nov-Mar) / 13:45 UTC (PDT, Mar-Nov)
# 12:45 PM PT = 20:45 UTC (PST) / 19:45 UTC (PDT)
# Using both UTC offsets to cover DST transitions
- cron: '45 13 * * 1-5' # 6:45 AM PDT
- cron: '45 14 * * 1-5' # 6:45 AM PST
- cron: '45 19 * * 1-5' # 12:45 PM PDT
- cron: '45 20 * * 1-5' # 12:45 PM PST
workflow_dispatch: # Allow manual trigger from GitHub UI
concurrency:
group: daily-trading
cancel-in-progress: false
permissions:
contents: write # Needed for commit-back of state files
env:
ALPACA_API_KEY: ${{ secrets.ALPACA_API_KEY }}
ALPACA_SECRET_KEY: ${{ secrets.ALPACA_SECRET_KEY }}
ALPACA_PAPER: ${{ secrets.ALPACA_PAPER || 'true' }}
FINNHUB_API_KEY: ${{ secrets.FINNHUB_API_KEY }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
SEC_EDGAR_USER_AGENT: ${{ secrets.SEC_EDGAR_USER_AGENT }}
jobs:
trade:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install Python and dependencies
run: uv sync
- name: Ensure data directory exists
run: mkdir -p data
- name: Restore state from trading-data branch
run: |
git fetch origin trading-data 2>/dev/null || true
git checkout origin/trading-data -- data/ 2>/dev/null || echo "No prior trading data"
# Git restores files at 0644; preflight requires 0600 on the state DB.
chmod 600 data/trading_state.db 2>/dev/null || true
- name: 0. Pre-flight Health Check
id: preflight
timeout-minutes: 2
run: uv run python scripts/preflight.py
- name: 1. Position Manager (manage exits)
id: position_manager
if: steps.preflight.outcome == 'success'
timeout-minutes: 10
run: uv run python scripts/position_manager.py --execute --check-sentiment
- name: 2. Screener Auto-Trade (biggest dips)
id: screener
if: steps.preflight.outcome == 'success' && steps.position_manager.outcome == 'success'
timeout-minutes: 10
run: uv run python scripts/screener_trade.py --min-dip -7.0 --max-trades 8 --amount 3000 --execute
continue-on-error: true
- name: 3. Watchlist Scan (3-layer analysis)
id: watchlist
if: steps.preflight.outcome == 'success' && steps.position_manager.outcome == 'success'
timeout-minutes: 10
run: uv run python scripts/scan_with_sentiment.py --execute
continue-on-error: true
- name: 4. Momentum Auto-Trade (trend following)
id: momentum
if: steps.preflight.outcome == 'success' && steps.position_manager.outcome == 'success'
timeout-minutes: 10
run: uv run python scripts/momentum_trade.py --min-momentum 10.0 --max-trades 5 --amount 3000 --execute
continue-on-error: true
- name: 5. Portfolio Summary
if: always()
timeout-minutes: 2
run: uv run python scripts/portfolio.py
continue-on-error: true
- name: 6. Performance Report
if: always()
timeout-minutes: 2
run: uv run python scripts/performance.py
continue-on-error: true
- name: 7. Strategy Agent (Event Check)
if: always()
timeout-minutes: 30
run: uv run python scripts/strategy_agent.py --mode event-check
continue-on-error: true
- name: Commit state to trading-data branch
if: always()
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Save data directory
cp -r data/ /tmp/trading-data-snapshot/
# Switch to trading-data branch (create if needed)
git fetch origin trading-data 2>/dev/null || true
git checkout trading-data 2>/dev/null || {
git checkout --orphan trading-data
git rm -rf . 2>/dev/null || true
}
# Clean and restore data
rm -rf data/
cp -r /tmp/trading-data-snapshot/ data/
git add data/
git diff --cached --quiet || git commit -m "chore: update trading state [$(date -u +%Y-%m-%dT%H:%M:%SZ)]"
git push origin trading-data
# Return to original branch for any subsequent steps
git checkout ${{ github.ref_name }} 2>/dev/null || true
- name: Alert on pipeline failures
if: always()
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
GH_REPO: ${{ github.repository }}
GH_RUN_ID: ${{ github.run_id }}
run: |
FAILED=""
[ "${{ steps.preflight.outcome }}" = "failure" ] && FAILED="${FAILED}preflight "
[ "${{ steps.position_manager.outcome }}" = "failure" ] && FAILED="${FAILED}position_manager "
[ "${{ steps.screener.outcome }}" = "failure" ] && FAILED="${FAILED}screener "
[ "${{ steps.watchlist.outcome }}" = "failure" ] && FAILED="${FAILED}watchlist "
[ "${{ steps.momentum.outcome }}" = "failure" ] && FAILED="${FAILED}momentum "
if [ -n "$FAILED" ] && [ -n "$TELEGRAM_BOT_TOKEN" ] && [ -n "$TELEGRAM_CHAT_ID" ]; then
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d chat_id="$TELEGRAM_CHAT_ID" \
-d text="GHA Pipeline Alert: Failed steps: ${FAILED}. Check: https://github.com/${GH_REPO}/actions/runs/${GH_RUN_ID}"
fi