Metadata-Version: 2.4
Name: xbbg
Version: 1.1.2
Summary: Intuitive Bloomberg data API
Author-email: Alpha x1 <alpha.xone@outlook.com>
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/alpha-xone/xbbg
Project-URL: Documentation, https://alpha-xone.github.io/xbbg
Project-URL: Source, https://github.com/alpha-xone/xbbg
Project-URL: Issues, https://github.com/alpha-xone/xbbg/issues
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Rust
Classifier: Typing :: Typed
Requires-Python: <3.15,>=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: narwhals>=2.0
Requires-Dist: pyarrow>=22.0.0
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Requires-Dist: pytest-asyncio; extra == "test"
Dynamic: license-file

<!-- markdownlint-disable MD013 MD031 MD032 MD033 MD036 MD041 MD051 MD060 -->
<div align="center">

<a href="https://github.com/alpha-xone/xbbg"><img src="https://raw.githubusercontent.com/alpha-xone/xbbg/main/docs/src/assets/xbbg-logo.png" alt="xbbg logo" width="150"></a>

<!-- markdownlint-disable MD036 -->
**xbbg: An intuitive Bloomberg API for Python**
<!-- markdownlint-enable MD036 -->

[![PyPI version](https://img.shields.io/pypi/v/xbbg.svg)](https://pypi.org/project/xbbg/)
[![Python versions](https://img.shields.io/pypi/pyversions/xbbg.svg)](https://pypi.org/project/xbbg/)
[![PyPI Downloads](https://img.shields.io/pypi/dm/xbbg)](https://pypistats.org/packages/xbbg)
[![Discord](https://img.shields.io/badge/Discord-Join%20Chat-5865F2?logo=discord&logoColor=white)](https://discord.gg/P34uMwgCjC)

<a href="https://www.buymeacoffee.com/Lntx29Oof"><img src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-1E3A8A?style=plastic&logo=buy-me-a-coffee&logoColor=white" alt="Buy Me a Coffee"></a>

**Quick Links:** [Discord](https://discord.gg/P34uMwgCjC) • [Documentation](https://alpha-xone.github.io/xbbg/) • [Installation](#installation) • [Quickstart](#quickstart) • [Examples](#examples) • [Contributing](CONTRIBUTING.md) • [Changelog](CHANGELOG.md)

</div>
<!-- markdownlint-enable MD033 MD041 -->

---

<!-- xbbg:latest-release-start -->
Latest release: xbbg==1.1.2 (release: [notes](https://github.com/alpha-xone/xbbg/releases/tag/v1.1.2))
<!-- xbbg:latest-release-end -->

> This `main` branch is the Rust-powered v1 release, a significant upgrade over 0.x in performance and architecture.
> Need the legacy pure-Python behavior? Use [`release/0.x`](https://github.com/alpha-xone/xbbg/tree/release/0.x).

## Table of Contents

- [Overview](#overview)
- [Why Choose xbbg?](#why-choose-xbbg)
- [Complete API Reference](#complete-api-reference)
- [Requirements](#requirements)
- [Installation](#installation)
- [Quickstart](#quickstart)
  - [Basic Usage](#basic-usage)
  - [Common Workflows](#common-workflows)
  - [Connection Options](#connection-options)
  - [Async Functions](#async-functions)
  - [Multi-Backend Support](#multi-backend-support)
- [Power User and Infrastructure APIs](#power-user-and-infrastructure-apis)
  - [Generic Requests and Raw JSON](#generic-requests-and-raw-json)
  - [Schema, Operations, and IDE Stubs](#schema-operations-and-ide-stubs)
  - [Field Metadata, Cache, and Type Resolution](#field-metadata-cache-and-type-resolution)
  - [Market Metadata and Session Overrides](#market-metadata-and-session-overrides)
  - [SDK Detection, Logging, and Diagnostics](#sdk-detection-logging-and-diagnostics)
  - [Testing Utilities](#testing-utilities)
  - [Exception Types](#exception-types)
- [Examples](#examples)
  - [📊 Reference Data](#-reference-data)
  - [📈 Historical Data](#-historical-data)
  - [⏱️ Intraday Data](#-intraday-data)
  - [🔍 Screening & Queries](#-screening-queries)
  - [📡 Real-time](#-real-time)
  - [🔧 Utilities](#-utilities)
- [Data Storage](#data-storage)
- [Troubleshooting](#-troubleshooting)
- [Development](#development)
- [Contributing](#contributing)
- [Getting Help](#getting-help)
- [Links](#links)
- [License](#license)

## Overview

xbbg is a comprehensive Bloomberg API wrapper for Python, providing a clean, Pythonic interface to Bloomberg data services.
This `main` branch is the v1 release: core request execution is Rust-powered while preserving the familiar xbbg API for day-to-day workflows.

### Key Features

<table>
<tr>
<td width="50%">

**Complete API Coverage**
- Reference data (BDP/BDS)
- Historical time series (BDH)
- Intraday bars and tick data
- Real-time subscriptions
- BQL, BEQS, and BSRCH queries
- Technical analysis (BTA)

</td>
<td width="50%">

**Production-Grade Features**
- Parquet caching for intraday bars
- Async/await support for non-blocking operations
- **Multi-backend output** (pandas, Polars, PyArrow, DuckDB)
- Full type hints for IDE integration
- Comprehensive error handling
- Exchange-aware market hours

</td>
</tr>
<tr>
<td width="50%">

**Excel Compatibility**
- Familiar Bloomberg Excel syntax
- Same field names and date formats
- Minimal learning curve for Excel users
- Direct migration path from Excel workflows

</td>
<td width="50%">

**Developer Experience**
- Consistent, intuitive API design
- Extensive documentation and examples
- Active community support (Discord)
- Regular updates and maintenance
- Semantic versioning

</td>
</tr>
</table>

### Quick Example

```python
from xbbg import blp

# Reference data
prices = blp.bdp(['AAPL US Equity', 'MSFT US Equity'], 'PX_LAST')

# Historical data
hist = blp.bdh('SPX Index', 'PX_LAST', '2024-01-01', '2024-12-31')

# Intraday bars with sub-minute precision
intraday = blp.bdib('TSLA US Equity', dt='2024-01-15', interval=10, intervalHasSeconds=True)
```

See [`examples/xbbg_jupyter_examples.ipynb`](examples/xbbg_jupyter_examples.ipynb) for comprehensive tutorials and examples.

## Why Choose xbbg?

xbbg is the **most complete and production-ready** Bloomberg API wrapper for Python, trusted by quantitative researchers and financial engineers worldwide. Here's what sets it apart:

### 🎯 Unmatched Feature Coverage

xbbg is the **only Python library** that provides:
- **Complete Bloomberg API access**: All major services (Reference, Historical, Intraday, Real-time, BQL, BEQS, BSRCH)
- **Sub-second precision**: Down to 10-second intraday bars (unique to xbbg)
- **Real-time streaming**: Live market data with async support
- **Advanced utilities**: Futures/CDX contract resolution, currency conversion, market hours

### 📊 Production-Grade Features

- **Intraday caching**: Automatic Parquet storage for `bdib()` bar data
- **Async/await support**: Non-blocking operations for modern Python applications
- **Exchange-aware sessions**: Precise market hour handling for 50+ global exchanges
- **Type safety**: Full type hints for IDE autocomplete and static analysis
- **Comprehensive error handling**: Clear, actionable error messages

### 💡 Developer Experience

- **Excel-compatible**: Use familiar Bloomberg Excel syntax - zero learning curve
- **Pythonic API**: Consistent, intuitive function names (`bdp`, `bdh`, `bdib`)
- **Rich documentation**: 100+ examples, Jupyter notebooks, comprehensive guides
- **Active community**: Discord support, regular updates, responsive maintainers

### 🚀 Performance & Reliability

- **Battle-tested**: Used in production by hedge funds, asset managers, and research teams
- **Modern Python**: Supports Python 3.10-3.14 with latest language features
- **CI/CD pipeline**: Automated testing across multiple Python versions and platforms
- **Semantic versioning**: Predictable releases with clear upgrade paths

### Comparison with Alternatives

| Feature | xbbg | pdblp | blp | polars-bloomberg |
|---------|------|-------|-----|------------------|
| **Data Services** | | | | |
| Reference Data (BDP/BDS) | ✅ | ✅ | ✅ | ✅ |
| Historical Data (BDH) | ✅ | ✅ | ✅ | ✅ |
| Intraday Bars (BDIB) | ✅ | ❌ | ❌ | ✅ |
| Tick-by-Tick Data | ✅ | ❌ | ❌ | ❌ |
| Real-time Subscriptions | ✅ | ❌ | ❌ | ❌ |
| **Advanced Features** | | | | |
| Equity Screening (BEQS) | ✅ | ❌ | ❌ | ❌ |
| Query Language (BQL) | ✅ | ❌ | ❌ | ✅ |
| Quote Request (BQR) | ✅ | ❌ | ❌ | ❌ |
| Search (BSRCH) | ✅ | ❌ | ❌ | ✅ |
| Technical Analysis (BTA) | ✅ | ❌ | ❌ | ❌ |
| Yield & Spread Analysis (YAS) | ✅ | ❌ | ❌ | ❌ |
| **Developer Features** | | | | |
| Excel-compatible syntax | ✅ | ❌ | ❌ | ❌ |
| Sub-minute intervals (10s bars) | ✅ | ❌ | ❌ | ❌ |
| Async/await support | ✅ | ❌ | ❌ | ❌ |
| Intraday bar caching (Parquet) | ✅ | ❌ | ❌ | ❌ |
| Multi-backend output | ✅ | ❌ | ❌ | ❌ |
| **Utilities** | | | | |
| Currency conversion | ✅ | ❌ | ❌ | ❌ |
| Futures contract resolution | ✅ | ❌ | ❌ | ❌ |
| CDX index resolution | ✅ | ❌ | ❌ | ❌ |
| Exchange market hours | ✅ | ❌ | ❌ | ❌ |
| **Project Health** | | | | |
| Active development | ✅ | ❌[^1] | ✅ | ✅ |
| Python version support | 3.10-3.14 | 3.8+ | 3.8+ | 3.12+ |
| DataFrame library | **Multi-backend** | pandas | pandas | Polars |
| Type hints | ✅ Full | ❌ | Partial | ✅ Full |
| CI/CD testing | ✅ | ❌ | ✅ | ✅ |

[^1]: pdblp has been superseded by blp and is no longer under active development.

**Bottom line**: If you need comprehensive Bloomberg API access with production-grade features, xbbg is the clear choice.

## Complete API Reference

> Unless noted otherwise, the typed request helpers below have async `a...` counterparts (`bdp` → `abdp`, `bcurves` → `abcurves`, etc.).

### Reference Data - Point-in-Time Snapshots

| Function | Description | Key Features |
|----------|--------------|-------------|
| **`bdp()`** | Get current/reference data | Multiple tickers & fields<br>Excel-style overrides<br>ISIN/CUSIP/SEDOL support |
| **`bds()`** | Bulk/multi-row data | Portfolio holdings<br>Fixed income cash flows<br>Corporate actions |
| **`abdp()`** | Async reference data | Non-blocking operations<br>Concurrent requests<br>Web application friendly |
| **`abds()`** | Async bulk data | Parallel bulk queries<br>Same API as `bds()` |
| **`bflds()`** | Unified field metadata | Get field info or search by keyword<br>Single function for both use cases |
| **`fieldInfo()`** | Field metadata lookup (alias for `bflds`) | Data types & descriptions<br>Discover available fields |
| **`fieldSearch()`** | Search Bloomberg fields (alias for `bflds`) | Find fields by keyword<br>Explore data catalog |
| **`blkp()`** | Find tickers by name | Company name search<br>Asset class filtering |
| **`bport()`** | Portfolio data queries | Dedicated portfolio API<br>Holdings & weights |

### Fixed Income Analytics

| Function | Description | Key Features |
|----------|-------------|--------------|
| **`yas()`** | Yield & Spread Analysis | YAS calculator wrapper<br>YTM/YTC yield types<br>Price↔yield conversion<br>Spread calculations |
| **`preferreds()`** | Preferred security screening | Issuer-centric lookup<br>BQL-backed preferred universe |
| **`corporate_bonds()`** | Corporate bond screening | Cross-market debt lookup<br>Issuer-to-bond discovery |

> `xbbg.ext` follows the same async naming convention as the core API: extension helpers generally expose `a...` variants without needing separate conceptual documentation for each async name.

### Bond Analytics (via `xbbg.ext`)

| Function | Description | Key Features |
|----------|-------------|--------------|
| **`bond_info()`** | Bond reference metadata | Ratings, maturity, coupon |
| **`bond_risk()`** | Duration and risk metrics | Modified/Macaulay duration, convexity, DV01 |
| **`bond_spreads()`** | Spread analytics | OAS, Z-spread, I-spread, ASW |
| **`bond_cashflows()`** | Cash flow schedule | Coupon and principal payments |
| **`bond_key_rates()`** | Key rate durations | Key rate DV01s and risks |
| **`bond_curve()`** | Relative value comparison | Multi-bond analytics |

### Options Analytics (via `xbbg.ext`)

| Function | Description | Key Features |
|----------|-------------|--------------|
| **`option_info()`** | Contract metadata | Strike, expiry, exercise type |
| **`option_greeks()`** | Greeks and implied vol | Delta, gamma, theta, vega, IV |
| **`option_pricing()`** | Value decomposition | Intrinsic/time value, activity |
| **`option_chain()`** | Chain via overrides | `CHAIN_TICKERS` with filtering |
| **`option_chain_bql()`** | Chain via BQL | Rich filtering, expiry/strike ranges |
| **`option_screen()`** | Multi-option comparison | Side-by-side analytics |

Options helper enums exported by `xbbg.ext`:
- **`PutCall`** — put/call selector
- **`ChainPeriodicity`** — chain interval / expiry grouping
- **`StrikeRef`** — strike-reference mode
- **`ExerciseType`** — American/European exercise metadata
- **`ExpiryMatch`** — expiry matching strategy

### CDX Analytics (via `xbbg.ext`)

| Function | Description | Key Features |
|----------|-------------|--------------|
| **`cdx_info()`** | CDX reference metadata | Series, version, constituents |
| **`cdx_defaults()`** | Default history | Settled defaults in index |
| **`cdx_pricing()`** | Market pricing | Spread, price, recovery rate |
| **`cdx_risk()`** | Risk metrics | DV01, duration, spread sensitivity |
| **`cdx_basis()`** | Basis analytics | CDX vs intrinsics spread |
| **`cdx_default_prob()`** | Default probability | Implied default rates |
| **`cdx_cashflows()`** | Cash flow schedule | Premium and protection legs |
| **`cdx_curve()`** | Term structure | Multi-tenor curve analytics |

### Historical Data - Time Series Analysis

| Function | Description | Key Features |
|----------|-------------|--------------|
| **`bdh()`** | End-of-day historical data | Flexible date ranges<br>Multiple frequencies<br>Dividend/split adjustments |
| **`abdh()`** | Async historical data | Non-blocking time series<br>Batch historical queries |
| **`dividend()`** | Dividend & split history | All dividend types<br>Projected dividends<br>Date range filtering |
| **`earnings()`** | Corporate earnings | Geographic breakdowns<br>Product segments<br>Fiscal period analysis |
| **`turnover()`** | Trading volume & turnover | Multi-currency support<br>Automatic FX conversion |

### Intraday Data - High-Frequency Analysis

| Function | Description | Key Features |
|----------|-------------|--------------|
| **`bdib()`** | Intraday bar data | Sub-minute bars (10s intervals)<br>Session filtering (open/close)<br>Exchange-aware timing<br>Timezone control (`tz` parameter) |
| **`bdtick()`** | Tick-by-tick data | Trade & quote events<br>Condition codes<br>Exchange/broker details |

### Screening & Advanced Queries

| Function | Description | Key Features |
|----------|-------------|--------------|
| **`beqs()`** | Bloomberg Equity Screening | Custom screening criteria<br>Private & public screens |
| **`bql()`** | Bloomberg Query Language | SQL-like syntax<br>Complex transformations<br>Options chain analysis |
| **`bqr()`** | Bloomberg Quote Request | Tick-level dealer quotes<br>Broker attribution codes<br>Spread price & yield data<br>Date offset support (-2d, -1w) |
| **`bsrch()`** | SRCH (Search) queries | Fixed income searches<br>Commodity screens<br>Weather data |
| **`bcurves()`** | Yield-curve discovery | Country/currency filters<br>Curve ID lookup |
| **`bgovts()`** | Government security search | Treasury/sovereign lookup<br>Partial or exact matching |
| **`bta()`** | Technical Analysis | 50+ technical indicators<br>Custom studies |
| **`ta_studies()`** | Technical analysis catalog | Discover available studies |
| **`ta_study_params()`** | TA parameter inspection | Study inputs, defaults, and metadata |
| **`etf_holdings()`** | ETF holdings via BQL | Complete holdings list<br>Weights & positions |

### Real-Time - Live Market Data

| Function | Description | Key Features |
|----------|-------------|--------------|
| **`subscribe()`** | Real-time subscriptions | Async iteration<br>Topic failure isolation<br>`status` / `events` / `stats` observability |
| **`stream()`** | Simplified streaming | Context manager<br>Non-blocking updates |
| **`vwap()`** | Real-time VWAP | Streaming volume-weighted average price |
| **`mktbar()`** | Real-time market bars | Streaming OHLCV bars |
| **`depth()`** | Market depth | Streaming order book levels<br>B-PIPE required |
| **`chains()`** | Option/futures chains | Real-time chain data<br>B-PIPE required |

### Utilities

| Function | Description | Key Features |
|----------|-------------|--------------|
| **`convert_ccy()`** | Currency conversion | Multi-currency DataFrames<br>Historical FX rates<br>Automatic alignment |
| **`fut_ticker()`** | Futures contract resolution | Generic to specific mapping<br>Date-aware selection |
| **`active_futures()`** | Active futures selection | Volume-based logic<br>Roll date handling |
| **`cdx_ticker()`** | CDX index resolution | Series mapping<br>Index family support |
| **`active_cdx()`** | Active CDX selection | On-the-run detection<br>Lookback windows |

### Schema Introspection

| Function | Description | Key Features |
|----------|-------------|--------------|
| **`bops()`** | List service operations | Discover available request types |
| **`bschema()`** | Get operation schema | Field definitions, types, enums |
| **`list_valid_elements()`** | Valid request elements | Check parameter names before sending |
| **`get_enum_values()`** | Enum values for a field | Discover valid override values |
| **`generate_stubs()`** | IDE stub generation | Auto-complete for request parameters |

### Engine Lifecycle

| Function | Description | Key Features |
|----------|-------------|--------------|
| **`configure()`** | Engine and session setup | Server host/port, auth, options<br>Replaces `connect()` / `disconnect()` |
| **`shutdown()`** | Stop engine and sessions | Graceful cleanup |
| **`reset()`** | Reset engine state | Clear sessions and caches |
| **`is_connected()`** | Check connection status | Boolean connectivity check |

### Request Middleware

| Function | Description | Key Features |
|----------|-------------|--------------|
| **`add_middleware()`** | Register request middleware | Logging, caching, instrumentation |
| **`set_middleware()`** | Replace middleware chain | Install a known pipeline in one call |
| **`get_middleware()`** | Inspect middleware chain | Useful in apps/tests before mutation |
| **`remove_middleware()`** | Unregister middleware | Clean removal |
| **`clear_middleware()`** | Clear middleware chain | Reset to a pristine request path |
| **`RequestContext`** | Request metadata | Request ID, request payload, timing, results |
| **`RequestEnvironment`** | Engine/auth snapshot | Host, auth method, validation mode, server list |

### Additional Features

- **Intraday Caching**: Automatic Parquet storage for `bdib()` bar data
- **Timezone Support**: Exchange-aware market hours for 50+ global exchanges; `bdib()` / `bdtick()` support `request_tz` and `output_tz` (interpretation and `time`-column relabeling in the Rust engine; Bloomberg wire format remains UTC)
- **Per-request field validation**: `validate_fields=` can override engine-level validation on `bdp()` / `bds()` / `bdh()`
- **Scoped engines**: `Engine(...)` lets you route a block of requests to a dedicated connection without mutating global state
- **Configurable Logging**: Debug mode for troubleshooting
- **Batch Processing**: Efficient multi-ticker queries
- **Standardized Output**: Consistent DataFrame column naming
- **Non-live test helpers**: `xbbg.testing` exposes `mock_engine()` and TestUtil-backed helpers for unit testing Bloomberg flows without a live terminal

## Requirements

- **Bloomberg C++ SDK** version 3.12.1 or higher:
  - Visit [Bloomberg API Library](https://www.bloomberg.com/professional/support/api-library/) and download C++ Supported Release
  - For local source builds in this repo, install it with `bash ./scripts/sdktool.sh` on macOS/Linux or `.\scripts\sdktool.ps1` on Windows PowerShell
  - If you manage the SDK yourself, set `BLPAPI_ROOT` to the extracted SDK root

- **Bloomberg official Python API**:

```cmd
pip install blpapi --index-url=https://blpapi.bloomberg.com/repository/releases/python/simple/
```

- **Python dependencies (core)**: `narwhals>=1.30`, `pyarrow>=22.0.0`

- **Optional backends** (install separately if needed):
  - `pandas` - For pandas DataFrame output (`pip install pandas`)
  - `polars` - For Polars DataFrame output
  - `duckdb` - For DuckDB relation output

## Installation

```cmd
pip install xbbg
```

For most users, also install Bloomberg's official Python package so xbbg can auto-detect the SDK runtime:

```cmd
pip install blpapi --index-url=https://blpapi.bloomberg.com/repository/releases/python/simple/
```

Supported Python versions: **3.10 – 3.14** (universal wheel).

Quick verification:

```python
import xbbg

print(xbbg.__version__)
print(xbbg.get_sdk_info())

# Optional: if you manage the SDK yourself instead of using blpapi/DAPI
# xbbg.set_sdk_path('/path/to/blpapi_cpp')
# xbbg.clear_sdk_path()  # remove a manual override
```

### MCP Server for Claude Code / OpenCode

Need xbbg inside a coding agent instead of Python code? Install the local MCP wrapper + binary from GitHub Releases:

```bash
curl -fsSL https://raw.githubusercontent.com/alpha-xone/xbbg/main/scripts/install-xbbg-mcp.sh | sh
```

The installer currently targets **macOS arm64** and **Linux amd64**. Windows `.zip` assets are attached to GitHub releases for manual installation.

GitHub release assets include only the xbbg wrapper/binary pair. They do **not** bundle Bloomberg SDK files or the Bloomberg runtime; you must supply those locally via `blpapi`, `BLPAPI_ROOT`, or another supported runtime source.

Claude Code:

```bash
claude mcp add --transport stdio xbbg -- ~/.local/bin/xbbg-mcp
```

OpenCode:

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "xbbg": {
      "type": "local",
      "command": ["/Users/you/.local/bin/xbbg-mcp"],
      "enabled": true
    }
  }
}
```

The launcher searches for the Bloomberg runtime via `XBBG_MCP_LIB_DIR`, `BLPAPI_LIB_DIR`, `BLPAPI_ROOT`, a vendored SDK under `vendor/blpapi-sdk/`, or the official `blpapi` Python package. See [`apps/xbbg-mcp/README.md`](apps/xbbg-mcp/README.md) for the full env surface.

## Quickstart

### Basic Usage

```python
from xbbg import blp

# Get current stock prices
prices = blp.bdp(['AAPL US Equity', 'MSFT US Equity'], 'PX_LAST')
print(prices)
```

### Common Workflows

<details>
<summary><b>📊 Get Reference Data (Current Snapshot)</b></summary>

```python
# Single ticker, multiple fields
info = blp.bdp('NVDA US Equity', ['Security_Name', 'GICS_Sector_Name', 'PX_LAST'])

# Multiple tickers, single field
prices = blp.bdp(['AAPL US Equity', 'MSFT US Equity', 'GOOGL US Equity'], 'PX_LAST')

# With overrides (e.g., VWAP for specific date)
vwap = blp.bdp('AAPL US Equity', 'Eqy_Weighted_Avg_Px', VWAP_Dt='20240115')
```

</details>

<details>
<summary><b>📈 Get Historical Data (Time Series)</b></summary>

```python
# Simple historical query
hist = blp.bdh('SPX Index', 'PX_LAST', '2024-01-01', '2024-12-31')

# Multiple fields
ohlc = blp.bdh('AAPL US Equity', ['open', 'high', 'low', 'close'], '2024-01-01', '2024-01-31')

# With dividend/split adjustments
adjusted = blp.bdh('AAPL US Equity', 'px_last', '2024-01-01', '2024-12-31', adjust='all')

# Weekly data with forward fill
weekly = blp.bdh('SPX Index', 'PX_LAST', '2024-01-01', '2024-12-31', Per='W', Fill='P')
```

</details>

<details>
<summary><b>⏱️ Get Intraday Data (High Frequency)</b></summary>

```python
# 5-minute bars
bars_5m = blp.bdib('SPY US Equity', dt='2024-01-15', interval=5)

# 1-minute bars (default)
bars_1m = blp.bdib('TSLA US Equity', dt='2024-01-15')

# Sub-minute bars (10-second intervals) - UNIQUE TO XBBG!
bars_10s = blp.bdib('AAPL US Equity', dt='2024-01-15', interval=10, intervalHasSeconds=True)

# Session filtering (e.g., first 30 minutes)
opening = blp.bdib('SPY US Equity', dt='2024-01-15', session='day_open_30')

# Get data in UTC instead of exchange local time
bars_utc = blp.bdib('SPY US Equity', dt='2024-01-15', tz='UTC')
```

</details>

<details>
<summary><b>🔍 Advanced Queries (BQL, Screening)</b></summary>

```python
# Bloomberg Query Language
result = blp.bql("get(px_last) for('AAPL US Equity')")

# Equity screening
screen_results = blp.beqs(screen='MyScreen', asof='2024-01-01')

# ETF holdings
holdings = blp.etf_holdings('SPY US Equity')

# Search queries
bonds = blp.bsrch("FI:MYSEARCH")

# Dealer quotes with broker codes (BQR)
quotes = blp.bqr("XYZ 4.5 01/15/30@MSG1 Corp", date_offset="-2d")
```

</details>

<details>
<summary><b>🔧 Utilities (Futures, Currency, etc.)</b></summary>

```python
# Resolve futures contract
contract = blp.fut_ticker('ES1 Index', '2024-01-15', freq='ME')  # → 'ESH24 Index'

# Get active futures
active = blp.active_futures('ESA Index', '2024-01-15')

# Currency conversion
hist_usd = blp.bdh('BMW GR Equity', 'PX_LAST', '2024-01-01', '2024-01-31')
hist_eur = blp.convert_ccy(hist_usd, ccy='EUR')

# Dividend history
divs = blp.dividend('AAPL US Equity', start_date='2024-01-01', end_date='2024-12-31')
```

</details>

<details>
<summary><b>Fixed Income Analytics (Bond, CDX)</b></summary>

```python
from xbbg.ext import bond_info, bond_risk, bond_spreads, cdx_info, cdx_pricing

# Bond reference data
info = bond_info('T 4.5 05/15/38 Govt')

# Bond risk metrics (duration, convexity, DV01)
risk = bond_risk('T 4.5 05/15/38 Govt')

# Bond spreads (OAS, Z-spread, ASW)
spreads = bond_spreads('T 4.5 05/15/38 Govt')

# CDX index info
cdx = cdx_info('CDX IG CDSI GEN 5Y Corp')

# CDX pricing
px = cdx_pricing('CDX IG CDSI GEN 5Y Corp')
```

</details>

<details>
<summary><b>Options Analytics</b></summary>

```python
from xbbg.ext import option_info, option_greeks, option_chain_bql

# Option contract metadata
info = option_info('AAPL US 01/17/25 C200 Equity')

# Greeks and implied volatility
greeks = option_greeks('AAPL US 01/17/25 C200 Equity')

# Option chain via BQL (rich filtering)
chain = option_chain_bql('AAPL US Equity', expiry='2025-01-17')
```

</details>

### Best Practices

- **Excel users**: Use the same field names and date formats as Bloomberg Excel
- **Performance**: `bdib()` caches intraday bars as Parquet files automatically (see [Data Storage](#data-storage))
- **Async operations**: Use `abdp()`, `abdh()`, `abds()` for non-blocking requests
- **Debugging**: Set `logging.getLogger('xbbg').setLevel(logging.DEBUG)` for detailed logs

### Connection Options

By default, xbbg connects to `localhost` on port `8194`. To connect to a remote Bloomberg server or configure authentication, use `configure()`:

```python
from xbbg import blp

# Connect to a remote Bloomberg server
blp.configure(server_host='192.168.1.100', server_port=18194)

# With SAPI authentication
blp.configure(
    server_host='192.168.1.100',
    server_port=18194,
    auth_method='app',
    app_name='myapp:SAPI',
)

# All subsequent calls use the configured connection
blp.bdp(tickers='NVDA US Equity', flds=['Security_Name'])
```

You can also pass `server` and `port` as kwargs to individual function calls for ad-hoc connections.

### Engine Architecture

xbbg v1 is powered by a Rust async engine with **pre-warmed worker pools**:

```
┌─────────────────────────────────────────────────┐
│                   xbbg Engine                   │
│                                                 │
│  ┌──────────────────────┐  ┌─────────────────┐  │
│  │  Request Worker Pool │  │ Subscription    │  │
│  │  (request_pool_size) │  │ Session Pool    │  │
│  │                      │  │ (sub_pool_size) │  │
│  │  Worker 1 ──session  │  │                 │  │
│  │  Worker 2 ──session  │  │  Session 1      │  │
│  │  ...                 │  │  ...            │  │
│  └──────────────────────┘  └─────────────────┘  │
│           │ round-robin          │ isolated      │
│           ▼                      ▼               │
│    bdp/bdh/bds/bdib       subscribe/stream       │
│    bql/bsrch/beqs         vwap/mktbar/depth      │
└─────────────────────────────────────────────────┘
```

- **Request workers** each hold an independent Bloomberg session. Concurrent `bdp`/`bdh`/`bds` calls are dispatched round-robin across workers, so `request_pool_size=4` allows 4 parallel Bloomberg requests.
- **Subscription sessions** are isolated per session to avoid cross-contamination between topic streams. Each `subscribe()` call gets its own Bloomberg session from the pool.
- Workers are **pre-warmed** at first use — sessions are started and services opened before your first request, eliminating cold-start latency.

### EngineConfig Reference

Call `configure()` before any Bloomberg request to tune the engine. All fields have sensible defaults:

```python
from xbbg import configure, EngineConfig

# Keyword arguments (most common)
configure(request_pool_size=4, subscription_pool_size=2)

# Or use an EngineConfig object
configure(EngineConfig(request_pool_size=4, subscription_pool_size=2))
```

#### Connection & Session

| Parameter | Default | Description |
|-----------|---------|-------------|
| `host` | `'localhost'` | Bloomberg server host. Aliases: `server_host`, `server` |
| `port` | `8194` | Bloomberg server port. Alias: `server_port` |
| `num_start_attempts` | `3` | Retries before giving up on session start. Alias: `max_attempt` |
| `auto_restart_on_disconnection` | `True` | Auto-reconnect on session disconnect. Alias: `auto_restart` |

#### Worker Pools

| Parameter | Default | Description |
|-----------|---------|-------------|
| `request_pool_size` | `2` | Number of pre-warmed request workers (parallel Bloomberg sessions for bdp/bdh/bds/etc.) |
| `subscription_pool_size` | `1` | Number of pre-warmed subscription sessions (isolated sessions for subscribe/stream) |
| `warmup_services` | `['//blp/refdata', '//blp/apiflds']` | Services to pre-open on startup |

#### Subscription Tuning

| Parameter | Default | Description |
|-----------|---------|-------------|
| `subscription_flush_threshold` | `1` | Ticks buffered before flushing to Python (increase for throughput, decrease for latency) |
| `subscription_stream_capacity` | `256` | Backpressure buffer size per subscription stream |
| `overflow_policy` | `'drop_newest'` | Slow consumer policy: `'drop_newest'` or `'block'` |

#### Internal Buffers

| Parameter | Default | Description |
|-----------|---------|-------------|
| `max_event_queue_size` | `10000` | Bloomberg SDK event queue depth |
| `command_queue_size` | `256` | Internal command channel capacity |

#### Validation

| Parameter | Default | Description |
|-----------|---------|-------------|
| `validation_mode` | `'disabled'` | Field validation: `'disabled'`, `'strict'` (reject unknown fields), or `'lenient'` (warn) |
| `field_cache_path` | `~/.xbbg/field_cache.json` | Path for persistent field type cache. Set to customize location |

#### Authentication (SAPI / B-PIPE)

| Parameter | Default | Description |
|-----------|---------|-------------|
| `auth_method` | `None` | Auth mode: `'user'`, `'app'`, `'userapp'`, `'dir'`, `'manual'`, or `'token'` |
| `app_name` | `None` | Application name (required for `app`, `userapp`, `manual`) |
| `user_id` | `None` | Bloomberg user ID (required for `manual`) |
| `ip_address` | `None` | Bloomberg IP address (required for `manual`) |
| `dir_property` | `None` | Active Directory property (required for `dir`) |
| `token` | `None` | Auth token (required for `token`) |

**Auth mode examples:**

```python
# B-PIPE application auth
configure(auth_method='app', app_name='myapp:8888', host='bpipe-host')

# Manual auth (SAPI)
configure(auth_method='manual', app_name='myapp', user_id='12345', ip_address='10.0.0.1')

# Active Directory auth
configure(auth_method='dir', dir_property='mail')
```

### Async Functions

Every sync function has an async counterpart prefixed with `a` — for example `bdp()` → `abdp()`, `bdh()` → `abdh()`, `bdib()` → `abdib()`. In the v1 architecture, async implementations are the canonical source of truth and sync functions delegate via `_run_sync()`.

Common async families:

| Sync | Async |
|------|-------|
| `bdp`, `bds`, `bdh`, `bdib`, `bdtick` | `abdp`, `abds`, `abdh`, `abdib`, `abdtick` |
| `bql`, `bsrch`, `bqr`, `beqs` | `abql`, `absrch`, `abqr`, `abeqs` |
| `blkp`, `bport`, `bcurves`, `bgovts` | `ablkp`, `abport`, `abcurves`, `abgovts` |
| `subscribe`, `stream`, `vwap`, `mktbar`, `depth`, `chains` | `asubscribe`, `astream`, `avwap`, `amktbar`, `adepth`, `achains` |
| `bta`, `bflds`, `fieldInfo`, `fieldSearch` | `abta`, `abflds`, `afieldInfo`, `afieldSearch` |

#### In scripts (no existing event loop)

```python
import asyncio
from xbbg import blp

async def get_data():
    df = await blp.abdp(tickers='AAPL US Equity', flds=['PX_LAST', 'VOLUME'])
    return df

async def get_multiple():
    # Concurrent requests — runs in parallel on a single thread
    results = await asyncio.gather(
        blp.abdp(tickers='AAPL US Equity', flds=['PX_LAST']),
        blp.abdp(tickers='MSFT US Equity', flds=['PX_LAST']),
        blp.abdh(tickers='GOOGL US Equity', start_date='2024-01-01'),
    )
    return results

data = asyncio.run(get_data())
multiple = asyncio.run(get_multiple())
```

#### In Jupyter notebooks

Jupyter already runs an event loop, so `asyncio.run()` will raise `RuntimeError: asyncio.run() cannot be called from a running event loop`. Use `await` directly in notebook cells instead:

```python
from xbbg import blp

# Just await directly — Jupyter cells are already async
df = await blp.abdp(tickers='AAPL US Equity', flds=['PX_LAST', 'VOLUME'])

# Concurrent requests work the same way
import asyncio
results = await asyncio.gather(
    blp.abdp(tickers='AAPL US Equity', flds=['PX_LAST']),
    blp.abdp(tickers='MSFT US Equity', flds=['PX_LAST']),
)
```

> **Tip:** If you don't need async, the sync functions (`bdp`, `bdh`, `bdib`, etc.) work everywhere — scripts, notebooks, and async contexts — without any special handling.

**Benefits:**
- Non-blocking: doesn't block the event loop
- Concurrent: use `asyncio.gather()` for parallel requests
- Compatible: works with async web frameworks, Jupyter, and async codebases
- Same API: identical parameters to sync versions (`bdp`, `bds`, `bdh`)

### Multi-Backend Support

Starting with v0.11.0, xbbg is **DataFrame-library agnostic**. You can get output in your preferred format:

#### Supported Backends

| Backend | Type | Output | Best For |
|---------|------|--------|----------|
| **Eager Backends** ||||
| `pandas` | Eager | `pd.DataFrame` | Traditional workflows, compatibility |
| `polars` | Eager | `pl.DataFrame` | High performance, large datasets |
| `pyarrow` | Eager | `pa.Table` | Zero-copy interop, memory efficiency |
| `narwhals` | Eager | Narwhals DataFrame | Library-agnostic code |
| `modin` | Eager | Modin DataFrame | Pandas API with parallel execution |
| `cudf` | Eager | cuDF DataFrame | GPU-accelerated processing (NVIDIA) |
| **Lazy Backends** ||||
| `polars_lazy` | Lazy | `pl.LazyFrame` | Deferred execution, query optimization |
| `narwhals_lazy` | Lazy | Narwhals LazyFrame | Library-agnostic lazy evaluation |
| `duckdb` | Lazy | DuckDB relation | SQL analytics, OLAP queries |
| `dask` | Lazy | Dask DataFrame | Out-of-core and distributed computing |
| `ibis` | Lazy | Ibis Table | Unified interface to many backends |
| `pyspark` | Lazy | Spark DataFrame | Big data processing (requires Java) |
| `sqlframe` | Lazy | SQLFrame DataFrame | SQL-first DataFrame operations |

**Note:** `wide` / `Format.WIDE` was removed in v1.0.0rc4. Use `semi_long` for one row per security, or pivot a `long` result explicitly in your downstream DataFrame library.

#### Check Backend Availability

```python
from xbbg import get_available_backends, print_backend_status, is_backend_available

# List installed backends
print(get_available_backends())  # ['pandas', 'polars', 'pyarrow', ...]

# Check if a specific backend is available
if is_backend_available('polars'):
    print("Polars is installed!")

# Print detailed status of all backends
print_backend_status()
```

#### Usage

```python
from xbbg import blp, Backend

# Get data as Polars DataFrame
df_polars = blp.bdp('AAPL US Equity', 'PX_LAST', backend=Backend.POLARS)

# Get data as PyArrow Table
table = blp.bdh('SPX Index', 'PX_LAST', '2024-01-01', '2024-12-31', backend=Backend.PYARROW)

# Get data as pandas (default)
df_pandas = blp.bdp('MSFT US Equity', 'PX_LAST', backend=Backend.PANDAS)
```

#### Output Formats

Control the shape of your data with the `format` parameter:

| Format | Description | Use Case |
|--------|-------------|----------|
| `long` | Tidy format with ticker, field, value columns | Analysis, joins, aggregations |
| `long_typed` | Typed value columns per data type | Type-safe analysis, no casting needed |
| `long_metadata` | String values with dtype metadata column | Serialization, debugging, data catalogs |
| `semi_long` | One row per security/date, fields as columns | Quick inspection, Excel-style output, replacement for removed `wide` |

```python
from xbbg import blp

# Long format (tidy data, default)
df_long = blp.bdp(['AAPL US Equity', 'MSFT US Equity'], ['PX_LAST', 'VOLUME'], format='long')

# Semi-long format (one row per ticker, fields as columns)
df_semi = blp.bdh('SPX Index', 'PX_LAST', '2024-01-01', '2024-12-31', format='semi_long')
```

#### Global Configuration

Set defaults for your entire session:

```python
from xbbg import set_backend, Backend
from xbbg import blp, get_backend

# Set Polars as default backend
set_backend(Backend.POLARS)
print(get_backend())  # Backend.POLARS

# All subsequent calls use this default
df = blp.bdp('AAPL US Equity', 'PX_LAST')  # Returns Polars DataFrame
```

#### Why Multi-Backend?

- **Performance**: Polars and PyArrow can be 10-100x faster for large datasets
- **Memory**: Arrow-based backends use zero-copy and columnar storage
- **Interoperability**: Direct integration with DuckDB, Spark, and other Arrow-compatible tools
- **Future-proof**: Write library-agnostic code with narwhals backend

## Power User and Infrastructure APIs

The core `blp.bdp()` / `blp.bdh()` workflow covers most day-to-day usage, but the current package exposes several advanced surfaces that are easy to miss if you only skim the quickstart. This section summarizes the non-obvious parts of the public API so the README tracks the package more faithfully.

### Generic Requests and Raw JSON

For uncommon Bloomberg operations, raw service access, or debugging request payloads, use the generic request layer:

| Surface | Purpose | Notes |
|---------|---------|-------|
| `request()` / `arequest()` | Low-level request entrypoint | Works with arbitrary Bloomberg services and operations |
| `Service` / `Operation` | Enum wrappers for service URIs and request types | Safer than hand-typed strings |
| `RequestParams` | Dataclass for validated request payloads | Useful in middleware or reusable request builders |
| `OutputMode` | Output transport (`ARROW` or `JSON`) | `JSON` is useful for debugging Bloomberg payloads |
| `ExtractorHint` | Override extraction strategy | Advanced escape hatch for bulk/custom responses |

```python
from xbbg import Operation, OutputMode, Service, request

# Generic refdata request
df = request(
    Service.REFDATA,
    Operation.REFERENCE_DATA,
    securities=['AAPL US Equity'],
    fields=['PX_LAST', 'VOLUME'],
)

# Raw JSON transport for debugging/custom parsing
raw = request(
    Service.REFDATA,
    Operation.REFERENCE_DATA,
    securities=['AAPL US Equity'],
    fields=['PX_LAST'],
    output=OutputMode.JSON,
)
```

### Schema, Operations, and IDE Stubs

xbbg ships two schema surfaces:

- `blp.bops()` / `blp.bschema()` for quick interactive discovery
- `xbbg.schema` for typed schema objects and stub-generation utilities

| Surface | Purpose |
|---------|---------|
| `bops()` / `abops()` | List operations available on a Bloomberg service |
| `bschema()` / `abschema()` | Return service/operation schema as plain dictionaries |
| `xbbg.schema.get_schema()` / `aget_schema()` | Return typed `ServiceSchema` objects |
| `xbbg.schema.get_operation()` / `aget_operation()` | Return typed `OperationSchema` objects backed by `ElementInfo` trees |
| `xbbg.schema.list_operations()` / `alist_operations()` | Enumerate operations for a service |
| `xbbg.schema.get_enum_values()` / `aget_enum_values()` | Discover valid enum values for an element |
| `xbbg.schema.list_valid_elements()` / `alist_valid_elements()` | Inspect valid request element names before sending |
| `generate_stubs()` / `configure_ide_stubs()` | Generate IDE-friendly stubs from cached Bloomberg schema |
| `generate_ta_stubs()` | Generate TA helper stubs for study-specific autocomplete |

```python
from xbbg import blp
from xbbg.schema import get_operation, list_operations

print(blp.bops())  # quick list for //blp/refdata
print(list_operations('//blp/instruments'))

hist_schema = get_operation('//blp/refdata', 'HistoricalDataRequest')
print(hist_schema.request.children[0].name)
```

### Field Metadata, Cache, and Type Resolution

There are three related layers here:

1. `bflds()` / `fieldInfo()` / `fieldSearch()` for Bloomberg field catalog discovery
2. `xbbg.field_cache` for Arrow type resolution and cache management
3. `field_types=` request overrides for per-call control

| Surface | Purpose |
|---------|---------|
| `bflds()` / `abflds()` | Unified field-info and field-search entrypoint |
| `bfld()` / `abfld()` | Alias for `bflds()` |
| `fieldInfo()` / `afieldInfo()` | Alias for `bflds(fields=...)` |
| `fieldSearch()` / `afieldSearch()` | Alias for `bflds(search_spec=...)` |
| `resolve_field_types()` / `aresolve_field_types()` | Resolve Bloomberg fields to Arrow types |
| `cache_field_types()` | Pre-warm the field cache |
| `get_field_info()` | Return structured `FieldInfo` objects |
| `get_field_cache_stats()` | Inspect cache path and entry count |
| `clear_field_cache()` | Clear in-memory and on-disk field cache |
| `FieldTypeCache` | Compatibility facade over the Rust resolver |

```python
from xbbg import blp
from xbbg.field_cache import (
    get_field_cache_stats,
    resolve_field_types,
)

catalog = blp.fieldSearch('vwap')
details = blp.fieldInfo(['PX_LAST', 'VOLUME'])
types = resolve_field_types(['PX_LAST', 'NAME', 'DVD_EX_DT'])
stats = get_field_cache_stats()
```

**Important current behavior:**

- Field type resolution is Rust-backed and persistent
- `field_cache_path=` can be set via `configure(...)` before the engine starts
- `long_typed` and `long_metadata` formats are driven by these resolved field types

### Market Metadata and Session Overrides

The `xbbg.markets` module exposes exchange/session helpers that complement request APIs:

| Surface | Purpose |
|---------|---------|
| `ExchangeInfo` | Structured exchange metadata record returned by Bloomberg-backed helpers |
| `SessionWindows` | Dataclass representing derived market sessions (`day`, `pre`, `post`, etc.) |
| `market_info()` | Lightweight market metadata for a security |
| `market_timing()` | Session timing lookup for a ticker/session combination |
| `ccy_pair()` | FX conversion metadata for currency pair normalization |
| `exch_info()` | Exchange/session metadata lookup |
| `get_session_windows()` / `derive_sessions()` | Derive named session windows without a data request |
| `fetch_exchange_info()` / `afetch_exchange_info()` | Bloomberg-backed exchange metadata fetch |
| `set_exchange_override()` / `get_exchange_override()` / `clear_exchange_override()` | Runtime override lifecycle for timezone/session metadata |
| `list_exchange_overrides()` / `has_override()` | Inspect override state |
| `convert_session_times_to_utc()` | Convert local market sessions to UTC |

```python
from xbbg.markets import get_session_windows, market_info, set_exchange_override

print(market_info('ES1 Index'))
print(get_session_windows('AAPL US Equity', mic='XNAS', regular_hours=('09:30', '16:00')))

set_exchange_override(
    'MY_PRIVATE_TICKER Equity',
    timezone='America/New_York',
    sessions={'regular': ('09:30', '16:00')},
)
```

### SDK Detection, Logging, and Diagnostics

Beyond `configure()`, the package exposes helpers for SDK discovery, backend validation, and engine diagnostics:

| Surface | Purpose |
|---------|---------|
| `get_sdk_info()` | Show detected Bloomberg SDK sources and active runtime |
| `set_sdk_path()` / `clear_sdk_path()` | Manually override SDK discovery |
| `set_log_level()` / `get_log_level()` | Control Rust-side logging verbosity |
| `enable_sdk_logging()` | Surface underlying Bloomberg SDK logs |
| `get_available_backends()` / `is_backend_available()` | Inspect installed dataframe backends |
| `check_backend()` | Validate backend availability/version and get install guidance |
| `get_supported_formats()` / `is_format_supported()` | Inspect backend/format compatibility |
| `check_format_compatibility()` / `validate_backend_format()` | Guard backend + format combinations programmatically |

```python
from xbbg import Backend, check_backend, get_sdk_info, print_backend_status, validate_backend_format

print(get_sdk_info())
check_backend('polars')
validate_backend_format(Backend.PANDAS, 'semi_long')
print_backend_status()
```

### Testing Utilities

`xbbg.testing` is part of the supported public surface for non-live tests:

| Helper | Purpose |
|--------|---------|
| `MockResponse` | Structured canned-response container used by `mock_engine()` |
| `create_mock_response()` | Build canned request responses without a live terminal |
| `mock_engine()` | Context manager that intercepts xbbg calls and returns canned responses |
| `create_mock_event()` | Build Bloomberg `blpapi.test` events when `blpapi` is installed |
| `get_admin_message_definition()` | Fetch TestUtil admin-message definitions for mocked admin events |
| `deserialize_service()` | Deserialize service XML for TestUtil-backed mocks |
| `append_message_dict()` | Populate mock Bloomberg messages from Python dictionaries |

```python
from xbbg import blp
from xbbg.testing import create_mock_response, mock_engine

response = create_mock_response(
    service='//blp/refdata',
    operation='ReferenceDataRequest',
    data={'AAPL US Equity': {'PX_LAST': 210.5}},
)

with mock_engine([response]):
    df = blp.bdp('AAPL US Equity', 'PX_LAST')
```

### Exception Types

The current exception surface is intentionally typed and worth calling out in the README because many workflows want to catch Bloomberg failures explicitly:

| Exception | Typical use |
|-----------|-------------|
| `BlpError` | Base class for Bloomberg-related failures |
| `BlpSessionError` | Session startup/connectivity/auth failures |
| `BlpRequestError` | Request-level failure with service/operation context |
| `BlpSecurityError` | Security-specific request failure |
| `BlpFieldError` | Field-specific request failure |
| `BlpValidationError` | Invalid request shape, bad elements, bad enum values |
| `BlpTimeoutError` | Request timed out |
| `BlpInternalError` | Internal engine/runtime failure |
| `BlpBPipeError` | B-PIPE-only feature used without B-PIPE access (`depth`, `chains`) |

```python
from xbbg import blp
from xbbg.exceptions import BlpBPipeError, BlpValidationError

try:
    blp.depth('AAPL US Equity')
except BlpBPipeError:
    ...
except BlpValidationError as exc:
    print(exc.element, exc.suggestion)
```

## Examples

### 📊 Reference Data

#### Equity and Index Securities

```python
from xbbg import blp

# Single point-in-time data (BDP)
blp.bdp(tickers='NVDA US Equity', flds=['Security_Name', 'GICS_Sector_Name'])
```

```pydocstring
Out[2]:
               security_name        gics_sector_name
NVDA US Equity   NVIDIA Corp  Information Technology
```

```python
# With field overrides
blp.bdp('AAPL US Equity', 'Eqy_Weighted_Avg_Px', VWAP_Dt='20181224')
```

```pydocstring
Out[3]:
                eqy_weighted_avg_px
AAPL US Equity               148.75
```

```python
# Multiple tickers and fields
blp.bdp(
    tickers=['AAPL US Equity', 'MSFT US Equity', 'GOOGL US Equity'],
    flds=['Security_Name', 'GICS_Sector_Name', 'PX_LAST']
)
```

```pydocstring
Out[3a]:
                  security_name        gics_sector_name px_last
AAPL US Equity        Company A  Information Technology  150.25
GOOGL US Equity    Company B  Communication Services  165.30
MSFT US Equity   Company C  Information Technology  180.45
```

```python
# Bulk/block data (BDS) - multi-row per ticker
blp.bds('AAPL US Equity', 'DVD_Hist_All', DVD_Start_Dt='20180101', DVD_End_Dt='20180531')
```

```pydocstring
Out[8]:
               declared_date     ex_date record_date payable_date  dividend_amount dividend_frequency dividend_type
AAPL US Equity    2018-05-01  2018-05-11  2018-05-14   2018-05-17             0.73            Quarter  Regular Cash
AAPL US Equity    2018-02-01  2018-02-09  2018-02-12   2018-02-15             0.63            Quarter  Regular Cash
```

#### Fixed Income Securities

xbbg supports fixed income securities using standard security identifiers (ISIN, CUSIP, SEDOL). Use the `/isin/{isin}`, `/cusip/{cusip}`, or `/sedol/{sedol}` format as the ticker:

```python
# Reference data using ISIN
blp.bdp(tickers='/isin/US1234567890', flds=['SECURITY_NAME', 'MATURITY', 'COUPON', 'PX_LAST'])
```

```pydocstring
Out[9]:
                       security_name    maturity coupon    px_last
/isin/US1234567890  US Treasury Note  2035-05-15   4.25  101.25
```

```python
# Cash flow schedule using ISIN
blp.bds(tickers='/isin/US1234567890', flds='DES_CASH_FLOW')
```

```pydocstring
Out[10]:
                   payment_date  coupon_amount  principal_amount
/isin/US1234567890   2026-05-15        21250.0               0.0
/isin/US1234567890   2026-11-15        21250.0               0.0
/isin/US1234567890   2027-05-15        21250.0               0.0
```

**Note:** Fixed income securities work with `bdp()`, `bds()`, and `bdh()` functions. The identifier format (`/isin/`, `/cusip/`, `/sedol/`) is automatically passed to blpapi.

#### Yield & Spread Analysis (YAS)

The `yas()` function provides a convenient wrapper for Bloomberg's YAS calculator:

```python
from xbbg import blp
from xbbg.api.fixed_income import YieldType

# Get yield to maturity
blp.yas('T 4.5 05/15/38 Govt')
```

```pydocstring
Out[11]:
                     YAS_BOND_YLD
ticker
T 4.5 05/15/38 Govt         4.348
```

```python
# Calculate yield from price
blp.yas('T 4.5 05/15/38 Govt', price=95.0)
```

```pydocstring
Out[12]:
                     YAS_BOND_YLD
ticker
T 4.5 05/15/38 Govt          5.05
```

```python
# Calculate price from yield
blp.yas('T 4.5 05/15/38 Govt', flds='YAS_BOND_PX', yield_=4.8)
```

```pydocstring
Out[13]:
                     YAS_BOND_PX
ticker
T 4.5 05/15/38 Govt    97.229553
```

```python
# Yield to call for callable bonds
blp.yas('AAPL 2.65 05/11/50 Corp', yield_type=YieldType.YTC)
```

```pydocstring
Out[14]:
                          YAS_BOND_YLD
ticker
AAPL 2.65 05/11/50 Corp          5.431
```

```python
# Multiple YAS analytics
blp.yas('T 4.5 05/15/38 Govt', ['YAS_BOND_YLD', 'YAS_MOD_DUR', 'YAS_ASW_SPREAD'])
```

```pydocstring
Out[15]:
                     YAS_ASW_SPREAD  YAS_BOND_YLD  YAS_MOD_DUR
ticker
T 4.5 05/15/38 Govt       33.093531         4.348     9.324928
```

**Available parameters:**
- `settle_dt`: Settlement date (YYYYMMDD or datetime)
- `yield_type`: `YieldType.YTM` (default) or `YieldType.YTC`
- `price`: Input price to calculate yield
- `yield_`: Input yield to calculate price
- `spread`: Spread to benchmark in basis points
- `benchmark`: Benchmark bond ticker for spread calculations

#### Field Information and Search

```python
# Unified field lookup (recommended)
blp.bflds(fields=['PX_LAST', 'VOLUME'])  # Get metadata for specific fields
blp.bflds(search_spec='vwap')            # Search for fields by keyword

# Convenience aliases
blp.fieldInfo(['PX_LAST', 'VOLUME'])     # Same as bflds(fields=...)
blp.fieldSearch('vwap')                  # Same as bflds(search_spec=...)
```

#### Security Lookup

```python
# Look up securities by company name
blp.blkp('IBM', max_results=10)
```

```python
# Lookup with asset class filter
blp.blkp('Apple', yellowkey='eqty', max_results=20)
```

#### Portfolio Data

```python
# Get portfolio data (dedicated function)
blp.bport('PORTFOLIO_NAME', 'PORTFOLIO_MWEIGHT')
```

### 📈 Historical Data

```python
# End-of-day historical data (BDH)
blp.bdh(
    tickers='SPX Index', flds=['high', 'low', 'last_price'],
    start_date='2018-10-10', end_date='2018-10-20',
)
```

```pydocstring
Out[4]:
           SPX Index
                high      low last_price
2018-10-10  2,874.02 2,784.86   2,785.68
2018-10-11  2,795.14 2,710.51   2,728.37
2018-10-12  2,775.77 2,729.44   2,767.13
2018-10-15  2,775.99 2,749.03   2,750.79
2018-10-16  2,813.46 2,766.91   2,809.92
2018-10-17  2,816.94 2,781.81   2,809.21
2018-10-18  2,806.04 2,755.18   2,768.78
2018-10-19  2,797.77 2,760.27   2,767.78
```

```python
# Multiple tickers and fields
blp.bdh(
    tickers=['AAPL US Equity', 'MSFT US Equity'],
    flds=['px_last', 'volume'],
    start_date='2024-01-01', end_date='2024-01-10',
)
```

```pydocstring
Out[4a]:
           AAPL US Equity             MSFT US Equity            
                  px_last      volume        px_last      volume
2024-01-02         150.25  45000000.0         180.45  25000000.0
2024-01-03         151.30  42000000.0         181.20  23000000.0
2024-01-04         149.80  48000000.0         179.90  24000000.0
2024-01-05         150.10  44000000.0         180.15  22000000.0
2024-01-08         151.50  46000000.0         181.80  26000000.0
```

```python
# Excel-compatible inputs with periodicity
blp.bdh(
    tickers='SHCOMP Index', flds=['high', 'low', 'last_price'],
    start_date='2018-09-26', end_date='2018-10-20',
    Per='W', Fill='P', Days='A',
)
```

```pydocstring
Out[5]:
           SHCOMP Index
                   high      low last_price
2018-09-28     2,827.34 2,771.16   2,821.35
2018-10-05     2,827.34 2,771.16   2,821.35
2018-10-12     2,771.94 2,536.66   2,606.91
2018-10-19     2,611.97 2,449.20   2,550.47
```

```python
# Dividend/split adjustments
blp.bdh('AAPL US Equity', 'px_last', '20140606', '20140609', adjust='all')
```

```pydocstring
Out[15]:
           AAPL US Equity
                  px_last
2014-06-06          85.22
2014-06-09          86.58
```

```python
# Dividend history
blp.dividend(['C US Equity', 'MS US Equity'], start_date='2018-01-01', end_date='2018-05-01')
```

```pydocstring
Out[13]:
                dec_date     ex_date    rec_date    pay_date  dvd_amt dvd_freq      dvd_type
C US Equity   2018-01-18  2018-02-02  2018-02-05  2018-02-23     0.32  Quarter  Regular Cash
MS US Equity  2018-04-18  2018-04-27  2018-04-30  2018-05-15     0.25  Quarter  Regular Cash
MS US Equity  2018-01-18  2018-01-30  2018-01-31  2018-02-15     0.25  Quarter  Regular Cash
```

```python
# Earnings breakdowns
blp.earnings('AMD US Equity', by='Geo', Eqy_Fund_Year=2017, Number_Of_Periods=1)
```

```pydocstring
Out[12]:
                 level    fy2017  fy2017_pct
Asia-Pacific      1.00  3,540.00       66.43
    China         2.00  1,747.00       49.35
    Japan         2.00  1,242.00       35.08
    Singapore     2.00    551.00       15.56
United States     1.00  1,364.00       25.60
Europe            1.00    263.00        4.94
Other Countries   1.00    162.00        3.04
```

### ⏱️ Intraday Data

```python
# Intraday bars (1-minute default)
blp.bdib(ticker='BHP AU Equity', dt='2018-10-17').tail()
```

```pydocstring
Out[9]:
                          BHP AU Equity
                                   open  high   low close   volume num_trds
2018-10-17 15:56:00+11:00         33.62 33.65 33.62 33.64    16660      126
2018-10-17 15:57:00+11:00         33.65 33.65 33.63 33.64    13875      156
2018-10-17 15:58:00+11:00         33.64 33.65 33.62 33.63    16244      159
2018-10-17 15:59:00+11:00         33.63 33.63 33.61 33.62    16507      167
2018-10-17 16:10:00+11:00         33.66 33.66 33.66 33.66  1115523      216
```

**Selecting bar intervals:**

- **Minute-based intervals** (default): Use the `interval` parameter to specify minutes.
  By default, `interval=1` (1-minute bars). Common intervals:
  - `interval=5` → 5-minute bars
  - `interval=15` → 15-minute bars
  - `interval=30` → 30-minute bars
  - `interval=60` → 1-hour bars

```python
# 5-minute bars
blp.bdib(ticker='AAPL US Equity', dt='2025-11-12', interval=5).head()

# 15-minute bars
blp.bdib(ticker='AAPL US Equity', dt='2025-11-12', interval=15).head()
```

- **Sub-minute intervals** (seconds): Set `intervalHasSeconds=True` and specify seconds:

```python
# 10-second bars
blp.bdib(ticker='AAPL US Equity', dt='2025-11-12', interval=10, intervalHasSeconds=True).head()
```

```pydocstring
Out[9a]:
                          AAPL US Equity
                                   open    high     low   close volume num_trds
2025-11-12 09:31:00-05:00        150.25  150.35  150.20  150.30  25000      150
2025-11-12 09:31:10-05:00        150.30  150.40  150.25  150.35  18000      120
2025-11-12 09:31:20-05:00        150.35  150.45  150.30  150.40  22000      135
```

**Note:** By default, `interval` is interpreted as **minutes**. Set `intervalHasSeconds=True` to use seconds-based intervals.

```python
# Market session filtering
blp.bdib(ticker='7974 JT Equity', dt='2018-10-17', session='am_open_30').tail()
```

```pydocstring
Out[11]:
                          7974 JT Equity
                                    open      high       low     close volume num_trds
2018-10-17 09:27:00+09:00      39,970.00 40,020.00 39,970.00 39,990.00  10800       44
2018-10-17 09:28:00+09:00      39,990.00 40,020.00 39,980.00 39,980.00   6300       33
2018-10-17 09:29:00+09:00      39,970.00 40,000.00 39,960.00 39,970.00   3300       21
2018-10-17 09:30:00+09:00      39,960.00 40,010.00 39,950.00 40,000.00   3100       19
2018-10-17 09:31:00+09:00      39,990.00 40,000.00 39,980.00 39,990.00   2000       15
```

#### How the `session` parameter works

The `session` parameter is resolved by `xbbg.core.config.intervals.get_interval()`
and `xbbg.core.process.time_range()` using exchange metadata from
`xbbg/markets/config/exch.yml`:

- **Base sessions** (no underscores) map directly to session windows defined
  for the ticker's exchange in `exch.yml`:
  - `allday` - Full trading day including pre/post market (e.g., `[400, 2000]` for US equities)
  - `day` - Regular trading hours (e.g., `[0930, 1600]` for US equities)
  - `am` - Morning session (e.g., `[901, 1130]` for Japanese equities)
  - `pm` - Afternoon session (e.g., `[1230, 1458]` for Japanese equities)
  - `pre` - Pre-market session (e.g., `[400, 0930]` for US equities)
  - `post` - Post-market session (e.g., `[1601, 2000]` for US equities)
  - `night` - Night trading session (e.g., `[1710, 700]` for Australian futures)
  
  Not all exchanges define all sessions. For example, `GBP Curncy` uses
  `CurrencyGeneric` which defines `allday` and `day` only.

- **Compound sessions** (with underscores) allow finer control by combining
  a base session with modifiers (`open`, `close`, `normal`, `exact`):
  - **Open windows** (first N minutes of a session):
    - `day_open_30` → first 30 minutes of the `day` session
    - `am_open_30` → first 30 minutes of the `am` session
    - Note: `open` is not a base session; use `day_open_30`, not `open_30`
  - **Close windows** (last N minutes of a session):
    - `day_close_20` → last 20 minutes of the `day` session
    - `am_close_30` → last 30 minutes of the `am` session
    - Note: `close` is not a base session; use `day_close_20`, not `close_20`
  - **Normal windows** (skip open/close buffers):
    - `day_normal_30_20` → skips first 30 min and last 20 min of `day`
    - `am_normal_30_30` → skips first 30 min and last 30 min of `am`
  - **Exact clock times** (exchange-local HHMM format):
    - `day_exact_2130_2230` → [21:30, 22:30] local time (marker session)
    - `allday_exact_2130_2230` → [21:30, 22:30] local time (actual window)
    - `allday_exact_2130_0230` → [21:30, 02:30 next day] local time

- **Resolution order and fallbacks**:
  - `blp.bdib` / `blp.bdtick` call `time_range()`, which:
    1. Uses `exch.yml` + `get_interval()` and `const.exch_info()` to resolve
       local session times and exchange timezone.
    2. Converts that window to UTC and then to your requested `tz` argument
       (e.g., `'UTC'`, `'NY'`, `'Europe/London'`).
    3. If exchange metadata is missing for `session` and the asset, it may
       fall back to pandas‑market‑calendars (PMC) for simple sessions
       (`'day'` / `'allday'`), based on the exchange code.

- **Errors and diagnostics**:
  - If a `session` name is not defined for the ticker's exchange,
    `get_interval()` raises a `ValueError` listing the available sessions
    for that exchange and points to `xbbg/markets/exch.yml`.
  - For compound sessions whose base session doesn't exist (e.g. mis-typed
    `am_open_30` for an exchange that has no `am` section), `get_interval()`
    returns `SessNA` and `time_range()` will then try the PMC fallback or
    ultimately raise a clear `ValueError`.

In practice:

- Use simple names like `session='day'` or `session='allday'` when you just
  want the main trading hours.
- Use compound names like `session='day_open_30'` or `session='am_normal_30_30'`
  when you need to focus on opening/closing auctions or to exclude "micro"
  windows (e.g. the first X minutes).
- If you add or customize sessions, update `exch.yml` and rely on
  `get_interval()` to pick them up automatically.

```python
# Using reference exchange for market hours
blp.bdib(ticker='ESM0 Index', dt='2020-03-20', ref='ES1 Index').tail()
```

```pydocstring
out[10]:
                          ESM0 Index
                                open     high      low    close volume num_trds        value
2020-03-20 16:55:00-04:00   2,260.75 2,262.25 2,260.50 2,262.00    412      157   931,767.00
2020-03-20 16:56:00-04:00   2,262.25 2,267.00 2,261.50 2,266.75    812      209 1,838,823.50
2020-03-20 16:57:00-04:00   2,266.75 2,270.00 2,264.50 2,269.00   1136      340 2,576,590.25
2020-03-20 16:58:00-04:00   2,269.25 2,269.50 2,261.25 2,265.75   1077      408 2,439,276.00
2020-03-20 16:59:00-04:00   2,265.25 2,272.00 2,265.00 2,266.50   1271      378 2,882,978.25
```

```python
# Tick-by-tick data with event types and condition codes
blp.bdtick(ticker='XYZ US Equity', dt='2024-10-15', session='day', types=['TRADE']).head()
```

```pydocstring
Out[12]:
                          XYZ US Equity
                                   volume    typ   cond exch            trd_time
2024-10-15 09:30:15-04:00           1500  TRADE     @  NYSE  2024-10-15 09:30:15
2024-10-15 09:30:23-04:00            800  TRADE     @  NYSE  2024-10-15 09:30:23
2024-10-15 09:30:31-04:00           2200  TRADE     @  NYSE  2024-10-15 09:30:31
```

```python
# Tick data with timeout (useful for large requests)
blp.bdtick(ticker='XYZ US Equity', dt='2024-10-15', session='day', timeout=1000)
```

Note: `bdtick` requests can take longer to respond. Use `timeout` parameter (in milliseconds) if you encounter empty DataFrames due to timeout.

#### Timezone handling (`bdib` / `bdtick`)

Bloomberg intraday APIs use **UTC** on the wire. The Rust engine accepts two optional knobs:

- **`request_tz`**: How **naive** `start_datetime` / `end_datetime` (and the implicit full-day window when using `dt=`) are interpreted before the request. Omit or use `UTC` to keep the previous behavior (naive times treated as UTC wall times, matching older examples that use e.g. `14:30` for US cash open).
- **`output_tz`**: Relabel the Arrow/Pandas **`time`** column to an IANA zone (same instants; only the timestamp type metadata changes). Omit or `UTC` leaves UTC.

Supported labels (case-insensitive where noted): `UTC`, `local` (machine IANA zone), `exchange` (resolve via the request’s security and cached/Bloomberg metadata), short aliases `NY`, `LN`, `TK`, `HK`, a **reference ticker** string containing a space (same as `exch_info`), or any **IANA** name (e.g. `Europe/Zurich`).

```python
# Naive times in America/New_York → converted to UTC in the engine before the API call
bars = await blp.abdib(
    "SPY US Equity",
    start_datetime="2024-01-15 09:30",
    end_datetime="2024-01-15 16:00",
    interval=5,
    request_tz="America/New_York",
)

# Present tick times in the listing’s exchange zone (resolved in Rust)
ticks = await blp.abdtick(
    "SPY US Equity",
    "2024-01-15 09:30",
    "2024-01-15 10:00",
    request_tz="exchange",
    output_tz="exchange",
)
```

```python
# Trading volume & turnover (currency-adjusted, in millions)
blp.turnover(['ABC US Equity', 'DEF US Equity'], start_date='2024-01-01', end_date='2024-01-10', ccy='USD')
```

```pydocstring
Out[13]:
            ABC US Equity  DEF US Equity
2024-01-02        15,304        8,920
2024-01-03        18,450       12,340
2024-01-04        14,890        9,560
2024-01-05        16,720       11,230
2024-01-08        10,905        7,890
```

```python
# Currency conversion for historical data
hist = blp.bdh(['GHI US Equity'], ['px_last'], '2024-01-01', '2024-01-10')
blp.convert_ccy(hist, ccy='EUR')
```

```pydocstring
Out[14]:
            GHI US Equity
2024-01-02        169.66
2024-01-03        171.23
2024-01-04        170.45
2024-01-05        172.10
2024-01-08        169.46
```

### 🔍 Screening & Queries

```python
# Bloomberg Query Language (BQL)
# IMPORTANT: The 'for' clause must be OUTSIDE get(), not inside
# Correct: get(px_last) for('AAPL US Equity')
# Incorrect: get(px_last for('AAPL US Equity'))
# blp.bql("get(px_last) for('AAPL US Equity')")  # doctest: +SKIP

# BQL Options query example - sum open interest
# blp.bql("get(sum(group(open_int))) for(filter(options('SPX Index'), expire_dt=='2025-11-21'))")  # doctest: +SKIP

# BQL Options metadata - get available expiries
# blp.bql("get(expire_dt) for(options('INDEX Ticker'))")  # doctest: +SKIP

# BQL Options metadata - get option tickers for an underlying
# blp.bql("get(id) for(options('INDEX Ticker'))")  # doctest: +SKIP

# BQL Options metadata - get option chain (expiry, strike, put/call)
# blp.bql("get(id, expire_dt, strike_px, PUT_CALL) for(filter(options('INDEX Ticker'), expire_dt=='YYYY-MM-DD'))")  # doctest: +SKIP

# ETF Holdings (BQL)
# blp.etf_holdings('SPY US Equity')  # doctest: +SKIP
# Returns:
#               holding       id_isin SOURCE POSITION_TYPE  weights  position
# 0     MSFT US Equity  US5949181045    ETF             L   0.0725   123456.0
# 1     AAPL US Equity  US0378331005    ETF             L   0.0685   112233.0
# 2     NVDA US Equity  US67066G1040    ETF             L   0.0450    88776.0

# Bloomberg Equity Screening (BEQS)
# blp.beqs(screen='MyScreen', asof='2023-01-01')  # doctest: +SKIP

# SRCH (Search) - Fixed Income example
# blp.bsrch("FI:YOURSRCH")  # doctest: +SKIP
```

```pydocstring
Out[16]:
              id
0  !!ABC123 Mtge
1  !!DEF456 Mtge
2  !!GHI789 Mtge
3  !!JKL012 Mtge
4  !!MNO345 Mtge
```

```python
# SRCH - Weather data with parameters
blp.bsrch(  # doctest: +SKIP
    "comdty:weather",
    overrides={
        "provider": "wsi",
        "location": "US_XX",
        "model": "ACTUALS",
        "frequency": "DAILY",
        "target_start_date": "2021-01-01",
        "target_end_date": "2021-01-05",
        "location_time": "false",
        "fields": "WIND_SPEED|TEMPERATURE|HDD_65F|CDD_65F|HDD_18C|CDD_18C|PRECIPITATION_24HR|CLOUD_COVER|FEELS_LIKE_TEMPERATURE|MSL_PRESSURE|TEMPERATURE_MAX_24HR|TEMPERATURE_MIN_24HR"
    }
)
```

```pydocstring
Out[17]:
              Reported Time  Wind Speed (m/s)  Temperature (°C)  Heating Degree Days (°F)  Cooling Degree Days (°F)
0 2021-01-01 06:00:00+00:00              3.45              -2.15                   38.25                     0.0
1 2021-01-02 06:00:00+00:00              2.10              -1.85                   36.50                     0.0
2 2021-01-03 06:00:00+00:00              1.95              -2.30                   37.80                     0.0
3 2021-01-04 06:00:00+00:00              2.40              -2.65                   38.10                     0.0
4 2021-01-05 06:00:00+00:00              2.15              -1.20                   35.75                     0.0
```

**Note:** The `bsrch()` function uses the blpapi Excel service (`//blp/exrsvc`) and supports user-defined SRCH screens, commodity screens, and blpapi example screens. For weather data and other specialized searches, use the `overrides` parameter to pass search-specific parameters.

```python
# Bloomberg Quote Request (BQR) - Dealer quotes with broker codes
# Emulates Excel =BQR() function for fixed income dealer quotes

# Get quotes from last 2 days
# blp.bqr('XYZ 4.5 01/15/30@MSG1 Corp', date_offset='-2d')  # doctest: +SKIP

# Using ISIN with MSG1 pricing source
# blp.bqr('/isin/US123456789@MSG1', date_offset='-2d')  # doctest: +SKIP

# With broker codes and spread data
# blp.bqr(  # doctest: +SKIP
#     'XYZ 4.5 01/15/30@MSG1 Corp',
#     date_offset='-2d',
#     include_broker_codes=True,
#     include_spread_price=True,
# )

# With explicit date range
# blp.bqr('XYZ 4.5 01/15/30@MSG1 Corp', start_date='2024-01-15', end_date='2024-01-17')  # doctest: +SKIP

# Get only trade events
# blp.bqr('XYZ 4.5 01/15/30@MSG1 Corp', date_offset='-1d', event_types=['TRADE'])  # doctest: +SKIP

```pydocstring
Out[18]:
                              ticker                 time event_type   price      size spread_price broker_buy broker_sell
0  XYZ 4.5 01/15/30@MSG1 Corp  2024-01-15 10:30:00        BID   98.75  10000000         29.0       DLRA         NaN
1  XYZ 4.5 01/15/30@MSG1 Corp  2024-01-15 10:30:05        ASK   99.00   5000000         24.1        NaN        DLRB
2  XYZ 4.5 01/15/30@MSG1 Corp  2024-01-15 11:45:00      TRADE   98.85   2500000          NaN       DLRC        DLRC
```

**Note:** The `bqr()` function emulates Bloomberg Excel's `=BQR()` formula. Use the `@MSG1` pricing source suffix to get dealer-level quote attribution. Optional parameters include `include_broker_codes`, `include_spread_price`, `include_yield`, `include_condition_codes`, and `include_exchange_codes`.

### 📡 Real-time

```python
# Real-time market data streaming (async)
# async for tick in blp.astream(['AAPL US Equity'], ['LAST_PRICE']):  # doctest: +SKIP
#     print(tick)  # doctest: +SKIP

# Subscriptions with failure isolation and health metadata
# sub = await blp.asubscribe(['AAPL US Equity'], ['LAST_PRICE'])  # doctest: +SKIP
# async for update in sub:  # doctest: +SKIP
#     print(update)  # doctest: +SKIP

# Full Bloomberg payload (e.g. INITPAINT summary fields beyond your request list):
# sub = await blp.asubscribe(['XBTUSD Curncy'], ['LAST_PRICE', 'BID', 'ASK'], all_fields=True)  # doctest: +SKIP

# Real-time VWAP streaming
# async for bar in blp.avwap(['AAPL US Equity']):  # doctest: +SKIP
#     print(bar)  # doctest: +SKIP
```

### 🔧 Utilities

```python
# Futures ticker resolution (generic to specific contract)
blp.fut_ticker('ES1 Index', '2024-01-15', freq='ME')
```

```pydocstring
Out[15]:
'ESH24 Index'
```

```python
# Active futures contract selection (volume-based)
blp.active_futures('ESA Index', '2024-01-15')
```

```pydocstring
Out[16]:
'ESH24 Index'
```

```python
# CDX index ticker resolution (series mapping)
blp.cdx_ticker('CDX IG CDSI GEN 5Y Corp', '2024-01-15')
```

```pydocstring
Out[17]:
'CDX IG CDSI S45 5Y Corp'
```

```python
# Active CDX contract selection
blp.active_cdx('CDX IG CDSI GEN 5Y Corp', '2024-01-15', lookback_days=10)
```

```pydocstring
Out[18]:
'CDX IG CDSI S45 5Y Corp'
```

## Data Storage

### What gets cached

Currently, only **`bdib()` intraday bar data** is cached as local Parquet files. Other functions (`bdp`, `bds`, `bdh`, `bql`, `beqs`, `bsrch`, `bta`, `bqr`) always make live Bloomberg API calls — they are not cached.

When `bdib()` fetches intraday bars, it will:

1. **Check the cache first** — if a Parquet file exists for that ticker/date/interval, return it instead of calling Bloomberg.
2. **Save results to cache** — after a successful Bloomberg fetch, save the bars as a Parquet file (only once the trading session has ended, to avoid saving incomplete data).

Exchange metadata (timezone, session hours) is also cached locally to avoid repeated lookups.

### Cache location

By default, xbbg uses a platform-specific cache directory:

| Platform | Default location |
|----------|-----------------|
| Windows | `%APPDATA%\xbbg` |
| Linux/macOS | `~/.cache/xbbg` or `~/.xbbg` |

To use a custom location, set `BBG_ROOT` before importing xbbg:

```python
import os
os.environ['BBG_ROOT'] = '/path/to/your/cache/directory'
```

### Cache structure

Intraday bar files are organized as:

```
{BBG_ROOT}/{asset_class}/{ticker}/{event_type}/{interval}/{date}.parq
```

For example, 1-minute TRADE bars for AAPL on 2024-01-15:

```
/path/to/cache/Equity/AAPL US Equity/TRADE/1m/2024-01-15.parq
```

### Controlling cache behavior

```python
# Disable cache for a specific call (always fetch from Bloomberg)
blp.bdib('AAPL US Equity', dt='2024-01-15', cache=False)

# Force reload (fetch from Bloomberg even if cached, then overwrite cache)
blp.bdib('AAPL US Equity', dt='2024-01-15', reload=True)
```

### Bloomberg data license compliance

Local data usage must be compliant with the Bloomberg Datafeed Addendum (see `DAPI<GO>`):

> To access Bloomberg data via the API (and use that data in Microsoft Excel), your company must sign the 'Datafeed Addendum' to the Bloomberg Agreement. This legally binding contract describes the terms and conditions of your use of the data and information available via the API (the "Data"). The most fundamental requirement regarding your use of Data is that it cannot leave the local PC you use to access the BLOOMBERG PROFESSIONAL service.

## 🔧 Troubleshooting

<details>
<summary><b>❌ Empty DataFrame Returned</b></summary>

**Possible causes:**
- ✅ Bloomberg Terminal not running → Start Bloomberg Terminal
- ✅ Wrong ticker format → Use `'AAPL US Equity'` not `'AAPL'`
- ✅ Data not available for date/time → Check Bloomberg Terminal
- ✅ Timeout too short → Increase: `blp.bdtick(..., timeout=1000)`

**Quick fix:**
```python
# Verify ticker exists
blp.blkp('Apple', yellowkey='eqty')

# Check field availability
blp.fieldSearch('price')
```

</details>

<details>
<summary><b>🔌 Connection Errors</b></summary>

**Checklist:**
- ✅ Bloomberg Terminal is running and logged in
- ✅ Default connection is `localhost:8194`
- ✅ For remote: `blp.bdp(..., server='192.168.1.100', port=18194)`
- ✅ Bloomberg API (blpapi) is installed

**Test connection:**
```python
from xbbg import blp
blp.bdp('AAPL US Equity', 'PX_LAST')  # Should return data
```

</details>

<details>
<summary><b>⏱️ Timeout Errors</b></summary>

**Solutions:**
```python
# Increase timeout (milliseconds)
blp.bdtick('AAPL US Equity', dt='2024-01-15', timeout=5000)

# Break large requests into chunks
dates = pd.date_range('2024-01-01', '2024-12-31', freq='MS')
chunks = [blp.bdh('SPX Index', 'PX_LAST', start, end) for start, end in zip(dates[:-1], dates[1:])]
result = pd.concat(chunks)
```

</details>

<details>
<summary><b>🔍 Field Not Found</b></summary>

**Find the right field:**
```python
# Search for fields
blp.fieldSearch('vwap')  # Find VWAP-related fields

# Get field info
blp.fieldInfo(['PX_LAST', 'VOLUME'])  # See data types & descriptions

# Check in Bloomberg Terminal
# Type FLDS<GO> to browse all fields
```

</details>

<details>
<summary><b>🐛 Still Stuck?</b></summary>

**Get help fast:**
- 💬 **Discord**: [Join our community](https://discord.gg/P34uMwgCjC) - Usually get answers within hours
- 🐛 **GitHub Issues**: [Report bugs](https://github.com/alpha-xone/xbbg/issues) - Include error messages & code
- 📚 **Documentation**: [Docs](https://alpha-xone.github.io/xbbg/) - Comprehensive guides
- 📓 **Examples**: [`xbbg_jupyter_examples.ipynb`](examples/xbbg_jupyter_examples.ipynb) - 100+ working examples

**When reporting issues, include:**
1. xbbg version: `import xbbg; print(xbbg.__version__)`
2. Python version: `python --version`
3. Error message (full traceback)
4. Minimal code to reproduce

</details>

## Development

### Setup

Set up the development environment with [pixi](https://pixi.sh/):

```bash
# Install the Bloomberg SDK into vendor/blpapi-sdk/ and let xbbg discover it
bash ./scripts/sdktool.sh               # macOS/Linux
# .\scripts\sdktool.ps1                # Windows PowerShell

# Install environment and compile the Rust extension
pixi install
pixi run install
```

If you already manage the SDK yourself, you can still set `BLPAPI_ROOT` manually.

### Running Tests and Linting

```bash
pixi run test                  # run tests
pixi run lint                  # lint Python + Rust
pixi run ci                    # full sweep: fmt-check + lint + typecheck + test
```

For non-live application tests, `xbbg.testing` can mock Bloomberg-style responses:

```python
from xbbg import blp
from xbbg.testing import create_mock_response, mock_engine

response = create_mock_response(
    service="//blp/refdata",
    operation="ReferenceDataRequest",
    data={"AAPL US Equity": {"PX_LAST": 254.23}},
)

with mock_engine([response]):
    df = blp.bdp("AAPL US Equity", "PX_LAST")
```

### Building

```bash
pixi run build
```

Publishing is handled via GitHub Actions using PyPI Trusted Publishing (OIDC).

### Documentation

The docs site uses [Astro](https://astro.build/):

```bash
pixi run -e docs docs-install  # install npm deps
pixi run -e docs docs-dev      # local dev server
pixi run -e docs docs-build    # production build
```

## Contributing

We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines on:

- Setting up your development environment
- Code style and standards
- Testing requirements
- Pull request process
- Community guidelines

Quick start for contributors:

```bash
# Fork and clone the repository
git clone https://github.com/YOUR-USERNAME/xbbg.git
cd xbbg

# Set up development environment
pixi install && pixi run install

# Run tests and linting
pixi run ci
```

## Getting Help

### Community Support

- **Discord**: [Join our community](https://discord.gg/P34uMwgCjC) for discussions, questions, and help
- **GitHub Issues**: [Report bugs or request features](https://github.com/alpha-xone/xbbg/issues)
- **GitHub Discussions**: Share ideas and ask questions

### Resources

- **Documentation**: [alpha-xone.github.io/xbbg](https://alpha-xone.github.io/xbbg/)
- **Examples**: [`examples/xbbg_jupyter_examples.ipynb`](examples/xbbg_jupyter_examples.ipynb)
- **Changelog**: [CHANGELOG.md](CHANGELOG.md)
- **Security**: [SECURITY.md](SECURITY.md)

## Links

- [PyPI Package](https://pypi.org/project/xbbg/)
- [Documentation](https://alpha-xone.github.io/xbbg/)
- [Source Code](https://github.com/alpha-xone/xbbg)
- [Issue Tracker](https://github.com/alpha-xone/xbbg/issues)
- [Discord Community](https://discord.gg/P34uMwgCjC)
- [Changelog](CHANGELOG.md)
- [Contributing Guidelines](CONTRIBUTING.md)
- [Code of Conduct](CODE_OF_CONDUCT.md)
- [Security Policy](SECURITY.md)

## License

This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.

---

## Star History

[![Star History Chart](https://api.star-history.com/svg?repos=alpha-xone/xbbg&type=Date)](https://star-history.com/#alpha-xone/xbbg&Date)

## Project Status

| Category       | Badge                                                                                                                                    |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Docs           | [![Documentation](https://img.shields.io/badge/docs-Starlight-blue)](https://alpha-xone.github.io/xbbg/)                                |
| Build          | [![CI](https://github.com/alpha-xone/xbbg/actions/workflows/ci-rust.yml/badge.svg)](https://github.com/alpha-xone/xbbg/actions/workflows/ci-rust.yml) |
| License        | [![GitHub license](https://img.shields.io/github/license/alpha-xone/xbbg.svg)](https://github.com/alpha-xone/xbbg/blob/main/LICENSE)   |

For detailed release history, see [CHANGELOG.md](CHANGELOG.md).
