spb/hfmarketdata
Public
Open high-frequency market data platform — FirstRate full-history downloader, DuckDB/Parquet lake, open REST API and React docs platform (www.hfmarketdata.io)
JavaScript 53.7%
Python 38.3%
CSS 4.6%
TypeScript 3.1%
1#!/usr/bin/env python32"""Build the downloadable skills pack.341. Copies `skills/_shared/hfmd.py` into every `skills/hfmd-*/scripts/hfmd.py` (self-contained skills).52. Zips the five skill folders (+ a top-level INSTALL.md) into6 `hfmarketdata/web/public/downloads/hfmarketdata-skills.zip`, deterministically (fixed timestamps,7 sorted entries) so re-running without changes yields an identical file.89Usage: python3 skills/scripts/build_skills.py [--check] [--out PATH]10"""11from __future__ import annotations1213import argparse14import shutil15import sys16import zipfile17from pathlib import Path1819ROOT = Path(__file__).resolve().parents[2]20SKILLS = ROOT / "skills"21SHARED = SKILLS / "_shared" / "hfmd.py"22OUT = ROOT / "hfmarketdata" / "web" / "public" / "downloads" / "hfmarketdata-skills.zip"23FIXED_TIME = (2026, 1, 1, 0, 0, 0)2425INSTALL = """# HF Market Data skills — install2627Unzip into `~/.claude/skills/` (all projects) or `.claude/skills/` (this project only):2829 unzip -o hfmarketdata-skills.zip -d ~/.claude/skills/3031Requirements: Python 3.10+, `pip install requests pandas` (+ `matplotlib` for charts).32Optional: `export HFMD_API_KEY=hfmd_live_…` (free API key → 120 requests/min; keyless mode has low hourly limits, 30/h).33Everything is free; higher limits are granted on request by e-mail to contact@spboucher.ai (also free). See https://www.hfmarketdata.io/limits3435Skills: hfmd-data-analysis · hfmd-quick-backtest · hfmd-continuous-futures · hfmd-fundamentals-screen · hfmd-term-structure36Docs: https://www.hfmarketdata.io/integrations/skills37"""383940def skill_dirs() -> list[Path]:41 return sorted(p for p in SKILLS.iterdir() if p.is_dir() and p.name.startswith("hfmd-") and (p / "SKILL.md").exists())424344def sync_shared(check: bool) -> int:45 drift = 046 src = SHARED.read_bytes()47 for d in skill_dirs():48 dst = d / "scripts" / "hfmd.py"49 if dst.exists() and dst.read_bytes() == src:50 continue51 if check:52 print(f"DRIFT: {dst.relative_to(ROOT)} differs from _shared/hfmd.py", file=sys.stderr)53 drift += 154 else:55 dst.parent.mkdir(parents=True, exist_ok=True)56 shutil.copyfile(SHARED, dst)57 print(f"synced {dst.relative_to(ROOT)}")58 return drift596061def build_zip(out: Path) -> None:62 out.parent.mkdir(parents=True, exist_ok=True)63 entries: list[tuple[str, bytes]] = [("INSTALL.md", INSTALL.encode())]64 for d in skill_dirs():65 for f in sorted(d.rglob("*")):66 if f.is_dir() or "__pycache__" in f.parts or f.suffix in {".pyc", ".png", ".csv"}:67 continue68 entries.append((str(f.relative_to(SKILLS)), f.read_bytes()))69 with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as z:70 for name, data in entries:71 info = zipfile.ZipInfo(name, date_time=FIXED_TIME)72 info.compress_type = zipfile.ZIP_DEFLATED73 info.external_attr = (0o755 if name.endswith(".py") else 0o644) << 1674 z.writestr(info, data)75 print(f"wrote {out.relative_to(ROOT)} ({out.stat().st_size:,} bytes, {len(entries)} files, {len(skill_dirs())} skills)")767778def main() -> int:79 ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)80 ap.add_argument("--check", action="store_true", help="only verify the copies of hfmd.py are in sync")81 ap.add_argument("--out", type=Path, default=OUT)82 a = ap.parse_args()83 drift = sync_shared(a.check)84 if a.check:85 print("in sync" if not drift else f"{drift} file(s) out of sync")86 return 1 if drift else 087 build_zip(a.out)88 return 0899091if __name__ == "__main__":92 sys.exit(main())93