@fullstackcraftllc/floe 0.0.3 → 0.0.5

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.
@@ -1,2 +1,450 @@
1
+ import { NormalizedOption } from '../../types';
2
+ /**
3
+ * Schwab option chain response
4
+ */
5
+ interface SchwabOptionChainResponse {
6
+ symbol: string;
7
+ status: string;
8
+ underlying: {
9
+ symbol: string;
10
+ description: string;
11
+ change: number;
12
+ percentChange: number;
13
+ close: number;
14
+ quoteTime: number;
15
+ tradeTime: number;
16
+ bid: number;
17
+ ask: number;
18
+ last: number;
19
+ mark: number;
20
+ markChange: number;
21
+ markPercentChange: number;
22
+ bidSize: number;
23
+ askSize: number;
24
+ highPrice: number;
25
+ lowPrice: number;
26
+ openPrice: number;
27
+ totalVolume: number;
28
+ exchangeName: string;
29
+ fiftyTwoWeekHigh: number;
30
+ fiftyTwoWeekLow: number;
31
+ delayed: boolean;
32
+ };
33
+ strategy: string;
34
+ interval: number;
35
+ isDelayed: boolean;
36
+ isIndex: boolean;
37
+ interestRate: number;
38
+ underlyingPrice: number;
39
+ volatility: number;
40
+ daysToExpiration: number;
41
+ numberOfContracts: number;
42
+ assetMainType: string;
43
+ assetSubType: string;
44
+ isChainTruncated: boolean;
45
+ callExpDateMap: Record<string, Record<string, SchwabOptionContract[]>>;
46
+ putExpDateMap: Record<string, Record<string, SchwabOptionContract[]>>;
47
+ }
48
+ /**
49
+ * Schwab option contract from chain
50
+ */
51
+ interface SchwabOptionContract {
52
+ putCall: 'CALL' | 'PUT';
53
+ symbol: string;
54
+ description: string;
55
+ exchangeName: string;
56
+ bid: number;
57
+ ask: number;
58
+ last: number;
59
+ mark: number;
60
+ bidSize: number;
61
+ askSize: number;
62
+ bidAskSize: string;
63
+ lastSize: number;
64
+ highPrice: number;
65
+ lowPrice: number;
66
+ openPrice: number;
67
+ closePrice: number;
68
+ totalVolume: number;
69
+ tradeDate: number | null;
70
+ tradeTimeInLong: number;
71
+ quoteTimeInLong: number;
72
+ netChange: number;
73
+ volatility: number;
74
+ delta: number;
75
+ gamma: number;
76
+ theta: number;
77
+ vega: number;
78
+ rho: number;
79
+ openInterest: number;
80
+ timeValue: number;
81
+ theoreticalOptionValue: number;
82
+ theoreticalVolatility: number;
83
+ optionDeliverablesList: unknown;
84
+ strikePrice: number;
85
+ expirationDate: string;
86
+ daysToExpiration: number;
87
+ expirationType: string;
88
+ lastTradingDay: number;
89
+ multiplier: number;
90
+ settlementType: string;
91
+ deliverableNote: string;
92
+ percentChange: number;
93
+ markChange: number;
94
+ markPercentChange: number;
95
+ intrinsicValue: number;
96
+ extrinsicValue: number;
97
+ optionRoot: string;
98
+ exerciseType: string;
99
+ high52Week: number;
100
+ low52Week: number;
101
+ nonStandard: boolean;
102
+ pennyPilot: boolean;
103
+ inTheMoney: boolean;
104
+ mini: boolean;
105
+ }
106
+ /**
107
+ * Aggressor side of a trade
108
+ */
109
+ export type AggressorSide = 'buy' | 'sell' | 'unknown';
110
+ /**
111
+ * Intraday trade information with aggressor classification
112
+ */
113
+ export interface IntradayTrade {
114
+ /** OCC option symbol */
115
+ occSymbol: string;
116
+ /** Trade price */
117
+ price: number;
118
+ /** Trade size (number of contracts) */
119
+ size: number;
120
+ /** Bid at time of trade */
121
+ bid: number;
122
+ /** Ask at time of trade */
123
+ ask: number;
124
+ /** Aggressor side determined from price vs NBBO */
125
+ aggressorSide: AggressorSide;
126
+ /** Timestamp of the trade */
127
+ timestamp: number;
128
+ /** Estimated OI change */
129
+ estimatedOIChange: number;
130
+ }
131
+ /**
132
+ * Event types emitted by SchwabClient
133
+ */
134
+ type SchwabClientEventType = 'tickerUpdate' | 'optionUpdate' | 'optionTrade' | 'connected' | 'disconnected' | 'error';
135
+ /**
136
+ * Event listener callback type
137
+ */
138
+ type SchwabEventListener<T> = (data: T) => void;
139
+ /**
140
+ * SchwabClient handles real-time streaming connections to the Charles Schwab API
141
+ * via WebSockets.
142
+ *
143
+ * @remarks
144
+ * This client manages WebSocket connections to Schwab's streaming API,
145
+ * normalizes incoming quote and book data, and emits events for upstream
146
+ * consumption by the FloeClient.
147
+ *
148
+ * Authentication flow:
149
+ * 1. Use OAuth access token to call get_user_preferences endpoint
150
+ * 2. Extract streaming credentials (customerId, correlId, channel, functionId, socketUrl)
151
+ * 3. Connect to WebSocket and send ADMIN/LOGIN request with access token
152
+ * 4. Subscribe to LEVELONE_OPTIONS for quotes and OPTIONS_BOOK for order book
153
+ *
154
+ * @example
155
+ * ```typescript
156
+ * const client = new SchwabClient({
157
+ * accessToken: 'your-oauth-access-token'
158
+ * });
159
+ *
160
+ * client.on('tickerUpdate', (ticker) => {
161
+ * console.log(`${ticker.symbol}: ${ticker.spot}`);
162
+ * });
163
+ *
164
+ * await client.connect();
165
+ * client.subscribe(['SPY', 'SPY 240517C00500000']); // Equity and option
166
+ * ```
167
+ */
1
168
  export declare class SchwabClient {
169
+ /** Schwab OAuth access token */
170
+ private accessToken;
171
+ /** WebSocket connection */
172
+ private ws;
173
+ /** Connection state */
174
+ private connected;
175
+ /** Logged in state */
176
+ private loggedIn;
177
+ /** Streaming credentials */
178
+ private streamCustomerId;
179
+ private streamCorrelId;
180
+ private streamChannel;
181
+ private streamFunctionId;
182
+ private streamSocketUrl;
183
+ /** Request ID counter */
184
+ private requestId;
185
+ /** Currently subscribed symbols */
186
+ private subscribedSymbols;
187
+ /** Map from Schwab symbol to OCC symbol */
188
+ private schwabToOccMap;
189
+ /** Map from OCC symbol to Schwab symbol */
190
+ private occToSchwabMap;
191
+ /** Cached ticker data */
192
+ private tickerCache;
193
+ /** Cached option data */
194
+ private optionCache;
195
+ /** Base open interest from REST API */
196
+ private baseOpenInterest;
197
+ /** Cumulative estimated OI change from intraday trades */
198
+ private cumulativeOIChange;
199
+ /** History of intraday trades */
200
+ private intradayTrades;
201
+ /** Event listeners */
202
+ private eventListeners;
203
+ /** Reconnection attempt counter */
204
+ private reconnectAttempts;
205
+ /** Maximum reconnection attempts */
206
+ private readonly maxReconnectAttempts;
207
+ /** Reconnection delay in ms */
208
+ private readonly baseReconnectDelay;
209
+ /** Keepalive interval handle */
210
+ private keepaliveInterval;
211
+ /** Schwab API base URL */
212
+ private readonly apiBaseUrl;
213
+ /** Whether to log verbose debug information */
214
+ private readonly verbose;
215
+ /**
216
+ * Creates a new SchwabClient instance.
217
+ *
218
+ * @param options - Client configuration options
219
+ * @param options.accessToken - Schwab OAuth access token (required)
220
+ * @param options.verbose - Whether to log verbose debug information (default: false)
221
+ */
222
+ constructor(options: {
223
+ accessToken: string;
224
+ verbose?: boolean;
225
+ });
226
+ /**
227
+ * Establishes a streaming connection to Schwab.
228
+ *
229
+ * @returns Promise that resolves when connected and logged in
230
+ * @throws {Error} If credentials retrieval or WebSocket connection fails
231
+ */
232
+ connect(): Promise<void>;
233
+ /**
234
+ * Disconnects from the Schwab streaming API.
235
+ */
236
+ disconnect(): void;
237
+ /**
238
+ * Subscribes to real-time updates for the specified symbols.
239
+ *
240
+ * @param symbols - Array of ticker symbols and/or OCC option symbols
241
+ *
242
+ * @remarks
243
+ * For options, you can pass OCC format symbols (e.g., 'SPY240119C00500000')
244
+ * or Schwab format with spaces (e.g., 'SPY 240119C00500000').
245
+ * The client will handle conversion automatically.
246
+ */
247
+ subscribe(symbols: string[]): void;
248
+ /**
249
+ * Unsubscribes from real-time updates for the specified symbols.
250
+ *
251
+ * @param symbols - Array of symbols to unsubscribe from
252
+ */
253
+ unsubscribe(symbols: string[]): void;
254
+ /**
255
+ * Returns whether the client is currently connected.
256
+ */
257
+ isConnected(): boolean;
258
+ /**
259
+ * Fetches option chain from Schwab REST API.
260
+ * This provides the base open interest data.
261
+ *
262
+ * @param symbol - Underlying symbol (e.g., 'SPY')
263
+ * @param options - Optional parameters for filtering the chain
264
+ * @returns Promise resolving to the option chain response
265
+ */
266
+ fetchOptionChain(symbol: string, options?: {
267
+ contractType?: 'CALL' | 'PUT' | 'ALL';
268
+ strikeCount?: number;
269
+ includeUnderlyingQuote?: boolean;
270
+ fromDate?: string;
271
+ toDate?: string;
272
+ }): Promise<SchwabOptionChainResponse | null>;
273
+ /**
274
+ * Fetches open interest and other static data for subscribed options.
275
+ *
276
+ * @param occSymbols - Array of OCC option symbols to fetch data for
277
+ */
278
+ fetchOpenInterest(occSymbols: string[]): Promise<void>;
279
+ /**
280
+ * Returns cached option data for a symbol.
281
+ */
282
+ getOption(occSymbol: string): NormalizedOption | undefined;
283
+ /**
284
+ * Returns all cached options.
285
+ */
286
+ getAllOptions(): Map<string, NormalizedOption>;
287
+ /**
288
+ * Registers an event listener.
289
+ */
290
+ on<T>(event: SchwabClientEventType, listener: SchwabEventListener<T>): this;
291
+ /**
292
+ * Removes an event listener.
293
+ */
294
+ off<T>(event: SchwabClientEventType, listener: SchwabEventListener<T>): this;
295
+ /**
296
+ * Returns intraday trades for an option.
297
+ */
298
+ getIntradayTrades(occSymbol: string): IntradayTrade[];
299
+ /**
300
+ * Returns flow summary for an option.
301
+ */
302
+ getFlowSummary(occSymbol: string): {
303
+ buyVolume: number;
304
+ sellVolume: number;
305
+ unknownVolume: number;
306
+ netOIChange: number;
307
+ tradeCount: number;
308
+ };
309
+ /**
310
+ * Resets intraday tracking data.
311
+ */
312
+ resetIntradayData(occSymbols?: string[]): void;
313
+ /**
314
+ * Gets user preferences containing streaming info from Schwab.
315
+ */
316
+ private getUserPreferences;
317
+ /**
318
+ * Returns authorization headers for API calls.
319
+ */
320
+ private getAuthHeaders;
321
+ /**
322
+ * Connects to Schwab WebSocket.
323
+ */
324
+ private connectWebSocket;
325
+ /**
326
+ * Sends login request to Schwab streaming.
327
+ */
328
+ private sendLogin;
329
+ /**
330
+ * Sends logout request to Schwab streaming.
331
+ */
332
+ private sendLogout;
333
+ /**
334
+ * Subscribes to LEVELONE_EQUITIES service.
335
+ */
336
+ private subscribeLevelOneEquity;
337
+ /**
338
+ * Unsubscribes from LEVELONE_EQUITIES service.
339
+ */
340
+ private unsubscribeLevelOneEquity;
341
+ /**
342
+ * Subscribes to LEVELONE_OPTIONS service for level 1 option quotes.
343
+ */
344
+ private subscribeLevelOneOptions;
345
+ /**
346
+ * Unsubscribes from LEVELONE_OPTIONS service.
347
+ */
348
+ private unsubscribeLevelOneOptions;
349
+ /**
350
+ * Subscribes to OPTIONS_BOOK service for level 2 order book.
351
+ * This provides "live" open interest intraday.
352
+ */
353
+ private subscribeOptionsBook;
354
+ /**
355
+ * Unsubscribes from OPTIONS_BOOK service.
356
+ */
357
+ private unsubscribeOptionsBook;
358
+ /**
359
+ * Makes a streaming request object.
360
+ */
361
+ private makeRequest;
362
+ /**
363
+ * Handles incoming WebSocket messages.
364
+ */
365
+ private handleMessage;
366
+ /**
367
+ * Handles a response message (command acknowledgment).
368
+ */
369
+ private handleResponse;
370
+ /**
371
+ * Handles streaming data messages.
372
+ */
373
+ private handleDataMessage;
374
+ /**
375
+ * Handles LEVELONE_EQUITIES data.
376
+ */
377
+ private handleLevelOneEquity;
378
+ /**
379
+ * Handles LEVELONE_OPTIONS data.
380
+ */
381
+ private handleLevelOneOption;
382
+ /**
383
+ * Handles OPTIONS_BOOK data (level 2 order book).
384
+ * This provides depth of market which can indicate intraday activity.
385
+ */
386
+ private handleOptionsBook;
387
+ /**
388
+ * Records a trade and updates OI tracking.
389
+ */
390
+ private recordTrade;
391
+ /**
392
+ * Processes a contract from the option chain response.
393
+ */
394
+ private processChainContract;
395
+ /**
396
+ * Starts keepalive interval.
397
+ */
398
+ private startKeepalive;
399
+ /**
400
+ * Determines aggressor side from trade price vs NBBO.
401
+ */
402
+ private determineAggressorSide;
403
+ /**
404
+ * Calculates estimated OI change from trade.
405
+ */
406
+ private calculateOIChangeFromTrade;
407
+ /**
408
+ * Calculates live open interest.
409
+ */
410
+ private calculateLiveOpenInterest;
411
+ /**
412
+ * Converts Schwab option symbol to OCC format.
413
+ * Schwab format: "AAPL 240517C00170000" (6-char padded underlying)
414
+ */
415
+ private schwabToOcc;
416
+ /**
417
+ * Normalizes an OCC symbol to consistent format.
418
+ * Removes extra spaces, ensures proper formatting.
419
+ */
420
+ private normalizeOccSymbol;
421
+ /**
422
+ * Converts OCC symbol to Schwab format (space-padded).
423
+ */
424
+ private toSchwabOptionSymbol;
425
+ /**
426
+ * Checks if symbol is an option symbol.
427
+ */
428
+ private isOptionSymbol;
429
+ /**
430
+ * Attempts to reconnect with exponential backoff.
431
+ */
432
+ private attemptReconnect;
433
+ /**
434
+ * Sends a message to the WebSocket.
435
+ */
436
+ private sendMessage;
437
+ /**
438
+ * Emits an event to all listeners.
439
+ */
440
+ private emit;
441
+ /**
442
+ * Converts value to number, handling NaN and null.
443
+ */
444
+ private toNumber;
445
+ /**
446
+ * Sleep utility.
447
+ */
448
+ private sleep;
2
449
  }
450
+ export {};