@fullstackcraftllc/floe 0.0.2 → 0.0.4

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ![npm](https://img.shields.io/npm/v/@fullstackcraftllc/floe?style=flat-square) ![License](https://img.shields.io/npm/l/@fullstackcraftllc/floe?style=flat-square) ![TypeScript](https://img.shields.io/badge/TypeScript-4.9-blue?style=flat-square&logo=typescript)
4
4
 
5
- Browser-only TypeScript functions for calculating Black-Scholes, Greeks, and dealer exposures with a clean, type-safe API. Built for use in trading platforms and fintech applications.
5
+ Zero-dependency TypeScript functions for options flow: Black-Scholes, Greeks, and dealer exposures, and more, with a clean, type-safe API. Built for use in trading platforms and fintech applications.
6
6
 
7
7
  The same library that is used in Full Stack Craft's various fintech products including [The Wheel Screener](https://wheelscreener.com), [LEAPS Screener](https://leapsscreener.com), [Option Screener](https://option-screener.com), [AMT JOY](https://amtjoy.com), and [VannaCharm](https://vannacharm.com).
8
8
 
@@ -30,6 +30,28 @@ The same library that is used in Full Stack Craft's various fintech products inc
30
30
  - 💪 **Type-Safe** - Full TypeScript support
31
31
  - ⚡ **Zero Dependencies** - Lightweight and fast
32
32
 
33
+ ## Broker Support Roadmap
34
+
35
+ Due to the overwhelming variety of how broker APIs structure their data (and how they make it available), there is a wide variety of how much support we can provide out-of-the-box for different brokers, summarized in this table:
36
+
37
+ | Broker | Black-Scholes | Greeks | Open Interest Based Exposures | Options-Book Based Exposures | Implied PDF Calculations |
38
+ |-----------------------|--------------|--------|-------------------------------|------------------------------|-------------------------|
39
+ | Tradier (via WebSocket) | ✅ | ✅ | ✅ | ✅ | Coming soon |
40
+ | Schwab (via WebSocket) | Coming soon | Coming soon | Coming soon | Coming soon | Coming soon |
41
+ | Tastytrade (via WebSocket - DXLink Streamer) | Coming soon | Coming soon | Coming soon | Coming soon | Coming soon |
42
+ | TradeStation (via HTTP Streaming) | Coming soon | Coming soon | Coming soon | Coming soon | Coming soon |
43
+
44
+ Ideally all aspects of `floe` will be available for all brokers, but this will take time to determine as we work through the various data structures and formats that each broker provides.
45
+
46
+ ## Unsupported Brokers
47
+
48
+ The following brokers have no public API:
49
+
50
+ - Fidelity
51
+ - Robinhood
52
+
53
+ If your broker is not listed above, you can still use `floe` by normalizing your broker's data structures to match the expected input types. With options, you can get quite far with `floe` just by having the market price for the underlying and each option. (From those alone you can back out the IV, greeks, and exposures.)
54
+
33
55
  ## Installation
34
56
 
35
57
  ```bash
@@ -5,7 +5,9 @@ import { NormalizedOption, NormalizedTicker } from "../types";
5
5
  */
6
6
  export declare enum Broker {
7
7
  /** Tradier brokerage API */
8
- TRADIER = "tradier"
8
+ TRADIER = "tradier",
9
+ /** TastyTrade brokerage API (uses DxLink WebSocket) */
10
+ TASTYTRADE = "tastytrade"
9
11
  }
10
12
  /**
11
13
  * Event types emitted by the FloeClient.
@@ -69,12 +71,10 @@ export declare class FloeClient {
69
71
  private currentSubscribedTickers;
70
72
  /** List of option symbols (OCC format) currently subscribed to */
71
73
  private currentSubscribedOptions;
72
- /** Cache of the latest normalized ticker data */
73
- private normalizedTickers;
74
- /** Cache of the latest normalized option data */
75
- private normalizedOptions;
76
74
  /** Tradier broker client instance */
77
75
  private tradierClient;
76
+ /** TastyTrade broker client instance */
77
+ private tastyTradeClient;
78
78
  /** Event listeners registry for the EventEmitter pattern */
79
79
  private eventListeners;
80
80
  /** Callback for ticker data changes (legacy callback pattern) */
@@ -200,6 +200,65 @@ export declare class FloeClient {
200
200
  * ```
201
201
  */
202
202
  unsubscribeFromOptions(symbols: Array<string>): void;
203
+ /**
204
+ * Fetches open interest and initial option data via REST API.
205
+ *
206
+ * @param symbols - Array of option symbols in OCC format to fetch data for.
207
+ * If not provided, fetches data for all currently subscribed options.
208
+ * @returns Promise that resolves when all data has been fetched
209
+ *
210
+ * @throws {Error} Throws if no broker connection has been established
211
+ *
212
+ * @remarks
213
+ * Open interest is not available via streaming and must be fetched via REST API.
214
+ * This method should be called after subscribing to options to populate
215
+ * open interest, volume, and initial bid/ask values.
216
+ *
217
+ * The fetched data is automatically merged into the option cache and
218
+ * emitted via 'optionUpdate' events.
219
+ *
220
+ * @example
221
+ * ```typescript
222
+ * // Subscribe to options
223
+ * client.subscribeToOptions(optionSymbols);
224
+ *
225
+ * // Fetch open interest data
226
+ * await client.fetchOpenInterest();
227
+ *
228
+ * // Options now have open interest populated
229
+ * client.on('optionUpdate', (option) => {
230
+ * console.log(`${option.occSymbol}: OI=${option.openInterest}`);
231
+ * });
232
+ * ```
233
+ */
234
+ fetchOpenInterest(symbols?: string[]): Promise<void>;
235
+ /**
236
+ * Returns cached option data for a specific symbol.
237
+ *
238
+ * @param occSymbol - OCC option symbol
239
+ * @returns Cached option data, or undefined if not found
240
+ *
241
+ * @example
242
+ * ```typescript
243
+ * const option = client.getOption('QQQ250117C00530000');
244
+ * console.log(`Open Interest: ${option?.openInterest}`);
245
+ * ```
246
+ */
247
+ getOption(occSymbol: string): NormalizedOption | undefined;
248
+ /**
249
+ * Returns all cached options.
250
+ *
251
+ * @returns Map of OCC symbols to option data
252
+ *
253
+ * @example
254
+ * ```typescript
255
+ * const allOptions = client.getAllOptions();
256
+ * for (const [symbol, option] of allOptions) {
257
+ * console.log(`${symbol}: OI=${option.openInterest}`);
258
+ * }
259
+ * ```
260
+ */
261
+ getAllOptions(): Map<string, NormalizedOption>;
203
262
  /**
204
263
  * Registers an event listener for the specified event type.
205
264
  *
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FloeClient = exports.Broker = void 0;
4
4
  const TradierClient_1 = require("./brokers/TradierClient");
5
+ const TastyTradeClient_1 = require("./brokers/TastyTradeClient");
5
6
  /**
6
7
  * Supported broker integrations for the FloeClient.
7
8
  * @enum {string}
@@ -10,7 +11,8 @@ var Broker;
10
11
  (function (Broker) {
11
12
  /** Tradier brokerage API */
12
13
  Broker["TRADIER"] = "tradier";
13
- // Future brokers can be added here
14
+ /** TastyTrade brokerage API (uses DxLink WebSocket) */
15
+ Broker["TASTYTRADE"] = "tastytrade";
14
16
  })(Broker || (exports.Broker = Broker = {}));
15
17
  /**
16
18
  * FloeClient provides a unified, broker-agnostic interface for subscribing to
@@ -61,12 +63,10 @@ class FloeClient {
61
63
  this.currentSubscribedTickers = [];
62
64
  /** List of option symbols (OCC format) currently subscribed to */
63
65
  this.currentSubscribedOptions = [];
64
- /** Cache of the latest normalized ticker data */
65
- this.normalizedTickers = [];
66
- /** Cache of the latest normalized option data */
67
- this.normalizedOptions = [];
68
66
  /** Tradier broker client instance */
69
67
  this.tradierClient = null;
68
+ /** TastyTrade broker client instance */
69
+ this.tastyTradeClient = null;
70
70
  /** Event listeners registry for the EventEmitter pattern */
71
71
  this.eventListeners = new Map();
72
72
  /** Callback for ticker data changes (legacy callback pattern) */
@@ -119,6 +119,27 @@ class FloeClient {
119
119
  // Connect to the streaming API
120
120
  await this.tradierClient.connect();
121
121
  break;
122
+ case Broker.TASTYTRADE:
123
+ // For TastyTrade, authKey is the session token
124
+ this.tastyTradeClient = new TastyTradeClient_1.TastyTradeClient({
125
+ sessionToken: authKey,
126
+ });
127
+ // Wire up TastyTradeClient events to FloeClient events
128
+ this.tastyTradeClient.on('tickerUpdate', (ticker) => {
129
+ this.emit('tickerUpdate', ticker);
130
+ });
131
+ this.tastyTradeClient.on('optionUpdate', (option) => {
132
+ this.emit('optionUpdate', option);
133
+ });
134
+ this.tastyTradeClient.on('error', (error) => {
135
+ this.emit('error', error);
136
+ });
137
+ this.tastyTradeClient.on('disconnected', () => {
138
+ this.emit('disconnected', { broker, reason: 'DxLink WebSocket disconnected' });
139
+ });
140
+ // Connect to the streaming API
141
+ await this.tastyTradeClient.connect();
142
+ break;
122
143
  default:
123
144
  throw new Error(`Unsupported broker: ${broker}`);
124
145
  }
@@ -140,6 +161,10 @@ class FloeClient {
140
161
  this.tradierClient.disconnect();
141
162
  this.tradierClient = null;
142
163
  }
164
+ if (this.tastyTradeClient) {
165
+ this.tastyTradeClient.disconnect();
166
+ this.tastyTradeClient = null;
167
+ }
143
168
  const broker = this.currentBroker;
144
169
  this.currentBroker = null;
145
170
  this.currentSubscribedTickers = [];
@@ -170,6 +195,9 @@ class FloeClient {
170
195
  case Broker.TRADIER:
171
196
  this.tradierClient?.subscribe(tickers);
172
197
  break;
198
+ case Broker.TASTYTRADE:
199
+ this.tastyTradeClient?.subscribe(tickers);
200
+ break;
173
201
  default:
174
202
  throw new Error(`Unsupported broker: ${this.currentBroker}`);
175
203
  }
@@ -204,6 +232,9 @@ class FloeClient {
204
232
  case Broker.TRADIER:
205
233
  this.tradierClient?.subscribe(symbols);
206
234
  break;
235
+ case Broker.TASTYTRADE:
236
+ this.tastyTradeClient?.subscribe(symbols);
237
+ break;
207
238
  default:
208
239
  throw new Error(`Unsupported broker: ${this.currentBroker}`);
209
240
  }
@@ -230,6 +261,9 @@ class FloeClient {
230
261
  case Broker.TRADIER:
231
262
  this.tradierClient?.unsubscribe(tickers);
232
263
  break;
264
+ case Broker.TASTYTRADE:
265
+ this.tastyTradeClient?.unsubscribe(tickers);
266
+ break;
233
267
  default:
234
268
  throw new Error(`Unsupported broker: ${this.currentBroker}`);
235
269
  }
@@ -256,10 +290,105 @@ class FloeClient {
256
290
  case Broker.TRADIER:
257
291
  this.tradierClient?.unsubscribe(symbols);
258
292
  break;
293
+ case Broker.TASTYTRADE:
294
+ this.tastyTradeClient?.unsubscribe(symbols);
295
+ break;
296
+ default:
297
+ throw new Error(`Unsupported broker: ${this.currentBroker}`);
298
+ }
299
+ }
300
+ /**
301
+ * Fetches open interest and initial option data via REST API.
302
+ *
303
+ * @param symbols - Array of option symbols in OCC format to fetch data for.
304
+ * If not provided, fetches data for all currently subscribed options.
305
+ * @returns Promise that resolves when all data has been fetched
306
+ *
307
+ * @throws {Error} Throws if no broker connection has been established
308
+ *
309
+ * @remarks
310
+ * Open interest is not available via streaming and must be fetched via REST API.
311
+ * This method should be called after subscribing to options to populate
312
+ * open interest, volume, and initial bid/ask values.
313
+ *
314
+ * The fetched data is automatically merged into the option cache and
315
+ * emitted via 'optionUpdate' events.
316
+ *
317
+ * @example
318
+ * ```typescript
319
+ * // Subscribe to options
320
+ * client.subscribeToOptions(optionSymbols);
321
+ *
322
+ * // Fetch open interest data
323
+ * await client.fetchOpenInterest();
324
+ *
325
+ * // Options now have open interest populated
326
+ * client.on('optionUpdate', (option) => {
327
+ * console.log(`${option.occSymbol}: OI=${option.openInterest}`);
328
+ * });
329
+ * ```
330
+ */
331
+ async fetchOpenInterest(symbols) {
332
+ const symbolsToFetch = symbols ?? this.currentSubscribedOptions;
333
+ if (symbolsToFetch.length === 0) {
334
+ return;
335
+ }
336
+ switch (this.currentBroker) {
337
+ case Broker.TRADIER:
338
+ await this.tradierClient?.fetchOpenInterest(symbolsToFetch);
339
+ break;
340
+ case Broker.TASTYTRADE:
341
+ await this.tastyTradeClient?.fetchOpenInterest(symbolsToFetch);
342
+ break;
259
343
  default:
260
344
  throw new Error(`Unsupported broker: ${this.currentBroker}`);
261
345
  }
262
346
  }
347
+ /**
348
+ * Returns cached option data for a specific symbol.
349
+ *
350
+ * @param occSymbol - OCC option symbol
351
+ * @returns Cached option data, or undefined if not found
352
+ *
353
+ * @example
354
+ * ```typescript
355
+ * const option = client.getOption('QQQ250117C00530000');
356
+ * console.log(`Open Interest: ${option?.openInterest}`);
357
+ * ```
358
+ */
359
+ getOption(occSymbol) {
360
+ switch (this.currentBroker) {
361
+ case Broker.TRADIER:
362
+ return this.tradierClient?.getOption(occSymbol);
363
+ case Broker.TASTYTRADE:
364
+ return this.tastyTradeClient?.getOption(occSymbol);
365
+ default:
366
+ return undefined;
367
+ }
368
+ }
369
+ /**
370
+ * Returns all cached options.
371
+ *
372
+ * @returns Map of OCC symbols to option data
373
+ *
374
+ * @example
375
+ * ```typescript
376
+ * const allOptions = client.getAllOptions();
377
+ * for (const [symbol, option] of allOptions) {
378
+ * console.log(`${symbol}: OI=${option.openInterest}`);
379
+ * }
380
+ * ```
381
+ */
382
+ getAllOptions() {
383
+ switch (this.currentBroker) {
384
+ case Broker.TRADIER:
385
+ return this.tradierClient?.getAllOptions() ?? new Map();
386
+ case Broker.TASTYTRADE:
387
+ return this.tastyTradeClient?.getAllOptions() ?? new Map();
388
+ default:
389
+ return new Map();
390
+ }
391
+ }
263
392
  // ==================== Event Emitter Pattern ====================
264
393
  /**
265
394
  * Registers an event listener for the specified event type.
@@ -0,0 +1,2 @@
1
+ export declare class SchwabClient {
2
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SchwabClient = void 0;
4
+ class SchwabClient {
5
+ }
6
+ exports.SchwabClient = SchwabClient;