@discomedia/utils 1.0.63 → 1.0.64

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/test.js CHANGED
@@ -1614,7 +1614,7 @@ const safeJSON = (text) => {
1614
1614
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
1615
1615
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1616
1616
 
1617
- const VERSION = '6.27.0'; // x-release-please-version
1617
+ const VERSION = '6.31.0'; // x-release-please-version
1618
1618
 
1619
1619
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
1620
1620
  const isRunningInBrowser = () => {
@@ -4858,7 +4858,7 @@ class Speech extends APIResource {
4858
4858
  * const speech = await client.audio.speech.create({
4859
4859
  * input: 'input',
4860
4860
  * model: 'string',
4861
- * voice: 'ash',
4861
+ * voice: 'string',
4862
4862
  * });
4863
4863
  *
4864
4864
  * const content = await speech.blob();
@@ -8139,7 +8139,7 @@ class Videos extends APIResource {
8139
8139
  * Create a new video generation job from a prompt and optional reference assets.
8140
8140
  */
8141
8141
  create(body, options) {
8142
- return this._client.post('/videos', maybeMultipartFormRequestOptions({ body, ...options }, this._client));
8142
+ return this._client.post('/videos', multipartFormRequestOptions({ body, ...options }, this._client));
8143
8143
  }
8144
8144
  /**
8145
8145
  * Fetch the latest metadata for a generated video.
@@ -8159,6 +8159,12 @@ class Videos extends APIResource {
8159
8159
  delete(videoID, options) {
8160
8160
  return this._client.delete(path `/videos/${videoID}`, options);
8161
8161
  }
8162
+ /**
8163
+ * Create a character from an uploaded video.
8164
+ */
8165
+ createCharacter(body, options) {
8166
+ return this._client.post('/videos/characters', multipartFormRequestOptions({ body, ...options }, this._client));
8167
+ }
8162
8168
  /**
8163
8169
  * Download the generated video bytes or a derived preview asset.
8164
8170
  *
@@ -8172,6 +8178,25 @@ class Videos extends APIResource {
8172
8178
  __binaryResponse: true,
8173
8179
  });
8174
8180
  }
8181
+ /**
8182
+ * Create a new video generation job by editing a source video or existing
8183
+ * generated video.
8184
+ */
8185
+ edit(body, options) {
8186
+ return this._client.post('/videos/edits', multipartFormRequestOptions({ body, ...options }, this._client));
8187
+ }
8188
+ /**
8189
+ * Create an extension of a completed video.
8190
+ */
8191
+ extend(body, options) {
8192
+ return this._client.post('/videos/extensions', multipartFormRequestOptions({ body, ...options }, this._client));
8193
+ }
8194
+ /**
8195
+ * Fetch a character.
8196
+ */
8197
+ getCharacter(characterID, options) {
8198
+ return this._client.get(path `/videos/characters/${characterID}`, options);
8199
+ }
8175
8200
  /**
8176
8201
  * Create a remix of a completed video using a refreshed prompt.
8177
8202
  */
@@ -8453,8 +8478,9 @@ class OpenAI {
8453
8478
  new URL(path)
8454
8479
  : new URL(baseURL + (baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path));
8455
8480
  const defaultQuery = this.defaultQuery();
8456
- if (!isEmptyObj$1(defaultQuery)) {
8457
- query = { ...defaultQuery, ...query };
8481
+ const pathQuery = Object.fromEntries(url.searchParams);
8482
+ if (!isEmptyObj$1(defaultQuery) || !isEmptyObj$1(pathQuery)) {
8483
+ query = { ...pathQuery, ...defaultQuery, ...query };
8458
8484
  }
8459
8485
  if (typeof query === 'object' && query && !Array.isArray(query)) {
8460
8486
  url.search = this.stringifyQuery(query);
@@ -22297,7 +22323,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
22297
22323
  */
22298
22324
  async getHistoricalBars(params) {
22299
22325
  const symbols = params.symbols;
22300
- symbols.join(',');
22326
+ const symbolsStr = symbols.join(',');
22301
22327
  let allBars = {};
22302
22328
  let pageToken = null;
22303
22329
  let hasMorePages = true;
@@ -22308,7 +22334,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
22308
22334
  symbols.forEach((symbol) => {
22309
22335
  allBars[symbol] = [];
22310
22336
  });
22311
- log(`Starting historical bars fetch for ${symbols.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`);
22337
+ log(`Starting ${params.timeframe}bars fetch for ${symbols.length} symbols (${symbolsStr}), ${params.start || 'no start'} to ${params.end || 'no end'})`, { symbol: symbolsStr });
22312
22338
  while (hasMorePages) {
22313
22339
  pageCount++;
22314
22340
  const requestParams = {
@@ -22319,7 +22345,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
22319
22345
  };
22320
22346
  const response = await this.makeRequest('/stocks/bars', 'GET', requestParams);
22321
22347
  if (!response.bars) {
22322
- log(`No bars data found in response for ${symbols.length} symbols`, { type: 'warn' });
22348
+ log(`No bars data found in response for ${symbols.length} symbols`, { symbol: symbolsStr, type: 'warn' });
22323
22349
  break;
22324
22350
  }
22325
22351
  // Track currency from first response
@@ -22353,7 +22379,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
22353
22379
  const dateRangeStr = earliestTimestamp && latestTimestamp
22354
22380
  ? `${new Date(earliestTimestamp).toLocaleDateString('en-US', { timeZone: 'America/New_York' })} to ${new Date(latestTimestamp).toLocaleDateString('en-US', { timeZone: 'America/New_York' })}`
22355
22381
  : 'unknown range';
22356
- log(`Page ${pageCount}: Fetched ${pageBarsCount.toLocaleString()} bars (total: ${totalBarsCount.toLocaleString()}) for ${symbols.length} symbols, date range: ${dateRangeStr}${hasMorePages ? ', more pages available' : ', complete'}`, { type: 'debug' });
22382
+ log(`Page ${pageCount}: Fetched ${pageBarsCount.toLocaleString()} bars (total: ${totalBarsCount.toLocaleString()}) for ${symbols.length} symbols, date range: ${dateRangeStr}${hasMorePages ? ', more pages available' : ', complete'}`, { symbol: symbolsStr, type: 'debug' });
22357
22383
  // Prevent infinite loops
22358
22384
  if (pageCount > 1000) {
22359
22385
  log(`Stopping pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
@@ -22364,7 +22390,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
22364
22390
  const symbolCounts = Object.entries(allBars)
22365
22391
  .map(([symbol, bars]) => `${symbol}: ${bars.length}`)
22366
22392
  .join(', ');
22367
- log(`Bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages for ${symbols.length} symbols (${symbolCounts})`);
22393
+ log(`${params.timeframe} bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages for ${symbols.length} symbols (${symbolCounts})`, { symbol: symbolsStr });
22368
22394
  return {
22369
22395
  bars: allBars,
22370
22396
  next_page_token: null, // Always null since we fetch all pages
@@ -23216,6 +23242,26 @@ class AlpacaMarketDataAPI extends EventEmitter {
23216
23242
  const marketDataAPI = AlpacaMarketDataAPI.getInstance();
23217
23243
 
23218
23244
  const limitPriceSlippagePercent100 = 0.1; // 0.1%
23245
+ class AlpacaRequestError extends Error {
23246
+ method;
23247
+ status;
23248
+ url;
23249
+ responseCode;
23250
+ responseDetails;
23251
+ rawResponse;
23252
+ symbol;
23253
+ constructor(params) {
23254
+ super(params.message);
23255
+ this.name = 'AlpacaRequestError';
23256
+ this.method = params.method;
23257
+ this.status = params.status ?? null;
23258
+ this.url = params.url;
23259
+ this.responseCode = params.responseCode ?? null;
23260
+ this.responseDetails = params.responseDetails ?? null;
23261
+ this.rawResponse = params.rawResponse ?? null;
23262
+ this.symbol = params.symbol;
23263
+ }
23264
+ }
23219
23265
  /**
23220
23266
  Websocket example
23221
23267
  const alpacaAPI = createAlpacaTradingAPI(credentials); // type AlpacaCredentials
@@ -23299,6 +23345,75 @@ class AlpacaTradingAPI {
23299
23345
  roundPriceForAlpaca = (price) => {
23300
23346
  return price >= 1 ? Math.round(price * 100) / 100 : Math.round(price * 10000) / 10000;
23301
23347
  };
23348
+ isJsonObject(value) {
23349
+ return value !== null && !Array.isArray(value) && typeof value === 'object';
23350
+ }
23351
+ parseAlpacaAPIErrorResponse(rawResponse) {
23352
+ if (rawResponse.trim() === '') {
23353
+ return null;
23354
+ }
23355
+ try {
23356
+ const parsedValue = JSON.parse(rawResponse);
23357
+ if (!this.isJsonObject(parsedValue)) {
23358
+ return null;
23359
+ }
23360
+ const code = parsedValue['code'];
23361
+ const message = parsedValue['message'];
23362
+ const symbol = parsedValue['symbol'];
23363
+ if (typeof code !== 'number' || typeof message !== 'string') {
23364
+ return null;
23365
+ }
23366
+ if (symbol !== undefined && typeof symbol !== 'string') {
23367
+ return null;
23368
+ }
23369
+ return parsedValue;
23370
+ }
23371
+ catch {
23372
+ return null;
23373
+ }
23374
+ }
23375
+ formatJsonValueForLog(value) {
23376
+ if (value === undefined) {
23377
+ return 'undefined';
23378
+ }
23379
+ if (Array.isArray(value)) {
23380
+ return value.map((entry) => this.formatJsonValueForLog(entry)).join(', ');
23381
+ }
23382
+ if (value === null) {
23383
+ return 'null';
23384
+ }
23385
+ if (typeof value === 'object') {
23386
+ return JSON.stringify(value);
23387
+ }
23388
+ return `${value}`;
23389
+ }
23390
+ formatAlpacaAPIErrorDetails(errorResponse) {
23391
+ return Object.entries(errorResponse)
23392
+ .filter(([key]) => key !== 'code' && key !== 'message' && key !== 'symbol')
23393
+ .map(([key, value]) => `${key}=${this.formatJsonValueForLog(value)}`)
23394
+ .join(', ');
23395
+ }
23396
+ formatRequestAction(requestContext) {
23397
+ return requestContext?.action ? `${requestContext.action} failed` : 'Alpaca request failed';
23398
+ }
23399
+ buildAlpacaAPIErrorMessage(params) {
23400
+ const { requestContext, status, url, errorResponse, rawResponse } = params;
23401
+ const action = this.formatRequestAction(requestContext);
23402
+ const message = errorResponse?.message ?? (rawResponse || 'No error body returned');
23403
+ const responseCode = errorResponse?.code ? ` (code ${errorResponse.code})` : '';
23404
+ const detailSummary = errorResponse ? this.formatAlpacaAPIErrorDetails(errorResponse) : '';
23405
+ const detailText = detailSummary !== ''
23406
+ ? ` Details: ${detailSummary}.`
23407
+ : rawResponse.trim() !== '' && errorResponse === null
23408
+ ? ` Response: ${rawResponse}.`
23409
+ : '';
23410
+ return `${action}: Alpaca API ${status}${responseCode}: ${message}.${detailText} Url: ${url}`;
23411
+ }
23412
+ buildRequestExecutionErrorMessage(params) {
23413
+ const { requestContext, url, errorMessage } = params;
23414
+ const action = this.formatRequestAction(requestContext);
23415
+ return `${action}: ${errorMessage}. Url: ${url}`;
23416
+ }
23302
23417
  handleAuthMessage(data) {
23303
23418
  if (data.status === 'authorized') {
23304
23419
  this.authenticated = true;
@@ -23526,7 +23641,7 @@ class AlpacaTradingAPI {
23526
23641
  this.ws?.on('message', handleListenResponse);
23527
23642
  });
23528
23643
  }
23529
- async makeRequest(endpoint, method = 'GET', body, queryString = '') {
23644
+ async makeRequest(endpoint, method = 'GET', body, queryString = '', requestContext) {
23530
23645
  const url = `${this.apiBaseUrl}${endpoint}${queryString}`;
23531
23646
  try {
23532
23647
  const response = await fetch(url, {
@@ -23535,9 +23650,27 @@ class AlpacaTradingAPI {
23535
23650
  body: body ? JSON.stringify(body) : undefined,
23536
23651
  });
23537
23652
  if (!response.ok) {
23538
- const errorText = await response.text();
23539
- this.log(`Alpaca API error (${response.status}): ${errorText}`, { type: 'error' });
23540
- throw new Error(`Alpaca API error (${response.status}): ${errorText}`);
23653
+ const rawResponse = await response.text();
23654
+ const errorResponse = this.parseAlpacaAPIErrorResponse(rawResponse);
23655
+ const symbol = requestContext?.symbol ?? errorResponse?.symbol;
23656
+ const error = new AlpacaRequestError({
23657
+ message: this.buildAlpacaAPIErrorMessage({
23658
+ requestContext,
23659
+ status: response.status,
23660
+ url,
23661
+ errorResponse,
23662
+ rawResponse,
23663
+ }),
23664
+ method,
23665
+ status: response.status,
23666
+ url,
23667
+ responseCode: errorResponse?.code ?? null,
23668
+ responseDetails: errorResponse,
23669
+ rawResponse,
23670
+ symbol,
23671
+ });
23672
+ this.log(error.message, { symbol, type: 'error' });
23673
+ throw error;
23541
23674
  }
23542
23675
  // Handle responses with no content (e.g., 204 No Content)
23543
23676
  if (response.status === 204 || response.headers.get('content-length') === '0') {
@@ -23545,23 +23678,37 @@ class AlpacaTradingAPI {
23545
23678
  }
23546
23679
  const contentType = response.headers.get('content-type');
23547
23680
  if (contentType && contentType.includes('application/json')) {
23548
- return await response.json();
23681
+ return (await response.json());
23549
23682
  }
23550
23683
  // For non-JSON responses, return the text content
23551
23684
  const textContent = await response.text();
23552
- return textContent || null;
23685
+ return (textContent || null);
23553
23686
  }
23554
- catch (err) {
23555
- const error = err;
23556
- this.log(`Error in makeRequest: ${error.message}. Url: ${url}`, {
23557
- source: 'AlpacaAPI',
23558
- type: 'error',
23687
+ catch (error) {
23688
+ if (error instanceof AlpacaRequestError) {
23689
+ throw error;
23690
+ }
23691
+ const normalizedError = error instanceof Error ? error : new Error(`${error}`);
23692
+ const symbol = requestContext?.symbol;
23693
+ const requestError = new AlpacaRequestError({
23694
+ message: this.buildRequestExecutionErrorMessage({
23695
+ requestContext,
23696
+ url,
23697
+ errorMessage: normalizedError.message,
23698
+ }),
23699
+ method,
23700
+ url,
23701
+ rawResponse: null,
23702
+ symbol,
23559
23703
  });
23560
- throw error;
23704
+ this.log(requestError.message, { symbol, type: 'error' });
23705
+ throw requestError;
23561
23706
  }
23562
23707
  }
23563
23708
  async getPositions(assetClass) {
23564
- const positions = (await this.makeRequest('/positions'));
23709
+ const positions = await this.makeRequest('/positions', 'GET', undefined, '', {
23710
+ action: 'Get positions',
23711
+ });
23565
23712
  if (assetClass) {
23566
23713
  return positions.filter((position) => position.asset_class === assetClass);
23567
23714
  }
@@ -23599,22 +23746,15 @@ class AlpacaTradingAPI {
23599
23746
  if (params.side)
23600
23747
  queryParams.append('side', params.side);
23601
23748
  const endpoint = `/orders${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;
23602
- try {
23603
- return await this.makeRequest(endpoint);
23604
- }
23605
- catch (error) {
23606
- this.log(`Error getting orders: ${error}`, { type: 'error' });
23607
- throw error;
23608
- }
23749
+ return this.makeRequest(endpoint, 'GET', undefined, '', {
23750
+ action: 'Get orders',
23751
+ symbol: params.symbols?.join(','),
23752
+ });
23609
23753
  }
23610
23754
  async getAccountDetails() {
23611
- try {
23612
- return await this.makeRequest('/account');
23613
- }
23614
- catch (error) {
23615
- this.log(`Error getting account details: ${error}`, { type: 'error' });
23616
- throw error;
23617
- }
23755
+ return this.makeRequest('/account', 'GET', undefined, '', {
23756
+ action: 'Get account details',
23757
+ });
23618
23758
  }
23619
23759
  /**
23620
23760
  * Create a trailing stop order
@@ -23632,27 +23772,21 @@ class AlpacaTradingAPI {
23632
23772
  });
23633
23773
  const body = {
23634
23774
  symbol,
23635
- qty: Math.abs(qty),
23775
+ qty: Math.abs(qty).toString(),
23636
23776
  side,
23637
23777
  position_intent,
23638
23778
  order_class: 'simple',
23639
23779
  type: 'trailing_stop',
23640
- trail_percent: trailPercent100, // Already in decimal form (e.g., 4 for 4%)
23780
+ trail_percent: trailPercent100.toString(),
23641
23781
  time_in_force: 'gtc',
23642
23782
  };
23643
23783
  if (client_order_id !== undefined) {
23644
23784
  body.client_order_id = client_order_id;
23645
23785
  }
23646
- try {
23647
- return await this.makeRequest(`/orders`, 'POST', body);
23648
- }
23649
- catch (error) {
23650
- this.log(`Error creating trailing stop: ${error}`, {
23651
- symbol,
23652
- type: 'error',
23653
- });
23654
- throw error;
23655
- }
23786
+ return this.makeRequest('/orders', 'POST', body, '', {
23787
+ action: 'Create trailing stop order',
23788
+ symbol,
23789
+ });
23656
23790
  }
23657
23791
  /**
23658
23792
  * Create a stop order (stop or stop-limit)
@@ -23678,25 +23812,19 @@ class AlpacaTradingAPI {
23678
23812
  position_intent,
23679
23813
  order_class: 'simple',
23680
23814
  type: isStopLimit ? 'stop_limit' : 'stop',
23681
- stop_price: this.roundPriceForAlpaca(stopPrice),
23815
+ stop_price: this.roundPriceForAlpaca(stopPrice).toString(),
23682
23816
  time_in_force: 'gtc',
23683
23817
  };
23684
- if (isStopLimit) {
23685
- body.limit_price = this.roundPriceForAlpaca(limitPrice);
23818
+ if (limitPrice !== undefined) {
23819
+ body.limit_price = this.roundPriceForAlpaca(limitPrice).toString();
23686
23820
  }
23687
23821
  if (client_order_id !== undefined) {
23688
23822
  body.client_order_id = client_order_id;
23689
23823
  }
23690
- try {
23691
- return await this.makeRequest(`/orders`, 'POST', body);
23692
- }
23693
- catch (error) {
23694
- this.log(`Error creating ${orderType} order: ${error}`, {
23695
- symbol,
23696
- type: 'error',
23697
- });
23698
- throw error;
23699
- }
23824
+ return this.makeRequest('/orders', 'POST', body, '', {
23825
+ action: `Create ${orderType} order`,
23826
+ symbol,
23827
+ });
23700
23828
  }
23701
23829
  /**
23702
23830
  * Create a market order
@@ -23722,13 +23850,10 @@ class AlpacaTradingAPI {
23722
23850
  if (client_order_id !== undefined) {
23723
23851
  body.client_order_id = client_order_id;
23724
23852
  }
23725
- try {
23726
- return await this.makeRequest('/orders', 'POST', body);
23727
- }
23728
- catch (error) {
23729
- this.log(`Error creating market order: ${error}`, { type: 'error' });
23730
- throw error;
23731
- }
23853
+ return this.makeRequest('/orders', 'POST', body, '', {
23854
+ action: 'Create market order',
23855
+ symbol,
23856
+ });
23732
23857
  }
23733
23858
  /**
23734
23859
  * Create a Market on Open (MOO) order - executes in the opening auction
@@ -23762,13 +23887,10 @@ class AlpacaTradingAPI {
23762
23887
  if (client_order_id !== undefined) {
23763
23888
  body.client_order_id = client_order_id;
23764
23889
  }
23765
- try {
23766
- return await this.makeRequest('/orders', 'POST', body);
23767
- }
23768
- catch (error) {
23769
- this.log(`Error creating MOO order: ${error}`, { type: 'error' });
23770
- throw error;
23771
- }
23890
+ return this.makeRequest('/orders', 'POST', body, '', {
23891
+ action: 'Create market on open order',
23892
+ symbol,
23893
+ });
23772
23894
  }
23773
23895
  /**
23774
23896
  * Create a Market on Close (MOC) order - executes in the closing auction
@@ -23802,13 +23924,10 @@ class AlpacaTradingAPI {
23802
23924
  if (client_order_id !== undefined) {
23803
23925
  body.client_order_id = client_order_id;
23804
23926
  }
23805
- try {
23806
- return await this.makeRequest('/orders', 'POST', body);
23807
- }
23808
- catch (error) {
23809
- this.log(`Error creating MOC order: ${error}`, { type: 'error' });
23810
- throw error;
23811
- }
23927
+ return this.makeRequest('/orders', 'POST', body, '', {
23928
+ action: 'Create market on close order',
23929
+ symbol,
23930
+ });
23812
23931
  }
23813
23932
  /**
23814
23933
  * Create an OCO (One-Cancels-Other) order with take profit and stop loss
@@ -23834,32 +23953,29 @@ class AlpacaTradingAPI {
23834
23953
  position_intent,
23835
23954
  order_class: 'oco',
23836
23955
  type: 'limit',
23837
- limit_price: this.roundPriceForAlpaca(limitPrice),
23956
+ limit_price: this.roundPriceForAlpaca(limitPrice).toString(),
23838
23957
  time_in_force: 'gtc',
23839
23958
  take_profit: {
23840
- limit_price: this.roundPriceForAlpaca(takeProfitPrice),
23959
+ limit_price: this.roundPriceForAlpaca(takeProfitPrice).toString(),
23841
23960
  },
23842
23961
  stop_loss: {
23843
- stop_price: this.roundPriceForAlpaca(stopLossPrice),
23962
+ stop_price: this.roundPriceForAlpaca(stopLossPrice).toString(),
23844
23963
  },
23845
23964
  };
23846
23965
  // If stop loss limit price is provided, create stop-limit order
23847
23966
  if (stopLossLimitPrice !== undefined) {
23848
- body.stop_loss.limit_price = this.roundPriceForAlpaca(stopLossLimitPrice);
23967
+ body.stop_loss = {
23968
+ stop_price: this.roundPriceForAlpaca(stopLossPrice).toString(),
23969
+ limit_price: this.roundPriceForAlpaca(stopLossLimitPrice).toString(),
23970
+ };
23849
23971
  }
23850
23972
  if (client_order_id !== undefined) {
23851
23973
  body.client_order_id = client_order_id;
23852
23974
  }
23853
- try {
23854
- return await this.makeRequest(`/orders`, 'POST', body);
23855
- }
23856
- catch (error) {
23857
- this.log(`Error creating OCO order: ${error}`, {
23858
- symbol,
23859
- type: 'error',
23860
- });
23861
- throw error;
23862
- }
23975
+ return this.makeRequest('/orders', 'POST', body, '', {
23976
+ action: 'Create OCO order',
23977
+ symbol,
23978
+ });
23863
23979
  }
23864
23980
  /**
23865
23981
  * 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.
@@ -23867,35 +23983,26 @@ class AlpacaTradingAPI {
23867
23983
  * @returns the current trail percent
23868
23984
  */
23869
23985
  async getCurrentTrailPercent(symbol) {
23870
- try {
23871
- const orders = await this.getOrders({
23872
- status: 'open',
23873
- symbols: [symbol],
23986
+ const orders = await this.getOrders({
23987
+ status: 'open',
23988
+ symbols: [symbol],
23989
+ });
23990
+ const trailingStopOrder = orders.find((order) => order.type === 'trailing_stop' &&
23991
+ (order.position_intent === 'sell_to_close' || order.position_intent === 'buy_to_close'));
23992
+ if (!trailingStopOrder) {
23993
+ this.log(`No closing trailing stop order found for ${symbol}`, {
23994
+ symbol,
23874
23995
  });
23875
- const trailingStopOrder = orders.find((order) => order.type === 'trailing_stop' &&
23876
- (order.position_intent === 'sell_to_close' || order.position_intent === 'buy_to_close'));
23877
- if (!trailingStopOrder) {
23878
- this.log(`No closing trailing stop order found for ${symbol}`, {
23879
- symbol,
23880
- });
23881
- return null;
23882
- }
23883
- if (!trailingStopOrder.trail_percent) {
23884
- this.log(`Trailing stop order found for ${symbol} but no trail_percent value`, {
23885
- symbol,
23886
- });
23887
- return null;
23888
- }
23889
- const trailPercent = parseFloat(trailingStopOrder.trail_percent);
23890
- return trailPercent;
23996
+ return null;
23891
23997
  }
23892
- catch (error) {
23893
- this.log(`Error getting current trail percent: ${error}`, {
23998
+ if (!trailingStopOrder.trail_percent) {
23999
+ this.log(`Trailing stop order found for ${symbol} but no trail_percent value`, {
23894
24000
  symbol,
23895
- type: 'error',
23896
24001
  });
23897
- throw error;
24002
+ return null;
23898
24003
  }
24004
+ const trailPercent = parseFloat(trailingStopOrder.trail_percent);
24005
+ return trailPercent;
23899
24006
  }
23900
24007
  /**
23901
24008
  * Update the trail percent for a trailing stop order
@@ -23927,18 +24034,10 @@ class AlpacaTradingAPI {
23927
24034
  this.log(`Updating trailing stop for ${symbol} from ${currentTrailPercent}% to ${trailPercent100}%`, {
23928
24035
  symbol,
23929
24036
  });
23930
- try {
23931
- await this.makeRequest(`/orders/${trailingStopOrder.id}`, 'PATCH', {
23932
- trail: trailPercent100.toString(), // Changed from trail_percent to trail
23933
- });
23934
- }
23935
- catch (error) {
23936
- this.log(`Error updating trailing stop: ${error}`, {
23937
- symbol,
23938
- type: 'error',
23939
- });
23940
- throw error;
23941
- }
24037
+ await this.makeRequest(`/orders/${trailingStopOrder.id}`, 'PATCH', { trail: trailPercent100.toString() }, '', {
24038
+ action: 'Update trailing stop order',
24039
+ symbol,
24040
+ });
23942
24041
  }
23943
24042
  /**
23944
24043
  * Cancel all open orders
@@ -23946,10 +24045,16 @@ class AlpacaTradingAPI {
23946
24045
  async cancelAllOrders() {
23947
24046
  this.log(`Canceling all open orders`);
23948
24047
  try {
23949
- await this.makeRequest('/orders', 'DELETE');
24048
+ await this.makeRequest('/orders', 'DELETE', undefined, '', {
24049
+ action: 'Cancel all open orders',
24050
+ });
23950
24051
  }
23951
24052
  catch (error) {
23952
- this.log(`Error canceling all orders: ${error}`, { type: 'error' });
24053
+ if (!(error instanceof AlpacaRequestError)) {
24054
+ this.log(`Error canceling all orders: ${error instanceof Error ? error.message : `${error}`}`, {
24055
+ type: 'error',
24056
+ });
24057
+ }
23953
24058
  }
23954
24059
  }
23955
24060
  /**
@@ -23961,15 +24066,14 @@ class AlpacaTradingAPI {
23961
24066
  async cancelOrder(orderId) {
23962
24067
  this.log(`Attempting to cancel order ${orderId}`);
23963
24068
  try {
23964
- await this.makeRequest(`/orders/${orderId}`, 'DELETE');
24069
+ await this.makeRequest(`/orders/${orderId}`, 'DELETE', undefined, '', {
24070
+ action: `Cancel order ${orderId}`,
24071
+ });
23965
24072
  this.log(`Successfully canceled order ${orderId}`);
23966
24073
  }
23967
24074
  catch (error) {
23968
24075
  // If the error is a 422, it means the order is not cancelable
23969
- if (error instanceof Error && error.message.includes('422')) {
23970
- this.log(`Order ${orderId} is not cancelable`, {
23971
- type: 'error',
23972
- });
24076
+ if (error instanceof AlpacaRequestError && error.status === 422) {
23973
24077
  throw new Error(`Order ${orderId} is not cancelable`);
23974
24078
  }
23975
24079
  // Re-throw other errors
@@ -24004,13 +24108,10 @@ class AlpacaTradingAPI {
24004
24108
  if (client_order_id !== undefined) {
24005
24109
  body.client_order_id = client_order_id;
24006
24110
  }
24007
- try {
24008
- return await this.makeRequest('/orders', 'POST', body);
24009
- }
24010
- catch (error) {
24011
- this.log(`Error creating limit order: ${error}`, { type: 'error' });
24012
- throw error;
24013
- }
24111
+ return this.makeRequest('/orders', 'POST', body, '', {
24112
+ action: 'Create limit order',
24113
+ symbol,
24114
+ });
24014
24115
  }
24015
24116
  /**
24016
24117
  * Close all equities positions
@@ -24076,7 +24177,9 @@ class AlpacaTradingAPI {
24076
24177
  }
24077
24178
  }
24078
24179
  else {
24079
- await this.makeRequest('/positions', 'DELETE', undefined, options.cancel_orders ? '?cancel_orders=true' : '');
24180
+ await this.makeRequest('/positions', 'DELETE', undefined, options.cancel_orders ? '?cancel_orders=true' : '', {
24181
+ action: 'Close all positions',
24182
+ });
24080
24183
  }
24081
24184
  }
24082
24185
  /**
@@ -24155,7 +24258,9 @@ class AlpacaTradingAPI {
24155
24258
  queryParams.append('end', params.end);
24156
24259
  if (params.date_end)
24157
24260
  queryParams.append('date_end', params.date_end);
24158
- const response = await this.makeRequest(`/account/portfolio/history?${queryParams.toString()}`);
24261
+ const response = await this.makeRequest(`/account/portfolio/history?${queryParams.toString()}`, 'GET', undefined, '', {
24262
+ action: 'Get portfolio history',
24263
+ });
24159
24264
  return response;
24160
24265
  }
24161
24266
  /**
@@ -24246,7 +24351,10 @@ class AlpacaTradingAPI {
24246
24351
  this.log(`Fetching option contracts for ${params.underlying_symbols.join(', ')}`, {
24247
24352
  symbol: params.underlying_symbols.join(', '),
24248
24353
  });
24249
- const response = (await this.makeRequest(`/options/contracts?${queryParams.toString()}`));
24354
+ const response = await this.makeRequest(`/options/contracts?${queryParams.toString()}`, 'GET', undefined, '', {
24355
+ action: 'Get option contracts',
24356
+ symbol: params.underlying_symbols.join(', '),
24357
+ });
24250
24358
  this.log(`Found ${response.option_contracts.length} option contracts`, {
24251
24359
  symbol: params.underlying_symbols.join(', '),
24252
24360
  });
@@ -24261,7 +24369,10 @@ class AlpacaTradingAPI {
24261
24369
  this.log(`Fetching option contract details for ${symbolOrId}`, {
24262
24370
  symbol: symbolOrId,
24263
24371
  });
24264
- const response = (await this.makeRequest(`/options/contracts/${symbolOrId}`));
24372
+ const response = await this.makeRequest(`/options/contracts/${symbolOrId}`, 'GET', undefined, '', {
24373
+ action: 'Get option contract details',
24374
+ symbol: symbolOrId,
24375
+ });
24265
24376
  this.log(`Found option contract details for ${symbolOrId}: ${response.name}`, {
24266
24377
  symbol: symbolOrId,
24267
24378
  });
@@ -24279,10 +24390,10 @@ class AlpacaTradingAPI {
24279
24390
  */
24280
24391
  async createOptionOrder(symbol, qty, side, position_intent, type, limitPrice) {
24281
24392
  if (!Number.isInteger(qty) || qty <= 0) {
24282
- this.log('Quantity must be a positive whole number for option orders', { type: 'error' });
24393
+ this.log('Quantity must be a positive whole number for option orders', { symbol, type: 'error' });
24283
24394
  }
24284
24395
  if (type === 'limit' && limitPrice === undefined) {
24285
- this.log('Limit price is required for limit orders', { type: 'error' });
24396
+ this.log('Limit price is required for limit orders', { symbol, type: 'error' });
24286
24397
  }
24287
24398
  this.log(`Creating ${type} option order for ${symbol}: ${side} ${qty} contracts (${position_intent})${type === 'limit' ? ` at $${limitPrice?.toFixed(2)}` : ''}`, {
24288
24399
  symbol,
@@ -24300,7 +24411,10 @@ class AlpacaTradingAPI {
24300
24411
  if (type === 'limit' && limitPrice !== undefined) {
24301
24412
  orderData.limit_price = this.roundPriceForAlpaca(limitPrice).toString();
24302
24413
  }
24303
- return this.makeRequest('/orders', 'POST', orderData);
24414
+ return this.makeRequest('/orders', 'POST', orderData, '', {
24415
+ action: 'Create option order',
24416
+ symbol,
24417
+ });
24304
24418
  }
24305
24419
  /**
24306
24420
  * Create a multi-leg option order
@@ -24311,16 +24425,19 @@ class AlpacaTradingAPI {
24311
24425
  * @returns The created multi-leg order
24312
24426
  */
24313
24427
  async createMultiLegOptionOrder(legs, qty, type, limitPrice) {
24428
+ const legSymbols = legs.map((leg) => leg.symbol).join(', ');
24314
24429
  if (!Number.isInteger(qty) || qty <= 0) {
24315
- this.log('Quantity must be a positive whole number for option orders', { type: 'error' });
24430
+ this.log('Quantity must be a positive whole number for option orders', {
24431
+ symbol: legSymbols,
24432
+ type: 'error',
24433
+ });
24316
24434
  }
24317
24435
  if (type === 'limit' && limitPrice === undefined) {
24318
- this.log('Limit price is required for limit orders', { type: 'error' });
24436
+ this.log('Limit price is required for limit orders', { symbol: legSymbols, type: 'error' });
24319
24437
  }
24320
24438
  if (legs.length < 2) {
24321
- this.log('Multi-leg orders require at least 2 legs', { type: 'error' });
24439
+ this.log('Multi-leg orders require at least 2 legs', { symbol: legSymbols, type: 'error' });
24322
24440
  }
24323
- const legSymbols = legs.map((leg) => leg.symbol).join(', ');
24324
24441
  this.log(`Creating multi-leg ${type} option order with ${legs.length} legs (${legSymbols})${type === 'limit' ? ` at $${limitPrice?.toFixed(2)}` : ''}`, {
24325
24442
  symbol: legSymbols,
24326
24443
  });
@@ -24334,7 +24451,10 @@ class AlpacaTradingAPI {
24334
24451
  if (type === 'limit' && limitPrice !== undefined) {
24335
24452
  orderData.limit_price = this.roundPriceForAlpaca(limitPrice).toString();
24336
24453
  }
24337
- return this.makeRequest('/orders', 'POST', orderData);
24454
+ return this.makeRequest('/orders', 'POST', orderData, '', {
24455
+ action: 'Create multi-leg option order',
24456
+ symbol: legSymbols,
24457
+ });
24338
24458
  }
24339
24459
  /**
24340
24460
  * Exercise an option contract
@@ -24345,7 +24465,10 @@ class AlpacaTradingAPI {
24345
24465
  this.log(`Exercising option contract ${symbolOrContractId}`, {
24346
24466
  symbol: symbolOrContractId,
24347
24467
  });
24348
- return this.makeRequest(`/positions/${symbolOrContractId}/exercise`, 'POST');
24468
+ return this.makeRequest(`/positions/${symbolOrContractId}/exercise`, 'POST', undefined, '', {
24469
+ action: 'Exercise option contract',
24470
+ symbol: symbolOrContractId,
24471
+ });
24349
24472
  }
24350
24473
  /**
24351
24474
  * Get option positions
@@ -24381,7 +24504,9 @@ class AlpacaTradingAPI {
24381
24504
  queryParams.append('date', date);
24382
24505
  }
24383
24506
  this.log(`Fetching option activities${activityType ? ` of type ${activityType}` : ''}${date ? ` for date ${date}` : ''}`);
24384
- return this.makeRequest(`/account/activities?${queryParams.toString()}`);
24507
+ return this.makeRequest(`/account/activities?${queryParams.toString()}`, 'GET', undefined, '', {
24508
+ action: 'Get option activities',
24509
+ });
24385
24510
  }
24386
24511
  /**
24387
24512
  * Create a long call spread (buy lower strike call, sell higher strike call)
@@ -24479,13 +24604,7 @@ class AlpacaTradingAPI {
24479
24604
  position_intent: 'buy_to_open',
24480
24605
  },
24481
24606
  ];
24482
- try {
24483
- return await this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
24484
- }
24485
- catch (error) {
24486
- this.log(`Error creating iron condor: ${error}`, { type: 'error' });
24487
- throw error;
24488
- }
24607
+ return this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
24489
24608
  }
24490
24609
  /**
24491
24610
  * Create a covered call (sell call option against owned stock)
@@ -24501,13 +24620,7 @@ class AlpacaTradingAPI {
24501
24620
  });
24502
24621
  // For covered calls, we don't need to include the stock leg if we already own the shares
24503
24622
  // We just create a simple sell order for the call option
24504
- try {
24505
- return await this.createOptionOrder(callOptionSymbol, qty, 'sell', 'sell_to_open', 'limit', limitPrice);
24506
- }
24507
- catch (error) {
24508
- this.log(`Error creating covered call: ${error}`, { type: 'error' });
24509
- throw error;
24510
- }
24623
+ return this.createOptionOrder(callOptionSymbol, qty, 'sell', 'sell_to_open', 'limit', limitPrice);
24511
24624
  }
24512
24625
  /**
24513
24626
  * Roll an option position to a new expiration or strike
@@ -24542,13 +24655,7 @@ class AlpacaTradingAPI {
24542
24655
  position_intent: openPositionIntent,
24543
24656
  },
24544
24657
  ];
24545
- try {
24546
- return await this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
24547
- }
24548
- catch (error) {
24549
- this.log(`Error rolling option position: ${error}`, { type: 'error' });
24550
- throw error;
24551
- }
24658
+ return this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
24552
24659
  }
24553
24660
  /**
24554
24661
  * Get option chain for a specific underlying symbol and expiration date
@@ -24572,10 +24679,12 @@ class AlpacaTradingAPI {
24572
24679
  return response.option_contracts || [];
24573
24680
  }
24574
24681
  catch (error) {
24575
- this.log(`Failed to fetch option chain for ${underlyingSymbol}: ${error instanceof Error ? error.message : 'Unknown error'}`, {
24576
- type: 'error',
24577
- symbol: underlyingSymbol,
24578
- });
24682
+ if (!(error instanceof AlpacaRequestError)) {
24683
+ this.log(`Failed to fetch option chain for ${underlyingSymbol}: ${error instanceof Error ? error.message : `${error}`}`, {
24684
+ type: 'error',
24685
+ symbol: underlyingSymbol,
24686
+ });
24687
+ }
24579
24688
  return [];
24580
24689
  }
24581
24690
  }
@@ -24606,10 +24715,12 @@ class AlpacaTradingAPI {
24606
24715
  return Array.from(expirationDates).sort();
24607
24716
  }
24608
24717
  catch (error) {
24609
- this.log(`Failed to fetch expiration dates for ${underlyingSymbol}: ${error instanceof Error ? error.message : 'Unknown error'}`, {
24610
- type: 'error',
24611
- symbol: underlyingSymbol,
24612
- });
24718
+ if (!(error instanceof AlpacaRequestError)) {
24719
+ this.log(`Failed to fetch expiration dates for ${underlyingSymbol}: ${error instanceof Error ? error.message : `${error}`}`, {
24720
+ type: 'error',
24721
+ symbol: underlyingSymbol,
24722
+ });
24723
+ }
24613
24724
  return [];
24614
24725
  }
24615
24726
  }
@@ -24668,7 +24779,10 @@ class AlpacaTradingAPI {
24668
24779
  this.log(`Canceling open order for ${order.symbol}`, {
24669
24780
  symbol: order.symbol,
24670
24781
  });
24671
- await this.makeRequest(`/orders/${order.id}`, 'DELETE');
24782
+ await this.makeRequest(`/orders/${order.id}`, 'DELETE', undefined, '', {
24783
+ action: `Cancel option order ${order.id}`,
24784
+ symbol: order.symbol,
24785
+ });
24672
24786
  }
24673
24787
  }
24674
24788
  }
@@ -24691,13 +24805,7 @@ class AlpacaTradingAPI {
24691
24805
  const quantityToClose = qty || parseInt(position.qty);
24692
24806
  const side = position.side === 'long' ? 'sell' : 'buy';
24693
24807
  const positionIntent = side === 'sell' ? 'sell_to_close' : 'buy_to_close';
24694
- try {
24695
- return await this.createOptionOrder(symbol, quantityToClose, side, positionIntent, 'market');
24696
- }
24697
- catch (error) {
24698
- this.log(`Error closing option position: ${error}`, { type: 'error' });
24699
- throw error;
24700
- }
24808
+ return this.createOptionOrder(symbol, quantityToClose, side, positionIntent, 'market');
24701
24809
  }
24702
24810
  /**
24703
24811
  * Create a complete equities trade with optional stop loss and take profit
@@ -24832,16 +24940,10 @@ class AlpacaTradingAPI {
24832
24940
  this.log(logMessage, {
24833
24941
  symbol,
24834
24942
  });
24835
- try {
24836
- return await this.makeRequest('/orders', 'POST', orderData);
24837
- }
24838
- catch (error) {
24839
- this.log(`Error creating equities trade: ${error}`, {
24840
- symbol,
24841
- type: 'error',
24842
- });
24843
- throw error;
24844
- }
24943
+ return this.makeRequest('/orders', 'POST', orderData, '', {
24944
+ action: 'Create equities trade',
24945
+ symbol,
24946
+ });
24845
24947
  }
24846
24948
  }
24847
24949