tardis-dev 14.1.4 → 14.2.0

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,9 +1,54 @@
1
1
  import { onlyUnique } from '../handy'
2
2
  import { Filter } from '../types'
3
- import { RealTimeFeedBase } from './realtimefeed'
3
+ import { MultiConnectionRealTimeFeedBase, RealTimeFeedBase } from './realtimefeed'
4
4
 
5
- export class BinanceEuropeanOptionsRealTimeFeed extends RealTimeFeedBase {
6
- protected wssURL = 'wss://nbstream.binance.com/eoptions/stream'
5
+ export class BinanceEuropeanOptionsRealTimeFeed extends MultiConnectionRealTimeFeedBase {
6
+ protected *_getRealTimeFeeds(exchange: string, filters: Filter<string>[], timeoutIntervalMS?: number, onError?: (error: Error) => void) {
7
+ // V2 API uses two separate WebSocket endpoints:
8
+ // 1. Public path: for optionTrade, depth20, bookTicker, optionTicker
9
+ // 2. Market path: for optionIndexPrice, optionMarkPrice, optionOpenInterest
10
+
11
+ const publicChannels = ['optionTrade', 'depth20', 'bookTicker', 'optionTicker']
12
+ const marketChannels = ['optionIndexPrice', 'optionMarkPrice', 'optionOpenInterest']
13
+
14
+ const publicFilters = filters.filter((f) => publicChannels.includes(f.channel))
15
+ const marketFilters = filters.filter((f) => marketChannels.includes(f.channel))
16
+
17
+ if (publicFilters.length > 0) {
18
+ yield new BinanceEuropeanOptionsSingleFeed(
19
+ 'wss://fstream.binance.com/public/stream',
20
+ exchange,
21
+ publicFilters,
22
+ filters, // Pass all filters so we can look up optionTicker when processing optionOpenInterest
23
+ timeoutIntervalMS,
24
+ onError
25
+ )
26
+ }
27
+
28
+ if (marketFilters.length > 0) {
29
+ yield new BinanceEuropeanOptionsSingleFeed(
30
+ 'wss://fstream.binance.com/market/stream',
31
+ exchange,
32
+ marketFilters,
33
+ filters, // Pass all filters so we can look up optionTicker when processing optionOpenInterest
34
+ timeoutIntervalMS,
35
+ onError
36
+ )
37
+ }
38
+ }
39
+ }
40
+
41
+ class BinanceEuropeanOptionsSingleFeed extends RealTimeFeedBase {
42
+ constructor(
43
+ protected wssURL: string,
44
+ exchange: string,
45
+ filters: Filter<string>[],
46
+ private readonly _allFilters: Filter<string>[],
47
+ timeoutIntervalMS: number | undefined,
48
+ onError?: (error: Error) => void
49
+ ) {
50
+ super(exchange, filters, timeoutIntervalMS, onError)
51
+ }
7
52
 
8
53
  protected mapToSubscribeMessages(filters: Filter<string>[]): any[] {
9
54
  const payload = filters.map((filter, index) => {
@@ -15,29 +60,59 @@ export class BinanceEuropeanOptionsRealTimeFeed extends RealTimeFeedBase {
15
60
  method: 'SUBSCRIBE',
16
61
  params: filter.symbols
17
62
  .map((symbol) => {
18
- if (filter.channel === 'depth100') {
19
- return [`${symbol}@${filter.channel}@100ms`]
63
+ const lowerSymbol = symbol.toLowerCase()
64
+
65
+ // Public path channels - use lowercase symbol directly
66
+ if (filter.channel === 'optionTrade') {
67
+ return [`${lowerSymbol}@${filter.channel}`]
20
68
  }
21
69
 
22
- if (filter.channel === 'openInterest') {
23
- const matchingTickerChannel = filters.find((f) => f.channel === 'ticker')
70
+ if (filter.channel === 'depth20') {
71
+ return [`${lowerSymbol}@${filter.channel}@100ms`]
72
+ }
73
+
74
+ if (filter.channel === 'bookTicker') {
75
+ return [`${lowerSymbol}@${filter.channel}`]
76
+ }
77
+
78
+ if (filter.channel === 'optionTicker') {
79
+ return [`${lowerSymbol}@${filter.channel}`]
80
+ }
81
+
82
+ // Market path channels - use lowercase underlying
83
+ if (filter.channel === 'optionIndexPrice' || filter.channel === 'optionMarkPrice') {
84
+ // Symbol is the underlying (e.g., 'btcusdt')
85
+ return [`${lowerSymbol}@${filter.channel}`]
86
+ }
87
+
88
+ if (filter.channel === 'optionOpenInterest') {
89
+ // Need to extract expirations from option symbols
90
+ // The symbol here is the underlying (e.g., 'btcusdt')
91
+ // We need to find all option symbols that match this underlying to extract expirations
92
+
93
+ // Look for optionTicker filter in all filters to get actual option symbols
94
+ const optionTickerFilter = this._allFilters.find((f) => f.channel === 'optionTicker')
95
+
96
+ if (optionTickerFilter !== undefined) {
97
+ // Extract expirations from option symbols that match this underlying
98
+ const underlyingBase = lowerSymbol.replace('usdt', '').toUpperCase()
24
99
 
25
- if (matchingTickerChannel !== undefined) {
26
- const expirations = matchingTickerChannel
27
- .symbols!.filter((s) => s.startsWith(symbol))
100
+ const expirations = optionTickerFilter
101
+ .symbols!.filter((s) => s.toUpperCase().startsWith(underlyingBase + '-'))
28
102
  .map((s) => {
29
103
  const symbolParts = s.split('-')
30
- return `${symbolParts[1]}`
104
+ return symbolParts[1] // Extract expiration (e.g., '251219')
31
105
  })
32
106
  .filter(onlyUnique)
107
+ .map((exp) => exp.toLowerCase())
33
108
 
34
109
  return expirations.map((expiration) => {
35
- return `${symbol}@${filter.channel}@${expiration}`
110
+ return `${lowerSymbol}@${filter.channel}@${expiration}`
36
111
  })
37
112
  }
38
113
  }
39
114
 
40
- return [`${symbol}@${filter.channel}`]
115
+ return [`${lowerSymbol}@${filter.channel}`]
41
116
  })
42
117
  .flatMap((s) => s),
43
118
  id: index + 1