@adaptic/utils 0.0.379 → 0.0.381

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/dist/index.mjs CHANGED
@@ -11456,22 +11456,28 @@ class AlpacaMarketDataAPI extends EventEmitter {
11456
11456
  v1beta1url;
11457
11457
  stockStreamUrl = 'wss://stream.data.alpaca.markets/v2/sip'; // production values
11458
11458
  optionStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/options'; // production values
11459
+ cryptoStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/crypto/us'; // production values
11459
11460
  stockWs = null;
11460
11461
  optionWs = null;
11462
+ cryptoWs = null;
11461
11463
  stockSubscriptions = { trades: [], quotes: [], bars: [] };
11462
11464
  optionSubscriptions = { trades: [], quotes: [], bars: [] };
11465
+ cryptoSubscriptions = { trades: [], quotes: [], bars: [] };
11463
11466
  setMode(mode = 'production') {
11464
11467
  if (mode === 'sandbox') { // sandbox mode
11465
11468
  this.stockStreamUrl = 'wss://stream.data.sandbox.alpaca.markets/v2/sip';
11466
11469
  this.optionStreamUrl = 'wss://stream.data.sandbox.alpaca.markets/v1beta3/options';
11470
+ this.cryptoStreamUrl = 'wss://stream.data.sandbox.alpaca.markets/v1beta3/crypto/us';
11467
11471
  }
11468
11472
  else if (mode === 'test') { // test mode, can only use ticker FAKEPACA
11469
11473
  this.stockStreamUrl = 'wss://stream.data.alpaca.markets/v2/test';
11470
11474
  this.optionStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/options'; // there's no test mode for options
11475
+ this.cryptoStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/crypto/us'; // there's no test mode for crypto
11471
11476
  }
11472
11477
  else { // production
11473
11478
  this.stockStreamUrl = 'wss://stream.data.alpaca.markets/v2/sip';
11474
11479
  this.optionStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/options';
11480
+ this.cryptoStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/crypto/us';
11475
11481
  }
11476
11482
  }
11477
11483
  getMode() {
@@ -11513,14 +11519,26 @@ class AlpacaMarketDataAPI extends EventEmitter {
11513
11519
  return super.emit(event, ...args);
11514
11520
  }
11515
11521
  connect(streamType) {
11516
- const url = streamType === 'stock' ? this.stockStreamUrl : this.optionStreamUrl;
11522
+ let url;
11523
+ if (streamType === 'stock') {
11524
+ url = this.stockStreamUrl;
11525
+ }
11526
+ else if (streamType === 'option') {
11527
+ url = this.optionStreamUrl;
11528
+ }
11529
+ else {
11530
+ url = this.cryptoStreamUrl;
11531
+ }
11517
11532
  const ws = new WebSocket(url);
11518
11533
  if (streamType === 'stock') {
11519
11534
  this.stockWs = ws;
11520
11535
  }
11521
- else {
11536
+ else if (streamType === 'option') {
11522
11537
  this.optionWs = ws;
11523
11538
  }
11539
+ else {
11540
+ this.cryptoWs = ws;
11541
+ }
11524
11542
  ws.on('open', () => {
11525
11543
  log(`${streamType} stream connected`, { type: 'info' });
11526
11544
  const authMessage = {
@@ -11531,20 +11549,36 @@ class AlpacaMarketDataAPI extends EventEmitter {
11531
11549
  ws.send(JSON.stringify(authMessage));
11532
11550
  });
11533
11551
  ws.on('message', (data) => {
11534
- //log(`RAW MESSASGE: ${data.toString()}`);
11535
- const messages = JSON.parse(data.toString());
11552
+ const rawData = data.toString();
11553
+ let messages;
11554
+ try {
11555
+ messages = JSON.parse(rawData);
11556
+ }
11557
+ catch (e) {
11558
+ log(`${streamType} stream received invalid JSON: ${rawData.substring(0, 200)}`, { type: 'error' });
11559
+ return;
11560
+ }
11536
11561
  for (const message of messages) {
11537
11562
  if (message.T === 'success' && message.msg === 'authenticated') {
11538
11563
  log(`${streamType} stream authenticated`, { type: 'info' });
11539
11564
  this.sendSubscription(streamType);
11540
11565
  }
11566
+ else if (message.T === 'success' && message.msg === 'connected') {
11567
+ log(`${streamType} stream connected message received`, { type: 'debug' });
11568
+ }
11569
+ else if (message.T === 'subscription') {
11570
+ log(`${streamType} subscription confirmed: trades=${message.trades?.length || 0}, quotes=${message.quotes?.length || 0}, bars=${message.bars?.length || 0}`, { type: 'info' });
11571
+ }
11541
11572
  else if (message.T === 'error') {
11542
- log(`${streamType} stream error: ${message.msg}`, { type: 'error' });
11573
+ log(`${streamType} stream error: ${message.msg} (code: ${message.code}, raw: ${JSON.stringify(message)})`, { type: 'error' });
11543
11574
  }
11544
11575
  else if (message.S) {
11545
11576
  super.emit(`${streamType}-${message.T}`, message);
11546
11577
  super.emit(`${streamType}-data`, message);
11547
11578
  }
11579
+ else {
11580
+ log(`${streamType} received unknown message type: ${JSON.stringify(message)}`, { type: 'debug' });
11581
+ }
11548
11582
  }
11549
11583
  });
11550
11584
  ws.on('close', () => {
@@ -11552,9 +11586,12 @@ class AlpacaMarketDataAPI extends EventEmitter {
11552
11586
  if (streamType === 'stock') {
11553
11587
  this.stockWs = null;
11554
11588
  }
11555
- else {
11589
+ else if (streamType === 'option') {
11556
11590
  this.optionWs = null;
11557
11591
  }
11592
+ else {
11593
+ this.cryptoWs = null;
11594
+ }
11558
11595
  // Optional: implement reconnect logic
11559
11596
  });
11560
11597
  ws.on('error', (error) => {
@@ -11562,8 +11599,23 @@ class AlpacaMarketDataAPI extends EventEmitter {
11562
11599
  });
11563
11600
  }
11564
11601
  sendSubscription(streamType) {
11565
- const ws = streamType === 'stock' ? this.stockWs : this.optionWs;
11566
- const subscriptions = streamType === 'stock' ? this.stockSubscriptions : this.optionSubscriptions;
11602
+ let ws;
11603
+ let subscriptions;
11604
+ if (streamType === 'stock') {
11605
+ ws = this.stockWs;
11606
+ subscriptions = this.stockSubscriptions;
11607
+ }
11608
+ else if (streamType === 'option') {
11609
+ ws = this.optionWs;
11610
+ subscriptions = this.optionSubscriptions;
11611
+ }
11612
+ else {
11613
+ ws = this.cryptoWs;
11614
+ subscriptions = this.cryptoSubscriptions;
11615
+ }
11616
+ log(`sendSubscription called for ${streamType} (wsReady=${ws?.readyState === WebSocket.OPEN}, trades=${subscriptions.trades?.length || 0}, quotes=${subscriptions.quotes?.length || 0}, bars=${subscriptions.bars?.length || 0})`, {
11617
+ type: 'debug',
11618
+ });
11567
11619
  if (ws && ws.readyState === WebSocket.OPEN) {
11568
11620
  const subMessagePayload = {};
11569
11621
  if (subscriptions.trades.length > 0) {
@@ -11580,8 +11632,16 @@ class AlpacaMarketDataAPI extends EventEmitter {
11580
11632
  action: 'subscribe',
11581
11633
  ...subMessagePayload,
11582
11634
  };
11583
- ws.send(JSON.stringify(subMessage));
11635
+ const messageJson = JSON.stringify(subMessage);
11636
+ log(`Sending ${streamType} subscription: ${messageJson}`, { type: 'info' });
11637
+ ws.send(messageJson);
11584
11638
  }
11639
+ else {
11640
+ log(`No ${streamType} subscriptions to send (all arrays empty)`, { type: 'debug' });
11641
+ }
11642
+ }
11643
+ else {
11644
+ log(`Cannot send ${streamType} subscription: WebSocket not ready`, { type: 'warn' });
11585
11645
  }
11586
11646
  }
11587
11647
  connectStockStream() {
@@ -11594,6 +11654,11 @@ class AlpacaMarketDataAPI extends EventEmitter {
11594
11654
  this.connect('option');
11595
11655
  }
11596
11656
  }
11657
+ connectCryptoStream() {
11658
+ if (!this.cryptoWs) {
11659
+ this.connect('crypto');
11660
+ }
11661
+ }
11597
11662
  disconnectStockStream() {
11598
11663
  if (this.stockWs) {
11599
11664
  this.stockWs.close();
@@ -11604,8 +11669,38 @@ class AlpacaMarketDataAPI extends EventEmitter {
11604
11669
  this.optionWs.close();
11605
11670
  }
11606
11671
  }
11672
+ disconnectCryptoStream() {
11673
+ if (this.cryptoWs) {
11674
+ this.cryptoWs.close();
11675
+ }
11676
+ }
11677
+ /**
11678
+ * Check if a specific stream is connected
11679
+ * @param streamType - The type of stream to check
11680
+ * @returns True if the stream is connected
11681
+ */
11682
+ isStreamConnected(streamType) {
11683
+ if (streamType === 'stock') {
11684
+ return this.stockWs !== null && this.stockWs.readyState === WebSocket.OPEN;
11685
+ }
11686
+ else if (streamType === 'option') {
11687
+ return this.optionWs !== null && this.optionWs.readyState === WebSocket.OPEN;
11688
+ }
11689
+ else {
11690
+ return this.cryptoWs !== null && this.cryptoWs.readyState === WebSocket.OPEN;
11691
+ }
11692
+ }
11607
11693
  subscribe(streamType, subscriptions) {
11608
- const currentSubscriptions = streamType === 'stock' ? this.stockSubscriptions : this.optionSubscriptions;
11694
+ let currentSubscriptions;
11695
+ if (streamType === 'stock') {
11696
+ currentSubscriptions = this.stockSubscriptions;
11697
+ }
11698
+ else if (streamType === 'option') {
11699
+ currentSubscriptions = this.optionSubscriptions;
11700
+ }
11701
+ else {
11702
+ currentSubscriptions = this.cryptoSubscriptions;
11703
+ }
11609
11704
  Object.entries(subscriptions).forEach(([key, value]) => {
11610
11705
  if (value) {
11611
11706
  currentSubscriptions[key] = [...new Set([...(currentSubscriptions[key] || []), ...value])];
@@ -11614,7 +11709,16 @@ class AlpacaMarketDataAPI extends EventEmitter {
11614
11709
  this.sendSubscription(streamType);
11615
11710
  }
11616
11711
  unsubscribe(streamType, subscriptions) {
11617
- const currentSubscriptions = streamType === 'stock' ? this.stockSubscriptions : this.optionSubscriptions;
11712
+ let currentSubscriptions;
11713
+ if (streamType === 'stock') {
11714
+ currentSubscriptions = this.stockSubscriptions;
11715
+ }
11716
+ else if (streamType === 'option') {
11717
+ currentSubscriptions = this.optionSubscriptions;
11718
+ }
11719
+ else {
11720
+ currentSubscriptions = this.cryptoSubscriptions;
11721
+ }
11618
11722
  Object.entries(subscriptions).forEach(([key, value]) => {
11619
11723
  if (value) {
11620
11724
  currentSubscriptions[key] = (currentSubscriptions[key] || []).filter(s => !value.includes(s));
@@ -11624,7 +11728,16 @@ class AlpacaMarketDataAPI extends EventEmitter {
11624
11728
  action: 'unsubscribe',
11625
11729
  ...subscriptions,
11626
11730
  };
11627
- const ws = streamType === 'stock' ? this.stockWs : this.optionWs;
11731
+ let ws;
11732
+ if (streamType === 'stock') {
11733
+ ws = this.stockWs;
11734
+ }
11735
+ else if (streamType === 'option') {
11736
+ ws = this.optionWs;
11737
+ }
11738
+ else {
11739
+ ws = this.cryptoWs;
11740
+ }
11628
11741
  if (ws && ws.readyState === WebSocket.OPEN) {
11629
11742
  ws.send(JSON.stringify(unsubMessage));
11630
11743
  }