@fullstackcraftllc/floe 0.0.18 → 0.0.19

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.
@@ -12,6 +12,8 @@ export declare enum Broker {
12
12
  TASTYTRADE = "tastytrade",
13
13
  /** TradeStation brokerage API (uses HTTP streaming) */
14
14
  TRADESTATION = "tradestation",
15
+ /** Webull brokerage API (uses proxied HTTP snapshots) */
16
+ WEBULL = "webull",
15
17
  /** Charles Schwab brokerage API (uses WebSocket streaming) */
16
18
  SCHWAB = "schwab",
17
19
  /** Interactive Brokers Web API (OAuth) */
@@ -89,6 +91,8 @@ export declare class FloeClient {
89
91
  private schwabClient;
90
92
  /** Interactive Brokers broker client instance */
91
93
  private ibkrClient;
94
+ /** Webull broker client instance */
95
+ private webullClient;
92
96
  /** Event listeners registry for the EventEmitter pattern */
93
97
  private eventListeners;
94
98
  /** Callback for ticker data changes (legacy callback pattern) */
@@ -6,6 +6,7 @@ const TastyTradeClient_1 = require("./brokers/TastyTradeClient");
6
6
  const TradeStationClient_1 = require("./brokers/TradeStationClient");
7
7
  const SchwabClient_1 = require("./brokers/SchwabClient");
8
8
  const IBKRClient_1 = require("./brokers/IBKRClient");
9
+ const WebullClient_1 = require("./brokers/WebullClient");
9
10
  /**
10
11
  * Supported broker integrations for the FloeClient.
11
12
  * @enum {string}
@@ -20,6 +21,8 @@ var Broker;
20
21
  Broker["TASTYTRADE"] = "tastytrade";
21
22
  /** TradeStation brokerage API (uses HTTP streaming) */
22
23
  Broker["TRADESTATION"] = "tradestation";
24
+ /** Webull brokerage API (uses proxied HTTP snapshots) */
25
+ Broker["WEBULL"] = "webull";
23
26
  /** Charles Schwab brokerage API (uses WebSocket streaming) */
24
27
  Broker["SCHWAB"] = "schwab";
25
28
  /** Interactive Brokers Web API (OAuth) */
@@ -93,6 +96,8 @@ class FloeClient {
93
96
  this.schwabClient = null;
94
97
  /** Interactive Brokers broker client instance */
95
98
  this.ibkrClient = null;
99
+ /** Webull broker client instance */
100
+ this.webullClient = null;
96
101
  /** Event listeners registry for the EventEmitter pattern */
97
102
  this.eventListeners = new Map();
98
103
  /** Callback for ticker data changes (legacy callback pattern) */
@@ -193,6 +198,25 @@ class FloeClient {
193
198
  // Connect to the streaming API
194
199
  await this.tradeStationClient.connect();
195
200
  break;
201
+ case Broker.WEBULL:
202
+ this.webullClient = new WebullClient_1.WebullClient({
203
+ authToken,
204
+ verbose: this.verbose,
205
+ });
206
+ this.webullClient.on('tickerUpdate', (ticker) => {
207
+ this.emit('tickerUpdate', ticker);
208
+ });
209
+ this.webullClient.on('optionUpdate', (option) => {
210
+ this.emit('optionUpdate', option);
211
+ });
212
+ this.webullClient.on('error', (error) => {
213
+ this.emit('error', error);
214
+ });
215
+ this.webullClient.on('disconnected', () => {
216
+ this.emit('disconnected', { broker, reason: 'Webull market-data polling stopped' });
217
+ });
218
+ await this.webullClient.connect();
219
+ break;
196
220
  case Broker.SCHWAB:
197
221
  // For Schwab, authToken is the OAuth access token
198
222
  this.schwabClient = new SchwabClient_1.SchwabClient({
@@ -275,6 +299,10 @@ class FloeClient {
275
299
  this.ibkrClient.disconnect();
276
300
  this.ibkrClient = null;
277
301
  }
302
+ if (this.webullClient) {
303
+ this.webullClient.disconnect();
304
+ this.webullClient = null;
305
+ }
278
306
  const broker = this.currentBroker;
279
307
  this.currentBroker = null;
280
308
  this.currentSubscribedTickers = [];
@@ -311,6 +339,9 @@ class FloeClient {
311
339
  case Broker.TRADESTATION:
312
340
  this.tradeStationClient?.subscribe(tickers);
313
341
  break;
342
+ case Broker.WEBULL:
343
+ this.webullClient?.subscribe(tickers);
344
+ break;
314
345
  case Broker.SCHWAB:
315
346
  this.schwabClient?.subscribe(tickers);
316
347
  break;
@@ -357,6 +388,9 @@ class FloeClient {
357
388
  case Broker.TRADESTATION:
358
389
  this.tradeStationClient?.subscribe(symbols);
359
390
  break;
391
+ case Broker.WEBULL:
392
+ this.webullClient?.subscribe(symbols);
393
+ break;
360
394
  case Broker.SCHWAB:
361
395
  this.schwabClient?.subscribe(symbols);
362
396
  break;
@@ -395,6 +429,9 @@ class FloeClient {
395
429
  case Broker.TRADESTATION:
396
430
  this.tradeStationClient?.unsubscribe(tickers);
397
431
  break;
432
+ case Broker.WEBULL:
433
+ this.webullClient?.unsubscribe(tickers);
434
+ break;
398
435
  case Broker.SCHWAB:
399
436
  this.schwabClient?.unsubscribe(tickers);
400
437
  break;
@@ -433,6 +470,9 @@ class FloeClient {
433
470
  case Broker.TRADESTATION:
434
471
  this.tradeStationClient?.unsubscribe(symbols);
435
472
  break;
473
+ case Broker.WEBULL:
474
+ this.webullClient?.unsubscribe(symbols);
475
+ break;
436
476
  case Broker.SCHWAB:
437
477
  this.schwabClient?.unsubscribe(symbols);
438
478
  break;
@@ -470,6 +510,9 @@ class FloeClient {
470
510
  case Broker.TRADESTATION:
471
511
  this.tradeStationClient?.unsubscribeFromAll();
472
512
  break;
513
+ case Broker.WEBULL:
514
+ this.webullClient?.unsubscribeFromAll();
515
+ break;
473
516
  case Broker.SCHWAB:
474
517
  this.schwabClient?.unsubscribeFromAll();
475
518
  break;
@@ -527,6 +570,9 @@ class FloeClient {
527
570
  // TradeStation provides open interest via stream, no separate fetch needed
528
571
  // OI is automatically populated when streaming option chains
529
572
  break;
573
+ case Broker.WEBULL:
574
+ await this.webullClient?.fetchOpenInterest(symbolsToFetch);
575
+ break;
530
576
  case Broker.SCHWAB:
531
577
  await this.schwabClient?.fetchOpenInterest(symbolsToFetch);
532
578
  break;
@@ -557,6 +603,8 @@ class FloeClient {
557
603
  return this.tastyTradeClient?.getOption(occSymbol);
558
604
  case Broker.TRADESTATION:
559
605
  return this.tradeStationClient?.getOption(occSymbol);
606
+ case Broker.WEBULL:
607
+ return this.webullClient?.getOption(occSymbol);
560
608
  case Broker.SCHWAB:
561
609
  return this.schwabClient?.getOption(occSymbol);
562
610
  case Broker.IBKR:
@@ -586,6 +634,8 @@ class FloeClient {
586
634
  return this.tastyTradeClient?.getAllOptions() ?? new Map();
587
635
  case Broker.TRADESTATION:
588
636
  return this.tradeStationClient?.getAllOptions() ?? new Map();
637
+ case Broker.WEBULL:
638
+ return this.webullClient?.getAllOptions() ?? new Map();
589
639
  case Broker.SCHWAB:
590
640
  return this.schwabClient?.getAllOptions() ?? new Map();
591
641
  case Broker.IBKR:
@@ -0,0 +1,53 @@
1
+ import { BaseBrokerClient, BaseBrokerClientOptions } from './BaseBrokerClient';
2
+ export interface WebullAuthTokenConfig {
3
+ accessToken: string;
4
+ proxyUrl?: string;
5
+ }
6
+ export interface ParsedWebullAuthToken {
7
+ accessToken: string;
8
+ proxyUrl: string;
9
+ }
10
+ export declare function createWebullAuthToken(config: WebullAuthTokenConfig): string;
11
+ export declare function parseWebullAuthToken(authToken: string): ParsedWebullAuthToken;
12
+ export interface WebullClientOptions extends BaseBrokerClientOptions {
13
+ authToken: string;
14
+ pollIntervalMs?: number;
15
+ optionRequestIntervalMs?: number;
16
+ }
17
+ export declare class WebullClient extends BaseBrokerClient {
18
+ protected readonly brokerName = "Webull";
19
+ private readonly accessToken;
20
+ private readonly proxyUrl;
21
+ private readonly pollIntervalMs;
22
+ private readonly optionRequestIntervalMs;
23
+ private connected;
24
+ private tickerTimer;
25
+ private optionTimer;
26
+ private tickerRequestInFlight;
27
+ private optionRequestChain;
28
+ private lastOptionRequestAt;
29
+ private optionChunkIndex;
30
+ private pendingOptionRequests;
31
+ constructor(options: WebullClientOptions);
32
+ connect(): Promise<void>;
33
+ disconnect(): void;
34
+ subscribe(symbols: string[]): void;
35
+ unsubscribe(symbols: string[]): void;
36
+ unsubscribeFromAll(): void;
37
+ isConnected(): boolean;
38
+ fetchOpenInterest(occSymbols: string[]): Promise<void>;
39
+ private startPolling;
40
+ private stopPolling;
41
+ private getSubscribedTickers;
42
+ private getSubscribedOptions;
43
+ private pollTickers;
44
+ private pollNextOptionChunk;
45
+ private enqueueOptionSnapshot;
46
+ private requestSnapshots;
47
+ private updateTicker;
48
+ private updateOption;
49
+ private buildGreeks;
50
+ private toTimestamp;
51
+ private isWebullOptionSymbol;
52
+ private chunk;
53
+ }
@@ -0,0 +1,290 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebullClient = void 0;
4
+ exports.createWebullAuthToken = createWebullAuthToken;
5
+ exports.parseWebullAuthToken = parseWebullAuthToken;
6
+ const occ_1 = require("../../utils/occ");
7
+ const BaseBrokerClient_1 = require("./BaseBrokerClient");
8
+ const DEFAULT_PROXY_URL = '/.netlify/functions/webullMarketData';
9
+ const AUTH_TOKEN_PREFIX = 'floe-webull:';
10
+ const OPTION_BATCH_SIZE = 20;
11
+ function createWebullAuthToken(config) {
12
+ if (!config.accessToken) {
13
+ throw new Error('Webull access token is required');
14
+ }
15
+ return `${AUTH_TOKEN_PREFIX}${encodeURIComponent(JSON.stringify({
16
+ accessToken: config.accessToken,
17
+ proxyUrl: config.proxyUrl ?? DEFAULT_PROXY_URL,
18
+ }))}`;
19
+ }
20
+ function parseWebullAuthToken(authToken) {
21
+ if (!authToken) {
22
+ throw new Error('Webull auth token is required');
23
+ }
24
+ if (!authToken.startsWith(AUTH_TOKEN_PREFIX)) {
25
+ return { accessToken: authToken, proxyUrl: DEFAULT_PROXY_URL };
26
+ }
27
+ try {
28
+ const parsed = JSON.parse(decodeURIComponent(authToken.slice(AUTH_TOKEN_PREFIX.length)));
29
+ if (!parsed.accessToken) {
30
+ throw new Error('missing accessToken');
31
+ }
32
+ return {
33
+ accessToken: parsed.accessToken,
34
+ proxyUrl: parsed.proxyUrl || DEFAULT_PROXY_URL,
35
+ };
36
+ }
37
+ catch (error) {
38
+ throw new Error(`Invalid Webull auth token: ${error instanceof Error ? error.message : String(error)}`);
39
+ }
40
+ }
41
+ class WebullClient extends BaseBrokerClient_1.BaseBrokerClient {
42
+ constructor(options) {
43
+ super(options);
44
+ this.brokerName = 'Webull';
45
+ this.connected = false;
46
+ this.tickerTimer = null;
47
+ this.optionTimer = null;
48
+ this.tickerRequestInFlight = false;
49
+ this.optionRequestChain = Promise.resolve();
50
+ this.lastOptionRequestAt = 0;
51
+ this.optionChunkIndex = 0;
52
+ this.pendingOptionRequests = 0;
53
+ const auth = parseWebullAuthToken(options.authToken);
54
+ this.accessToken = auth.accessToken;
55
+ this.proxyUrl = auth.proxyUrl;
56
+ this.pollIntervalMs = options.pollIntervalMs ?? 1000;
57
+ this.optionRequestIntervalMs = options.optionRequestIntervalMs ?? 1000;
58
+ }
59
+ async connect() {
60
+ if (this.connected)
61
+ return;
62
+ this.connected = true;
63
+ this.emit('connected', undefined);
64
+ this.startPolling();
65
+ this.log('Connected through market-data proxy');
66
+ }
67
+ disconnect() {
68
+ if (!this.connected)
69
+ return;
70
+ this.connected = false;
71
+ this.stopPolling();
72
+ this.subscribedSymbols.clear();
73
+ this.emit('disconnected', undefined);
74
+ }
75
+ subscribe(symbols) {
76
+ for (const symbol of symbols) {
77
+ const normalized = symbol.replace(/\s+/g, '').toUpperCase();
78
+ if (normalized)
79
+ this.subscribedSymbols.add(normalized);
80
+ }
81
+ if (symbols.some((symbol) => !this.isWebullOptionSymbol(symbol))) {
82
+ void this.pollTickers();
83
+ }
84
+ }
85
+ unsubscribe(symbols) {
86
+ for (const symbol of symbols) {
87
+ this.subscribedSymbols.delete(symbol.replace(/\s+/g, '').toUpperCase());
88
+ }
89
+ }
90
+ unsubscribeFromAll() {
91
+ this.subscribedSymbols.clear();
92
+ this.optionChunkIndex = 0;
93
+ }
94
+ isConnected() {
95
+ return this.connected;
96
+ }
97
+ async fetchOpenInterest(occSymbols) {
98
+ const options = Array.from(new Set(occSymbols
99
+ .map((symbol) => symbol.replace(/\s+/g, '').toUpperCase())
100
+ .filter((symbol) => this.isWebullOptionSymbol(symbol))));
101
+ const requests = [];
102
+ for (let index = 0; index < options.length; index += OPTION_BATCH_SIZE) {
103
+ requests.push(this.enqueueOptionSnapshot(options.slice(index, index + OPTION_BATCH_SIZE)));
104
+ }
105
+ await Promise.all(requests);
106
+ }
107
+ startPolling() {
108
+ if (!this.tickerTimer) {
109
+ this.tickerTimer = setInterval(() => void this.pollTickers(), this.pollIntervalMs);
110
+ }
111
+ if (!this.optionTimer) {
112
+ this.optionTimer = setInterval(() => void this.pollNextOptionChunk(), this.pollIntervalMs);
113
+ }
114
+ }
115
+ stopPolling() {
116
+ if (this.tickerTimer)
117
+ clearInterval(this.tickerTimer);
118
+ if (this.optionTimer)
119
+ clearInterval(this.optionTimer);
120
+ this.tickerTimer = null;
121
+ this.optionTimer = null;
122
+ }
123
+ getSubscribedTickers() {
124
+ return Array.from(this.subscribedSymbols).filter((symbol) => !this.isWebullOptionSymbol(symbol));
125
+ }
126
+ getSubscribedOptions() {
127
+ return Array.from(this.subscribedSymbols).filter((symbol) => this.isWebullOptionSymbol(symbol));
128
+ }
129
+ async pollTickers() {
130
+ const symbols = this.getSubscribedTickers();
131
+ if (!this.connected || this.tickerRequestInFlight || symbols.length === 0)
132
+ return;
133
+ this.tickerRequestInFlight = true;
134
+ try {
135
+ for (let index = 0; index < symbols.length; index += OPTION_BATCH_SIZE) {
136
+ const snapshots = await this.requestSnapshots('stockSnapshot', symbols.slice(index, index + OPTION_BATCH_SIZE));
137
+ snapshots.forEach((snapshot) => this.updateTicker(snapshot));
138
+ }
139
+ }
140
+ catch (error) {
141
+ this.emit('error', error instanceof Error ? error : new Error(String(error)));
142
+ }
143
+ finally {
144
+ this.tickerRequestInFlight = false;
145
+ }
146
+ }
147
+ async pollNextOptionChunk() {
148
+ const symbols = this.getSubscribedOptions();
149
+ if (!this.connected || symbols.length === 0 || this.pendingOptionRequests > 0)
150
+ return;
151
+ const chunks = this.chunk(symbols, OPTION_BATCH_SIZE);
152
+ const chunk = chunks[this.optionChunkIndex % chunks.length];
153
+ this.optionChunkIndex = (this.optionChunkIndex + 1) % chunks.length;
154
+ try {
155
+ await this.enqueueOptionSnapshot(chunk);
156
+ }
157
+ catch (error) {
158
+ this.emit('error', error instanceof Error ? error : new Error(String(error)));
159
+ }
160
+ }
161
+ enqueueOptionSnapshot(symbols) {
162
+ this.pendingOptionRequests += 1;
163
+ const request = this.optionRequestChain.then(async () => {
164
+ try {
165
+ const waitMs = Math.max(0, this.optionRequestIntervalMs - (Date.now() - this.lastOptionRequestAt));
166
+ if (waitMs > 0)
167
+ await this.sleep(waitMs);
168
+ if (!this.connected)
169
+ return;
170
+ this.lastOptionRequestAt = Date.now();
171
+ const snapshots = await this.requestSnapshots('optionSnapshot', symbols);
172
+ snapshots.forEach((snapshot) => this.updateOption(snapshot));
173
+ }
174
+ finally {
175
+ this.pendingOptionRequests -= 1;
176
+ }
177
+ });
178
+ this.optionRequestChain = request.catch(() => undefined);
179
+ return request;
180
+ }
181
+ async requestSnapshots(operation, symbols) {
182
+ const response = await fetch(this.proxyUrl, {
183
+ method: 'POST',
184
+ headers: { 'Content-Type': 'application/json' },
185
+ body: JSON.stringify({ operation, symbols, accessToken: this.accessToken }),
186
+ });
187
+ if (!response.ok) {
188
+ const body = await response.text();
189
+ throw new Error(`Webull ${operation} request failed (${response.status}): ${body || response.statusText}`);
190
+ }
191
+ const data = await response.json();
192
+ if (Array.isArray(data))
193
+ return data;
194
+ if (Array.isArray(data.data))
195
+ return data.data;
196
+ throw new Error(`Webull ${operation} returned an invalid response`);
197
+ }
198
+ updateTicker(snapshot) {
199
+ const symbol = snapshot.symbol?.toUpperCase();
200
+ if (!symbol)
201
+ return;
202
+ const bid = this.toNumber(snapshot.bid);
203
+ const ask = this.toNumber(snapshot.ask);
204
+ const last = this.toNumber(snapshot.price);
205
+ const ticker = {
206
+ symbol,
207
+ spot: bid > 0 && ask > 0 ? (bid + ask) / 2 : last,
208
+ bid,
209
+ bidSize: this.toNumber(snapshot.bid_size),
210
+ ask,
211
+ askSize: this.toNumber(snapshot.ask_size),
212
+ last,
213
+ volume: this.toNumber(snapshot.volume),
214
+ timestamp: this.toTimestamp(snapshot.quote_time ?? snapshot.last_trade_time),
215
+ };
216
+ this.tickerCache.set(symbol, ticker);
217
+ this.emit('tickerUpdate', ticker);
218
+ }
219
+ updateOption(snapshot) {
220
+ const occSymbol = snapshot.symbol?.replace(/\s+/g, '').toUpperCase();
221
+ if (!occSymbol)
222
+ return;
223
+ let parsed;
224
+ try {
225
+ parsed = (0, occ_1.parseOCCSymbol)(occSymbol);
226
+ }
227
+ catch {
228
+ return;
229
+ }
230
+ const bid = this.toNumber(snapshot.bid);
231
+ const ask = this.toNumber(snapshot.ask);
232
+ const last = this.toNumber(snapshot.price);
233
+ const openInterest = this.toNumber(snapshot.open_interest);
234
+ this.setBaseOpenInterest(occSymbol, openInterest);
235
+ const option = {
236
+ occSymbol,
237
+ underlying: parsed.symbol,
238
+ strike: this.toNumber(snapshot.strike_price) || parsed.strike,
239
+ expiration: parsed.expiration.toISOString().split('T')[0],
240
+ expirationTimestamp: parsed.expiration.getTime(),
241
+ optionType: parsed.optionType,
242
+ bid,
243
+ bidSize: this.toNumber(snapshot.bid_size),
244
+ ask,
245
+ askSize: this.toNumber(snapshot.ask_size),
246
+ mark: bid > 0 && ask > 0 ? (bid + ask) / 2 : last,
247
+ last,
248
+ volume: this.toNumber(snapshot.volume),
249
+ openInterest,
250
+ liveOpenInterest: this.calculateLiveOpenInterest(occSymbol),
251
+ impliedVolatility: this.toNumber(snapshot.imp_vol),
252
+ timestamp: this.toTimestamp(snapshot.quote_time ?? snapshot.last_trade_time),
253
+ greeks: this.buildGreeks(snapshot),
254
+ };
255
+ this.optionCache.set(occSymbol, option);
256
+ this.emit('optionUpdate', option);
257
+ }
258
+ buildGreeks(snapshot) {
259
+ return {
260
+ price: this.toNumber(snapshot.price),
261
+ delta: this.toNumber(snapshot.delta),
262
+ gamma: this.toNumber(snapshot.gamma),
263
+ theta: this.toNumber(snapshot.theta),
264
+ vega: this.toNumber(snapshot.vega),
265
+ rho: this.toNumber(snapshot.rho),
266
+ charm: 0,
267
+ vanna: 0,
268
+ volga: 0,
269
+ speed: 0,
270
+ zomma: 0,
271
+ color: 0,
272
+ ultima: 0,
273
+ };
274
+ }
275
+ toTimestamp(value) {
276
+ const timestamp = this.toNumber(value);
277
+ return timestamp > 0 ? timestamp : Date.now();
278
+ }
279
+ isWebullOptionSymbol(symbol) {
280
+ return BaseBrokerClient_1.OCC_OPTION_PATTERN.test(symbol.replace(/\s+/g, '').toUpperCase());
281
+ }
282
+ chunk(items, size) {
283
+ const chunks = [];
284
+ for (let index = 0; index < items.length; index += size) {
285
+ chunks.push(items.slice(index, index + size));
286
+ }
287
+ return chunks;
288
+ }
289
+ }
290
+ exports.WebullClient = WebullClient;
package/dist/index.d.ts CHANGED
@@ -20,6 +20,8 @@ export { FloeClient, Broker } from './client/FloeClient';
20
20
  export { TradierClient } from './client/brokers/TradierClient';
21
21
  export { TastyTradeClient } from './client/brokers/TastyTradeClient';
22
22
  export { TradeStationClient } from './client/brokers/TradeStationClient';
23
+ export { WebullClient, createWebullAuthToken, parseWebullAuthToken, } from './client/brokers/WebullClient';
24
+ export type { ParsedWebullAuthToken, WebullAuthTokenConfig, WebullClientOptions, } from './client/brokers/WebullClient';
23
25
  export type { AggressorSide, IntradayTrade } from './client/brokers/TradierClient';
24
26
  export { genericAdapter, schwabAdapter, ibkrAdapter, tdaAdapter, brokerAdapters, getAdapter, createOptionChain, } from './adapters';
25
27
  export { computeVarianceSwapIV, computeImpliedVolatility, } from './iv';
@@ -28,3 +30,5 @@ export { computeRealizedVolatility, } from './rv';
28
30
  export type { PriceObservation, RealizedVolatilityResult, } from './rv';
29
31
  export { computeVolResponseZScore, buildVolResponseObservation, } from './volresponse';
30
32
  export type { VolResponseObservation, VolResponseCoefficients, VolResponseConfig, VolResponseResult, } from './volresponse';
33
+ export { ApiClient, NewApiClient, APIError, } from './apiclient';
34
+ export type { FetchLike, ApiContext, CtxEquivalent, HindsightDataRequest, DealerMinuteSurfacesRequest, AMTRequest, HindsightEvent, DealerMinuteSurface, AMTSessionStatsRow, AMTEventsRow, MinuteSurface, SurfacePoint, OptionsScreenerRequest, OptionsScreenerResponse, } from './apiclient';
package/dist/index.js CHANGED
@@ -20,8 +20,8 @@ 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.computeRealizedVolatility = exports.computeImpliedVolatility = exports.computeVarianceSwapIV = exports.createOptionChain = exports.getAdapter = exports.brokerAdapters = exports.tdaAdapter = exports.ibkrAdapter = exports.schwabAdapter = exports.genericAdapter = exports.TradeStationClient = exports.TastyTradeClient = exports.TradierClient = exports.Broker = exports.FloeClient = exports.analyzeHedgeFlow = exports.computePressureCloud = exports.computeCharmIntegral = exports.computeHedgeImpulseCurve = exports.interpolateIVAtStrike = exports.deriveRegimeParams = exports.OPEX_CONFIG = exports.CRISIS_CONFIG = exports.LOW_VOL_CONFIG = exports.DEFAULT_ADJUSTMENT_CONFIG = exports.getSignificantAdjustmentLevels = exports.getEdgeAtPrice = exports.estimateExposureAdjustedPDF = exports.getQuantile = exports.getCumulativeProbability = exports.getProbabilityInRange = exports.estimateImpliedProbabilityDistributions = exports.estimateImpliedProbabilityDistribution = 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
- exports.buildVolResponseObservation = exports.computeVolResponseZScore = void 0;
23
+ exports.createOptionChain = exports.getAdapter = exports.brokerAdapters = exports.tdaAdapter = exports.ibkrAdapter = exports.schwabAdapter = exports.genericAdapter = exports.parseWebullAuthToken = exports.createWebullAuthToken = exports.WebullClient = exports.TradeStationClient = exports.TastyTradeClient = exports.TradierClient = exports.Broker = exports.FloeClient = exports.analyzeHedgeFlow = exports.computePressureCloud = exports.computeCharmIntegral = exports.computeHedgeImpulseCurve = exports.interpolateIVAtStrike = exports.deriveRegimeParams = exports.OPEX_CONFIG = exports.CRISIS_CONFIG = exports.LOW_VOL_CONFIG = exports.DEFAULT_ADJUSTMENT_CONFIG = exports.getSignificantAdjustmentLevels = exports.getEdgeAtPrice = exports.estimateExposureAdjustedPDF = exports.getQuantile = exports.getCumulativeProbability = exports.getProbabilityInRange = exports.estimateImpliedProbabilityDistributions = exports.estimateImpliedProbabilityDistribution = 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
+ exports.APIError = exports.NewApiClient = exports.ApiClient = exports.buildVolResponseObservation = exports.computeVolResponseZScore = exports.computeRealizedVolatility = exports.computeImpliedVolatility = exports.computeVarianceSwapIV = void 0;
25
25
  // Core types
26
26
  __exportStar(require("./types"), exports);
27
27
  // Black-Scholes pricing and Greeks
@@ -91,6 +91,10 @@ var TastyTradeClient_1 = require("./client/brokers/TastyTradeClient");
91
91
  Object.defineProperty(exports, "TastyTradeClient", { enumerable: true, get: function () { return TastyTradeClient_1.TastyTradeClient; } });
92
92
  var TradeStationClient_1 = require("./client/brokers/TradeStationClient");
93
93
  Object.defineProperty(exports, "TradeStationClient", { enumerable: true, get: function () { return TradeStationClient_1.TradeStationClient; } });
94
+ var WebullClient_1 = require("./client/brokers/WebullClient");
95
+ Object.defineProperty(exports, "WebullClient", { enumerable: true, get: function () { return WebullClient_1.WebullClient; } });
96
+ Object.defineProperty(exports, "createWebullAuthToken", { enumerable: true, get: function () { return WebullClient_1.createWebullAuthToken; } });
97
+ Object.defineProperty(exports, "parseWebullAuthToken", { enumerable: true, get: function () { return WebullClient_1.parseWebullAuthToken; } });
94
98
  // Broker adapters
95
99
  var adapters_1 = require("./adapters");
96
100
  Object.defineProperty(exports, "genericAdapter", { enumerable: true, get: function () { return adapters_1.genericAdapter; } });
@@ -111,3 +115,8 @@ Object.defineProperty(exports, "computeRealizedVolatility", { enumerable: true,
111
115
  var volresponse_1 = require("./volresponse");
112
116
  Object.defineProperty(exports, "computeVolResponseZScore", { enumerable: true, get: function () { return volresponse_1.computeVolResponseZScore; } });
113
117
  Object.defineProperty(exports, "buildVolResponseObservation", { enumerable: true, get: function () { return volresponse_1.buildVolResponseObservation; } });
118
+ // Dataset API clients (Hindsight + Dealer minute surfaces + AMT + Options Screeners)
119
+ var apiclient_1 = require("./apiclient");
120
+ Object.defineProperty(exports, "ApiClient", { enumerable: true, get: function () { return apiclient_1.ApiClient; } });
121
+ Object.defineProperty(exports, "NewApiClient", { enumerable: true, get: function () { return apiclient_1.NewApiClient; } });
122
+ Object.defineProperty(exports, "APIError", { enumerable: true, get: function () { return apiclient_1.APIError; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fullstackcraftllc/floe",
3
- "version": "0.0.18",
3
+ "version": "0.0.19",
4
4
  "description": "Production-ready options analytics toolkit. Normalize broker data structures and calculate Black-Scholes, Greeks, and exposures with a clean, type-safe API. Built for trading platforms and fintech applications.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,7 +9,6 @@
9
9
  "test": "jest",
10
10
  "prepublishOnly": "npm run build",
11
11
  "type-check": "tsc --noEmit",
12
- "prepare": "husky",
13
12
  "copy-whitepaper": "cp whitepaper/whitepaper.pdf site/public/whitepaper.pdf"
14
13
  },
15
14
  "keywords": [
@@ -44,7 +43,6 @@
44
43
  "@types/jest": "^29.5.0",
45
44
  "@types/node": "^20.0.0",
46
45
  "dotenv": "^17.2.3",
47
- "husky": "^9.1.7",
48
46
  "jest": "^29.5.0",
49
47
  "ts-jest": "^29.1.0",
50
48
  "typescript": "^5.9.3"