@xbbg/core 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # @xbbg/core
2
+
3
+ Bloomberg data API for Node.js — powered by Rust.
4
+
5
+ ## Status
6
+
7
+ 🚧 **Experimental alpha** — native N-API bindings are implemented, high-level API is in active development.
8
+
9
+ ## Install
10
+
11
+ Supported prebuilt addon targets:
12
+ - macOS arm64
13
+ - Linux x64
14
+ - Windows x64
15
+
16
+ ```bash
17
+ bun add @xbbg/core
18
+ # or
19
+ npm install @xbbg/core
20
+ ```
21
+
22
+ `@xbbg/core` loads a packaged native `napi_xbbg.node` addon via platform-specific optional dependencies on supported targets. If no packaged addon is available for your platform, build from source locally instead.
23
+
24
+ ## Local Development
25
+
26
+ ```bash
27
+ # Preferred: build and copy js-xbbg/napi_xbbg.node into the package directory
28
+ npm --prefix js-xbbg run build
29
+
30
+ # Lower-level Rust build (useful while hacking on napi-xbbg itself)
31
+ cargo build -p napi-xbbg
32
+
33
+ # Stage the current platform package template with the built addon
34
+ npm --prefix js-xbbg run stage:native-package
35
+
36
+ # Run JS smoke test from js-xbbg/
37
+ npm test
38
+ ```
39
+
40
+ The JS package automatically loads a local `js-xbbg/napi_xbbg.node` addon first, then falls back to packaged optional native dependencies for supported platforms.
41
+
42
+ ## Planned Usage
43
+
44
+ ```typescript
45
+ import * as xbbg from '@xbbg/core';
46
+
47
+ xbbg.configure({
48
+ host: 'localhost',
49
+ port: 8194,
50
+ });
51
+
52
+ // BPIPE / leased-line capable session config
53
+ const engine = await xbbg.connect({
54
+ servers: [
55
+ { host: 'bpipe-primary.example.com', port: 8194 },
56
+ { host: 'bpipe-secondary.example.com', port: 8196 },
57
+ ],
58
+ auth: { method: 'userapp', appName: 'my-bpipe-app' },
59
+ tls: {
60
+ clientCredentials: '/secure/client.p12',
61
+ clientCredentialsPassword: process.env.BPIPE_TLS_PASSWORD,
62
+ trustMaterial: '/secure/trust.p7',
63
+ },
64
+ zfpRemote: '8194',
65
+ });
66
+
67
+ // Python-style blp namespace
68
+ const hist = await xbbg.blp.abdh(['AAPL US Equity'], ['PX_LAST'], '2024-01-01', '2024-12-31');
69
+ const ref = await xbbg.blp.abdp(['AAPL US Equity'], ['PX_LAST', 'SECURITY_NAME']);
70
+ const bulk = await xbbg.blp.abds(['ES1 Index'], ['FUT_CHAIN_LAST_TRADE_DATES']);
71
+ const bars = await xbbg.blp.abdib('AAPL US Equity', '2024-12-01', 5);
72
+ const ticks = await xbbg.blp.abdtick('AAPL US Equity', '2024-12-01T09:30:00', '2024-12-01T10:00:00');
73
+
74
+ // Live streaming
75
+ const sub = await xbbg.blp.asubscribe(['AAPL US Equity'], ['LAST_PRICE', 'BID', 'ASK']);
76
+ for await (const tick of sub) {
77
+ console.log(tick);
78
+ }
79
+
80
+ // CDX analytics
81
+ const cdxInfo = await xbbg.ext.cdx.acdx_info('CDX IG CDSI GEN 5Y Corp');
82
+ const cdxPricing = await xbbg.ext.cdx.acdx_pricing('CDX IG CDSI GEN 5Y Corp');
83
+ const cdxRisk = await xbbg.ext.cdx.acdx_risk('CDX IG CDSI GEN 5Y Corp');
84
+
85
+ engine.signalShutdown();
86
+ ```
87
+
88
+ ## Recipes
89
+
90
+ High-level workflows that wrap common Bloomberg request patterns. Each recipe returns an Arrow `Table` by default (or a JSON/Polars result when `backend` is set) and errors are mapped to the standard `BlpError` hierarchy.
91
+
92
+ ```javascript
93
+ // Fixed income
94
+ const yas = await engine.yas(['US912810TM69 Govt'], ['YAS_BOND_YLD'], {
95
+ settleDt: '20240115',
96
+ yieldType: 1, // 1=YTM, 2=YTC, 3=YTW, 4=YTB, 5=YTP, 6=YTN, 7=OAS, 8=YTS, 9=YTAL
97
+ price: 99.5,
98
+ });
99
+ const bqr = await engine.bqr('US912810TM69 Govt', {
100
+ startDatetime: '2024-06-03T14:30:00',
101
+ endDatetime: '2024-06-03T15:00:00',
102
+ eventTypes: ['BID', 'ASK'],
103
+ });
104
+ const preferreds = await engine.preferreds('BAC US Equity');
105
+ const corpBonds = await engine.corporateBonds('AAPL', { ccy: 'USD' });
106
+
107
+ // Futures and CDX resolution
108
+ const front = await engine.futTicker('ES1 Index', '20240301');
109
+ const active = await engine.activeFutures('CL1 Comdty', '20240301', { freq: 'M' });
110
+ const cdx = await engine.cdxTicker('CDX IG CDSI GEN 5Y Corp', '20240301');
111
+ const activeCdx = await engine.activeCdx('CDX IG CDSI GEN 5Y Corp', '20240301', {
112
+ lookbackDays: 10,
113
+ });
114
+
115
+ // Historical helpers
116
+ const dvd = await engine.dividend(['AAPL US Equity'], '20230101', '20231231');
117
+ const turn = await engine.turnover(['AAPL US Equity'], '20240101', '20240131', {
118
+ ccy: 'USD',
119
+ });
120
+ const holdings = await engine.etfHoldings('SPY US Equity');
121
+
122
+ // Currency-converted prices
123
+ const px = await engine.currencyConversion('700 HK Equity', 'USD', '20240101', '20240131');
124
+ ```
125
+
126
+ ## Engine configuration
127
+
128
+ `connect()` and `configure()` accept a structured `EngineConfig` object. The most important connection controls are:
129
+
130
+ - `host` / `port` for a single Bloomberg session endpoint
131
+ - `servers` for ordered failover across multiple Bloomberg hosts
132
+ - `auth` for Bloomberg session identity auth: `user`, `app`, `userapp`, `dir`, `manual`, or `token`
133
+ - `tls` plus `zfpRemote` for leased-line / BPIPE-style sessions
134
+ - `socks5` for proxied Bloomberg connectivity
135
+ - `retryPolicy`, `numStartAttempts`, and recovery settings for reconnect behavior
136
+
137
+ The JS binding now forwards these fields directly to the Rust engine, so Node can configure the same auth and transport features already available in the core runtime.
138
+
139
+ ## Features (planned)
140
+
141
+ - Native N-API bindings (no HTTP overhead)
142
+ - Zero-copy Arrow buffers via `apache-arrow`
143
+ - Async/await with proper backpressure
144
+ - TypeScript-first with full type definitions
145
+ - Cross-platform prebuilt addon packaging: macOS arm64, Linux x64, Windows x64
package/errors.js ADDED
@@ -0,0 +1,160 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Base error class for all Bloomberg API errors.
5
+ * All xbbg errors inherit from BlpError, allowing users to catch all
6
+ * Bloomberg-related errors with a single except clause.
7
+ */
8
+ class BlpError extends Error {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = this.constructor.name;
12
+ }
13
+ }
14
+
15
+ /**
16
+ * Session lifecycle errors (start, connect, service open).
17
+ */
18
+ class BlpSessionError extends BlpError {}
19
+
20
+ /**
21
+ * Request-level errors from the Bloomberg API.
22
+ *
23
+ * Attributes:
24
+ * service: The Bloomberg service URI (e.g., "//blp/refdata").
25
+ * operation: The request operation name (e.g., "ReferenceDataRequest").
26
+ * request_id: Optional correlation ID for debugging.
27
+ * code: Optional Bloomberg error code.
28
+ */
29
+ class BlpRequestError extends BlpError {
30
+ constructor(message, options = {}) {
31
+ super(message);
32
+ this.service = options.service;
33
+ this.operation = options.operation;
34
+ this.request_id = options.request_id;
35
+ this.code = options.code;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Request validation errors.
41
+ *
42
+ * Raised when request parameters fail validation against Bloomberg schemas.
43
+ * Includes helpful suggestions for typos and invalid enum values.
44
+ *
45
+ * Attributes:
46
+ * element: The element name that caused the error (if available).
47
+ * suggestion: Suggested correction for typos (if available).
48
+ */
49
+ class BlpValidationError extends BlpError {
50
+ constructor(message, options = {}) {
51
+ super(message);
52
+ this.element = options.element;
53
+ this.suggestion = options.suggestion;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Request timeout.
59
+ */
60
+ class BlpTimeoutError extends BlpError {}
61
+
62
+ /**
63
+ * Internal errors (should not happen in normal operation).
64
+ * If you encounter this error, please report it as a bug.
65
+ */
66
+ class BlpInternalError extends BlpError {}
67
+
68
+ /**
69
+ * Wraps a native NAPI error into the appropriate xbbg error class.
70
+ *
71
+ * Pattern-matches on error message to determine the error type.
72
+ *
73
+ * @param {Error} napiError - The native NAPI error to wrap
74
+ * @returns {BlpError} - An instance of the appropriate error class
75
+ */
76
+ function wrapError(napiError) {
77
+ const msg = napiError.message || '';
78
+
79
+ // Session errors
80
+ if (
81
+ msg.includes('Session start failed') ||
82
+ msg.includes('session start failed') ||
83
+ msg.includes('Failed to start session') ||
84
+ msg.includes('Failed to open service') ||
85
+ msg.includes('failed to spawn worker') ||
86
+ msg.includes('connect event failed')
87
+ ) {
88
+ return new BlpSessionError(msg);
89
+ }
90
+
91
+ // Request errors
92
+ if (msg.includes('Request failed') || msg.includes('Subscription failed')) {
93
+ const options = {};
94
+
95
+ // Parse service and operation from "Request failed on {service}::{op}"
96
+ const serviceOpMatch = msg.match(/on ([^:]+)::([^ (]+)/);
97
+ if (serviceOpMatch) {
98
+ options.service = serviceOpMatch[1];
99
+ options.operation = serviceOpMatch[2];
100
+ }
101
+
102
+ // Native messages append " [request_id=<uuid>]" when a correlation id is known.
103
+ const requestIdMatch = msg.match(/\[request_id=([^\]]+)\]/);
104
+ if (requestIdMatch) {
105
+ options.request_id = requestIdMatch[1];
106
+ }
107
+
108
+ return new BlpRequestError(msg, options);
109
+ }
110
+
111
+ // Validation errors
112
+ if (
113
+ msg.includes('Invalid argument') ||
114
+ msg.includes('Configuration error') ||
115
+ msg.includes('Operation not found') ||
116
+ msg.includes('Schema element not found') ||
117
+ msg.includes('Schema type mismatch') ||
118
+ msg.includes('Unsupported schema') ||
119
+ msg.includes('invalid extractor') ||
120
+ msg.includes('(did you mean') // Validation with suggestion
121
+ ) {
122
+ const options = {};
123
+
124
+ // Parse suggestion from "(did you mean 'xxx'?)" pattern
125
+ const suggestionMatch = msg.match(/\(did you mean '([^']+)'\?\)/);
126
+ if (suggestionMatch) {
127
+ options.suggestion = suggestionMatch[1];
128
+ }
129
+
130
+ return new BlpValidationError(msg, options);
131
+ }
132
+
133
+ // Timeout errors
134
+ if (msg.includes('Request timed out')) {
135
+ return new BlpTimeoutError(msg);
136
+ }
137
+
138
+ // Internal errors
139
+ if (
140
+ msg.includes('Internal error') ||
141
+ msg.includes('Channel closed') ||
142
+ msg.includes('Stream buffer full') ||
143
+ msg.includes('Request was cancelled')
144
+ ) {
145
+ return new BlpInternalError(msg);
146
+ }
147
+
148
+ // Default fallback
149
+ return new BlpError(msg);
150
+ }
151
+
152
+ module.exports = {
153
+ BlpError,
154
+ BlpSessionError,
155
+ BlpRequestError,
156
+ BlpValidationError,
157
+ BlpTimeoutError,
158
+ BlpInternalError,
159
+ wrapError,
160
+ };