The AlphaVantage stock script now uses a cache file that is built slowly while waiting 15 seconds per API call, because the free AlphaVantage API keys are rate limited and only the first stock symbol was being returned.

This commit is contained in:
Matthew Grotke 2026-01-03 21:43:01 -05:00
parent f78d5d4778
commit 579509fe87

View file

@ -1,9 +1,16 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os
import sys
import time
import requests import requests
from datetime import datetime, timedelta from datetime import datetime, timedelta
import argparse import argparse
CACHE_FILE = os.path.expanduser("~/.cache/mgconky/stocks_alphavantage.txt")
API_DELAY_SECONDS = 15
USE_CACHE = True
def fetch_intraday_data(api_key, symbol, interval="1min"): def fetch_intraday_data(api_key, symbol, interval="1min"):
"""Fetch current and historical intraday price using TIME_SERIES_INTRADAY.""" """Fetch current and historical intraday price using TIME_SERIES_INTRADAY."""
url = "https://www.alphavantage.co/query" url = "https://www.alphavantage.co/query"
@ -99,6 +106,7 @@ def fetch_historical_data(api_key, symbol, range_in_days):
print(f"Error: Failed to fetch historical data for {symbol} - {e}") print(f"Error: Failed to fetch historical data for {symbol} - {e}")
return None return None
def main(): def main():
# Parse command-line arguments # Parse command-line arguments
parser = argparse.ArgumentParser(description="Fetch stock data from Alpha Vantage.") parser = argparse.ArgumentParser(description="Fetch stock data from Alpha Vantage.")
@ -126,9 +134,15 @@ def main():
line_tab3_offset = "${alignr}" # Could also replace this with goto 120 if you don't like the right alignment line_tab3_offset = "${alignr}" # Could also replace this with goto 120 if you don't like the right alignment
# Iterate symbols # Iterate symbols
for symbol in symbols: had_success = False
for i, symbol in enumerate(symbols):
fetched_data = fetch_intraday_data(args.api_key, symbol) if args.range_in_days < 1 else fetch_historical_data(args.api_key, symbol, args.range_in_days) fetched_data = fetch_intraday_data(args.api_key, symbol) if args.range_in_days < 1 else fetch_historical_data(args.api_key, symbol, args.range_in_days)
if fetched_data: if (
isinstance(fetched_data, dict)
and "current_price" in fetched_data
and isinstance(fetched_data["current_price"], (int, float))
):
had_success = True
current_price = fetched_data["current_price"] current_price = fetched_data["current_price"]
compare_price = fetched_data["compare_price"] compare_price = fetched_data["compare_price"]
price_difference = current_price - compare_price price_difference = current_price - compare_price
@ -146,11 +160,81 @@ def main():
else: else:
output.append(f"{symbol}: Error fetching data") output.append(f"{symbol}: Error fetching data")
# --- Rate limit protection for Alpha Vantage ---
if i < len(symbols) - 1:
time.sleep(API_DELAY_SECONDS) # wait 15 seconds between queries
# Join all parts of the output and print it # Join all parts of the output and print it
header_label = f"Intraday" if args.range_in_days < 1 else f"{args.range_in_days} Day" header_label = f"Intraday" if args.range_in_days < 1 else f"{args.range_in_days} Day"
header_line = f"{line_tab1_offset}{color_header}Ticker{line_tab2_offset}Price ($$){line_tab3_offset}{header_label}{color_label}" header_line = f"{line_tab1_offset}{color_header}Ticker{line_tab2_offset}Price ($$){line_tab3_offset}{header_label}{color_label}"
return header_line + "\n" + f"{line_tab1_offset}{color_header}${{voffset -5}}${{hr 1}}" + "\n" + "\n".join(output) final_output = (
header_line
+ "\n"
+ f"{line_tab1_offset}{color_header}${{voffset -5}}${{hr 1}}"
+ "\n"
+ "\n".join(output)
)
if had_success:
if USE_CACHE:
# Save results to cache file
os.makedirs(os.path.dirname(CACHE_FILE), exist_ok=True)
tmp_file = CACHE_FILE + ".tmp"
with open(tmp_file, "w") as f:
f.write(final_output)
os.replace(tmp_file, CACHE_FILE)
else:
# Direct output mode (no cache)
return final_output
else:
# No valid data was returned
# Do NOT overwrite cache
# Do NOT return anything in cache mode
return None
if __name__ == "__main__": if __name__ == "__main__":
print(main())
# The free alpha vantage plan may require you to rate limit.
# Conky will not wait for rate limited output.
# We must instead return the previous results
# (from when this script was last called)
# Set RATE_LIMIT to false if you have the paid plan.
if USE_CACHE:
# Fork immediately so the process Conky launched can exit right away.
pid = os.fork()
if pid > 0:
# PARENT PROCESS (this is the process Conky launched)
# Return contents of cache file (from early query to Conky)
if os.path.exists(CACHE_FILE):
try:
with open(CACHE_FILE, "r") as f:
print(f.read(), end="")
sys.stdout.flush()
except Exception:
pass
# Parent exits immediately ---> Conky renders output
sys.exit(0)
else:
# CHILD PROCESS (background updater, not waited on by Conky)
# Detach from the parent session so Conky does not wait on this process
os.setsid()
# Perform new query
main()
sys.exit(0)
else:
# Return the results to Conky
result = main()
if result is not None:
print(result)