@ckirg/corelib-markets 0.1.22
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/LICENSE +21 -0
- package/README.md +313 -0
- package/dist/index.d.ts +784 -0
- package/dist/index.js +2124 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Costas Kirgoussios
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# @ckirg/corelib-markets
|
|
2
|
+
|
|
3
|
+
Financial market utilities and data providers for the Corelib monorepo, featuring high-resilience wrappers for Nasdaq and real-time streaming via Alpaca, Finnhub, and Yahoo Finance.
|
|
4
|
+
|
|
5
|
+
> [!IMPORTANT]
|
|
6
|
+
> Not on the public npm registry — install from GitHub Releases. See the [root install guide](../README.md#-installation-for-external-projects).
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Nasdaq API Wrapper**: Resilient requests with custom headers and error handling.
|
|
11
|
+
- **Market Status & Scheduling**: Intelligent pollers and sleep-calculators based on market phases.
|
|
12
|
+
- **Market Monitor**: Adaptive poller with heuristic fallback during API failures.
|
|
13
|
+
- **CNN Fear & Greed Index**: Retrieval and filtering of the popular sentiment indicator.
|
|
14
|
+
- **Nasdaq 100 Symbols**: Fast, cached access to the Nasdaq 100 constituent symbols.
|
|
15
|
+
- **Real-Time Streaming**: Zero event-loop-blocking live feeds via Alpaca, Finnhub, and Yahoo Finance — the Rust WebSocket engine delivers data to JS via napi `ThreadsafeFunction`.
|
|
16
|
+
- **Market Symbols**: Persistent symbol database with auto-refresh and environment-aware search sequencing.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
Install from the GitHub Release — see the [root install guide](../README.md#-installation-for-external-projects) for package manager overrides (required because `@ckirg/corelib` is a peer dependency also not on npm).
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# pnpm
|
|
24
|
+
pnpm add https://github.com/ckir/corelib/releases/download/v0.1.17/ckirg-corelib-markets-0.1.17.tgz
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage Examples
|
|
28
|
+
|
|
29
|
+
### 1. Market Monitor (Resilient Status Poller)
|
|
30
|
+
Intelligent long-running task that adapts to market hours and handles failures gracefully. It emits events only on market phase changes or after the first successful poll.
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { MarketMonitor, type MarketPhase } from '@ckirg/corelib-markets';
|
|
34
|
+
|
|
35
|
+
const monitor = new MarketMonitor({
|
|
36
|
+
liveIntervalSec: 15, // Frequency when market is open
|
|
37
|
+
closedIntervalSec: 1800, // Frequency when market is closed
|
|
38
|
+
warnIntervalSec: 60 // Warning throttle during failures
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Emits on first successful poll and whenever the market phase (open/closed/pre/after) changes
|
|
42
|
+
monitor.on("status-change", (phase: MarketPhase, data, heuristic) => {
|
|
43
|
+
console.log(`Current phase: ${phase}`);
|
|
44
|
+
console.log(`Is heuristic (using fallback data due to fetch failure): ${!!heuristic}`);
|
|
45
|
+
console.log('Full Market Data:', data);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
monitor.on("stopped", () => {
|
|
49
|
+
console.log("Monitor gracefully stopped.");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
monitor.start();
|
|
53
|
+
|
|
54
|
+
// Check current state
|
|
55
|
+
console.log('Is Running:', monitor.isRunningState);
|
|
56
|
+
console.log('Current Phase:', monitor.currentPhase);
|
|
57
|
+
|
|
58
|
+
// Later...
|
|
59
|
+
// monitor.stop();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 2. Nasdaq Market Status & Sleep Calculation
|
|
63
|
+
Direct retrieval and wait-time calculation.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { MarketStatus } from '@ckirg/corelib-markets';
|
|
67
|
+
|
|
68
|
+
const result = await MarketStatus.getStatus();
|
|
69
|
+
|
|
70
|
+
if (result.status === 'success') {
|
|
71
|
+
const info = result.value;
|
|
72
|
+
console.log(`Nasdaq Status: ${info.mrktStatus}`);
|
|
73
|
+
|
|
74
|
+
// Calculate milliseconds until next open or pre-market (returns 0 if already open)
|
|
75
|
+
const sleepMs = MarketStatus.getSleepDuration(info);
|
|
76
|
+
console.log(`Sleeping ${sleepMs}ms until next event.`);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3. CNN Fear & Greed Index
|
|
81
|
+
Retrieve sentiment data with optional historical filtering.
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { CnnFearAndGreed, CnnFearAndGreedFilter } from '@ckirg/corelib-markets';
|
|
85
|
+
|
|
86
|
+
// Fetch current Fear & Greed Index (returns the 'fear_and_greed' sub-object by default)
|
|
87
|
+
const current = await CnnFearAndGreed.getFearAndGreed();
|
|
88
|
+
|
|
89
|
+
// Fetch historical scores (full 1-year data)
|
|
90
|
+
const historical = await CnnFearAndGreed.getFearAndGreed("Historical", "full");
|
|
91
|
+
|
|
92
|
+
// Fetch specific metric for a specific date
|
|
93
|
+
const vix = await CnnFearAndGreed.getFearAndGreed(
|
|
94
|
+
"2026-03-15",
|
|
95
|
+
CnnFearAndGreedFilter.MarketVolatilityVix
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
if (current.status === 'success') {
|
|
99
|
+
console.log(`Score: ${current.value.score} (${current.value.rating})`);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 4. Historical Data (Yahoo Finance v3)
|
|
104
|
+
Retrieve standardized historical OHLCV data using a resilient Yahoo Finance v3 integration.
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { Historical } from '@ckirg/corelib-markets';
|
|
108
|
+
|
|
109
|
+
// Fetch daily historical data for the last year
|
|
110
|
+
const result = await Historical.getData("AAPL", {
|
|
111
|
+
period1: "2023-01-01",
|
|
112
|
+
interval: "1d"
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
if (result.status === 'success') {
|
|
116
|
+
console.log(`Retrieved ${result.value.length} quotes for ${result.value[0].symbol}`);
|
|
117
|
+
const latest = result.value[result.value.length - 1];
|
|
118
|
+
console.log(`Latest Close (${latest.date}): ${latest.close}`);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 5. Real-Time Streaming (Rust WS Engine)
|
|
123
|
+
|
|
124
|
+
The flagship feature of `@ckirg/corelib-markets`. All three streaming providers share the same architecture:
|
|
125
|
+
|
|
126
|
+
**Architecture:** The Rust crate owns the WebSocket connection. Data arrives on a Rust worker thread and is forwarded to JS via napi `ThreadsafeFunction` — the JS event loop is never blocked. Each wrapper is a standard Node.js `EventEmitter`. Construction is synchronous (calls into the native addon); `init()` + `start()` are async. Wrap `new XxxStreaming()` in `try/catch`.
|
|
127
|
+
|
|
128
|
+
#### Alpaca (authenticated; IEX real-time feed)
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { AlpacaStreaming } from '@ckirg/corelib-markets';
|
|
132
|
+
|
|
133
|
+
const stream = new AlpacaStreaming();
|
|
134
|
+
|
|
135
|
+
// Credentials via init() or APCA_API_KEY_ID / APCA_API_SECRET_KEY env vars
|
|
136
|
+
await stream.init({
|
|
137
|
+
keyId: 'YOUR_KEY',
|
|
138
|
+
secretKey: 'YOUR_SECRET',
|
|
139
|
+
silenceSeconds: 60, // auto-reconnect after N seconds of silence (default: 60)
|
|
140
|
+
// dbPath: '/tmp/alpaca.redb' // optional: override persistence DB path
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
await stream.start();
|
|
144
|
+
|
|
145
|
+
// Simple array → mapped to quotes channel; or pass { trades?, quotes?, bars? }
|
|
146
|
+
stream.subscribe(['AAPL', 'TSLA', 'NVDA']);
|
|
147
|
+
|
|
148
|
+
stream.on('pricing', (data) => {
|
|
149
|
+
console.log(`${data.symbol}: $${data.price}`);
|
|
150
|
+
});
|
|
151
|
+
stream.on('market', (event) => console.log('market event', event));
|
|
152
|
+
stream.on('connected', () => console.log('connected'));
|
|
153
|
+
stream.on('disconnected', () => console.log('disconnected'));
|
|
154
|
+
stream.on('reconnecting', () => console.log('reconnecting'));
|
|
155
|
+
stream.on('log', (r) => console.log(`[${r.level}] ${r.msg}`));
|
|
156
|
+
stream.on('error', (e) => console.error('error', e));
|
|
157
|
+
|
|
158
|
+
// stream.unsubscribe(['AAPL']);
|
|
159
|
+
// stream.clean(); // wipe persistence DB (auto-runs in development mode)
|
|
160
|
+
stream.stop();
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### Finnhub (authenticated; token required)
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { FinnhubStreaming, type FinnhubPricingData } from '@ckirg/corelib-markets';
|
|
167
|
+
|
|
168
|
+
const stream = new FinnhubStreaming();
|
|
169
|
+
|
|
170
|
+
// Token via init() or FINNHUB_API_KEY env var
|
|
171
|
+
await stream.init({
|
|
172
|
+
token: 'YOUR_FINNHUB_API_KEY',
|
|
173
|
+
// name?: string — optional label
|
|
174
|
+
// baseUrl?: string — override WS endpoint
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
await stream.start();
|
|
178
|
+
|
|
179
|
+
await stream.subscribe(['AAPL', 'MSFT', 'NVDA']);
|
|
180
|
+
|
|
181
|
+
stream.on('pricing', (data: FinnhubPricingData) => {
|
|
182
|
+
// Payload fields are camelCase in JS (napi convention): messageType, not message_type.
|
|
183
|
+
// data.timestamp is a numeric epoch in milliseconds.
|
|
184
|
+
console.log(`[${data.messageType}] ${data.symbol}: $${data.price} @ ${data.timestamp}`);
|
|
185
|
+
});
|
|
186
|
+
stream.on('connected', () => console.log('connected'));
|
|
187
|
+
stream.on('disconnected', (r) => console.log('disconnected', r));
|
|
188
|
+
stream.on('reconnecting', (d) => console.log('reconnecting', d));
|
|
189
|
+
stream.on('log', (r) => console.log(`[${r.level}] ${r.msg}`));
|
|
190
|
+
stream.on('error', (e) => console.error('error', e));
|
|
191
|
+
|
|
192
|
+
await stream.stop();
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
#### Yahoo Finance (unauthenticated)
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import { YahooStreaming } from '@ckirg/corelib-markets';
|
|
199
|
+
|
|
200
|
+
const stream = new YahooStreaming();
|
|
201
|
+
|
|
202
|
+
await stream.init({
|
|
203
|
+
silenceSeconds: 45, // auto-reconnect threshold (default: 60)
|
|
204
|
+
// dbPath: '/tmp/yahoo_streaming.redb'
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
await stream.start();
|
|
208
|
+
|
|
209
|
+
stream.subscribe(['AAPL', 'TSLA', 'NVDA', 'BTC-USD']);
|
|
210
|
+
|
|
211
|
+
// Yahoo's raw payload is the proto-decoded JsPricingData: the ticker is `id` (not `symbol`).
|
|
212
|
+
stream.on('pricing', (data) => console.log(`${data.id}: $${data.price}`));
|
|
213
|
+
stream.on('silence-reconnect', () => console.log('silent too long, reconnecting'));
|
|
214
|
+
stream.on('connected', () => console.log('connected'));
|
|
215
|
+
stream.on('disconnected', () => console.log('disconnected'));
|
|
216
|
+
stream.on('error', (e) => console.error('error', e));
|
|
217
|
+
|
|
218
|
+
// stream.clean(); // wipe persistence DB (auto-runs in development mode)
|
|
219
|
+
stream.stop();
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Events emitted by all three:** `pricing`, `log` (`{level, msg, extras?}`), `connected`, `disconnected`, `reconnecting`, `error`. Alpaca/Yahoo also emit `market` (unified market event, parsed JSON object) and `silence-reconnect`.
|
|
223
|
+
|
|
224
|
+
### 6. Nasdaq 100 Symbols
|
|
225
|
+
Fast, cached retrieval of the Nasdaq 100 constituent symbols.
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
import { getSymbolsTop100 } from '@ckirg/corelib-markets';
|
|
229
|
+
|
|
230
|
+
const symbols = await getSymbolsTop100();
|
|
231
|
+
console.log(`Nasdaq 100 constituents (${symbols.length}):`, symbols);
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### 7. Persistent Symbol Database (MarketSymbols)
|
|
235
|
+
Automated Nasdaq symbol directory with auto-refresh and environment-aware search sequencing.
|
|
236
|
+
|
|
237
|
+
#### Features
|
|
238
|
+
- **Auto-Refresh**: Synchronizes with official Nasdaq directories (`nasdaqlisted.txt`, `otherlisted.txt`) if data is missing or outdated (older than today NY time).
|
|
239
|
+
- **Environment-Aware**: Automatically optimizes search sequence based on the runtime:
|
|
240
|
+
- **Standard (Node/Bun)**: `SQLite -> Nasdaq API -> Ingestors` (Prioritizes local speed).
|
|
241
|
+
- **Edge (Cloudflare/Lambda)**: `Nasdaq API -> Ingestors -> SQLite` (Prioritizes fresh API data over cold storage).
|
|
242
|
+
- **Turso Support**: Supports both local SQLite and remote Turso/LibSQL databases.
|
|
243
|
+
|
|
244
|
+
#### Basic Usage
|
|
245
|
+
```typescript
|
|
246
|
+
import { MarketSymbols } from '@ckirg/corelib-markets';
|
|
247
|
+
|
|
248
|
+
// Initialize (defaults to local SQLite: ./tmp/NasdaqSymbols.sqlite)
|
|
249
|
+
const symbols = new MarketSymbols();
|
|
250
|
+
|
|
251
|
+
// Get details (Sequence depends on runtime)
|
|
252
|
+
const aapl = await symbols.get("AAPL");
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### Cloud Usage (Turso)
|
|
256
|
+
```typescript
|
|
257
|
+
const symbols = new MarketSymbols({
|
|
258
|
+
dbUrl: "libsql://your-db.turso.io",
|
|
259
|
+
dbToken: "your-auth-token"
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
#### Custom Ingestors (e.g., Google Apps Script)
|
|
264
|
+
The ingestor should return a `MarketSymbolRow` JSON structure.
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
const ingestorUrl = "https://script.google.com/macros/s/.../exec";
|
|
268
|
+
const symbols = new MarketSymbols(undefined, [ingestorUrl]);
|
|
269
|
+
|
|
270
|
+
// Fallback search will hit the GAS endpoint if not found in DB or Nasdaq API
|
|
271
|
+
const custom = await symbols.get("PRIVATE_TICKER");
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
#### Manual Maintenance
|
|
275
|
+
```typescript
|
|
276
|
+
// Force a full directory refresh from Nasdaq sources
|
|
277
|
+
await symbols.refresh();
|
|
278
|
+
|
|
279
|
+
// Graceful shutdown
|
|
280
|
+
await symbols.close();
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### 8. Resilient Nasdaq API
|
|
284
|
+
Low-level wrapper for custom Nasdaq API interactions.
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { ApiNasdaqUnlimited } from '@ckirg/corelib-markets';
|
|
288
|
+
|
|
289
|
+
// Execute single high-resilience request
|
|
290
|
+
const result = await ApiNasdaqUnlimited.endPoint('https://api.nasdaq.com/api/quote/AAPL/info');
|
|
291
|
+
|
|
292
|
+
if (result.status === 'success') {
|
|
293
|
+
console.log('AAPL Info:', result.value);
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### 9. Integration with Core (Logging & Config)
|
|
298
|
+
`ts-markets` is designed to seamlessly use the logging and configuration systems provided by `@ckirg/corelib`.
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
import { logger, ConfigManager } from '@ckirg/corelib';
|
|
302
|
+
import { MarketMonitor } from '@ckirg/corelib-markets';
|
|
303
|
+
|
|
304
|
+
// The monitor automatically uses the global logger
|
|
305
|
+
const monitor = new MarketMonitor();
|
|
306
|
+
|
|
307
|
+
// You can override default headers via ConfigManager
|
|
308
|
+
// markets.nasdaq.headers
|
|
309
|
+
// markets.cnn.headers
|
|
310
|
+
|
|
311
|
+
logger.info("Starting market services...");
|
|
312
|
+
monitor.start();
|
|
313
|
+
```
|