@fullstackcraftllc/floe 0.0.8 → 0.0.9

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
- 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.
5
+ Zero-dependency TypeScript functions for options flow: Black-Scholes, greeks, dealer exposures, implied PDFs, 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](https://fullstackcraft.com) 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
 
@@ -127,7 +127,7 @@ class FloeClient {
127
127
  // No action needed for NONE broker; no-op
128
128
  break;
129
129
  case Broker.TRADIER:
130
- this.tradierClient = new TradierClient_1.TradierClient(authToken, { verbose: this.verbose });
130
+ this.tradierClient = new TradierClient_1.TradierClient({ authToken, verbose: this.verbose });
131
131
  // Wire up TradierClient events to FloeClient events
132
132
  this.tradierClient.on('tickerUpdate', (ticker) => {
133
133
  this.emit('tickerUpdate', ticker);
@@ -0,0 +1,296 @@
1
+ import { NormalizedOption, NormalizedTicker, OptionType } from '../../types';
2
+ /**
3
+ * Aggressor side of a trade - determined by comparing trade price to NBBO
4
+ */
5
+ export type AggressorSide = 'buy' | 'sell' | 'unknown';
6
+ /**
7
+ * Intraday trade information with aggressor classification
8
+ */
9
+ export interface IntradayTrade {
10
+ /** OCC option symbol */
11
+ occSymbol: string;
12
+ /** Trade price */
13
+ price: number;
14
+ /** Trade size (number of contracts) */
15
+ size: number;
16
+ /** Bid at time of trade */
17
+ bid: number;
18
+ /** Ask at time of trade */
19
+ ask: number;
20
+ /** Aggressor side determined from price vs NBBO */
21
+ aggressorSide: AggressorSide;
22
+ /** Timestamp of the trade */
23
+ timestamp: number;
24
+ /** Estimated OI change: +size for buy aggressor, -size for sell aggressor */
25
+ estimatedOIChange: number;
26
+ }
27
+ /**
28
+ * Flow summary statistics for an option
29
+ */
30
+ export interface FlowSummary {
31
+ buyVolume: number;
32
+ sellVolume: number;
33
+ unknownVolume: number;
34
+ netOIChange: number;
35
+ tradeCount: number;
36
+ }
37
+ /**
38
+ * Event types emitted by broker clients
39
+ */
40
+ export type BrokerClientEventType = 'tickerUpdate' | 'optionUpdate' | 'optionTrade' | 'connected' | 'disconnected' | 'error';
41
+ /**
42
+ * Event listener callback type
43
+ */
44
+ export type BrokerEventListener<T> = (data: T) => void;
45
+ /**
46
+ * Regex pattern to identify OCC option symbols (compact format)
47
+ * Format: ROOT + YYMMDD + C/P + 8-digit strike
48
+ * Example: "AAPL240517C00170000"
49
+ */
50
+ export declare const OCC_OPTION_PATTERN: RegExp;
51
+ /**
52
+ * Regex pattern that also matches space-padded OCC symbols
53
+ * Example: "AAPL 240517C00170000"
54
+ */
55
+ export declare const OCC_OPTION_PATTERN_WITH_SPACES: RegExp;
56
+ /**
57
+ * Base configuration options shared by all broker clients
58
+ */
59
+ export interface BaseBrokerClientOptions {
60
+ /** Whether to log verbose debug information */
61
+ verbose?: boolean;
62
+ /** Maximum reconnection attempts (default: 5) */
63
+ maxReconnectAttempts?: number;
64
+ /** Base reconnection delay in ms (default: 1000) */
65
+ baseReconnectDelay?: number;
66
+ }
67
+ /**
68
+ * Abstract base class for all broker streaming clients.
69
+ *
70
+ * @remarks
71
+ * This class provides shared state management, event handling, and utility methods
72
+ * that are common across all broker implementations. Subclasses implement the
73
+ * broker-specific connection, subscription, and data parsing logic.
74
+ *
75
+ * All broker clients normalize their data to `NormalizedOption` and `NormalizedTicker`
76
+ * formats, providing a consistent interface regardless of the underlying broker API.
77
+ */
78
+ export declare abstract class BaseBrokerClient {
79
+ /** Cached ticker data */
80
+ protected tickerCache: Map<string, NormalizedTicker>;
81
+ /** Cached option data */
82
+ protected optionCache: Map<string, NormalizedOption>;
83
+ /** Base open interest from REST API - used as t=0 reference */
84
+ protected baseOpenInterest: Map<string, number>;
85
+ /** Cumulative estimated OI change from intraday trades */
86
+ protected cumulativeOIChange: Map<string, number>;
87
+ /** History of intraday trades with aggressor classification */
88
+ protected intradayTrades: Map<string, IntradayTrade[]>;
89
+ /** Event listeners */
90
+ protected eventListeners: Map<BrokerClientEventType, Set<BrokerEventListener<unknown>>>;
91
+ /** Currently subscribed symbols */
92
+ protected subscribedSymbols: Set<string>;
93
+ /** Reconnection attempt counter */
94
+ protected reconnectAttempts: number;
95
+ /** Maximum reconnection attempts */
96
+ protected readonly maxReconnectAttempts: number;
97
+ /** Reconnection delay in ms */
98
+ protected readonly baseReconnectDelay: number;
99
+ /** Whether to log verbose debug information */
100
+ protected readonly verbose: boolean;
101
+ /** Broker name for logging */
102
+ protected abstract readonly brokerName: string;
103
+ constructor(options?: BaseBrokerClientOptions);
104
+ /**
105
+ * Establishes a streaming connection to the broker.
106
+ * @returns Promise that resolves when connected
107
+ * @throws {Error} If connection fails
108
+ */
109
+ abstract connect(): Promise<void>;
110
+ /**
111
+ * Disconnects from the broker streaming API.
112
+ */
113
+ abstract disconnect(): void;
114
+ /**
115
+ * Subscribes to real-time updates for the specified symbols.
116
+ * @param symbols - Array of ticker symbols and/or OCC option symbols
117
+ */
118
+ abstract subscribe(symbols: string[]): void;
119
+ /**
120
+ * Unsubscribes from real-time updates for the specified symbols.
121
+ * @param symbols - Array of symbols to unsubscribe from
122
+ */
123
+ abstract unsubscribe(symbols: string[]): void;
124
+ /**
125
+ * Unsubscribes from all real-time updates.
126
+ */
127
+ abstract unsubscribeFromAll(): void;
128
+ /**
129
+ * Returns whether the client is currently connected.
130
+ */
131
+ abstract isConnected(): boolean;
132
+ /**
133
+ * Fetches open interest and other static data for the specified options.
134
+ * @param occSymbols - Array of OCC option symbols to fetch data for
135
+ */
136
+ abstract fetchOpenInterest(occSymbols: string[]): Promise<void>;
137
+ /**
138
+ * Returns cached option data for a symbol.
139
+ * @param occSymbol - OCC option symbol
140
+ */
141
+ getOption(occSymbol: string): NormalizedOption | undefined;
142
+ /**
143
+ * Returns all cached options.
144
+ */
145
+ getAllOptions(): Map<string, NormalizedOption>;
146
+ /**
147
+ * Returns cached ticker data for a symbol.
148
+ * @param symbol - Ticker symbol
149
+ */
150
+ getTicker(symbol: string): NormalizedTicker | undefined;
151
+ /**
152
+ * Returns all cached tickers.
153
+ */
154
+ getAllTickers(): Map<string, NormalizedTicker>;
155
+ /**
156
+ * Returns intraday trades for an option.
157
+ * @param occSymbol - OCC option symbol
158
+ */
159
+ getIntradayTrades(occSymbol: string): IntradayTrade[];
160
+ /**
161
+ * Returns flow summary statistics for an option.
162
+ * @param occSymbol - OCC option symbol
163
+ */
164
+ getFlowSummary(occSymbol: string): FlowSummary;
165
+ /**
166
+ * Resets intraday tracking data.
167
+ * @param occSymbols - Optional specific symbols to reset. If not provided, resets all.
168
+ */
169
+ resetIntradayData(occSymbols?: string[]): void;
170
+ /**
171
+ * Registers an event listener.
172
+ * @param event - Event type to listen for
173
+ * @param listener - Callback function
174
+ */
175
+ on<T>(event: BrokerClientEventType, listener: BrokerEventListener<T>): this;
176
+ /**
177
+ * Removes an event listener.
178
+ * @param event - Event type
179
+ * @param listener - Callback function to remove
180
+ */
181
+ off<T>(event: BrokerClientEventType, listener: BrokerEventListener<T>): this;
182
+ /**
183
+ * Determines the aggressor side of a trade by comparing trade price to NBBO.
184
+ *
185
+ * @param tradePrice - The executed trade price
186
+ * @param bid - The bid price at time of trade
187
+ * @param ask - The ask price at time of trade
188
+ * @returns The aggressor side: 'buy' if lifting offer, 'sell' if hitting bid, 'unknown' if mid
189
+ *
190
+ * @remarks
191
+ * The aggressor is the party that initiated the trade by crossing the spread:
192
+ * - Buy aggressor: Buyer lifts the offer (trades at or above ask) → bullish intent
193
+ * - Sell aggressor: Seller hits the bid (trades at or below bid) → bearish intent
194
+ * - Unknown: Trade occurred mid-market (could be internalized, crossed, or negotiated)
195
+ */
196
+ protected determineAggressorSide(tradePrice: number, bid: number, ask: number): AggressorSide;
197
+ /**
198
+ * Calculates the estimated open interest change from a single trade.
199
+ *
200
+ * @param aggressorSide - The aggressor side of the trade
201
+ * @param size - Number of contracts traded
202
+ * @returns Estimated OI change (positive = OI increase, negative = OI decrease)
203
+ *
204
+ * @remarks
205
+ * This uses a simplified heuristic:
206
+ * - Buy aggressor → typically opening new long positions → +OI
207
+ * - Sell aggressor → typically closing longs or opening shorts → -OI
208
+ * - Unknown → ambiguous, assume neutral impact
209
+ */
210
+ protected calculateOIChangeFromTrade(aggressorSide: AggressorSide, size: number): number;
211
+ /**
212
+ * Calculates the live (intraday) open interest estimate for an option.
213
+ *
214
+ * @param occSymbol - OCC option symbol
215
+ * @returns Live OI estimate = base OI + cumulative estimated changes
216
+ */
217
+ protected calculateLiveOpenInterest(occSymbol: string): number;
218
+ /**
219
+ * Records a trade and updates OI tracking.
220
+ */
221
+ protected recordTrade(occSymbol: string, price: number, size: number, bid: number, ask: number, timestamp: number, optionType?: OptionType): void;
222
+ /**
223
+ * Sets base open interest for a symbol.
224
+ */
225
+ protected setBaseOpenInterest(occSymbol: string, openInterest: number): void;
226
+ /**
227
+ * Emits an event to all registered listeners.
228
+ */
229
+ protected emit<T>(event: BrokerClientEventType, data: T): void;
230
+ /**
231
+ * Normalizes an OCC symbol to consistent format.
232
+ * Removes extra spaces, ensures proper formatting.
233
+ */
234
+ protected normalizeOccSymbol(symbol: string): string;
235
+ /**
236
+ * Checks if a symbol is an OCC option symbol.
237
+ */
238
+ protected isOptionSymbol(symbol: string): boolean;
239
+ /**
240
+ * Converts value to number, handling NaN and null.
241
+ */
242
+ protected toNumber(value: unknown): number;
243
+ /**
244
+ * Sleep utility for delays and reconnection backoff.
245
+ */
246
+ protected sleep(ms: number): Promise<void>;
247
+ /**
248
+ * Calculates reconnection delay with exponential backoff.
249
+ */
250
+ protected getReconnectDelay(): number;
251
+ /**
252
+ * Logs a message if verbose mode is enabled.
253
+ */
254
+ protected log(message: string): void;
255
+ /**
256
+ * Updates or creates a ticker from quote data (bid/ask update).
257
+ * @returns The updated ticker
258
+ */
259
+ protected updateTickerFromQuoteData(symbol: string, bid: number, bidSize: number, ask: number, askSize: number, timestamp: number): NormalizedTicker;
260
+ /**
261
+ * Updates or creates a ticker from trade data (last price/volume update).
262
+ * @returns The updated ticker
263
+ */
264
+ protected updateTickerFromTradeData(symbol: string, price: number, size: number, dayVolume: number | null, timestamp: number): NormalizedTicker;
265
+ /**
266
+ * Updates or creates an option from quote data (bid/ask update).
267
+ * @returns The updated option, or null if symbol cannot be parsed
268
+ */
269
+ protected updateOptionFromQuoteData(occSymbol: string, bid: number, bidSize: number, ask: number, askSize: number, timestamp: number, parseSymbolFn: (symbol: string) => {
270
+ symbol: string;
271
+ expiration: Date;
272
+ optionType: OptionType;
273
+ strike: number;
274
+ }): NormalizedOption | null;
275
+ /**
276
+ * Updates or creates an option from trade data, including OI tracking.
277
+ * @returns The updated option, or null if symbol cannot be parsed
278
+ */
279
+ protected updateOptionFromTradeData(occSymbol: string, price: number, size: number, dayVolume: number | null, timestamp: number, parseSymbolFn: (symbol: string) => {
280
+ symbol: string;
281
+ expiration: Date;
282
+ optionType: OptionType;
283
+ strike: number;
284
+ }): NormalizedOption | null;
285
+ /**
286
+ * Updates or creates an option from timesale data (trade with bid/ask at time of sale).
287
+ * This is particularly useful for live OI tracking.
288
+ * @returns The updated option, or null if symbol cannot be parsed
289
+ */
290
+ protected updateOptionFromTimesaleData(occSymbol: string, price: number, size: number, bid: number, ask: number, timestamp: number, parseSymbolFn: (symbol: string) => {
291
+ symbol: string;
292
+ expiration: Date;
293
+ optionType: OptionType;
294
+ strike: number;
295
+ }): NormalizedOption | null;
296
+ }