crontoolModern Cron Expression Builder

Cron Job Stock Price Alert Every 5 Minutes

Poll a stock price or market API every 5 minutes during trading hours and fire a Slack alert when a threshold is crossed.

*/5 9-17 * * 1-5
Every 5 minutes, 09:00 AM–05:59 PM, Monday through Friday
Open in builder →

What this schedule does

The expression */5 9-17 * * 1-5 fires every 5 minutes during any hour from 9 to 17 (9am–5:59pm), Monday through Friday. Breaking it down: */5 means "every 5th minute" (0, 5, 10, …, 55), 9-17 restricts it to those hours, * * matches every day-of-month and month, and 1-5 limits it to weekdays.

This covers the standard US stock market session (9:30am–4pm ET) and gives you a comfortable buffer on either side. The job runs roughly 132 times per trading day — frequent enough to catch sudden moves, infrequent enough to stay well within free-tier API rate limits on most data providers.

When your script detects a price breach (above a target, below a stop, or outside a band), it sends a Slack webhook message. Outside these hours the cron never fires, so you avoid noisy alerts at 3am and unnecessary API calls on weekends.

What to monitor and how

Most retail-accessible market data APIs return JSON with a last-trade price field. Common free and low-cost options:

Your script should: fetch the current price, compare it to a stored threshold (env var or config file), and POST to a Slack incoming webhook URL if the condition is met. Keep it stateless — write a last_alerted timestamp to a file or environment variable if you want to suppress repeated alerts within a cooldown window.

Platform snippets

Python script (price_alert.py)
#!/usr/bin/env python3
"""
Stock price alert — runs every 5 min via cron.
Sends a Slack message when price crosses the configured threshold.

Env vars:
  TICKER          — stock symbol, e.g. AAPL
  THRESHOLD       — alert when price drops below this value
  FINNHUB_TOKEN   — API key from finnhub.io
  SLACK_WEBHOOK   — incoming webhook URL from Slack app settings
  COOLDOWN_FILE   — path to a file that tracks last alert time (optional)
"""
import os, sys, time, json, urllib.request

TICKER        = os.environ["TICKER"]
THRESHOLD     = float(os.environ["THRESHOLD"])
TOKEN         = os.environ["FINNHUB_TOKEN"]
WEBHOOK       = os.environ["SLACK_WEBHOOK"]
COOLDOWN_FILE = os.environ.get("COOLDOWN_FILE", "/tmp/price_alert_last.txt")
COOLDOWN_SECS = 900  # 15-minute cooldown between alerts

def get_price(ticker: str) -> float:
    url = f"https://finnhub.io/api/v1/quote?symbol={ticker}&token={TOKEN}"
    with urllib.request.urlopen(url, timeout=10) as r:
        data = json.loads(r.read())
    return float(data["c"])  # "c" = current price

def within_cooldown() -> bool:
    try:
        last = float(open(COOLDOWN_FILE).read().strip())
        return time.time() - last < COOLDOWN_SECS
    except (FileNotFoundError, ValueError):
        return False

def record_alert():
    with open(COOLDOWN_FILE, "w") as f:
        f.write(str(time.time()))

def send_slack(message: str):
    payload = json.dumps({"text": message}).encode()
    req = urllib.request.Request(WEBHOOK, data=payload,
                                  headers={"Content-Type": "application/json"})
    urllib.request.urlopen(req, timeout=10)

price = get_price(TICKER)
print(f"{TICKER} current price: ${price:.2f} (threshold: ${THRESHOLD:.2f})")

if price < THRESHOLD:
    if within_cooldown():
        print("Alert suppressed — within cooldown window.")
        sys.exit(0)
    msg = f":rotating_light: *{TICKER}* dropped to *${price:.2f}* (below threshold of ${THRESHOLD:.2f})"
    send_slack(msg)
    record_alert()
    print("Alert sent.")
else:
    print("No alert — price is above threshold.")
Standard crontab
# crontab -e
# Runs every 5 min during market hours Mon–Fri (adjust timezone with TZ= prefix)
*/5 9-17 * * 1-5    TICKER=AAPL THRESHOLD=170 FINNHUB_TOKEN=xxx SLACK_WEBHOOK=https://hooks.slack.com/... /usr/bin/python3 /opt/scripts/price_alert.py
GitHub Actions
on:
  schedule:
    - cron: '*/5 14-21 * * 1-5'   # UTC: 9am–5pm ET = 14:00–21:00 UTC

jobs:
  price-alert:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Check price and alert
        run: python scripts/price_alert.py
        env:
          TICKER: ${{ vars.TICKER }}
          THRESHOLD: ${{ vars.THRESHOLD }}
          FINNHUB_TOKEN: ${{ secrets.FINNHUB_TOKEN }}
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
AWS EventBridge
# EventBridge cron (6 fields — adds year, UTC)
# 9am–5pm ET = 14:00–22:00 UTC
cron(*/5 14-21 ? * MON-FRI *)
Kubernetes CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
  name: price-alert
spec:
  schedule: "*/5 9-17 * * 1-5"
  timeZone: "America/New_York"   # Kubernetes 1.27+
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: alert
            image: your-image:latest
            command: ["python", "price_alert.py"]
            env:
            - name: TICKER
              value: "AAPL"
            - name: THRESHOLD
              value: "170"
            - name: FINNHUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: market-secrets
                  key: finnhub-token
            - name: SLACK_WEBHOOK
              valueFrom:
                secretKeyRef:
                  name: market-secrets
                  key: slack-webhook
          restartPolicy: OnFailure

Timezone note

Standard crontab uses the system timezone. GitHub Actions and AWS EventBridge run in UTC. US Eastern time is UTC-5 (EST) or UTC-4 (EDT), so 9am–5pm ET translates to 14-21 or 13-21 UTC depending on daylight saving time. The safest approach is to run slightly wider hours in UTC (e.g., 13-22) so you never miss the open or close during DST transitions. Kubernetes 1.27+ supports spec.timeZone: "America/New_York", which handles DST automatically.

More guides

Morning brief Weekly metrics report Cache warm-up SSL certificate check