SPB Git

spb/wp40_uqo

Public MIT
5.8 KB · 111 lines cpp
1// FinData OS client (https://findata.spboucher.ai/api/v1) — bearer auth from FINDATA_API_KEY / FINDATA_API_KEY_FILE,2// TLS via libcurl, redacted logs, token-bucket rate limiting, bounded concurrency, retries with exponential backoff3// honouring Retry-After, cursor pagination, 31-day chunking for 1m bars, asynchronous exports with polling/download,4// on-disk cache keyed by dataset version, SHA-256 of every downloaded artifact, and provenance (request ids, versions).5#pragma once6#include <string>7#include <vector>8#include <map>9#include <optional>10#include <mutex>11#include <chrono>12#include <functional>13#include <nlohmann/json.hpp>14#include "raid/core/types.hpp"15#include "raid/core/estimate.hpp"16#include "raid/data/bar_series.hpp"1718namespace raid::findata {1920struct Config {21  std::string base_url{"https://findata.spboucher.ai/api/v1"};22  std::string api_key;                 // empty → FINDATA_API_KEY, then FINDATA_API_KEY_FILE23  std::string cache_dir;               // empty → $RAID_CACHE_DIR or ./datasets/cache24  double requests_per_second{5.0};     // token bucket rate (600/min limit → 5/s leaves headroom)25  double burst{10.0};26  int max_retries{5};27  long timeout_seconds{120};28  unsigned max_concurrency{4};29  bool verify_tls{true};30  bool offline{false};                 // only serve from cache; any network call throws (for CI)31  static Config from_env();32};3334/// Provenance of one HTTP exchange (no secrets).35struct Exchange {36  std::string method, path;            // query string without the key (the key is a header)37  long status{0};38  std::string request_id, dataset_id;  // X-Request-ID, X-Dataset-Id39  i64 dataset_version{-1};             // X-Dataset-Version40  std::string next_cursor;             // X-Next-Cursor (arrow pages)41  double seconds{0};42  usize bytes{0};43  int attempts{1};44};4546class TokenBucket {47 public:48  TokenBucket(double rate, double burst) : rate_(rate), capacity_(burst), tokens_(burst), last_(std::chrono::steady_clock::now()) {}49  /// Blocks until a token is available; returns the wait in seconds.50  double acquire();51 private:52  double rate_, capacity_, tokens_; std::chrono::steady_clock::time_point last_; std::mutex m_;53};5455/// Splits [start, end] (inclusive dates) into chunks of at most `max_days` calendar days (API constraint: 1m ≤ 31 d,56/// 5m ≤ 186 d, 1d ≤ 10 y; exports ≤ 12 y).57std::vector<std::pair<Date, Date>> chunk_dates(Date start, Date end, int max_days);58int max_days_per_call(Interval iv);5960struct BarsRequest {61  std::string symbol; Interval interval{Interval::m1}; Date start, end; std::string adjustment{"raw"}; usize page_limit{10000}; std::optional<i64> version;62};63struct BarsResult { BarSeries bars; std::vector<Exchange> exchanges; i64 dataset_version{-1}; std::string dataset_id; usize duplicates_dropped{0}; std::string cache_path; bool from_cache{false}; };6465struct ExportRequest { std::string dataset_id{"market_bars.stock.1m.raw"}; std::vector<std::string> symbols; Date start, end; std::string adjustment{"raw"}; };66struct ExportResult { std::string export_id, path, sha256; i64 dataset_version{-1}; usize rows{0}; usize bytes{0}; std::vector<Exchange> exchanges; bool from_cache{false}; double seconds_total{0}; };6768class Client {69 public:70  explicit Client(Config cfg = Config::from_env());71  ~Client();72  const Config& config() const { return cfg_; }73  std::string key_fingerprint() const;   // first 12 hex of sha256(key): identifies the key without revealing it7475  nlohmann::json health();76  nlohmann::json openapi();77  nlohmann::json catalog_datasets();78  nlohmann::json dataset(const std::string& id);79  nlohmann::json sessions(const std::string& symbol, Interval iv, Date start, Date end);80  nlohmann::json coverage_symbols(const std::string& dataset_id, int min_sessions, usize limit = 200, const std::string& cursor = "");81  /// All symbols with ≥ min_sessions sessions (follows cursors); returns {symbol, sessions, d0, d1, bars}.82  nlohmann::json coverage_symbols_all(const std::string& dataset_id, int min_sessions, usize max_symbols = 100000);83  nlohmann::json fundamentals(const std::string& entity, const std::string& as_of_iso, const std::vector<std::string>& metrics = {}, const std::string& period_type = "");84  /// Bounded read-only SQL (server: 30 s, ≤ 100 000 rows). Use only for universe/coverage questions.85  nlohmann::json query(const std::string& sql, usize max_rows = 1000);8687  /// Bars via GET /market/bars?format=arrow with cursor pagination and ≤ max_days chunking; cached per dataset version.88  BarsResult bars(const BarsRequest& req);89  /// Asynchronous export (POST /exports → poll → download) with ≤ 10-year chunking handled by the caller; cached.90  ExportResult export_bars(const ExportRequest& req, const std::string& out_path = "");91  /// Convenience: export, then read into BarSeries per symbol (requires Arrow build).92  std::map<std::string, BarSeries> export_and_read(const ExportRequest& req);9394  const std::vector<Exchange>& log() const { return log_; }95  Provenance provenance(const std::string& dataset_id) const;9697  struct Response { long status{0}; std::string body; std::map<std::string, std::string> headers; double seconds{0}; int attempts{1}; };98  Response request(const std::string& method, const std::string& path_and_query, const std::string& body = "", const std::string& content_type = "application/json", const std::string& download_to = "");99100 private:101  Response request_once(const std::string& method, const std::string& url, const std::string& body, const std::string& content_type, const std::string& download_to);102  std::string cache_file(const std::string& kind, const std::string& key_material, const std::string& ext) const;103  Config cfg_; std::string key_; TokenBucket bucket_; std::vector<Exchange> log_; std::mutex log_m_;104  void* curl_share_{nullptr};105};106107/// `raid data …` subcommands (returns exit code).108int cli_main(const std::vector<std::string>& args);109110}  // namespace raid::findata111
Files · main
Menu

Theme