Skill: oscraters/alpha-vantage (version 1.0.0)

--- name: alpha-vantage description: Use this skill when users need Alpha Vantage market data or indicators (stocks, ETFs, forex, crypto, commodities, macro, company fundamentals) via the official API. Apply it for ticker lookups, time series pulls, indicator calculations, screening/fundamental analysis, API integration code, and deployment-safe workflows requiring API key handling, throttling retries, and commercial/public-use guardrails. --- # Alpha Vantage ## Overview This skill provides a production-ready workflow for Alpha Vantage API usage: selecting the right endpoints, building validated requests, handling throttling/error responses, and preparing safe public/commercial deployment. ## Quick Start 1. Set API key: `export ALPHAVANTAGE_API_KEY=...` 2. For endpoint/params, read `references/api_docs.md` 3. For scriptable calls with retry/backoff, use `scripts/alpha_vantage_client.py` 4. For public deployment, follow the `Deployment Guardrails` section before release ## Workflow 1. Classify request type: - Price bars or latest price: time series functions - Indicators (RSI, SMA, MACD, etc.): technical indicator functions - Company info, earnings, statements: fundamentals - FX/Crypto/Commodities/Macro: their dedicated function families 2. Resolve mandatory parameters from `references/api_docs.md`. 3. Build request with `function=...` and `apikey=...`. 4. Parse response and branch for: - HTTP error status - `Error Message` - `Note` (usually rate-limit/throttle condition) - Empty/partial payload 5. If `Note`/throttled, retry using exponential backoff with jitter. 6. Normalize output to a stable schema before downstream use. ## Implementation Guidelines ### Authentication - Use `ALPHAVANTAGE_API_KEY` environment variable by default. - Never hardcode keys in source, logs, prompts, or examples. - Mask keys in debug output (show only short prefix/suffix). ### Reliability and Rate Limits - Treat responses containing `Note` as retriable throttle events. - Use bounded retries with exponential backoff and jitter. - For multi-symbol jobs, queue calls and pace to plan limits. - Cache stable responses (fundamentals, metadata) to reduce quota burn. ### Response Validation - Validate both transport and payload success. - Handle string-encoded numbers safely (`float(...)`/`Decimal` as needed). - Keep parser logic resilient to minor schema/key ordering changes. ### Data Quality - Preserve source timestamps/time zones from payload metadata. - Do not infer adjusted/unadjusted semantics; use explicit functions. - Record the function and params used for reproducibility/auditability. ## Deployment Guardrails ### Public/Commercial Readiness - Review Alpha Vantage terms before public/commercial release: https://www.alphavantage.co/terms_of_service/ - Ensure your usage tier and traffic profile are aligned with your plan: https://www.alphavantage.co/premium/ - Do not redistribute restricted content if terms disallow it. ### Security and Operations - Store API keys in secret managers (or environment variables for local dev only). - Add circuit-breaking and queue backpressure for upstream rate spikes. - Instrument call counts, throttle rate, retry count, and error classes. - Add alerting for sustained `Note` responses and non-2xx response spikes. ## Resources ### references/ - `references/api_docs.md` contains endpoint selection guidance and required parameters. ### scripts/ - `scripts/alpha_vantage_client.py` provides a reusable request wrapper with: - env-based auth - timeout and retry logic - throttle/error detection - optional compact output

path: README.md
content: # alpha-vantage-skill
Openclaw skill for using Alpha Vantage.


path: references/api_docs.md
content: # Alpha Vantage API Reference (Operational Summary)

Source of truth: https://www.alphavantage.co/documentation/

## Base Request Pattern

- Base URL: `https://www.alphavantage.co/query`
- Common parameters:
  - `function` (required)
  - `apikey` (required)
  - Function-specific parameters (`symbol`, `interval`, `outputsize`, etc.)

Example:

```text
https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=IBM&[REDACTED_SECRET]
```

## Auth and Access

- Get API key: https://www.alphavantage.co/support/#api-key
- Premium tiers and throughput: https://www.alphavantage.co/premium/
- Terms for public/commercial usage: https://www.alphavantage.co/terms_of_service/

## Response Guardrails

Handle these response classes in every integration:

1. HTTP error (non-2xx)
2. JSON payload with `Error Message`
3. JSON payload with `Note` (throttle/limit conditions)
4. Empty payload or missing expected keys

## Endpoint Families

## 1) Time Series (Equities/ETFs)

- `TIME_SERIES_INTRADAY`
  - Required: `symbol`, `interval`
  - Useful options: `adjusted`, `extended_hours`, `month`, `outputsize`, `datatype`
- `TIME_SERIES_DAILY`
- `TIME_SERIES_DAILY_ADJUSTED`
- `TIME_SERIES_WEEKLY`
- `TIME_SERIES_WEEKLY_ADJUSTED`
- `TIME_SERIES_MONTHLY`
- `TIME_SERIES_MONTHLY_ADJUSTED`

## 2) Technical Indicators

Examples:

- `SMA`, `EMA`, `RSI`, `MACD`, `ADX`, `BBANDS`, `STOCH`, `ATR`, `OBV`

Typical required parameters:

- `symbol`
- `interval`
- `time_period` (for many indicators)
- `series_type` (`open|high|low|close`) where applicable

## 3) Fundamental Data

- `OVERVIEW`
- `INCOME_STATEMENT`
- `BALANCE_SHEET`
- `CASH_FLOW`
- `EARNINGS`
- `LISTING_STATUS`
- `EARNINGS_CALENDAR`
- `IPO_CALENDAR`

## 4) FX, Crypto, Commodities, Macro

- FX:
  - `FX_INTRADAY`, `FX_DAILY`, `FX_WEEKLY`, `FX_MONTHLY`
  - Often requires `from_symbol`, `to_symbol`
- Crypto:
  - `CRYPTO_INTRADAY`, `DIGITAL_CURRENCY_DAILY`, `DIGITAL_CURRENCY_WEEKLY`, `DIGITAL_CURRENCY_MONTHLY`
- Commodities and macro datasets:
  - See the docs catalog for currently available functions and required params.

## 5) Search and Intelligence

- `SYMBOL_SEARCH`
- `NEWS_SENTIMENT`
- `TOP_GAINERS_LOSERS`

## Parsing Notes

- Numeric values are commonly encoded as strings.
- Keys and section names differ by function; do not assume one fixed schema.
- Preserve metadata section (`Meta Data`, timestamps, timezone) for auditability.

## Suggested Call Strategy for Production

1. Build a typed request object per function family.
2. Validate required params before request dispatch.
3. Execute request with timeout + retry/backoff on `Note`.
4. Normalize raw payload into a stable internal schema.
5. Log request id/function and retry count (never log full API key).


path: scripts/alpha_vantage_client.py
content: #!/usr/bin/env python3
"""Minimal Alpha Vantage client with throttle-aware retries."""

from __future__ import annotations

import argparse
import json
import os
import random
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
from typing import Any, Dict


BASE_URL = "https://www.alphavantage.co/query"


class AlphaVantageError(RuntimeError):
    pass


class AlphaVantageClient:
    def __init__(
        self,
        api_key: str | None = None,
        timeout: float = 20.0,
        max_retries: int = 5,
        backoff_base: float = 1.0,
        backoff_cap: float = 20.0,
    ) -> None:
        self.[REDACTED_SECRET] or os.getenv("ALPHAVANTAGE_API_KEY")
        if not self.api_key:
            raise AlphaVantageError(
                "Missing API key. Set ALPHAVANTAGE_API_KEY or pass --apikey."
            )
        self.timeout = timeout
        self.max_retries = max_retries
        self.backoff_base = backoff_base
        self.backoff_cap = backoff_cap

    def query(self, function: str, **params: str) -> Dict[str, Any]:
        query_params = {"function": function, "apikey": self.api_key, **params}
        url = f"{BASE_URL}?{urllib.parse.urlencode(query_params)}"

        for attempt in range(self.max_retries + 1):
            try:
                with urllib.request.urlopen(url, timeout=self.timeout) as resp:
                    status = getattr(resp, "status", 200)
                    body = resp.read().decode("utf-8")
            except urllib.error.HTTPError as exc:
                status = exc.code
                body = exc.read().decode("utf-8", errors="replace")
                if self._should_retry_http(status) and attempt < self.max_retries:
                    self._sleep_with_backoff(attempt)
                    continue
                raise AlphaVantageError(f"HTTP {status}: {body[:300]}") from exc
            except urllib.error.URLError as exc:
                if attempt < self.max_retries:
                    self._sleep_with_backoff(attempt)
                    continue
                raise AlphaVantageError(f"Network error: {exc}") from exc

            if status < 200 or status >= 300:
                if self._should_retry_http(status) and attempt < self.max_retries:
                    self._sleep_with_backoff(attempt)
                    continue
                raise AlphaVantageError(f"HTTP {status}: {body[:300]}")

            try:
                payload: Dict[str, Any] = json.loads(body)
            except json.JSONDecodeError as exc:
                raise AlphaVantageError("Non-JSON response from Alpha Vantage") from exc

            # "Note" commonly indicates throttling/temporary quota exceedance.
            if "Note" in payload:
                if attempt < self.max_retries:
                    self._sleep_with_backoff(attempt)
                    continue
                raise AlphaVantageError(payload["Note"])

            if "Error Message" in payload:
                raise AlphaVantageError(payload["Error Message"])

            return payload

        raise AlphaVantageError("Request failed after retries")

    @staticmethod
    def _should_retry_http(status: int) -> bool:
        return status in {408, 425, 429, 500, 502, 503, 504}

    def _sleep_with_backoff(self, attempt: int) -> None:
        raw = min(self.backoff_cap, self.backoff_base * (2**attempt))
        jitter = random.uniform(0, raw * 0.25)
        time.sleep(raw + jitter)


def _parse_params(raw_params: list[str]) -> Dict[str, str]:
    params: Dict[str, str] = {}
    for item in raw_params:
        if "=" not in item:
            raise AlphaVantageError(f"Invalid param '{item}'. Use key=value.")
        key, value = item.split("=", 1)
        key = key.strip()
        if not key:
            raise AlphaVantageError(f"Invalid param '{item}'. Empty key.")
        params[key] = value.strip()
    return params


def main() -> int:
    parser = argparse.ArgumentParser(description="Alpha Vantage API client")
    parser.add_argument("--function", required=True, help="Alpha Vantage function name")
    parser.add_argument(
        "--param",
        action="append",
        default=[],
        help="Query parameter in key=value form (repeatable)",
    )
    parser.add_argument("--apikey", help="API key (default: ALPHAVANTAGE_API_KEY)")
    parser.add_argument("--timeout", type=float, default=20.0)
    parser.add_argument("--retries", type=int, default=5)
    parser.add_argument(
        "--compact",
        action="store_true",
        help="Print compact JSON output (no indentation)",
    )
    args = parser.parse_args()

    try:
        params = _parse_params(args.param)
        client = AlphaVantageClient(
            [REDACTED_SECRET],
            timeout=args.timeout,
            max_retries=args.retries,
        )
        payload = client.query(args.function, **params)
    except AlphaVantageError as exc:
        print(f"error: {exc}", file=sys.stderr)
        return 1

    if args.compact:
        print(json.dumps(payload, separators=(",", ":"), sort_keys=True))
    else:
        print(json.dumps(payload, indent=2, sort_keys=True))
    return 0


if __name__ == "__main__":
    sys.exit(main())


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)