Created
July 3, 2025 06:20
-
-
Save mahrous-amer/3ad5d379b322f01b76167f63d8993284 to your computer and use it in GitHub Desktop.
Bash script to fetch symbol data from Alpha Vantage API, save as JSON, and generate a daily close price graph using gnuplot. Requires curl, jq, gnuplot, and an Alphavantage API key.
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
#!/bin/bash | |
# Author: Mahrous Amer | |
# Date: September 23, 2024 | |
set -euo pipefail | |
# Constants | |
BASE_URL='https://www.alphavantage.co' | |
CONFIG_FILE='config.conf' | |
LOG_FILE='alphavantage.log' | |
GRAPH_OUTPUT='graph.png' | |
RETRY_COUNT=3 | |
RETRY_DELAY=5 | |
DEFAULT_INTERVAL='DAILY' | |
DEFAULT_OUTPUT_SIZE='full' | |
# Helpers | |
log_info() { | |
local message="$1" | |
echo "$(date '+%Y-%m-%d %H:%M:%S') INFO: ${message}" | tee -a "$LOG_FILE" | |
} | |
log_error() { | |
local message="$1" | |
echo "$(date '+%Y-%m-%d %H:%M:%S') ERROR: ${message}" | tee -a "$LOG_FILE" >&2 | |
} | |
load_config() { | |
if [[ -f "$CONFIG_FILE" ]]; then | |
source "$CONFIG_FILE" | |
else | |
log_error "Configuration file not found: $CONFIG_FILE" | |
exit 1 | |
fi | |
} | |
check_dependencies() { | |
command -v curl >/dev/null 2>&1 || { log_error "curl is required but not installed."; exit 6; } | |
command -v jq >/dev/null 2>&1 || { log_error "jq is required but not installed."; exit 6; } | |
command -v gnuplot >/dev/null 2>&1 || { log_error "gnuplot is required but not installed."; exit 6; } | |
} | |
validate_args() { | |
if [[ -z "${SYMBOL-}" ]]; then | |
log_error "Symbol not specified." | |
exit 1 | |
fi | |
if [[ -z "${OUTPUT_FILE-}" ]]; then | |
OUTPUT_FILE="./${SYMBOL}.json" | |
elif [[ -d "$OUTPUT_FILE" ]]; then | |
OUTPUT_FILE="${OUTPUT_FILE}/${SYMBOL}.json" | |
fi | |
if [[ -z "${API_KEY-}" ]]; then | |
if [[ -z "${ALPHAVANTAGE_API_KEY-}" ]]; then | |
log_error "No API key provided, and 'ALPHAVANTAGE_API_KEY' not in environment." | |
exit 2 | |
else | |
API_KEY="${ALPHAVANTAGE_API_KEY}" | |
fi | |
fi | |
INTERVAL="${INTERVAL:-$DEFAULT_INTERVAL}" | |
OUTPUT_SIZE="${OUTPUT_SIZE:-$DEFAULT_OUTPUT_SIZE}" | |
} | |
fetch_data() { | |
local attempt=1 | |
while [[ $attempt -le $RETRY_COUNT ]]; do | |
log_info "Fetching data (attempt $attempt/$RETRY_COUNT) for symbol: '$SYMBOL'" | |
if curl -s --fail "${BASE_URL}/query?function=TIME_SERIES_${INTERVAL}&symbol=${SYMBOL}&datatype=json&outputsize=${OUTPUT_SIZE}&apikey=${API_KEY}" -o "$OUTPUT_FILE"; then | |
log_info "Data successfully saved to '$OUTPUT_FILE'" | |
return 0 | |
fi | |
log_error "Failed to fetch data for symbol: '$SYMBOL' (attempt $attempt/$RETRY_COUNT)" | |
((attempt++)) | |
[[ $attempt -le $RETRY_COUNT ]] && sleep $RETRY_DELAY | |
done | |
log_error "Failed to fetch data after $RETRY_COUNT attempts." | |
exit 4 | |
} | |
generate_graph() { | |
local json_file="$1" | |
local output_image="$2" | |
local series_key | |
case "$INTERVAL" in | |
DAILY) series_key="Time Series (Daily)";; | |
WEEKLY) series_key="Weekly Time Series";; | |
MONTHLY) series_key="Monthly Time Series";; | |
*) log_error "Unsupported interval: $INTERVAL"; exit 7;; | |
esac | |
# JSON -> CSV for gnuplot | |
echo "Date,Close" > data.csv | |
jq -r ".[\"$series_key\"] | to_entries[] | [.key, .value[\"4. close\"]] | @csv" "$json_file" >> data.csv | |
# Validate CSV data | |
if [[ ! -s data.csv ]] || [[ $(wc -l < data.csv) -le 1 ]]; then | |
log_error "No valid data found in CSV for graphing." | |
exit 5 | |
fi | |
# Generate graph with enhanced styling | |
gnuplot <<-EOF | |
set datafile separator "," | |
set terminal pngcairo size 1000,700 enhanced font "Arial,12" | |
set output "$output_image" | |
set xdata time | |
set timefmt "%Y-%m-%d" | |
set format x "%Y-%m-%d" | |
set xlabel "Date" font "Arial,14" | |
set ylabel "Close Price (USD)" font "Arial,14" | |
set title "${SYMBOL} ${INTERVAL} Adjusted Close Price" font "Arial,16" | |
set grid | |
set key top left | |
set style line 1 lc rgb "#0066cc" lw 2 | |
plot "data.csv" using 1:2 with lines ls 1 title "Close Price" | |
EOF | |
if [[ $? -ne 0 ]]; then | |
log_error "Failed to generate graph." | |
exit 5 | |
fi | |
log_info "Graph successfully saved as '$output_image'" | |
} | |
# Parse additional arguments | |
while [[ $# -ge 1 ]]; do | |
case "$1" in | |
-s|--symbol) | |
SYMBOL="$2"; shift 2 | |
;; | |
-o|--output) | |
OUTPUT_FILE="$2"; shift 2 | |
;; | |
--key) | |
API_KEY="$2"; shift 2 | |
;; | |
-i|--interval) | |
INTERVAL="$2"; shift 2 | |
;; | |
--output-size) | |
OUTPUT_SIZE="$2"; shift 2 | |
;; | |
*) | |
log_error "Unexpected argument '$1'" | |
exit 3 | |
;; | |
esac | |
done | |
# Main execution | |
check_dependencies | |
load_config | |
validate_args | |
fetch_data | |
generate_graph "$OUTPUT_FILE" "$GRAPH_OUTPUT" | |
# Cleanup | |
[[ -f data.csv ]] && rm -f data.csv |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment