@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 +1 -1
- package/dist/client/FloeClient.js +1 -1
- package/dist/client/brokers/BaseBrokerClient.d.ts +296 -0
- package/dist/client/brokers/BaseBrokerClient.js +509 -0
- package/dist/client/brokers/SchwabClient.d.ts +11 -128
- package/dist/client/brokers/SchwabClient.js +37 -246
- package/dist/client/brokers/TastyTradeClient.d.ts +15 -120
- package/dist/client/brokers/TastyTradeClient.js +15 -316
- package/dist/client/brokers/TradeStationClient.d.ts +31 -128
- package/dist/client/brokers/TradeStationClient.js +37 -201
- package/dist/client/brokers/TradierClient.d.ts +16 -196
- package/dist/client/brokers/TradierClient.js +19 -421
- package/package.json +1 -1
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NormalizedTicker } from '../../types';
|
|
2
|
+
import { BaseBrokerClient, BaseBrokerClientOptions, AggressorSide, IntradayTrade, FlowSummary, BrokerClientEventType, BrokerEventListener } from './BaseBrokerClient';
|
|
3
|
+
export { AggressorSide, IntradayTrade, FlowSummary };
|
|
4
|
+
export type TradeStationClientEventType = BrokerClientEventType;
|
|
5
|
+
export type TradeStationEventListener<T> = BrokerEventListener<T>;
|
|
2
6
|
/**
|
|
3
7
|
* TradeStation symbol details response
|
|
4
8
|
*/
|
|
@@ -23,38 +27,18 @@ interface TradeStationSymbolDetails {
|
|
|
23
27
|
}>;
|
|
24
28
|
}
|
|
25
29
|
/**
|
|
26
|
-
*
|
|
30
|
+
* TradeStation client configuration options
|
|
27
31
|
*/
|
|
28
|
-
export
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
/** Trade size (number of contracts) */
|
|
38
|
-
size: number;
|
|
39
|
-
/** Bid at time of trade */
|
|
40
|
-
bid: number;
|
|
41
|
-
/** Ask at time of trade */
|
|
42
|
-
ask: number;
|
|
43
|
-
/** Aggressor side determined from price vs NBBO */
|
|
44
|
-
aggressorSide: AggressorSide;
|
|
45
|
-
/** Timestamp of the trade */
|
|
46
|
-
timestamp: number;
|
|
47
|
-
/** Estimated OI change */
|
|
48
|
-
estimatedOIChange: number;
|
|
32
|
+
export interface TradeStationClientOptions extends BaseBrokerClientOptions {
|
|
33
|
+
/** TradeStation OAuth access token (required) */
|
|
34
|
+
accessToken: string;
|
|
35
|
+
/** OAuth refresh token for automatic token renewal */
|
|
36
|
+
refreshToken?: string;
|
|
37
|
+
/** Whether to use simulation environment (default: false) */
|
|
38
|
+
simulation?: boolean;
|
|
39
|
+
/** Callback when token is refreshed */
|
|
40
|
+
onTokenRefresh?: (newToken: string) => void;
|
|
49
41
|
}
|
|
50
|
-
/**
|
|
51
|
-
* Event types emitted by TradeStationClient
|
|
52
|
-
*/
|
|
53
|
-
type TradeStationClientEventType = 'tickerUpdate' | 'optionUpdate' | 'optionTrade' | 'connected' | 'disconnected' | 'error';
|
|
54
|
-
/**
|
|
55
|
-
* Event listener callback type
|
|
56
|
-
*/
|
|
57
|
-
type TradeStationEventListener<T> = (data: T) => void;
|
|
58
42
|
/**
|
|
59
43
|
* TradeStationClient handles real-time streaming connections to the TradeStation API
|
|
60
44
|
* via HTTP chunked transfer encoding.
|
|
@@ -82,7 +66,8 @@ type TradeStationEventListener<T> = (data: T) => void;
|
|
|
82
66
|
* client.subscribe(['MSFT', 'AAPL']);
|
|
83
67
|
* ```
|
|
84
68
|
*/
|
|
85
|
-
export declare class TradeStationClient {
|
|
69
|
+
export declare class TradeStationClient extends BaseBrokerClient {
|
|
70
|
+
protected readonly brokerName = "TradeStation";
|
|
86
71
|
/** TradeStation OAuth access token */
|
|
87
72
|
private accessToken;
|
|
88
73
|
/** Connection state */
|
|
@@ -93,24 +78,6 @@ export declare class TradeStationClient {
|
|
|
93
78
|
private subscribedOptions;
|
|
94
79
|
/** Active AbortControllers for streams */
|
|
95
80
|
private activeStreams;
|
|
96
|
-
/** Cached ticker data */
|
|
97
|
-
private tickerCache;
|
|
98
|
-
/** Cached option data */
|
|
99
|
-
private optionCache;
|
|
100
|
-
/** Base open interest from REST API */
|
|
101
|
-
private baseOpenInterest;
|
|
102
|
-
/** Cumulative estimated OI change from intraday trades */
|
|
103
|
-
private cumulativeOIChange;
|
|
104
|
-
/** History of intraday trades */
|
|
105
|
-
private intradayTrades;
|
|
106
|
-
/** Event listeners */
|
|
107
|
-
private eventListeners;
|
|
108
|
-
/** Reconnection attempt counter */
|
|
109
|
-
private reconnectAttempts;
|
|
110
|
-
/** Maximum reconnection attempts */
|
|
111
|
-
private readonly maxReconnectAttempts;
|
|
112
|
-
/** Reconnection delay in ms */
|
|
113
|
-
private readonly baseReconnectDelay;
|
|
114
81
|
/** TradeStation API base URL */
|
|
115
82
|
private readonly apiBaseUrl;
|
|
116
83
|
/** Whether to use simulation environment */
|
|
@@ -119,8 +86,6 @@ export declare class TradeStationClient {
|
|
|
119
86
|
private refreshToken;
|
|
120
87
|
/** Token refresh callback */
|
|
121
88
|
private onTokenRefresh;
|
|
122
|
-
/** Whether to log verbose debug information */
|
|
123
|
-
private readonly verbose;
|
|
124
89
|
/**
|
|
125
90
|
* Creates a new TradeStationClient instance.
|
|
126
91
|
*
|
|
@@ -131,13 +96,7 @@ export declare class TradeStationClient {
|
|
|
131
96
|
* @param options.onTokenRefresh - Callback when token is refreshed
|
|
132
97
|
* @param options.verbose - Whether to log verbose debug information (default: false)
|
|
133
98
|
*/
|
|
134
|
-
constructor(options:
|
|
135
|
-
accessToken: string;
|
|
136
|
-
refreshToken?: string;
|
|
137
|
-
simulation?: boolean;
|
|
138
|
-
onTokenRefresh?: (newToken: string) => void;
|
|
139
|
-
verbose?: boolean;
|
|
140
|
-
});
|
|
99
|
+
constructor(options: TradeStationClientOptions);
|
|
141
100
|
/**
|
|
142
101
|
* Establishes connection state for TradeStation streaming.
|
|
143
102
|
*
|
|
@@ -219,52 +178,21 @@ export declare class TradeStationClient {
|
|
|
219
178
|
* @returns Symbol details response
|
|
220
179
|
*/
|
|
221
180
|
fetchSymbolDetails(symbols: string[]): Promise<TradeStationSymbolDetails>;
|
|
222
|
-
/**
|
|
223
|
-
* Returns cached option data for a symbol.
|
|
224
|
-
*/
|
|
225
|
-
getOption(occSymbol: string): NormalizedOption | undefined;
|
|
226
|
-
/**
|
|
227
|
-
* Returns all cached options.
|
|
228
|
-
*/
|
|
229
|
-
getAllOptions(): Map<string, NormalizedOption>;
|
|
230
|
-
/**
|
|
231
|
-
* Returns cached ticker data for a symbol.
|
|
232
|
-
*/
|
|
233
|
-
getTicker(symbol: string): NormalizedTicker | undefined;
|
|
234
|
-
/**
|
|
235
|
-
* Returns all cached tickers.
|
|
236
|
-
*/
|
|
237
|
-
getAllTickers(): Map<string, NormalizedTicker>;
|
|
238
|
-
/**
|
|
239
|
-
* Registers an event listener.
|
|
240
|
-
*/
|
|
241
|
-
on<T>(event: TradeStationClientEventType, listener: TradeStationEventListener<T>): this;
|
|
242
|
-
/**
|
|
243
|
-
* Removes an event listener.
|
|
244
|
-
*/
|
|
245
|
-
off<T>(event: TradeStationClientEventType, listener: TradeStationEventListener<T>): this;
|
|
246
|
-
/**
|
|
247
|
-
* Returns intraday trades for an option.
|
|
248
|
-
*/
|
|
249
|
-
getIntradayTrades(occSymbol: string): IntradayTrade[];
|
|
250
|
-
/**
|
|
251
|
-
* Returns flow summary for an option.
|
|
252
|
-
*/
|
|
253
|
-
getFlowSummary(occSymbol: string): {
|
|
254
|
-
buyVolume: number;
|
|
255
|
-
sellVolume: number;
|
|
256
|
-
unknownVolume: number;
|
|
257
|
-
netOIChange: number;
|
|
258
|
-
tradeCount: number;
|
|
259
|
-
};
|
|
260
|
-
/**
|
|
261
|
-
* Resets intraday tracking data.
|
|
262
|
-
*/
|
|
263
|
-
resetIntradayData(occSymbols?: string[]): void;
|
|
264
181
|
/**
|
|
265
182
|
* Updates the access token (for token refresh scenarios).
|
|
266
183
|
*/
|
|
267
184
|
updateAccessToken(newToken: string): void;
|
|
185
|
+
/**
|
|
186
|
+
* Fetches open interest and other static data for subscribed options.
|
|
187
|
+
*
|
|
188
|
+
* @param occSymbols - Array of OCC option symbols to fetch data for
|
|
189
|
+
*
|
|
190
|
+
* @remarks
|
|
191
|
+
* TradeStation provides open interest via the streaming option quotes.
|
|
192
|
+
* This method is a no-op placeholder to satisfy the abstract interface.
|
|
193
|
+
* OI data will be populated automatically through the streaming connection.
|
|
194
|
+
*/
|
|
195
|
+
fetchOpenInterest(occSymbols: string[]): Promise<void>;
|
|
268
196
|
/**
|
|
269
197
|
* Gets authorization headers for API requests.
|
|
270
198
|
*/
|
|
@@ -334,32 +262,7 @@ export declare class TradeStationClient {
|
|
|
334
262
|
*/
|
|
335
263
|
private toTradeStationOptionSymbol;
|
|
336
264
|
/**
|
|
337
|
-
*
|
|
338
|
-
*/
|
|
339
|
-
private determineAggressorSide;
|
|
340
|
-
/**
|
|
341
|
-
* Calculates estimated OI change from trade.
|
|
342
|
-
*/
|
|
343
|
-
private calculateOIChangeFromTrade;
|
|
344
|
-
/**
|
|
345
|
-
* Calculates live open interest.
|
|
346
|
-
*/
|
|
347
|
-
private calculateLiveOpenInterest;
|
|
348
|
-
/**
|
|
349
|
-
* Checks if a symbol is an option symbol.
|
|
350
|
-
*/
|
|
351
|
-
private isOptionSymbol;
|
|
352
|
-
/**
|
|
353
|
-
* Parses a numeric string value.
|
|
354
|
-
*/
|
|
355
|
-
private parseNumber;
|
|
356
|
-
/**
|
|
357
|
-
* Emits an event to all listeners.
|
|
358
|
-
*/
|
|
359
|
-
private emit;
|
|
360
|
-
/**
|
|
361
|
-
* Sleep utility.
|
|
265
|
+
* Checks if a symbol is an option symbol (TradeStation or OCC format).
|
|
362
266
|
*/
|
|
363
|
-
private
|
|
267
|
+
private isTradeStationOptionSymbol;
|
|
364
268
|
}
|
|
365
|
-
export {};
|
|
@@ -2,16 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TradeStationClient = void 0;
|
|
4
4
|
const occ_1 = require("../../utils/occ");
|
|
5
|
+
const BaseBrokerClient_1 = require("./BaseBrokerClient");
|
|
5
6
|
/**
|
|
6
7
|
* Regex pattern to identify TradeStation option symbols
|
|
7
8
|
* TradeStation uses space-padded format: "MSFT 220916C305"
|
|
8
9
|
*/
|
|
9
10
|
const TS_OPTION_PATTERN = /^[A-Z]+\s+\d{6}[CP]\d+(\.\d+)?$/;
|
|
10
|
-
/**
|
|
11
|
-
* Regex pattern to identify OCC option symbols
|
|
12
|
-
* Format: ROOT + YYMMDD + C/P + 8-digit strike
|
|
13
|
-
*/
|
|
14
|
-
const OCC_OPTION_PATTERN = /^.{1,6}\d{6}[CP]\d{8}$/;
|
|
15
11
|
/**
|
|
16
12
|
* TradeStationClient handles real-time streaming connections to the TradeStation API
|
|
17
13
|
* via HTTP chunked transfer encoding.
|
|
@@ -39,7 +35,7 @@ const OCC_OPTION_PATTERN = /^.{1,6}\d{6}[CP]\d{8}$/;
|
|
|
39
35
|
* client.subscribe(['MSFT', 'AAPL']);
|
|
40
36
|
* ```
|
|
41
37
|
*/
|
|
42
|
-
class TradeStationClient {
|
|
38
|
+
class TradeStationClient extends BaseBrokerClient_1.BaseBrokerClient {
|
|
43
39
|
/**
|
|
44
40
|
* Creates a new TradeStationClient instance.
|
|
45
41
|
*
|
|
@@ -51,6 +47,8 @@ class TradeStationClient {
|
|
|
51
47
|
* @param options.verbose - Whether to log verbose debug information (default: false)
|
|
52
48
|
*/
|
|
53
49
|
constructor(options) {
|
|
50
|
+
super(options);
|
|
51
|
+
this.brokerName = 'TradeStation';
|
|
54
52
|
/** Connection state */
|
|
55
53
|
this.connected = false;
|
|
56
54
|
/** Currently subscribed ticker symbols */
|
|
@@ -59,24 +57,6 @@ class TradeStationClient {
|
|
|
59
57
|
this.subscribedOptions = new Set();
|
|
60
58
|
/** Active AbortControllers for streams */
|
|
61
59
|
this.activeStreams = new Map();
|
|
62
|
-
/** Cached ticker data */
|
|
63
|
-
this.tickerCache = new Map();
|
|
64
|
-
/** Cached option data */
|
|
65
|
-
this.optionCache = new Map();
|
|
66
|
-
/** Base open interest from REST API */
|
|
67
|
-
this.baseOpenInterest = new Map();
|
|
68
|
-
/** Cumulative estimated OI change from intraday trades */
|
|
69
|
-
this.cumulativeOIChange = new Map();
|
|
70
|
-
/** History of intraday trades */
|
|
71
|
-
this.intradayTrades = new Map();
|
|
72
|
-
/** Event listeners */
|
|
73
|
-
this.eventListeners = new Map();
|
|
74
|
-
/** Reconnection attempt counter */
|
|
75
|
-
this.reconnectAttempts = 0;
|
|
76
|
-
/** Maximum reconnection attempts */
|
|
77
|
-
this.maxReconnectAttempts = 5;
|
|
78
|
-
/** Reconnection delay in ms */
|
|
79
|
-
this.baseReconnectDelay = 1000;
|
|
80
60
|
/** TradeStation API base URL */
|
|
81
61
|
this.apiBaseUrl = 'https://api.tradestation.com/v3';
|
|
82
62
|
/** Refresh token for token refresh */
|
|
@@ -87,14 +67,6 @@ class TradeStationClient {
|
|
|
87
67
|
this.refreshToken = options.refreshToken ?? null;
|
|
88
68
|
this.simulation = options.simulation ?? false;
|
|
89
69
|
this.onTokenRefresh = options.onTokenRefresh ?? null;
|
|
90
|
-
this.verbose = options.verbose ?? false;
|
|
91
|
-
// Initialize event listener maps
|
|
92
|
-
this.eventListeners.set('tickerUpdate', new Set());
|
|
93
|
-
this.eventListeners.set('optionUpdate', new Set());
|
|
94
|
-
this.eventListeners.set('optionTrade', new Set());
|
|
95
|
-
this.eventListeners.set('connected', new Set());
|
|
96
|
-
this.eventListeners.set('disconnected', new Set());
|
|
97
|
-
this.eventListeners.set('error', new Set());
|
|
98
70
|
}
|
|
99
71
|
// ==================== Public API ====================
|
|
100
72
|
/**
|
|
@@ -163,7 +135,7 @@ class TradeStationClient {
|
|
|
163
135
|
const tickers = [];
|
|
164
136
|
const options = [];
|
|
165
137
|
for (const symbol of symbols) {
|
|
166
|
-
if (this.
|
|
138
|
+
if (this.isTradeStationOptionSymbol(symbol)) {
|
|
167
139
|
options.push(symbol);
|
|
168
140
|
this.subscribedOptions.add(symbol);
|
|
169
141
|
}
|
|
@@ -192,7 +164,7 @@ class TradeStationClient {
|
|
|
192
164
|
const tickersToRemove = [];
|
|
193
165
|
const optionsToRemove = [];
|
|
194
166
|
for (const symbol of symbols) {
|
|
195
|
-
if (this.
|
|
167
|
+
if (this.isTradeStationOptionSymbol(symbol)) {
|
|
196
168
|
this.subscribedOptions.delete(symbol);
|
|
197
169
|
optionsToRemove.push(symbol);
|
|
198
170
|
}
|
|
@@ -340,100 +312,29 @@ class TradeStationClient {
|
|
|
340
312
|
}
|
|
341
313
|
}
|
|
342
314
|
/**
|
|
343
|
-
*
|
|
344
|
-
*/
|
|
345
|
-
getOption(occSymbol) {
|
|
346
|
-
return this.optionCache.get(occSymbol);
|
|
347
|
-
}
|
|
348
|
-
/**
|
|
349
|
-
* Returns all cached options.
|
|
350
|
-
*/
|
|
351
|
-
getAllOptions() {
|
|
352
|
-
return new Map(this.optionCache);
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Returns cached ticker data for a symbol.
|
|
356
|
-
*/
|
|
357
|
-
getTicker(symbol) {
|
|
358
|
-
return this.tickerCache.get(symbol);
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Returns all cached tickers.
|
|
362
|
-
*/
|
|
363
|
-
getAllTickers() {
|
|
364
|
-
return new Map(this.tickerCache);
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
|
-
* Registers an event listener.
|
|
368
|
-
*/
|
|
369
|
-
on(event, listener) {
|
|
370
|
-
const listeners = this.eventListeners.get(event);
|
|
371
|
-
if (listeners) {
|
|
372
|
-
listeners.add(listener);
|
|
373
|
-
}
|
|
374
|
-
return this;
|
|
375
|
-
}
|
|
376
|
-
/**
|
|
377
|
-
* Removes an event listener.
|
|
378
|
-
*/
|
|
379
|
-
off(event, listener) {
|
|
380
|
-
const listeners = this.eventListeners.get(event);
|
|
381
|
-
if (listeners) {
|
|
382
|
-
listeners.delete(listener);
|
|
383
|
-
}
|
|
384
|
-
return this;
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* Returns intraday trades for an option.
|
|
388
|
-
*/
|
|
389
|
-
getIntradayTrades(occSymbol) {
|
|
390
|
-
return this.intradayTrades.get(occSymbol) ?? [];
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* Returns flow summary for an option.
|
|
315
|
+
* Updates the access token (for token refresh scenarios).
|
|
394
316
|
*/
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
let buyVolume = 0;
|
|
398
|
-
let sellVolume = 0;
|
|
399
|
-
let unknownVolume = 0;
|
|
400
|
-
for (const trade of trades) {
|
|
401
|
-
switch (trade.aggressorSide) {
|
|
402
|
-
case 'buy':
|
|
403
|
-
buyVolume += trade.size;
|
|
404
|
-
break;
|
|
405
|
-
case 'sell':
|
|
406
|
-
sellVolume += trade.size;
|
|
407
|
-
break;
|
|
408
|
-
case 'unknown':
|
|
409
|
-
unknownVolume += trade.size;
|
|
410
|
-
break;
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
return {
|
|
414
|
-
buyVolume,
|
|
415
|
-
sellVolume,
|
|
416
|
-
unknownVolume,
|
|
417
|
-
netOIChange: this.cumulativeOIChange.get(occSymbol) ?? 0,
|
|
418
|
-
tradeCount: trades.length,
|
|
419
|
-
};
|
|
317
|
+
updateAccessToken(newToken) {
|
|
318
|
+
this.accessToken = newToken;
|
|
420
319
|
}
|
|
421
320
|
/**
|
|
422
|
-
*
|
|
321
|
+
* Fetches open interest and other static data for subscribed options.
|
|
322
|
+
*
|
|
323
|
+
* @param occSymbols - Array of OCC option symbols to fetch data for
|
|
324
|
+
*
|
|
325
|
+
* @remarks
|
|
326
|
+
* TradeStation provides open interest via the streaming option quotes.
|
|
327
|
+
* This method is a no-op placeholder to satisfy the abstract interface.
|
|
328
|
+
* OI data will be populated automatically through the streaming connection.
|
|
423
329
|
*/
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
330
|
+
async fetchOpenInterest(occSymbols) {
|
|
331
|
+
// TradeStation provides OI through the option quote stream
|
|
332
|
+
// The subscribedOptions will receive OI data automatically
|
|
333
|
+
// This is a no-op to satisfy the abstract method requirement
|
|
334
|
+
if (this.verbose) {
|
|
335
|
+
console.log(`[TradeStation] fetchOpenInterest called for ${occSymbols.length} symbols - data comes via stream`);
|
|
429
336
|
}
|
|
430
337
|
}
|
|
431
|
-
/**
|
|
432
|
-
* Updates the access token (for token refresh scenarios).
|
|
433
|
-
*/
|
|
434
|
-
updateAccessToken(newToken) {
|
|
435
|
-
this.accessToken = newToken;
|
|
436
|
-
}
|
|
437
338
|
// ==================== Private Methods ====================
|
|
438
339
|
/**
|
|
439
340
|
* Gets authorization headers for API requests.
|
|
@@ -699,18 +600,18 @@ class TradeStationClient {
|
|
|
699
600
|
* Normalizes TradeStation quote to NormalizedTicker.
|
|
700
601
|
*/
|
|
701
602
|
normalizeQuote(quote) {
|
|
702
|
-
const bid = this.
|
|
703
|
-
const ask = this.
|
|
704
|
-
const last = this.
|
|
603
|
+
const bid = this.toNumber(quote.Bid);
|
|
604
|
+
const ask = this.toNumber(quote.Ask);
|
|
605
|
+
const last = this.toNumber(quote.Last);
|
|
705
606
|
return {
|
|
706
607
|
symbol: quote.Symbol,
|
|
707
608
|
spot: bid > 0 && ask > 0 ? (bid + ask) / 2 : last,
|
|
708
609
|
bid,
|
|
709
|
-
bidSize: this.
|
|
610
|
+
bidSize: this.toNumber(quote.BidSize),
|
|
710
611
|
ask,
|
|
711
|
-
askSize: this.
|
|
612
|
+
askSize: this.toNumber(quote.AskSize),
|
|
712
613
|
last,
|
|
713
|
-
volume: this.
|
|
614
|
+
volume: this.toNumber(quote.Volume),
|
|
714
615
|
timestamp: quote.TradeTime ? new Date(quote.TradeTime).getTime() : Date.now(),
|
|
715
616
|
};
|
|
716
617
|
}
|
|
@@ -739,9 +640,9 @@ class TradeStationClient {
|
|
|
739
640
|
strike: parseFloat(leg.StrikePrice),
|
|
740
641
|
};
|
|
741
642
|
}
|
|
742
|
-
const bid = this.
|
|
743
|
-
const ask = this.
|
|
744
|
-
const last = this.
|
|
643
|
+
const bid = this.toNumber(data.Bid);
|
|
644
|
+
const ask = this.toNumber(data.Ask);
|
|
645
|
+
const last = this.toNumber(data.Last);
|
|
745
646
|
const existingOI = this.baseOpenInterest.get(occSymbol) ?? 0;
|
|
746
647
|
return {
|
|
747
648
|
occSymbol,
|
|
@@ -759,7 +660,7 @@ class TradeStationClient {
|
|
|
759
660
|
volume: data.Volume ?? 0,
|
|
760
661
|
openInterest: data.DailyOpenInterest ?? leg.OpenInterest ?? existingOI,
|
|
761
662
|
liveOpenInterest: this.calculateLiveOpenInterest(occSymbol),
|
|
762
|
-
impliedVolatility: this.
|
|
663
|
+
impliedVolatility: this.toNumber(data.ImpliedVolatility),
|
|
763
664
|
timestamp: Date.now(),
|
|
764
665
|
};
|
|
765
666
|
}
|
|
@@ -772,7 +673,7 @@ class TradeStationClient {
|
|
|
772
673
|
if (!tsSymbol)
|
|
773
674
|
return null;
|
|
774
675
|
// Already in OCC format?
|
|
775
|
-
if (OCC_OPTION_PATTERN.test(tsSymbol.replace(/\s+/g, ''))) {
|
|
676
|
+
if (BaseBrokerClient_1.OCC_OPTION_PATTERN.test(tsSymbol.replace(/\s+/g, ''))) {
|
|
776
677
|
return tsSymbol.replace(/\s+/g, '');
|
|
777
678
|
}
|
|
778
679
|
// Parse TradeStation format
|
|
@@ -815,75 +716,10 @@ class TradeStationClient {
|
|
|
815
716
|
}
|
|
816
717
|
}
|
|
817
718
|
/**
|
|
818
|
-
*
|
|
819
|
-
*/
|
|
820
|
-
determineAggressorSide(tradePrice, bid, ask) {
|
|
821
|
-
if (bid <= 0 || ask <= 0)
|
|
822
|
-
return 'unknown';
|
|
823
|
-
const spread = ask - bid;
|
|
824
|
-
const tolerance = spread > 0 ? spread * 0.001 : 0.001;
|
|
825
|
-
if (tradePrice >= ask - tolerance) {
|
|
826
|
-
return 'buy';
|
|
827
|
-
}
|
|
828
|
-
else if (tradePrice <= bid + tolerance) {
|
|
829
|
-
return 'sell';
|
|
830
|
-
}
|
|
831
|
-
return 'unknown';
|
|
832
|
-
}
|
|
833
|
-
/**
|
|
834
|
-
* Calculates estimated OI change from trade.
|
|
835
|
-
*/
|
|
836
|
-
calculateOIChangeFromTrade(aggressorSide, size, _optionType) {
|
|
837
|
-
if (aggressorSide === 'unknown')
|
|
838
|
-
return 0;
|
|
839
|
-
return aggressorSide === 'buy' ? size : -size;
|
|
840
|
-
}
|
|
841
|
-
/**
|
|
842
|
-
* Calculates live open interest.
|
|
843
|
-
*/
|
|
844
|
-
calculateLiveOpenInterest(occSymbol) {
|
|
845
|
-
const baseOI = this.baseOpenInterest.get(occSymbol) ?? 0;
|
|
846
|
-
const cumulativeChange = this.cumulativeOIChange.get(occSymbol) ?? 0;
|
|
847
|
-
return Math.max(0, baseOI + cumulativeChange);
|
|
848
|
-
}
|
|
849
|
-
/**
|
|
850
|
-
* Checks if a symbol is an option symbol.
|
|
851
|
-
*/
|
|
852
|
-
isOptionSymbol(symbol) {
|
|
853
|
-
return TS_OPTION_PATTERN.test(symbol) || OCC_OPTION_PATTERN.test(symbol);
|
|
854
|
-
}
|
|
855
|
-
/**
|
|
856
|
-
* Parses a numeric string value.
|
|
857
|
-
*/
|
|
858
|
-
parseNumber(value) {
|
|
859
|
-
if (value === undefined || value === null)
|
|
860
|
-
return 0;
|
|
861
|
-
if (typeof value === 'number')
|
|
862
|
-
return isNaN(value) ? 0 : value;
|
|
863
|
-
const num = parseFloat(value);
|
|
864
|
-
return isNaN(num) ? 0 : num;
|
|
865
|
-
}
|
|
866
|
-
/**
|
|
867
|
-
* Emits an event to all listeners.
|
|
868
|
-
*/
|
|
869
|
-
emit(event, data) {
|
|
870
|
-
const listeners = this.eventListeners.get(event);
|
|
871
|
-
if (listeners) {
|
|
872
|
-
listeners.forEach(listener => {
|
|
873
|
-
try {
|
|
874
|
-
listener(data);
|
|
875
|
-
}
|
|
876
|
-
catch (error) {
|
|
877
|
-
console.error('Event listener error:', error);
|
|
878
|
-
}
|
|
879
|
-
});
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
/**
|
|
883
|
-
* Sleep utility.
|
|
719
|
+
* Checks if a symbol is an option symbol (TradeStation or OCC format).
|
|
884
720
|
*/
|
|
885
|
-
|
|
886
|
-
return
|
|
721
|
+
isTradeStationOptionSymbol(symbol) {
|
|
722
|
+
return TS_OPTION_PATTERN.test(symbol) || BaseBrokerClient_1.OCC_OPTION_PATTERN.test(symbol);
|
|
887
723
|
}
|
|
888
724
|
}
|
|
889
725
|
exports.TradeStationClient = TradeStationClient;
|