Skip to content

Instantly share code, notes, and snippets.

@mahrous-amer
Created July 3, 2025 06:20
Show Gist options
  • Save mahrous-amer/3ad5d379b322f01b76167f63d8993284 to your computer and use it in GitHub Desktop.
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.
#!/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