SPB Git

spb/findata

Public
4.7 KB · 59 lines
1#!/bin/bash2# fdq — quick FinData OS client for shell sessions (Claude Code or human).3# Reads the API key from ~/Desktop/Cluster/secrets/findata/api-key-claude-code.txt (or $FINDATA_API_KEY).4#   fdq health                                   # status + alerts5#   fdq datasets                                 # catalog (id, status, rows)6#   fdq bars AAPL 1m 2024-03-01 2024-03-31 [raw|split|split_div]   # JSON page (<= 10 000 rows); use export for more7#   fdq sql "SELECT ... FROM market_bars_stock_1m_raw WHERE symbol='AAPL' AND year=2024 ..." [max_rows]8#   fdq fund AAPL [as_of_iso] [metrics,comma]    # point-in-time fundamentals9#   fdq export "AAPL,MSFT" 2024-01-01 2024-12-31 [dataset_id] [out.parquet]   # async Parquet export -> file10#   fdq jobs | fdq enqueue '{"kind":"incremental","params":{"type":"stock","period":"day"}}'   # admin11#   fdq get /api/v1/...  | fdq post /api/v1/... '{json}'                                       # raw12set -euo pipefail13BASE="${FINDATA_URL:-https://findata.spboucher.ai}"14KEY="${FINDATA_API_KEY:-$(cat "$HOME/Desktop/Cluster/secrets/findata/api-key-claude-code.txt")}"15export BASE KEY16j() { python3 -c 'import sys,json; d=json.load(sys.stdin); print(json.dumps(d, indent=1, ensure_ascii=False))'; }17cmd="${1:-help}"; shift || true18case "$cmd" in19  health)   curl -s -H "Authorization: Bearer $KEY" "$BASE/api/v1/health" | j ;;20  datasets) curl -s -H "Authorization: Bearer $KEY" "$BASE/api/v1/catalog/datasets" | python3 -c 'import sys,json21for d in json.load(sys.stdin)["data"]:22    i, st, rows, a, b = d["id"], d["status"], d.get("row_count") or 0, d.get("first_session") or "", d.get("last_session") or ""23    print("%-44s %-10s rows=%14s  %s -> %s" % (i, st, format(rows, ","), a, b))' ;;24  bars)     curl -s -H "Authorization: Bearer $KEY" --get "$BASE/api/v1/market/bars" --data-urlencode "symbol=$1" --data-urlencode "interval=$2" --data-urlencode "start=$3" --data-urlencode "end=$4" --data-urlencode "adjustment=${5:-raw}" --data-urlencode "limit=${LIMIT:-10000}" | j ;;25  sql)      SQL="$1" MAXROWS="${2:-1000}" python3 -c '26import os, sys, json, urllib.request27req = urllib.request.Request(os.environ["BASE"] + "/api/v1/query", data=json.dumps({"sql": os.environ["SQL"], "max_rows": int(os.environ["MAXROWS"])}).encode(),28                             headers={"Authorization": "Bearer " + os.environ["KEY"], "Content-Type": "application/json"})29try:30    with urllib.request.urlopen(req, timeout=180) as r: d = json.load(r)31except urllib.error.HTTPError as e:32    sys.exit(e.read().decode()[:800])33print(json.dumps({"meta": d["meta"], "columns": [c["name"] for c in d["columns"]]}, indent=1))34for row in d["rows"]: print(json.dumps(row, ensure_ascii=False))' ;;35  fund)     curl -s -H "Authorization: Bearer $KEY" --get "$BASE/api/v1/fundamentals/$1" ${2:+--data-urlencode "as_of=$2"} ${3:+--data-urlencode "metrics=$3"} | j ;;36  export)   SYMS="$1" START="$2" END="$3" DS="${4:-market_bars.stock.1m.raw}" OUT="${5:-findata-export.parquet}" python3 -c '37import os, sys, json, time, urllib.request38base, key = os.environ["BASE"], os.environ["KEY"]39hdr = {"Authorization": "Bearer " + key, "Content-Type": "application/json"}40body = {"dataset_id": os.environ["DS"], "symbols": os.environ["SYMS"].split(","), "start": os.environ["START"], "end": os.environ["END"]}41r = json.load(urllib.request.urlopen(urllib.request.Request(base + "/api/v1/exports", data=json.dumps(body).encode(), headers=hdr)))42eid = r["id"]43while True:44    st = json.load(urllib.request.urlopen(urllib.request.Request(base + "/api/v1/exports/" + eid, headers=hdr)))45    if st["status"] == "ready": break46    if st["status"] in ("failed", "expired"): sys.exit("export " + st["status"] + ": " + str(st.get("error")))47    time.sleep(2)48with urllib.request.urlopen(urllib.request.Request(base + "/api/v1/exports/" + eid + "/download", headers=hdr)) as resp, open(os.environ["OUT"], "wb") as f:49    f.write(resp.read())50print(os.environ["OUT"] + ": " + str(st["row_count"]) + " rows, " + str(st["bytes"]) + " bytes, dataset v" + str(st["snapshot"].get("version")))' ;;51  jobs)     curl -s -H "Authorization: Bearer $KEY" "$BASE/api/v1/admin/jobs?limit=${LIMIT:-30}" | python3 -c 'import sys,json52for j in json.load(sys.stdin)["data"]:53    print("%s %-12s %-36s %-9s %s %s" % (j["id"][:8], j["kind"], str(j["dataset_id"]), j["status"], str(j["created_at"])[:16], str(j.get("last_error") or "")[:60]))' ;;54  enqueue)  curl -s -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d "$1" "$BASE/api/v1/admin/jobs" | j ;;55  get)      curl -s -H "Authorization: Bearer $KEY" "$BASE$1" | j ;;56  post)     curl -s -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d "${2:-{}}" "$BASE$1" | j ;;57  *)        sed -n '2,12p' "$0" ;;58esac59
Files · main
Menu

Theme