@discomedia/utils 1.0.51 → 1.0.53

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
@@ -967,10 +967,13 @@ function countTradingDays(startDate, endDate = new Date()) {
967
967
  /**
968
968
  * Returns the trading day N days back from a reference date, along with its market open time.
969
969
  * Trading days are counted as full or half trading days (days that end count as 1 full trading day).
970
+ * By default, the most recent completed trading day counts as day 1.
971
+ * Set includeMostRecentFullDay to false to count strictly before that day.
970
972
  *
971
973
  * @param options - Object with:
972
974
  * - referenceDate: Date to count back from (default: now)
973
- * - days: Number of trading days to go back (must be >= 1)
975
+ * - days: Number of trading days to go back (must be an integer >= 1)
976
+ * - includeMostRecentFullDay: Whether to include the most recent completed trading day (default: true)
974
977
  * @returns Object containing:
975
978
  * - date: Trading date in YYYY-MM-DD format
976
979
  * - marketOpenISO: Market open time as ISO string (e.g., "2025-11-15T13:30:00.000Z")
@@ -992,13 +995,17 @@ function countTradingDays(startDate, endDate = new Date()) {
992
995
  */
993
996
  function getTradingDaysBack(options) {
994
997
  const calendar = new MarketCalendar();
995
- const refDate = options.referenceDate || new Date();
996
- const daysBack = options.days;
997
- if (daysBack < 1) {
998
- throw new Error('days must be at least 1');
998
+ const { referenceDate, days, includeMostRecentFullDay = true } = options;
999
+ const refDate = referenceDate || new Date();
1000
+ const daysBack = days;
1001
+ if (!Number.isInteger(daysBack) || daysBack < 1) {
1002
+ throw new Error('days must be an integer >= 1');
999
1003
  }
1000
1004
  // Start from the last full trading date relative to reference
1001
1005
  let targetDate = getLastFullTradingDateImpl(refDate);
1006
+ if (!includeMostRecentFullDay) {
1007
+ targetDate = calendar.getPreviousMarketDay(targetDate);
1008
+ }
1002
1009
  // Go back the specified number of days (we're already at day 1, so go back days-1 more)
1003
1010
  for (let i = 1; i < daysBack; i++) {
1004
1011
  targetDate = calendar.getPreviousMarketDay(targetDate);
@@ -11374,6 +11381,7 @@ function requireConstants () {
11374
11381
 
11375
11382
  constants = {
11376
11383
  BINARY_TYPES,
11384
+ CLOSE_TIMEOUT: 30000,
11377
11385
  EMPTY_BUFFER: Buffer.alloc(0),
11378
11386
  GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
11379
11387
  hasBlob,
@@ -14144,6 +14152,7 @@ function requireWebsocket () {
14144
14152
 
14145
14153
  const {
14146
14154
  BINARY_TYPES,
14155
+ CLOSE_TIMEOUT,
14147
14156
  EMPTY_BUFFER,
14148
14157
  GUID,
14149
14158
  kForOnEventAttribute,
@@ -14158,7 +14167,6 @@ function requireWebsocket () {
14158
14167
  const { format, parse } = requireExtension();
14159
14168
  const { toBuffer } = requireBufferUtil();
14160
14169
 
14161
- const closeTimeout = 30 * 1000;
14162
14170
  const kAborted = Symbol('kAborted');
14163
14171
  const protocolVersions = [8, 13];
14164
14172
  const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
@@ -14214,6 +14222,7 @@ function requireWebsocket () {
14214
14222
  initAsClient(this, address, protocols, options);
14215
14223
  } else {
14216
14224
  this._autoPong = options.autoPong;
14225
+ this._closeTimeout = options.closeTimeout;
14217
14226
  this._isServer = true;
14218
14227
  }
14219
14228
  }
@@ -14755,6 +14764,8 @@ function requireWebsocket () {
14755
14764
  * times in the same tick
14756
14765
  * @param {Boolean} [options.autoPong=true] Specifies whether or not to
14757
14766
  * automatically send a pong in response to a ping
14767
+ * @param {Number} [options.closeTimeout=30000] Duration in milliseconds to wait
14768
+ * for the closing handshake to finish after `websocket.close()` is called
14758
14769
  * @param {Function} [options.finishRequest] A function which can be used to
14759
14770
  * customize the headers of each http request before it is sent
14760
14771
  * @param {Boolean} [options.followRedirects=false] Whether or not to follow
@@ -14781,6 +14792,7 @@ function requireWebsocket () {
14781
14792
  const opts = {
14782
14793
  allowSynchronousEvents: true,
14783
14794
  autoPong: true,
14795
+ closeTimeout: CLOSE_TIMEOUT,
14784
14796
  protocolVersion: protocolVersions[1],
14785
14797
  maxPayload: 100 * 1024 * 1024,
14786
14798
  skipUTF8Validation: false,
@@ -14799,6 +14811,7 @@ function requireWebsocket () {
14799
14811
  };
14800
14812
 
14801
14813
  websocket._autoPong = opts.autoPong;
14814
+ websocket._closeTimeout = opts.closeTimeout;
14802
14815
 
14803
14816
  if (!protocolVersions.includes(opts.protocolVersion)) {
14804
14817
  throw new RangeError(
@@ -15416,7 +15429,7 @@ function requireWebsocket () {
15416
15429
  function setCloseTimer(websocket) {
15417
15430
  websocket._closeTimer = setTimeout(
15418
15431
  websocket._socket.destroy.bind(websocket._socket),
15419
- closeTimeout
15432
+ websocket._closeTimeout
15420
15433
  );
15421
15434
  }
15422
15435
 
@@ -15434,23 +15447,23 @@ function requireWebsocket () {
15434
15447
 
15435
15448
  websocket._readyState = WebSocket.CLOSING;
15436
15449
 
15437
- let chunk;
15438
-
15439
15450
  //
15440
15451
  // The close frame might not have been received or the `'end'` event emitted,
15441
15452
  // for example, if the socket was destroyed due to an error. Ensure that the
15442
15453
  // `receiver` stream is closed after writing any remaining buffered data to
15443
15454
  // it. If the readable side of the socket is in flowing mode then there is no
15444
- // buffered data as everything has been already written and `readable.read()`
15445
- // will return `null`. If instead, the socket is paused, any possible buffered
15446
- // data will be read as a single chunk.
15455
+ // buffered data as everything has been already written. If instead, the
15456
+ // socket is paused, any possible buffered data will be read as a single
15457
+ // chunk.
15447
15458
  //
15448
15459
  if (
15449
15460
  !this._readableState.endEmitted &&
15450
15461
  !websocket._closeFrameReceived &&
15451
15462
  !websocket._receiver._writableState.errorEmitted &&
15452
- (chunk = websocket._socket.read()) !== null
15463
+ this._readableState.length !== 0
15453
15464
  ) {
15465
+ const chunk = this.read(this._readableState.length);
15466
+
15454
15467
  websocket._receiver.write(chunk);
15455
15468
  }
15456
15469
 
@@ -15782,7 +15795,7 @@ function requireWebsocketServer () {
15782
15795
  const PerMessageDeflate = requirePermessageDeflate();
15783
15796
  const subprotocol = requireSubprotocol();
15784
15797
  const WebSocket = requireWebsocket();
15785
- const { GUID, kWebSocket } = requireConstants();
15798
+ const { CLOSE_TIMEOUT, GUID, kWebSocket } = requireConstants();
15786
15799
 
15787
15800
  const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
15788
15801
 
@@ -15809,6 +15822,9 @@ function requireWebsocketServer () {
15809
15822
  * pending connections
15810
15823
  * @param {Boolean} [options.clientTracking=true] Specifies whether or not to
15811
15824
  * track clients
15825
+ * @param {Number} [options.closeTimeout=30000] Duration in milliseconds to
15826
+ * wait for the closing handshake to finish after `websocket.close()` is
15827
+ * called
15812
15828
  * @param {Function} [options.handleProtocols] A hook to handle protocols
15813
15829
  * @param {String} [options.host] The hostname where to bind the server
15814
15830
  * @param {Number} [options.maxPayload=104857600] The maximum allowed message
@@ -15838,6 +15854,7 @@ function requireWebsocketServer () {
15838
15854
  perMessageDeflate: false,
15839
15855
  handleProtocols: null,
15840
15856
  clientTracking: true,
15857
+ closeTimeout: CLOSE_TIMEOUT,
15841
15858
  verifyClient: null,
15842
15859
  noServer: false,
15843
15860
  backlog: null, // use default (511 as implemented in net.js)
@@ -18457,6 +18474,7 @@ class AlpacaTradingAPI {
18457
18474
  * @param qty (number) - the quantity of the order
18458
18475
  * @param side (string) - the side of the order
18459
18476
  * @param position_intent (string) - the position intent of the order. Important for knowing if a position needs a trailing stop.
18477
+ * @param client_order_id (string) - optional client order id
18460
18478
  */
18461
18479
  async createMarketOrder(symbol, qty, side, position_intent, client_order_id) {
18462
18480
  this.log(`Creating market order for ${symbol}: ${side} ${qty} shares (${position_intent})`, {
@@ -18482,6 +18500,86 @@ class AlpacaTradingAPI {
18482
18500
  throw error;
18483
18501
  }
18484
18502
  }
18503
+ /**
18504
+ * Create a Market on Open (MOO) order - executes in the opening auction
18505
+ *
18506
+ * IMPORTANT TIMING CONSTRAINTS:
18507
+ * - Valid submission window: After 7:00pm ET and before 9:28am ET
18508
+ * - Orders submitted between 9:28am and 7:00pm ET will be REJECTED
18509
+ * - Orders submitted after 7:00pm ET are queued for the next trading day's opening auction
18510
+ * - Example: An order at 8:00pm Monday will execute at Tuesday's market open (9:30am)
18511
+ *
18512
+ * @param symbol - The symbol of the order
18513
+ * @param qty - The quantity of shares
18514
+ * @param side - Buy or sell
18515
+ * @param position_intent - The position intent (buy_to_open, sell_to_close, etc.)
18516
+ * @param client_order_id - Optional client order id
18517
+ * @returns The created order
18518
+ */
18519
+ async createMOOOrder(symbol, qty, side, position_intent, client_order_id) {
18520
+ this.log(`Creating Market on Open order for ${symbol}: ${side} ${qty} shares (${position_intent})`, {
18521
+ symbol,
18522
+ });
18523
+ const body = {
18524
+ symbol,
18525
+ qty: Math.abs(qty).toString(),
18526
+ side,
18527
+ position_intent,
18528
+ type: 'market',
18529
+ time_in_force: 'opg',
18530
+ order_class: 'simple',
18531
+ };
18532
+ if (client_order_id !== undefined) {
18533
+ body.client_order_id = client_order_id;
18534
+ }
18535
+ try {
18536
+ return await this.makeRequest('/orders', 'POST', body);
18537
+ }
18538
+ catch (error) {
18539
+ this.log(`Error creating MOO order: ${error}`, { type: 'error' });
18540
+ throw error;
18541
+ }
18542
+ }
18543
+ /**
18544
+ * Create a Market on Close (MOC) order - executes in the closing auction
18545
+ *
18546
+ * IMPORTANT TIMING CONSTRAINTS:
18547
+ * - Valid submission window: After 7:00pm ET (previous day) and before 3:50pm ET (same day)
18548
+ * - Orders submitted between 3:50pm and 7:00pm ET will be REJECTED
18549
+ * - Orders submitted after 7:00pm ET are queued for the next trading day's closing auction
18550
+ * - Example: An order at 8:00pm Monday will execute at Tuesday's market close (4:00pm)
18551
+ *
18552
+ * @param symbol - The symbol of the order
18553
+ * @param qty - The quantity of shares
18554
+ * @param side - Buy or sell
18555
+ * @param position_intent - The position intent (buy_to_open, sell_to_close, etc.)
18556
+ * @param client_order_id - Optional client order id
18557
+ * @returns The created order
18558
+ */
18559
+ async createMOCOrder(symbol, qty, side, position_intent, client_order_id) {
18560
+ this.log(`Creating Market on Close order for ${symbol}: ${side} ${qty} shares (${position_intent})`, {
18561
+ symbol,
18562
+ });
18563
+ const body = {
18564
+ symbol,
18565
+ qty: Math.abs(qty).toString(),
18566
+ side,
18567
+ position_intent,
18568
+ type: 'market',
18569
+ time_in_force: 'cls',
18570
+ order_class: 'simple',
18571
+ };
18572
+ if (client_order_id !== undefined) {
18573
+ body.client_order_id = client_order_id;
18574
+ }
18575
+ try {
18576
+ return await this.makeRequest('/orders', 'POST', body);
18577
+ }
18578
+ catch (error) {
18579
+ this.log(`Error creating MOC order: ${error}`, { type: 'error' });
18580
+ throw error;
18581
+ }
18582
+ }
18485
18583
  /**
18486
18584
  * Get the current trail percent for a symbol, assuming that it has an open position and a trailing stop order to close it. Because this relies on an orders request for one symbol, you can't do it too often.
18487
18585
  * @param symbol (string) - the symbol of the order