Skip to content

xbbg 1.1.0

Choose a tag to compare

@github-actions github-actions released this 14 Apr 21:59
· 229 commits to main since this release

Added

  • xbbg-mcp local MCP server: Added a stdio Bloomberg MCP application under apps/xbbg-mcp with tool surfaces for bdp, bdh, bds, bdib, bql, bsrch, bflds, and generic request execution. Responses are bounded structured JSON with Arrow schema metadata for coding agents.
  • GitHub-release MCP distribution path: Added release packaging for xbbg-mcp, a Unix launcher wrapper (scripts/xbbg-mcp), and a convenience installer (scripts/install-xbbg-mcp.sh) so Claude Code and OpenCode users can install a local MCP binary without cloning or compiling the repo first.
  • @xbbg/core Node.js package: New first-class JavaScript/TypeScript client under js-xbbg/ that wraps the Rust engine via NAPI. Exposes the full request surface (bdp, bdh, bds, bdib, bdtick, bql, bqr, bsrch, beqs, blkp, bport, bcurves, bgovts, bflds), a typed error hierarchy, optional backends (Apache Arrow tables by default, nodejs-polars as an optional peer), and a BPIPE/auth-aware configure(). Native addons are prebuilt and distributed as optional platform packages (@xbbg/core-darwin-arm64, @xbbg/core-linux-x64, @xbbg/core-win32-x64) so npm install @xbbg/core Just Works without a Rust toolchain.
  • @xbbg/bridge async browser bridge: New companion package exposing the Rust engine through an async postMessage bridge, shipped alongside @xbbg/core with matching platform-specific native addons.
  • GitHub-only JS package release workflow: Added a manual js_github_release.yml path that builds, versions, validates, and attaches GitHub release tarballs for @xbbg/core and @xbbg/bridge without npm publishing. The workflow intentionally ships the currently supported 8-asset set only: @xbbg/core wrapper plus darwin-arm64/linux-x64/win32-x64, and @xbbg/bridge wrapper plus darwin-arm64/linux-x64/win32-x64. The unreleased @xbbg/bridge-darwin-x64 and @xbbg/bridge-linux-arm64 package stubs remain excluded until Bloomberg SDK archive support exists.
  • Friendlier AttributeError for removed blp legacy APIs: blp.connect, blp.disconnect, and blp.getBlpapiVersion now raise an AttributeError whose message points directly at the 1.0 replacement (xbbg.configure, xbbg.shutdown/xbbg.reset, xbbg.get_sdk_info) with a copy-pasteable B-PIPE example, instead of the bare "module has no attribute" default. Implemented via a module-level __getattr__ hook in py-xbbg/src/xbbg/blp.py.

Changed

  • Backend conversion moved to a single boundary with pa.Table as canonical form: _execute_request_terminal now returns the raw pa.Table from the Rust engine without wrapping in narwhals first; arequest does a single _convert_backend call at its return. _convert_backend dispatches directly from pa.Table via zero-copy primitives (pl.from_arrow, table.to_pandas, identity for pa.Table → pa.Table), bypassing the narwhals wrap/unwrap on the hot path. Short-circuiting middlewares that return non-pa.Table values (e.g. caches returning lists) keep full control over their result. Measured on a 1000×10 frame: pa.Table → pa.Table went from 17.78 µs to 0.04 µs (464×), pa.Table → pl.DataFrame from 39.89 µs to 18.67 µs (2.1×). Subscriptions streaming at 10k msgs/sec previously spent ~33% of a core on redundant wrap/unwrap in the dispatch layer; this cuts it roughly in half. Narwhals remains the canonical abstraction for backend-agnostic data manipulation in ext/historical.py, ext/currency.py, ext/futures.py, ext/_utils.py, and _reshape_bqr_generic — this refactor only removes it from the pure routing path where it added overhead without value.
  • Minimum narwhals version bumped to >=2.0: Required for the nw.Implementation enum and .implementation property used by the new _convert_backend dispatch. Downstream users pinned to narwhals 1.x will need to upgrade. No other narwhals 2.x breaking changes affect xbbg — get_native_namespace, new_series(native_namespace=), from_native idempotency, and .to_native() all remain stable across the 1→2 boundary.
  • xbbg.configure() rejects unknown kwargs: configure() now raises TypeError on any keyword it does not recognize. Previously unknown kwargs were silently dropped by the Rust PyEngineConfig constructor, which meant typos (e.g. hots=... instead of host=...) would leave the host at the default without any warning. The Python normalizer now validates the kwarg set against the canonical field list before handing off to Rust.
  • Docs site restructured with Python/JavaScript split: The Starlight site under docs/ now has distinct python/ and javascript/ sections, an auto-generated releases/changelog.mdx page (scripts/generate-changelog-docs.sh), and auto-generated Python API reference (scripts/generate-python-api-docs.sh, renamed from generate-api-docs.sh). The deploy-docs.yml workflow now drives publishing automatically, and the host build no longer depends on sharp so it works on macOS without libvips.

Fixed

  • Incorrect value types in bdp/bdh long format (issue #280): The default long format (LongMode::String) was converting all Bloomberg values to strings, ignoring resolved field_types. Now the Rust engine computes a common Arrow type from the field type hints at construction time — when all fields are numeric, the value column is Float64 instead of Utf8. Mixed-type queries (e.g., numeric + string fields) gracefully fall back to string. The fix is zero-copy: Value is moved into the Arrow builder instead of being stringified and re-parsed.
  • macOS/Linux: import xbbg._core fails with Library not loaded: @rpath/libblpapi3_64.so (issue #276): The pyo3 cdylib ships with zero LC_RPATH entries on macOS, so dyld had nowhere to look for libblpapi3_64.so at import time. Previously only Windows had pre-import SDK setup in __init__.py. Now _prepare_sdk_for_core_import() dispatches per-platform: Windows keeps the existing add_dll_directory path, while macOS and Linux preload libblpapi3_64.so via ctypes.CDLL(..., RTLD_GLOBAL) so dyld/ld.so resolves the @rpath reference via install-name / already-loaded image matching. This mirrors the idiom Bloomberg's own blpapi/internals.py uses for its ffiutils extension (which also ships with no rpath). All four SDK sources (xbbg.set_sdk_path(), blpapi package, DAPI, BLPAPI_ROOT) are now honored on every platform. The friendly ImportError wrapper also recognizes macOS dlopen error strings (Library not loaded, image not found).
  • RequestEnvironment.zfp_remote type annotation: Corrected the dataclass field annotation from int | None to str | None to match the Rust Option<String> (ZFP remote values are strings like "8194"/"8196"). The defensive getattr() access in _snapshot_request_environment had been masking this type mismatch from static analyzers.
  • _convert_backend no longer hard-imports polars: A follow-up to the Arrow-canonical refactor accidentally replaced the intentional hasattr(native, "to_arrow") capability check in the pyarrow branch with an unconditional isinstance(native, pl.DataFrame), which forced import polars as pl at module-load time and broke environments where polars (an optional backend) isn't installed. Restored the capability check, short-circuiting pandas inputs via isinstance first so the hasattr path only fires for genuine polars frames.
  • Polars/pyarrow global backend causes AttributeError in all generated endpoints (issue #287): _execute_generated_endpoint was effectively calling _convert_backend twice on the same frame — once inside the middleware terminal (which resolved backend=None to the global default and returned a native frame) and again in the outer call, which tried nw_df.to_native() on the already-native frame. Pandas users were silently masked by an isinstance(nw_df, pd.DataFrame) short-circuit at the top of _convert_backend; polars and pyarrow users had no equivalent guard and saw AttributeError: 'DataFrame' object has no attribute 'to_native'. Affected all 14 generated endpoints (bdp/abdp, bdh/abdh, bds/abds, bdib/abdib, bdtick/abdtick, bql/abql, bqr/abqr, bsrch/absrch, beqs/abeqs, blkp/ablkp, bport/abport, bcurves/abcurves, bgovts/abgovts, bflds/abflds) — not just bdp as reported. Verified end-to-end against a real Bloomberg Terminal.
  • ext/futures.py date-like duck-typing: Collapsed three hasattr(value, "year"/"month"/"day") calls into a single isinstance(value, date) check now that date is imported unconditionally. More precise and removes false positives from unrelated objects that happen to expose .year.
  • markets/{info,bloomberg}.py imports: Replaced importlib.import_module("xbbg") + getattr(..., "bdp"/"abdp") # noqa: B009 with deferred from xbbg.blp import bdp/abdp inside the consuming functions. Same lazy-loading behavior, removes the lint suppression, and lets static checkers resolve the symbol.
  • Incorrect timestamp in parse_rfc3339_utc test: Fixed hardcoded expected value from 1717242600 to 1717252200 (correct UTC epoch for 2024-06-01T14:30:00+00:00).

Removed

  • Legacy configure() kwarg aliases: xbbg.configure() no longer accepts the legacy connection-style aliases carried over from xbbg 0.x: server, server_host, server_port, max_attempt, auto_restart, max_recovery, retry_max, retry_delay, retry_backoff. The NotImplementedError placeholders for sess and tls_options are likewise gone — unknown kwargs now raise a uniform TypeError. Use the canonical EngineConfig field names instead: host, port, num_start_attempts, auto_restart_on_disconnection, max_recovery_attempts, retry_max_retries, retry_initial_delay_ms, retry_backoff_factor.

Full Changelog: v1.0.0...v1.1.0