#!/usr/bin/env bash # VPS wrapper for cron: download the latest trading day into a daily snapshot file, keep only # recent snapshots, and send an e-mail when something goes wrong. # # crontab (UTC; Taipei = UTC+8): # ALERT_TO=you@example.com # 30 6 * * 1-5 $HOME/stock/scripts/cron_daily.sh # 0 11 * * 1-5 $HOME/stock/scripts/cron_daily.sh # # Environment: STOCK_DIR (default ~/stock), KEEP_DAYS (default 30), ALERT_TO (e-mail address; # needs msmtp or mail). Snapshots go to $STOCK_DIR/updates/YYYY-MM-DD.csv, the log to # $STOCK_DIR/logs/daily.log. Failures: download failed after all retries, or no new data for 10 days. set -u root=${STOCK_DIR:-$HOME/stock} keep_days=${KEEP_DAYS:-30} updates="$root/updates"; logs="$root/logs"; log="$logs/daily.log" mkdir -p "$updates" "$logs" notify() { # SUBJECT BODY if [ -z "${ALERT_TO:-}" ]; then echo "ALERT_TO is not set; cannot send e-mail: $1" >> "$log"; return; fi if command -v msmtp >/dev/null; then printf 'To: %s\nSubject: %s\n\n%s\n' "$ALERT_TO" "$1" "$2" | msmtp "$ALERT_TO" || echo "msmtp failed" >> "$log" elif command -v mail >/dev/null; then printf '%s\n' "$2" | mail -s "$1" "$ALERT_TO" || echo "mail failed" >> "$log" else echo "no msmtp/mail command; cannot send e-mail: $1" >> "$log" fi } # One run at a time (mkdir is atomic); a lock older than 3 hours is treated as stale. lock="$root/.daily.lock" if ! mkdir "$lock" 2>/dev/null; then if [ -n "$(find "$lock" -maxdepth 0 -mmin +180 2>/dev/null)" ]; then rmdir "$lock" 2>/dev/null; mkdir "$lock" 2>/dev/null || exit 0 else echo "$(date -u +%FT%TZ) another run is in progress, skipping" >> "$log"; exit 0 fi fi out=$(mktemp) trap 'rmdir "$lock" 2>/dev/null; rm -f "$out"' EXIT "$(dirname "$0")/fetch_tw_daily.sh" --snapshot "$updates" > "$out" 2>&1 rc=$? { echo "== $(date -u +%FT%TZ) exit $rc"; cat "$out"; } >> "$log" find "$updates" -name '????-??-??.csv' -mtime +"$keep_days" -delete tail -n 3000 "$log" > "$log.tmp" && mv "$log.tmp" "$log" # keep the log small if [ $rc -ne 0 ]; then notify "stock daily update FAILED on $(hostname) ($(date -u +%F))" "$(tail -n 40 "$out")" exit $rc fi if grep -qE '^(new|updated):' "$out"; then echo " -> new data written" >> "$log" else echo " -> no new trading day" >> "$log" # A long silence (beyond the longest market holiday) means the exchange feed has probably changed. if ! find "$updates" -name '????-??-??.csv' -mtime -10 2>/dev/null | grep -q .; then notify "stock daily update: no new data for 10 days on $(hostname)" "No snapshot file has changed in $updates for 10 days. Last log lines: $(tail -n 20 "$log")" fi fi