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