@fullstackcraftllc/floe 0.0.1 → 0.0.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 +9 -209
- package/dist/adapters/index.js +64 -16
- package/dist/client/FloeClient.d.ts +352 -0
- package/dist/client/FloeClient.js +465 -0
- package/dist/client/brokers/TradierClient.d.ts +161 -0
- package/dist/client/brokers/TradierClient.js +434 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +14 -1
- package/dist/types/index.d.ts +35 -0
- package/dist/utils/occ.d.ts +164 -0
- package/dist/utils/occ.js +202 -0
- package/package.json +3 -3
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TradierClient = void 0;
|
|
4
|
+
const occ_1 = require("../../utils/occ");
|
|
5
|
+
/**
|
|
6
|
+
* Regex pattern to identify OCC option symbols
|
|
7
|
+
* Matches both compact format (e.g., AAPL230120C00150000) and
|
|
8
|
+
* padded format (e.g., 'AAPL 230120C00150000')
|
|
9
|
+
* Pattern: 1-6 char root + YYMMDD + C/P + 8-digit strike
|
|
10
|
+
*/
|
|
11
|
+
const OCC_OPTION_PATTERN = /^.{1,6}\d{6}[CP]\d{8}$/;
|
|
12
|
+
/**
|
|
13
|
+
* TradierClient handles real-time streaming connections to the Tradier API.
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* This client manages WebSocket connections to Tradier's streaming API,
|
|
17
|
+
* normalizes incoming quote and trade data, and emits events for upstream
|
|
18
|
+
* consumption by the FloeClient.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const client = new TradierClient('your-api-key');
|
|
23
|
+
*
|
|
24
|
+
* client.on('tickerUpdate', (ticker) => {
|
|
25
|
+
* console.log(`${ticker.symbol}: ${ticker.spot}`);
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* await client.connect();
|
|
29
|
+
* client.subscribe(['QQQ', 'AAPL 240119C00500000']);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
class TradierClient {
|
|
33
|
+
/**
|
|
34
|
+
* Creates a new TradierClient instance.
|
|
35
|
+
*
|
|
36
|
+
* @param authKey - Tradier API access token
|
|
37
|
+
*/
|
|
38
|
+
constructor(authKey) {
|
|
39
|
+
/** Current streaming session */
|
|
40
|
+
this.streamSession = null;
|
|
41
|
+
/** WebSocket connection */
|
|
42
|
+
this.ws = null;
|
|
43
|
+
/** Connection state */
|
|
44
|
+
this.connected = false;
|
|
45
|
+
/** Currently subscribed symbols (tickers and options) */
|
|
46
|
+
this.subscribedSymbols = new Set();
|
|
47
|
+
/** Cached ticker data (for merging quote and trade events) */
|
|
48
|
+
this.tickerCache = new Map();
|
|
49
|
+
/** Cached option data (for merging quote and trade events) */
|
|
50
|
+
this.optionCache = new Map();
|
|
51
|
+
/** Event listeners */
|
|
52
|
+
this.eventListeners = new Map();
|
|
53
|
+
/** Reconnection attempt counter */
|
|
54
|
+
this.reconnectAttempts = 0;
|
|
55
|
+
/** Maximum reconnection attempts */
|
|
56
|
+
this.maxReconnectAttempts = 5;
|
|
57
|
+
/** Reconnection delay in ms (doubles with each attempt) */
|
|
58
|
+
this.baseReconnectDelay = 1000;
|
|
59
|
+
/** Tradier API base URL */
|
|
60
|
+
this.apiBaseUrl = 'https://api.tradier.com/v1';
|
|
61
|
+
/** Tradier WebSocket URL */
|
|
62
|
+
this.wsUrl = 'wss://ws.tradier.com/v1/markets/events';
|
|
63
|
+
this.authKey = authKey;
|
|
64
|
+
// Initialize event listener maps
|
|
65
|
+
this.eventListeners.set('tickerUpdate', new Set());
|
|
66
|
+
this.eventListeners.set('optionUpdate', new Set());
|
|
67
|
+
this.eventListeners.set('connected', new Set());
|
|
68
|
+
this.eventListeners.set('disconnected', new Set());
|
|
69
|
+
this.eventListeners.set('error', new Set());
|
|
70
|
+
}
|
|
71
|
+
// ==================== Public API ====================
|
|
72
|
+
/**
|
|
73
|
+
* Establishes a streaming connection to Tradier.
|
|
74
|
+
*
|
|
75
|
+
* @returns Promise that resolves when connected
|
|
76
|
+
* @throws {Error} If session creation or WebSocket connection fails
|
|
77
|
+
*/
|
|
78
|
+
async connect() {
|
|
79
|
+
// Create streaming session
|
|
80
|
+
const session = await this.createStreamSession();
|
|
81
|
+
if (!session) {
|
|
82
|
+
throw new Error('Failed to create Tradier streaming session');
|
|
83
|
+
}
|
|
84
|
+
this.streamSession = session;
|
|
85
|
+
// Connect WebSocket
|
|
86
|
+
await this.connectWebSocket();
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Disconnects from the Tradier streaming API.
|
|
90
|
+
*/
|
|
91
|
+
disconnect() {
|
|
92
|
+
if (this.ws) {
|
|
93
|
+
this.ws.close(1000, 'Client disconnect');
|
|
94
|
+
this.ws = null;
|
|
95
|
+
}
|
|
96
|
+
this.connected = false;
|
|
97
|
+
this.streamSession = null;
|
|
98
|
+
this.subscribedSymbols.clear();
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Subscribes to real-time updates for the specified symbols.
|
|
102
|
+
*
|
|
103
|
+
* @param symbols - Array of ticker symbols and/or OCC option symbols
|
|
104
|
+
*/
|
|
105
|
+
subscribe(symbols) {
|
|
106
|
+
if (!this.connected || !this.ws || !this.streamSession) {
|
|
107
|
+
// Queue symbols for subscription when connected
|
|
108
|
+
symbols.forEach(s => this.subscribedSymbols.add(s));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// Add to tracked symbols
|
|
112
|
+
symbols.forEach(s => this.subscribedSymbols.add(s));
|
|
113
|
+
// Send subscription message
|
|
114
|
+
const payload = {
|
|
115
|
+
sessionid: this.streamSession.sessionid,
|
|
116
|
+
symbols: symbols,
|
|
117
|
+
};
|
|
118
|
+
this.ws.send(JSON.stringify(payload));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Unsubscribes from real-time updates for the specified symbols.
|
|
122
|
+
*
|
|
123
|
+
* @param symbols - Array of symbols to unsubscribe from
|
|
124
|
+
*
|
|
125
|
+
* @remarks
|
|
126
|
+
* Note: Tradier's streaming API doesn't support unsubscription for individual
|
|
127
|
+
* symbols. This method removes them from local tracking. To fully unsubscribe,
|
|
128
|
+
* you would need to disconnect and reconnect with the new symbol list.
|
|
129
|
+
*/
|
|
130
|
+
unsubscribe(symbols) {
|
|
131
|
+
symbols.forEach(s => this.subscribedSymbols.delete(s));
|
|
132
|
+
// Note: Tradier doesn't support selective unsubscribe
|
|
133
|
+
// Would need to reconnect with new symbol list for full effect
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Returns whether the client is currently connected.
|
|
137
|
+
*/
|
|
138
|
+
isConnected() {
|
|
139
|
+
return this.connected;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Registers an event listener.
|
|
143
|
+
*
|
|
144
|
+
* @param event - Event type to listen for
|
|
145
|
+
* @param listener - Callback function
|
|
146
|
+
*/
|
|
147
|
+
on(event, listener) {
|
|
148
|
+
const listeners = this.eventListeners.get(event);
|
|
149
|
+
if (listeners) {
|
|
150
|
+
listeners.add(listener);
|
|
151
|
+
}
|
|
152
|
+
return this;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Removes an event listener.
|
|
156
|
+
*
|
|
157
|
+
* @param event - Event type
|
|
158
|
+
* @param listener - Callback function to remove
|
|
159
|
+
*/
|
|
160
|
+
off(event, listener) {
|
|
161
|
+
const listeners = this.eventListeners.get(event);
|
|
162
|
+
if (listeners) {
|
|
163
|
+
listeners.delete(listener);
|
|
164
|
+
}
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
// ==================== Private Methods ====================
|
|
168
|
+
/**
|
|
169
|
+
* Creates a streaming session with Tradier API.
|
|
170
|
+
*/
|
|
171
|
+
async createStreamSession() {
|
|
172
|
+
try {
|
|
173
|
+
const response = await fetch(`${this.apiBaseUrl}/markets/events/session`, {
|
|
174
|
+
method: 'POST',
|
|
175
|
+
headers: {
|
|
176
|
+
'Authorization': `Bearer ${this.authKey}`,
|
|
177
|
+
'Accept': 'application/json',
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
if (!response.ok) {
|
|
181
|
+
this.emit('error', new Error(`Failed to create stream session: ${response.statusText}`));
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const data = await response.json();
|
|
185
|
+
return data.stream;
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
this.emit('error', error instanceof Error ? error : new Error(String(error)));
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Connects to the Tradier WebSocket.
|
|
194
|
+
*/
|
|
195
|
+
connectWebSocket() {
|
|
196
|
+
return new Promise((resolve, reject) => {
|
|
197
|
+
this.ws = new WebSocket(this.wsUrl);
|
|
198
|
+
this.ws.onopen = () => {
|
|
199
|
+
this.connected = true;
|
|
200
|
+
this.reconnectAttempts = 0;
|
|
201
|
+
this.emit('connected', undefined);
|
|
202
|
+
// Subscribe to any queued symbols
|
|
203
|
+
if (this.subscribedSymbols.size > 0 && this.streamSession) {
|
|
204
|
+
const payload = {
|
|
205
|
+
sessionid: this.streamSession.sessionid,
|
|
206
|
+
symbols: Array.from(this.subscribedSymbols),
|
|
207
|
+
};
|
|
208
|
+
this.ws.send(JSON.stringify(payload));
|
|
209
|
+
}
|
|
210
|
+
resolve();
|
|
211
|
+
};
|
|
212
|
+
this.ws.onmessage = (event) => {
|
|
213
|
+
this.handleMessage(event.data);
|
|
214
|
+
};
|
|
215
|
+
this.ws.onclose = (event) => {
|
|
216
|
+
this.connected = false;
|
|
217
|
+
this.emit('disconnected', { reason: event.reason });
|
|
218
|
+
// Attempt reconnection if not a clean close
|
|
219
|
+
if (event.code !== 1000) {
|
|
220
|
+
this.attemptReconnect();
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
this.ws.onerror = (error) => {
|
|
224
|
+
this.emit('error', new Error('WebSocket error'));
|
|
225
|
+
reject(error);
|
|
226
|
+
};
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Attempts to reconnect with exponential backoff.
|
|
231
|
+
*/
|
|
232
|
+
async attemptReconnect() {
|
|
233
|
+
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
234
|
+
this.emit('error', new Error('Max reconnection attempts reached'));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
this.reconnectAttempts++;
|
|
238
|
+
const delay = this.baseReconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
|
|
239
|
+
await this.sleep(delay);
|
|
240
|
+
try {
|
|
241
|
+
await this.connect();
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
// Reconnect attempt failed, will try again via onclose
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Handles incoming WebSocket messages.
|
|
249
|
+
*/
|
|
250
|
+
handleMessage(data) {
|
|
251
|
+
try {
|
|
252
|
+
const event = JSON.parse(data);
|
|
253
|
+
if (event.type === 'quote') {
|
|
254
|
+
this.handleQuoteEvent(event);
|
|
255
|
+
}
|
|
256
|
+
else if (event.type === 'trade') {
|
|
257
|
+
this.handleTradeEvent(event);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
// Ignore parse errors for heartbeat/status messages
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Handles quote events (bid/ask updates).
|
|
266
|
+
*/
|
|
267
|
+
handleQuoteEvent(event) {
|
|
268
|
+
const { symbol } = event;
|
|
269
|
+
const timestamp = parseInt(event.biddate, 10) || Date.now();
|
|
270
|
+
const isOption = this.isOptionSymbol(symbol);
|
|
271
|
+
if (isOption) {
|
|
272
|
+
this.updateOptionFromQuote(symbol, event, timestamp);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
this.updateTickerFromQuote(symbol, event, timestamp);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Handles trade events (last price/volume updates).
|
|
280
|
+
*/
|
|
281
|
+
handleTradeEvent(event) {
|
|
282
|
+
const { symbol } = event;
|
|
283
|
+
const timestamp = parseInt(event.date, 10) || Date.now();
|
|
284
|
+
const isOption = this.isOptionSymbol(symbol);
|
|
285
|
+
if (isOption) {
|
|
286
|
+
this.updateOptionFromTrade(symbol, event, timestamp);
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
this.updateTickerFromTrade(symbol, event, timestamp);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Updates ticker data from a quote event.
|
|
294
|
+
*/
|
|
295
|
+
updateTickerFromQuote(symbol, event, timestamp) {
|
|
296
|
+
const existing = this.tickerCache.get(symbol);
|
|
297
|
+
const ticker = {
|
|
298
|
+
symbol,
|
|
299
|
+
spot: (event.bid + event.ask) / 2,
|
|
300
|
+
bid: event.bid,
|
|
301
|
+
bidSize: event.bidsz,
|
|
302
|
+
ask: event.ask,
|
|
303
|
+
askSize: event.asksz,
|
|
304
|
+
last: existing?.last ?? 0,
|
|
305
|
+
volume: existing?.volume ?? 0,
|
|
306
|
+
timestamp,
|
|
307
|
+
};
|
|
308
|
+
this.tickerCache.set(symbol, ticker);
|
|
309
|
+
this.emit('tickerUpdate', ticker);
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Updates ticker data from a trade event.
|
|
313
|
+
*/
|
|
314
|
+
updateTickerFromTrade(symbol, event, timestamp) {
|
|
315
|
+
const existing = this.tickerCache.get(symbol);
|
|
316
|
+
const last = parseFloat(event.last);
|
|
317
|
+
const volume = parseInt(event.cvol, 10);
|
|
318
|
+
const ticker = {
|
|
319
|
+
symbol,
|
|
320
|
+
spot: existing?.spot ?? last,
|
|
321
|
+
bid: existing?.bid ?? 0,
|
|
322
|
+
bidSize: existing?.bidSize ?? 0,
|
|
323
|
+
ask: existing?.ask ?? 0,
|
|
324
|
+
askSize: existing?.askSize ?? 0,
|
|
325
|
+
last,
|
|
326
|
+
volume,
|
|
327
|
+
timestamp,
|
|
328
|
+
};
|
|
329
|
+
this.tickerCache.set(symbol, ticker);
|
|
330
|
+
this.emit('tickerUpdate', ticker);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Updates option data from a quote event.
|
|
334
|
+
*/
|
|
335
|
+
updateOptionFromQuote(occSymbol, event, timestamp) {
|
|
336
|
+
const existing = this.optionCache.get(occSymbol);
|
|
337
|
+
// Parse OCC symbol to extract option details
|
|
338
|
+
let parsed;
|
|
339
|
+
try {
|
|
340
|
+
parsed = (0, occ_1.parseOCCSymbol)(occSymbol);
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
// Invalid OCC symbol, skip
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
const option = {
|
|
347
|
+
occSymbol,
|
|
348
|
+
underlying: parsed.symbol,
|
|
349
|
+
strike: parsed.strike,
|
|
350
|
+
expiration: parsed.expiration.toISOString().split('T')[0],
|
|
351
|
+
expirationTimestamp: parsed.expiration.getTime(),
|
|
352
|
+
optionType: parsed.optionType,
|
|
353
|
+
bid: event.bid,
|
|
354
|
+
bidSize: event.bidsz,
|
|
355
|
+
ask: event.ask,
|
|
356
|
+
askSize: event.asksz,
|
|
357
|
+
mark: (event.bid + event.ask) / 2,
|
|
358
|
+
last: existing?.last ?? 0,
|
|
359
|
+
volume: existing?.volume ?? 0,
|
|
360
|
+
openInterest: existing?.openInterest ?? 0,
|
|
361
|
+
impliedVolatility: existing?.impliedVolatility ?? 0,
|
|
362
|
+
timestamp,
|
|
363
|
+
};
|
|
364
|
+
this.optionCache.set(occSymbol, option);
|
|
365
|
+
this.emit('optionUpdate', option);
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Updates option data from a trade event.
|
|
369
|
+
*/
|
|
370
|
+
updateOptionFromTrade(occSymbol, event, timestamp) {
|
|
371
|
+
const existing = this.optionCache.get(occSymbol);
|
|
372
|
+
// Parse OCC symbol to extract option details
|
|
373
|
+
let parsed;
|
|
374
|
+
try {
|
|
375
|
+
parsed = (0, occ_1.parseOCCSymbol)(occSymbol);
|
|
376
|
+
}
|
|
377
|
+
catch {
|
|
378
|
+
// Invalid OCC symbol, skip
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
const last = parseFloat(event.last);
|
|
382
|
+
const volume = parseInt(event.cvol, 10);
|
|
383
|
+
const option = {
|
|
384
|
+
occSymbol,
|
|
385
|
+
underlying: parsed.symbol,
|
|
386
|
+
strike: parsed.strike,
|
|
387
|
+
expiration: parsed.expiration.toISOString().split('T')[0],
|
|
388
|
+
expirationTimestamp: parsed.expiration.getTime(),
|
|
389
|
+
optionType: parsed.optionType,
|
|
390
|
+
bid: existing?.bid ?? 0,
|
|
391
|
+
bidSize: existing?.bidSize ?? 0,
|
|
392
|
+
ask: existing?.ask ?? 0,
|
|
393
|
+
askSize: existing?.askSize ?? 0,
|
|
394
|
+
mark: existing?.mark ?? last,
|
|
395
|
+
last,
|
|
396
|
+
volume,
|
|
397
|
+
openInterest: existing?.openInterest ?? 0,
|
|
398
|
+
impliedVolatility: existing?.impliedVolatility ?? 0,
|
|
399
|
+
timestamp,
|
|
400
|
+
};
|
|
401
|
+
this.optionCache.set(occSymbol, option);
|
|
402
|
+
this.emit('optionUpdate', option);
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Checks if a symbol is an OCC option symbol.
|
|
406
|
+
*/
|
|
407
|
+
isOptionSymbol(symbol) {
|
|
408
|
+
return OCC_OPTION_PATTERN.test(symbol);
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Emits an event to all registered listeners.
|
|
412
|
+
*/
|
|
413
|
+
emit(event, data) {
|
|
414
|
+
const listeners = this.eventListeners.get(event);
|
|
415
|
+
if (listeners) {
|
|
416
|
+
listeners.forEach(listener => {
|
|
417
|
+
try {
|
|
418
|
+
listener(data);
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
// Don't let listener errors break the stream
|
|
422
|
+
console.error('Event listener error:', error);
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Simple sleep utility.
|
|
429
|
+
*/
|
|
430
|
+
sleep(ms) {
|
|
431
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
exports.TradierClient = TradierClient;
|
package/dist/index.d.ts
CHANGED
|
@@ -10,4 +10,8 @@ export { getIVSurfaces, getIVForStrike, } from './volatility';
|
|
|
10
10
|
export { smoothTotalVarianceSmile, } from './volatility/smoothing';
|
|
11
11
|
export { calculateGammaVannaCharmExposures, calculateSharesNeededToCover, } from './exposure';
|
|
12
12
|
export { cumulativeNormalDistribution, normalPDF, } from './utils/statistics';
|
|
13
|
+
export { buildOCCSymbol, parseOCCSymbol, generateStrikesAroundSpot, generateOCCSymbolsForStrikes, generateOCCSymbolsAroundSpot, } from './utils/occ';
|
|
14
|
+
export type { OCCSymbolParams, ParsedOCCSymbol, StrikeGenerationParams, } from './utils/occ';
|
|
15
|
+
export { FloeClient, Broker } from './client/FloeClient';
|
|
16
|
+
export { TradierClient } from './client/brokers/TradierClient';
|
|
13
17
|
export { genericAdapter, schwabAdapter, ibkrAdapter, tdaAdapter, brokerAdapters, getAdapter, createOptionChain, } from './adapters';
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
20
20
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
21
21
|
};
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
-
exports.createOptionChain = exports.getAdapter = exports.brokerAdapters = exports.tdaAdapter = exports.ibkrAdapter = exports.schwabAdapter = exports.genericAdapter = exports.normalPDF = exports.cumulativeNormalDistribution = exports.calculateSharesNeededToCover = exports.calculateGammaVannaCharmExposures = exports.smoothTotalVarianceSmile = exports.getIVForStrike = exports.getIVSurfaces = exports.getTimeToExpirationInYears = exports.getMillisecondsToExpiration = exports.calculateImpliedVolatility = exports.calculateGreeks = exports.blackScholes = void 0;
|
|
23
|
+
exports.createOptionChain = exports.getAdapter = exports.brokerAdapters = exports.tdaAdapter = exports.ibkrAdapter = exports.schwabAdapter = exports.genericAdapter = exports.TradierClient = exports.Broker = exports.FloeClient = exports.generateOCCSymbolsAroundSpot = exports.generateOCCSymbolsForStrikes = exports.generateStrikesAroundSpot = exports.parseOCCSymbol = exports.buildOCCSymbol = exports.normalPDF = exports.cumulativeNormalDistribution = exports.calculateSharesNeededToCover = exports.calculateGammaVannaCharmExposures = exports.smoothTotalVarianceSmile = exports.getIVForStrike = exports.getIVSurfaces = exports.getTimeToExpirationInYears = exports.getMillisecondsToExpiration = exports.calculateImpliedVolatility = exports.calculateGreeks = exports.blackScholes = void 0;
|
|
24
24
|
// Core types
|
|
25
25
|
__exportStar(require("./types"), exports);
|
|
26
26
|
// Black-Scholes pricing and Greeks
|
|
@@ -45,6 +45,19 @@ Object.defineProperty(exports, "calculateSharesNeededToCover", { enumerable: tru
|
|
|
45
45
|
var statistics_1 = require("./utils/statistics");
|
|
46
46
|
Object.defineProperty(exports, "cumulativeNormalDistribution", { enumerable: true, get: function () { return statistics_1.cumulativeNormalDistribution; } });
|
|
47
47
|
Object.defineProperty(exports, "normalPDF", { enumerable: true, get: function () { return statistics_1.normalPDF; } });
|
|
48
|
+
// OCC symbol utilities
|
|
49
|
+
var occ_1 = require("./utils/occ");
|
|
50
|
+
Object.defineProperty(exports, "buildOCCSymbol", { enumerable: true, get: function () { return occ_1.buildOCCSymbol; } });
|
|
51
|
+
Object.defineProperty(exports, "parseOCCSymbol", { enumerable: true, get: function () { return occ_1.parseOCCSymbol; } });
|
|
52
|
+
Object.defineProperty(exports, "generateStrikesAroundSpot", { enumerable: true, get: function () { return occ_1.generateStrikesAroundSpot; } });
|
|
53
|
+
Object.defineProperty(exports, "generateOCCSymbolsForStrikes", { enumerable: true, get: function () { return occ_1.generateOCCSymbolsForStrikes; } });
|
|
54
|
+
Object.defineProperty(exports, "generateOCCSymbolsAroundSpot", { enumerable: true, get: function () { return occ_1.generateOCCSymbolsAroundSpot; } });
|
|
55
|
+
// Client
|
|
56
|
+
var FloeClient_1 = require("./client/FloeClient");
|
|
57
|
+
Object.defineProperty(exports, "FloeClient", { enumerable: true, get: function () { return FloeClient_1.FloeClient; } });
|
|
58
|
+
Object.defineProperty(exports, "Broker", { enumerable: true, get: function () { return FloeClient_1.Broker; } });
|
|
59
|
+
var TradierClient_1 = require("./client/brokers/TradierClient");
|
|
60
|
+
Object.defineProperty(exports, "TradierClient", { enumerable: true, get: function () { return TradierClient_1.TradierClient; } });
|
|
48
61
|
// Broker adapters
|
|
49
62
|
var adapters_1 = require("./adapters");
|
|
50
63
|
Object.defineProperty(exports, "genericAdapter", { enumerable: true, get: function () { return adapters_1.genericAdapter; } });
|
package/dist/types/index.d.ts
CHANGED
|
@@ -63,10 +63,37 @@ export interface Greeks {
|
|
|
63
63
|
/** Ultima: Rate of change of volga with respect to volatility */
|
|
64
64
|
ultima: number;
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Normalized ticker data structure (broker-agnostic)
|
|
68
|
+
*/
|
|
69
|
+
export interface NormalizedTicker {
|
|
70
|
+
/** Underlying symbol */
|
|
71
|
+
symbol: string;
|
|
72
|
+
/** Current spot price of the underlying (mid of bid/ask, or last trade) */
|
|
73
|
+
spot: number;
|
|
74
|
+
/** Current bid price */
|
|
75
|
+
bid: number;
|
|
76
|
+
/** Current bid size */
|
|
77
|
+
bidSize: number;
|
|
78
|
+
/** Current ask price */
|
|
79
|
+
ask: number;
|
|
80
|
+
/** Current ask size */
|
|
81
|
+
askSize: number;
|
|
82
|
+
/** Last traded price */
|
|
83
|
+
last: number;
|
|
84
|
+
/** Cumulative volume for the day */
|
|
85
|
+
volume: number;
|
|
86
|
+
/** Timestamp of the quote in milliseconds */
|
|
87
|
+
timestamp: number;
|
|
88
|
+
}
|
|
66
89
|
/**
|
|
67
90
|
* Normalized option data structure (broker-agnostic)
|
|
68
91
|
*/
|
|
69
92
|
export interface NormalizedOption {
|
|
93
|
+
/** OCC-formatted option symbol (e.g., 'AAPL 230120C00150000') */
|
|
94
|
+
occSymbol: string;
|
|
95
|
+
/** Underlying ticker symbol */
|
|
96
|
+
underlying: string;
|
|
70
97
|
/** Strike price */
|
|
71
98
|
strike: number;
|
|
72
99
|
/** Expiration date (ISO 8601) */
|
|
@@ -77,8 +104,12 @@ export interface NormalizedOption {
|
|
|
77
104
|
optionType: OptionType;
|
|
78
105
|
/** Current bid price */
|
|
79
106
|
bid: number;
|
|
107
|
+
/** Current bid size */
|
|
108
|
+
bidSize: number;
|
|
80
109
|
/** Current ask price */
|
|
81
110
|
ask: number;
|
|
111
|
+
/** Current ask size */
|
|
112
|
+
askSize: number;
|
|
82
113
|
/** Mark (mid) price */
|
|
83
114
|
mark: number;
|
|
84
115
|
/** Last traded price */
|
|
@@ -87,8 +118,12 @@ export interface NormalizedOption {
|
|
|
87
118
|
volume: number;
|
|
88
119
|
/** Open interest */
|
|
89
120
|
openInterest: number;
|
|
121
|
+
/** Live open interest - calculated intraday by comparing using open interest as t=0 and comparing trades to the current NBBO for that option */
|
|
122
|
+
liveOpenInterest?: number;
|
|
90
123
|
/** Implied volatility (as decimal) */
|
|
91
124
|
impliedVolatility: number;
|
|
125
|
+
/** Timestamp of the quote in milliseconds */
|
|
126
|
+
timestamp: number;
|
|
92
127
|
/** Pre-calculated Greeks (optional) */
|
|
93
128
|
greeks?: Greeks;
|
|
94
129
|
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OCC (Options Clearing Corporation) symbol utilities
|
|
3
|
+
*
|
|
4
|
+
* OCC symbols follow the format: ROOT + YYMMDD + C/P + STRIKE
|
|
5
|
+
* Example: AAPL230120C00150000 = AAPL $150 Call expiring Jan 20, 2023
|
|
6
|
+
*/
|
|
7
|
+
import { OptionType } from '../types';
|
|
8
|
+
/**
|
|
9
|
+
* Parameters for building an OCC option symbol
|
|
10
|
+
*/
|
|
11
|
+
export interface OCCSymbolParams {
|
|
12
|
+
/** Underlying ticker symbol (e.g., 'AAPL', 'QQQ') */
|
|
13
|
+
symbol: string;
|
|
14
|
+
/** Expiration date (Date object or ISO string) */
|
|
15
|
+
expiration: Date | string;
|
|
16
|
+
/** Option type: 'call' or 'put' */
|
|
17
|
+
optionType: OptionType;
|
|
18
|
+
/** Strike price in dollars (e.g., 150.50) */
|
|
19
|
+
strike: number;
|
|
20
|
+
/**
|
|
21
|
+
* If true, pads the symbol to 6 characters with spaces (standard OCC format).
|
|
22
|
+
* If false (default), uses compact format without spaces (common for APIs).
|
|
23
|
+
* @default false
|
|
24
|
+
*/
|
|
25
|
+
padded?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parsed components of an OCC option symbol
|
|
29
|
+
*/
|
|
30
|
+
export interface ParsedOCCSymbol {
|
|
31
|
+
/** Underlying ticker symbol */
|
|
32
|
+
symbol: string;
|
|
33
|
+
/** Expiration date */
|
|
34
|
+
expiration: Date;
|
|
35
|
+
/** Option type */
|
|
36
|
+
optionType: OptionType;
|
|
37
|
+
/** Strike price in dollars */
|
|
38
|
+
strike: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Parameters for generating strikes around a spot price
|
|
42
|
+
*/
|
|
43
|
+
export interface StrikeGenerationParams {
|
|
44
|
+
/** Current spot/underlying price */
|
|
45
|
+
spot: number;
|
|
46
|
+
/** Number of strikes above spot to include */
|
|
47
|
+
strikesAbove?: number;
|
|
48
|
+
/** Number of strikes below spot to include */
|
|
49
|
+
strikesBelow?: number;
|
|
50
|
+
/** Strike increment (e.g., 1 for $1 increments, 5 for $5) */
|
|
51
|
+
strikeIncrement?: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Builds an OCC-formatted option symbol.
|
|
55
|
+
*
|
|
56
|
+
* @param params - The option parameters
|
|
57
|
+
* @returns OCC-formatted symbol string
|
|
58
|
+
*
|
|
59
|
+
* @remarks
|
|
60
|
+
* OCC format: ROOT(6 chars, left-padded) + YYMMDD + C/P + STRIKE(8 digits, price × 1000)
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const symbol = buildOCCSymbol({
|
|
65
|
+
* symbol: 'AAPL',
|
|
66
|
+
* expiration: new Date('2023-01-20'),
|
|
67
|
+
* optionType: 'call',
|
|
68
|
+
* strike: 150
|
|
69
|
+
* });
|
|
70
|
+
* // Returns: 'AAPL230120C00150000' (compact format, default)
|
|
71
|
+
*
|
|
72
|
+
* // With padded format (standard OCC)
|
|
73
|
+
* const symbol2 = buildOCCSymbol({
|
|
74
|
+
* symbol: 'QQQ',
|
|
75
|
+
* expiration: '2024-03-15',
|
|
76
|
+
* optionType: 'put',
|
|
77
|
+
* strike: 425.50,
|
|
78
|
+
* padded: true
|
|
79
|
+
* });
|
|
80
|
+
* // Returns: 'QQQ 240315P00425500'
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export declare function buildOCCSymbol(params: OCCSymbolParams): string;
|
|
84
|
+
/**
|
|
85
|
+
* Parses an OCC-formatted option symbol into its components.
|
|
86
|
+
* Supports both compact format (e.g., 'AAPL230120C00150000') and
|
|
87
|
+
* padded format (e.g., 'AAPL 230120C00150000').
|
|
88
|
+
*
|
|
89
|
+
* @param occSymbol - The OCC symbol to parse
|
|
90
|
+
* @returns Parsed symbol components
|
|
91
|
+
* @throws {Error} If the symbol format is invalid
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* // Compact format
|
|
96
|
+
* const parsed = parseOCCSymbol('AAPL230120C00150000');
|
|
97
|
+
* // Returns: { symbol: 'AAPL', expiration: Date, optionType: 'call', strike: 150 }
|
|
98
|
+
*
|
|
99
|
+
* // Padded format (21 chars)
|
|
100
|
+
* const parsed2 = parseOCCSymbol('AAPL 230120C00150000');
|
|
101
|
+
* // Returns: { symbol: 'AAPL', expiration: Date, optionType: 'call', strike: 150 }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export declare function parseOCCSymbol(occSymbol: string): ParsedOCCSymbol;
|
|
105
|
+
/**
|
|
106
|
+
* Generates an array of strike prices centered around a spot price.
|
|
107
|
+
*
|
|
108
|
+
* @param params - Strike generation parameters
|
|
109
|
+
* @returns Array of strike prices, sorted ascending
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* const strikes = generateStrikesAroundSpot({
|
|
114
|
+
* spot: 450.25,
|
|
115
|
+
* strikesAbove: 10,
|
|
116
|
+
* strikesBelow: 10,
|
|
117
|
+
* strikeIncrement: 5
|
|
118
|
+
* });
|
|
119
|
+
* // Returns: [405, 410, 415, 420, 425, 430, 435, 440, 445, 450, 455, 460, 465, 470, 475, 480, 485, 490, 495, 500]
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export declare function generateStrikesAroundSpot(params: StrikeGenerationParams): number[];
|
|
123
|
+
/**
|
|
124
|
+
* Generates OCC symbols for calls and puts across multiple strikes.
|
|
125
|
+
*
|
|
126
|
+
* @param symbol - Underlying ticker symbol
|
|
127
|
+
* @param expiration - Option expiration date
|
|
128
|
+
* @param strikes - Array of strike prices
|
|
129
|
+
* @param includeTypes - Which option types to include (default: both)
|
|
130
|
+
* @returns Array of OCC symbol strings
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* const symbols = generateOCCSymbolsForStrikes(
|
|
135
|
+
* 'QQQ',
|
|
136
|
+
* new Date('2024-01-19'),
|
|
137
|
+
* [495, 500, 505],
|
|
138
|
+
* ['call', 'put']
|
|
139
|
+
* );
|
|
140
|
+
* // Returns 6 symbols: 3 calls + 3 puts
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export declare function generateOCCSymbolsForStrikes(symbol: string, expiration: Date | string, strikes: number[], includeTypes?: OptionType[]): string[];
|
|
144
|
+
/**
|
|
145
|
+
* Convenience function to generate OCC symbols around current spot price.
|
|
146
|
+
*
|
|
147
|
+
* @param symbol - Underlying ticker symbol
|
|
148
|
+
* @param expiration - Option expiration date
|
|
149
|
+
* @param spot - Current spot price
|
|
150
|
+
* @param options - Strike generation options
|
|
151
|
+
* @returns Array of OCC symbol strings for calls and puts
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* // Generate 20 calls + 20 puts around QQQ at $502.50
|
|
156
|
+
* const symbols = generateOCCSymbolsAroundSpot('QQQ', '2024-01-19', 502.50, {
|
|
157
|
+
* strikesAbove: 10,
|
|
158
|
+
* strikesBelow: 10,
|
|
159
|
+
* strikeIncrement: 5
|
|
160
|
+
* });
|
|
161
|
+
* // Returns 42 symbols (21 strikes × 2 types)
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
export declare function generateOCCSymbolsAroundSpot(symbol: string, expiration: Date | string, spot: number, options?: Omit<StrikeGenerationParams, 'spot'>): string[];
|