thetadatadx 13.0.0
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 +267 -0
- package/index.d.ts +7487 -0
- package/index.js +685 -0
- package/package.json +50 -0
- package/record_batch_forwarder.js +20 -0
- package/streaming-session.d.ts +266 -0
- package/streaming-session.js +789 -0
package/README.md
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="../assets/logo.svg" alt="ThetaDataDx" width="100%" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# thetadatadx (TypeScript / Node.js)
|
|
6
|
+
|
|
7
|
+
The Node.js SDK for [ThetaData](https://thetadata.us) market data. Pull US stock, option, index, and rate data three ways — point-in-time **history**, real-time **streaming**, and whole-universe **flat files** — all from a single authenticated client. Connects straight to ThetaData; nothing to install and run locally, no local proxy.
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/thetadatadx)
|
|
10
|
+
[](https://github.com/userFRM/ThetaDataDx/blob/main/LICENSE)
|
|
11
|
+
[](https://nodejs.org)
|
|
12
|
+
[](https://discord.thetadata.us/)
|
|
13
|
+
|
|
14
|
+
> [!IMPORTANT]
|
|
15
|
+
> A valid [ThetaData](https://thetadata.us) subscription is required. The SDK
|
|
16
|
+
> authenticates against ThetaData's Nexus API using your account credentials.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **Complete coverage** — stocks, options, indices, and rates across 65 typed endpoints.
|
|
21
|
+
- **Three access modes, one client** — point-in-time history, real-time streaming, and bulk flat-file downloads.
|
|
22
|
+
- **Fully typed** — every endpoint, tick, and streaming event ships with hand-checked `.d.ts` declarations.
|
|
23
|
+
- **Greeks on demand** — five tiers of Black-Scholes Greeks and implied volatility, served straight from the option endpoints.
|
|
24
|
+
- **Arrow on the way out** — flat-file results emit Arrow IPC for a zero-copy handoff to `apache-arrow`.
|
|
25
|
+
- **No terminal to run** — prebuilt native binaries; nothing to compile, nothing to babysit locally.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install thetadatadx
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Prebuilt binaries are downloaded automatically for Linux x64 (glibc), macOS arm64 (Apple Silicon), and Windows x64 (MSVC). No Rust toolchain is required.
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
> [!TIP]
|
|
38
|
+
> Pass your API key directly to the client and you are one line from a live connection. Generate a key from your [ThetaData user portal](https://www.thetadata.net/), then call `Client.connectWith({ apiKey: "td1_..." })`. The key can also come from the environment (`{ apiKeyFromEnv: true }`, reading `THETADATA_API_KEY`) or a `.env` file (`{ apiKeyFromDotenv: ".env" }`). Email and password is also supported: `{ email, password }` inline, or a `creds.txt` file (email on line 1, password on line 2) via `connectFromFile`. Target staging with `marketDataType: "STAGE"`. For full control over hosts and timeouts, build a typed `Credentials` + `Config` and call `Client.connect(creds, config)`.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { Client } from 'thetadatadx';
|
|
42
|
+
|
|
43
|
+
// Pass your API key directly. Add marketDataType: "STAGE" to target staging.
|
|
44
|
+
const client = await Client.connectWith({ apiKey: 'td1_...' });
|
|
45
|
+
|
|
46
|
+
// First-order Greeks for every strike on SPY's 2026-06-19 expiry, as of 2024-03-15
|
|
47
|
+
const greeks = await client.marketData.optionHistoryGreeksFirstOrder('SPY', '20260619', { date: '20240315' });
|
|
48
|
+
for (const t of greeks.slice(0, 5)) {
|
|
49
|
+
console.log(`K=${t.strike} ${t.right} delta=${t.delta.toFixed(4)} theta=${t.theta.toFixed(4)}`);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Other ways to construct the client:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { Client } from 'thetadatadx';
|
|
57
|
+
|
|
58
|
+
// API key from the THETADATA_API_KEY environment variable, or from a .env file
|
|
59
|
+
const fromEnv = await Client.connectWith({ apiKeyFromEnv: true });
|
|
60
|
+
const fromDotenv = await Client.connectWith({ apiKeyFromDotenv: '.env' });
|
|
61
|
+
|
|
62
|
+
// Email and password, inline
|
|
63
|
+
const withLogin = await Client.connectWith({ email: 'you@example.com', password: 'your_password' });
|
|
64
|
+
|
|
65
|
+
// Full control: load a typed credentials file (custom hosts, timeouts via Config)
|
|
66
|
+
const fullControl = await Client.connectFromFile('creds.txt');
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Every market-data method resolves a `Promise` of typed tick objects off the runtime's execution thread, so a fetch never holds the event loop:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
const eod = await client.marketData.stockHistoryEOD('AAPL', '20240101', '20240301');
|
|
73
|
+
console.log(eod.length, eod[0].close);
|
|
74
|
+
|
|
75
|
+
const bars = await client.marketData.stockHistoryOHLC('AAPL', { date: '20240315', interval: '1m' });
|
|
76
|
+
const exps = await client.marketData.optionListExpirations('SPY');
|
|
77
|
+
|
|
78
|
+
// Optional parameters — including a per-call timeout — ride in the trailing options object
|
|
79
|
+
const snap = await client.marketData.stockSnapshotQuote(['AAPL', 'MSFT'], { timeoutMs: 5000 });
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Streaming
|
|
83
|
+
|
|
84
|
+
Real-time quotes and trades flow through the same client. Register a callback with `startStreaming`; events are discriminated on `event.kind` and the typed payload narrows automatically:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { Contract, Client } from 'thetadatadx';
|
|
88
|
+
|
|
89
|
+
const client = await Client.connectWith({ apiKey: 'td1_...' });
|
|
90
|
+
|
|
91
|
+
await client.stream.startStreaming((event) => {
|
|
92
|
+
if (event.kind === 'trade' && event.trade) {
|
|
93
|
+
const { contract, price, size, exchange, msOfDay, sequence, condition } = event.trade;
|
|
94
|
+
console.log(
|
|
95
|
+
`${contract.symbol} ${contract.expiration} ${contract.strike} ${contract.right} trade price=${price} size=${size} ` +
|
|
96
|
+
`exchange=${exchange} ms_of_day=${msOfDay} sequence=${sequence} condition=${condition}`,
|
|
97
|
+
);
|
|
98
|
+
} else if (event.kind === 'quote' && event.quote) {
|
|
99
|
+
const { contract, bid, ask, bidSize, askSize, bidExchange, askExchange, msOfDay } = event.quote;
|
|
100
|
+
console.log(
|
|
101
|
+
`${contract.symbol} ${contract.expiration} ${contract.strike} ${contract.right} quote bid=${bid} ask=${ask} ` +
|
|
102
|
+
`bid_size=${bidSize} ask_size=${askSize} bid_exchange=${bidExchange} ` +
|
|
103
|
+
`ask_exchange=${askExchange} ms_of_day=${msOfDay}`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const leg = { expiration: '20260620', strike: '550', right: 'C' };
|
|
109
|
+
client.stream.subscribeMany([
|
|
110
|
+
Contract.option('SPY', leg).quote(),
|
|
111
|
+
Contract.option('SPY', leg).trade(),
|
|
112
|
+
]);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Build subscriptions with the fluent `Contract` API and pass them — one at a time or in bulk — to `subscribe` / `subscribeMany`. Every subscription is the same typed value, so quotes, trades, and open interest across contracts mix freely in one array:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { Contract, SecType } from 'thetadatadx';
|
|
119
|
+
|
|
120
|
+
const stock = Contract.stock('AAPL');
|
|
121
|
+
const option = Contract.option('SPY', { expiration: '20260620', strike: '550', right: 'C' });
|
|
122
|
+
|
|
123
|
+
client.stream.subscribe(stock.quote());
|
|
124
|
+
client.stream.subscribeMany([option.quote(), option.trade(), option.openInterest()]);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Or take a whole-market feed — every option trade across the universe, no per-contract setup. The full-trade feed sends a quote and an OHLC bar before each trade, so add an `ohlcvc` branch to the callback to handle the bars:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { SecType } from 'thetadatadx';
|
|
131
|
+
|
|
132
|
+
await client.stream.startStreaming((event) => {
|
|
133
|
+
if (event.kind === 'ohlcvc' && event.ohlcvc) {
|
|
134
|
+
const { contract, open, high, low, close, volume } = event.ohlcvc;
|
|
135
|
+
console.log(
|
|
136
|
+
`${contract.symbol} ${contract.expiration} ${contract.strike} ${contract.right} bar ` +
|
|
137
|
+
`o=${open} h=${high} l=${low} c=${close} volume=${volume}`,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
// ...plus the quote/trade branches from above.
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
client.stream.subscribe(SecType.option().fullTrades()); // the callback runs per event — keep it fast
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
When you are done, stop the stream and drain it. By the time `awaitDrain` resolves, the callback has stopped firing, so any state it closed over can be released safely:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
client.stream.stopStreaming();
|
|
150
|
+
const drained = await client.stream.awaitDrain(5000);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
> [!TIP]
|
|
154
|
+
> On an involuntary disconnect the client recovers on its own — exponential
|
|
155
|
+
> backoff with jitter, host failover, then a paced re-subscribe of every active
|
|
156
|
+
> contract.
|
|
157
|
+
|
|
158
|
+
Prefer columns? `client.stream.batches(...)` is a sibling to the callback: the same subscriptions, delivered as apache-arrow `RecordBatch` values under a fixed schema, consumed with `for await`. It closes (unsubscribe + tear down) on `close()`, or on `Symbol.asyncDispose` via `await using` where your runtime supports explicit resource management:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { Contract } from 'thetadatadx';
|
|
162
|
+
|
|
163
|
+
// `batches(...)` starts the streaming session, so open it first, then subscribe.
|
|
164
|
+
const batches = await client.stream.batches({ batchSize: 8192 });
|
|
165
|
+
try {
|
|
166
|
+
client.stream.subscribe(Contract.stock('AAPL').trade());
|
|
167
|
+
for await (const batch of batches) {
|
|
168
|
+
console.log(batch.numRows);
|
|
169
|
+
}
|
|
170
|
+
} finally {
|
|
171
|
+
batches.close();
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Decoding the batches needs `apache-arrow` installed alongside the SDK.
|
|
176
|
+
|
|
177
|
+
## Types
|
|
178
|
+
|
|
179
|
+
Every tick type and streaming event is exported. Import the ones you need:
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import type { OhlcTick, GreeksAllTick, Quote, Trade, StreamEvent } from 'thetadatadx';
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
The streaming callback receives a discriminated `StreamEvent`, narrowed on `event.kind`. Market-data events (`trade`, `quote`, `ohlcvc`, `open_interest`) carry their payload under a matching field; one typed payload also exists per lifecycle event (`connected`, `loginSuccess`, `disconnected`, `reconnecting`, …):
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
await client.stream.startStreaming((event: StreamEvent) => {
|
|
189
|
+
switch (event.kind) {
|
|
190
|
+
case 'trade': /* event.trade is Trade */ break;
|
|
191
|
+
case 'quote': /* event.quote is Quote */ break;
|
|
192
|
+
case 'ohlcvc': /* event.ohlcvc is Ohlcvc */ break;
|
|
193
|
+
case 'open_interest': /* event.openInterest is OpenInterest */ break;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
`kind` is a string-literal union (not a `const enum`), so the type information stays self-contained under `"isolatedModules": true` and works across Vite, esbuild, ts-jest, and Next.js.
|
|
199
|
+
|
|
200
|
+
> [!NOTE]
|
|
201
|
+
> Wherever a 64-bit integer crosses the boundary it surfaces as `bigint`, not
|
|
202
|
+
> `number` — `volume` and `count` on OHLC / EOD ticks, `droppedEventCount()`,
|
|
203
|
+
> and `receivedAtNs` on every event. Use `42n` literals for comparisons, or
|
|
204
|
+
> widen with `Number(x)` at the point of display.
|
|
205
|
+
|
|
206
|
+
## Flat files
|
|
207
|
+
|
|
208
|
+
Whole-universe daily snapshots for one `(security type, request type, date)` at a time. The decoded schema follows the request type, so the binding emits Arrow IPC bytes — pair with `apache-arrow`'s `tableFromIPC` to materialise a typed `Table`:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import { Client } from 'thetadatadx';
|
|
212
|
+
import { tableFromIPC } from 'apache-arrow'; // peer dependency
|
|
213
|
+
|
|
214
|
+
const client = await Client.connectFromFile('creds.txt');
|
|
215
|
+
|
|
216
|
+
const rows = await client.flatFiles.optionTradeQuote('20260428');
|
|
217
|
+
console.log(rows.len());
|
|
218
|
+
|
|
219
|
+
const table = tableFromIPC(rows.toArrowIpc());
|
|
220
|
+
// Or skip Arrow entirely: const json = JSON.parse(rows.toJson());
|
|
221
|
+
|
|
222
|
+
// Generic dispatcher when security type / request type come from config
|
|
223
|
+
const oi = await client.flatFiles.request('OPTION', 'OPEN_INTEREST', '20260428');
|
|
224
|
+
|
|
225
|
+
// Or write the raw vendor file straight to disk
|
|
226
|
+
await client.flatFileToPath('OPTION', 'TRADE_QUOTE', '20260428', '/tmp/option-trade-quote', 'csv');
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The flat-file distribution serves a fixed set of datasets: option `trade_quote` / `open_interest` / `eod`, stock `trade_quote` / `eod`. Available `flatFiles.*` methods: `optionTradeQuote`, `optionOpenInterest`, `optionEod`, `stockTradeQuote`, `stockEod`, plus `request(secType, reqType, date)`. The generic `request(...)` and `flatFileToPath(...)` paths reject an unserved `(security, request)` pair with a typed invalid-parameter error.
|
|
230
|
+
|
|
231
|
+
## Endpoint coverage
|
|
232
|
+
|
|
233
|
+
65 typed endpoints across stocks, options, indices, the market calendar, and interest rates, plus real-time streaming.
|
|
234
|
+
|
|
235
|
+
| Category | Endpoints | Examples |
|
|
236
|
+
|---|---|---|
|
|
237
|
+
| Stock | 16 | EOD, OHLC, trades, quotes, snapshots, at-time |
|
|
238
|
+
| Option | 36 | Every stock surface plus five Greeks tiers, open interest, contract lists |
|
|
239
|
+
| Index | 9 | EOD, OHLC, price, snapshots |
|
|
240
|
+
| Calendar | 3 | Market open/close, holidays, early closes |
|
|
241
|
+
| Interest rate | 1 | EOD rate history |
|
|
242
|
+
|
|
243
|
+
Every endpoint is a camelCase method on `client.marketData`. The full method list with JSDoc lives in `index.d.ts` and the [API reference](https://userfrm.github.io/ThetaDataDx/reference/).
|
|
244
|
+
|
|
245
|
+
## Errors
|
|
246
|
+
|
|
247
|
+
Every call rejects with a typed error under a common `ThetaDataError` base — authentication, rate limit, not found, deadline exceeded, invalid parameter, and the rest — so the same cases are catchable here exactly as they are in every other binding.
|
|
248
|
+
|
|
249
|
+
## Building from source
|
|
250
|
+
|
|
251
|
+
Only needed when a platform has no prebuilt binary or you are developing locally:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
cd thetadatadx-ts
|
|
255
|
+
npm install
|
|
256
|
+
npm run build # requires Rust stable + protoc
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Documentation
|
|
260
|
+
|
|
261
|
+
- [Documentation site](https://userfrm.github.io/ThetaDataDx/) — getting started, API reference, streaming, flat files
|
|
262
|
+
- [Repository, issues, contributing](https://github.com/userFRM/ThetaDataDx)
|
|
263
|
+
- Community discussion on the [ThetaData Discord](https://discord.thetadata.us/)
|
|
264
|
+
|
|
265
|
+
## License
|
|
266
|
+
|
|
267
|
+
Licensed under the Apache License, Version 2.0.
|