@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/index.mjs 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);
@@ -19037,7 +19063,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
19037
19063
  */
19038
19064
  async getHistoricalBars(params) {
19039
19065
  const symbols = params.symbols;
19040
- symbols.join(',');
19066
+ const symbolsStr = symbols.join(',');
19041
19067
  let allBars = {};
19042
19068
  let pageToken = null;
19043
19069
  let hasMorePages = true;
@@ -19048,7 +19074,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
19048
19074
  symbols.forEach((symbol) => {
19049
19075
  allBars[symbol] = [];
19050
19076
  });
19051
- log(`Starting historical bars fetch for ${symbols.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`);
19077
+ log(`Starting ${params.timeframe}bars fetch for ${symbols.length} symbols (${symbolsStr}), ${params.start || 'no start'} to ${params.end || 'no end'})`, { symbol: symbolsStr });
19052
19078
  while (hasMorePages) {
19053
19079
  pageCount++;
19054
19080
  const requestParams = {
@@ -19059,7 +19085,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
19059
19085
  };
19060
19086
  const response = await this.makeRequest('/stocks/bars', 'GET', requestParams);
19061
19087
  if (!response.bars) {
19062
- log(`No bars data found in response for ${symbols.length} symbols`, { type: 'warn' });
19088
+ log(`No bars data found in response for ${symbols.length} symbols`, { symbol: symbolsStr, type: 'warn' });
19063
19089
  break;
19064
19090
  }
19065
19091
  // Track currency from first response
@@ -19093,7 +19119,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
19093
19119
  const dateRangeStr = earliestTimestamp && latestTimestamp
19094
19120
  ? `${new Date(earliestTimestamp).toLocaleDateString('en-US', { timeZone: 'America/New_York' })} to ${new Date(latestTimestamp).toLocaleDateString('en-US', { timeZone: 'America/New_York' })}`
19095
19121
  : 'unknown range';
19096
- log(`Page ${pageCount}: Fetched ${pageBarsCount.toLocaleString()} bars (total: ${totalBarsCount.toLocaleString()}) for ${symbols.length} symbols, date range: ${dateRangeStr}${hasMorePages ? ', more pages available' : ', complete'}`, { type: 'debug' });
19122
+ 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' });
19097
19123
  // Prevent infinite loops
19098
19124
  if (pageCount > 1000) {
19099
19125
  log(`Stopping pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
@@ -19104,7 +19130,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
19104
19130
  const symbolCounts = Object.entries(allBars)
19105
19131
  .map(([symbol, bars]) => `${symbol}: ${bars.length}`)
19106
19132
  .join(', ');
19107
- log(`Bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages for ${symbols.length} symbols (${symbolCounts})`);
19133
+ log(`${params.timeframe} bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages for ${symbols.length} symbols (${symbolCounts})`, { symbol: symbolsStr });
19108
19134
  return {
19109
19135
  bars: allBars,
19110
19136
  next_page_token: null, // Always null since we fetch all pages
@@ -19956,6 +19982,26 @@ class AlpacaMarketDataAPI extends EventEmitter {
19956
19982
  const marketDataAPI = AlpacaMarketDataAPI.getInstance();
19957
19983
 
19958
19984
  const limitPriceSlippagePercent100 = 0.1; // 0.1%
19985
+ class AlpacaRequestError extends Error {
19986
+ method;
19987
+ status;
19988
+ url;
19989
+ responseCode;
19990
+ responseDetails;
19991
+ rawResponse;
19992
+ symbol;
19993
+ constructor(params) {
19994
+ super(params.message);
19995
+ this.name = 'AlpacaRequestError';
19996
+ this.method = params.method;
19997
+ this.status = params.status ?? null;
19998
+ this.url = params.url;
19999
+ this.responseCode = params.responseCode ?? null;
20000
+ this.responseDetails = params.responseDetails ?? null;
20001
+ this.rawResponse = params.rawResponse ?? null;
20002
+ this.symbol = params.symbol;
20003
+ }
20004
+ }
19959
20005
  /**
19960
20006
  Websocket example
19961
20007
  const alpacaAPI = createAlpacaTradingAPI(credentials); // type AlpacaCredentials
@@ -20039,6 +20085,75 @@ class AlpacaTradingAPI {
20039
20085
  roundPriceForAlpaca = (price) => {
20040
20086
  return price >= 1 ? Math.round(price * 100) / 100 : Math.round(price * 10000) / 10000;
20041
20087
  };
20088
+ isJsonObject(value) {
20089
+ return value !== null && !Array.isArray(value) && typeof value === 'object';
20090
+ }
20091
+ parseAlpacaAPIErrorResponse(rawResponse) {
20092
+ if (rawResponse.trim() === '') {
20093
+ return null;
20094
+ }
20095
+ try {
20096
+ const parsedValue = JSON.parse(rawResponse);
20097
+ if (!this.isJsonObject(parsedValue)) {
20098
+ return null;
20099
+ }
20100
+ const code = parsedValue['code'];
20101
+ const message = parsedValue['message'];
20102
+ const symbol = parsedValue['symbol'];
20103
+ if (typeof code !== 'number' || typeof message !== 'string') {
20104
+ return null;
20105
+ }
20106
+ if (symbol !== undefined && typeof symbol !== 'string') {
20107
+ return null;
20108
+ }
20109
+ return parsedValue;
20110
+ }
20111
+ catch {
20112
+ return null;
20113
+ }
20114
+ }
20115
+ formatJsonValueForLog(value) {
20116
+ if (value === undefined) {
20117
+ return 'undefined';
20118
+ }
20119
+ if (Array.isArray(value)) {
20120
+ return value.map((entry) => this.formatJsonValueForLog(entry)).join(', ');
20121
+ }
20122
+ if (value === null) {
20123
+ return 'null';
20124
+ }
20125
+ if (typeof value === 'object') {
20126
+ return JSON.stringify(value);
20127
+ }
20128
+ return `${value}`;
20129
+ }
20130
+ formatAlpacaAPIErrorDetails(errorResponse) {
20131
+ return Object.entries(errorResponse)
20132
+ .filter(([key]) => key !== 'code' && key !== 'message' && key !== 'symbol')
20133
+ .map(([key, value]) => `${key}=${this.formatJsonValueForLog(value)}`)
20134
+ .join(', ');
20135
+ }
20136
+ formatRequestAction(requestContext) {
20137
+ return requestContext?.action ? `${requestContext.action} failed` : 'Alpaca request failed';
20138
+ }
20139
+ buildAlpacaAPIErrorMessage(params) {
20140
+ const { requestContext, status, url, errorResponse, rawResponse } = params;
20141
+ const action = this.formatRequestAction(requestContext);
20142
+ const message = errorResponse?.message ?? (rawResponse || 'No error body returned');
20143
+ const responseCode = errorResponse?.code ? ` (code ${errorResponse.code})` : '';
20144
+ const detailSummary = errorResponse ? this.formatAlpacaAPIErrorDetails(errorResponse) : '';
20145
+ const detailText = detailSummary !== ''
20146
+ ? ` Details: ${detailSummary}.`
20147
+ : rawResponse.trim() !== '' && errorResponse === null
20148
+ ? ` Response: ${rawResponse}.`
20149
+ : '';
20150
+ return `${action}: Alpaca API ${status}${responseCode}: ${message}.${detailText} Url: ${url}`;
20151
+ }
20152
+ buildRequestExecutionErrorMessage(params) {
20153
+ const { requestContext, url, errorMessage } = params;
20154
+ const action = this.formatRequestAction(requestContext);
20155
+ return `${action}: ${errorMessage}. Url: ${url}`;
20156
+ }
20042
20157
  handleAuthMessage(data) {
20043
20158
  if (data.status === 'authorized') {
20044
20159
  this.authenticated = true;
@@ -20266,7 +20381,7 @@ class AlpacaTradingAPI {
20266
20381
  this.ws?.on('message', handleListenResponse);
20267
20382
  });
20268
20383
  }
20269
- async makeRequest(endpoint, method = 'GET', body, queryString = '') {
20384
+ async makeRequest(endpoint, method = 'GET', body, queryString = '', requestContext) {
20270
20385
  const url = `${this.apiBaseUrl}${endpoint}${queryString}`;
20271
20386
  try {
20272
20387
  const response = await fetch(url, {
@@ -20275,9 +20390,27 @@ class AlpacaTradingAPI {
20275
20390
  body: body ? JSON.stringify(body) : undefined,
20276
20391
  });
20277
20392
  if (!response.ok) {
20278
- const errorText = await response.text();
20279
- this.log(`Alpaca API error (${response.status}): ${errorText}`, { type: 'error' });
20280
- throw new Error(`Alpaca API error (${response.status}): ${errorText}`);
20393
+ const rawResponse = await response.text();
20394
+ const errorResponse = this.parseAlpacaAPIErrorResponse(rawResponse);
20395
+ const symbol = requestContext?.symbol ?? errorResponse?.symbol;
20396
+ const error = new AlpacaRequestError({
20397
+ message: this.buildAlpacaAPIErrorMessage({
20398
+ requestContext,
20399
+ status: response.status,
20400
+ url,
20401
+ errorResponse,
20402
+ rawResponse,
20403
+ }),
20404
+ method,
20405
+ status: response.status,
20406
+ url,
20407
+ responseCode: errorResponse?.code ?? null,
20408
+ responseDetails: errorResponse,
20409
+ rawResponse,
20410
+ symbol,
20411
+ });
20412
+ this.log(error.message, { symbol, type: 'error' });
20413
+ throw error;
20281
20414
  }
20282
20415
  // Handle responses with no content (e.g., 204 No Content)
20283
20416
  if (response.status === 204 || response.headers.get('content-length') === '0') {
@@ -20285,23 +20418,37 @@ class AlpacaTradingAPI {
20285
20418
  }
20286
20419
  const contentType = response.headers.get('content-type');
20287
20420
  if (contentType && contentType.includes('application/json')) {
20288
- return await response.json();
20421
+ return (await response.json());
20289
20422
  }
20290
20423
  // For non-JSON responses, return the text content
20291
20424
  const textContent = await response.text();
20292
- return textContent || null;
20425
+ return (textContent || null);
20293
20426
  }
20294
- catch (err) {
20295
- const error = err;
20296
- this.log(`Error in makeRequest: ${error.message}. Url: ${url}`, {
20297
- source: 'AlpacaAPI',
20298
- type: 'error',
20427
+ catch (error) {
20428
+ if (error instanceof AlpacaRequestError) {
20429
+ throw error;
20430
+ }
20431
+ const normalizedError = error instanceof Error ? error : new Error(`${error}`);
20432
+ const symbol = requestContext?.symbol;
20433
+ const requestError = new AlpacaRequestError({
20434
+ message: this.buildRequestExecutionErrorMessage({
20435
+ requestContext,
20436
+ url,
20437
+ errorMessage: normalizedError.message,
20438
+ }),
20439
+ method,
20440
+ url,
20441
+ rawResponse: null,
20442
+ symbol,
20299
20443
  });
20300
- throw error;
20444
+ this.log(requestError.message, { symbol, type: 'error' });
20445
+ throw requestError;
20301
20446
  }
20302
20447
  }
20303
20448
  async getPositions(assetClass) {
20304
- const positions = (await this.makeRequest('/positions'));
20449
+ const positions = await this.makeRequest('/positions', 'GET', undefined, '', {
20450
+ action: 'Get positions',
20451
+ });
20305
20452
  if (assetClass) {
20306
20453
  return positions.filter((position) => position.asset_class === assetClass);
20307
20454
  }
@@ -20339,22 +20486,15 @@ class AlpacaTradingAPI {
20339
20486
  if (params.side)
20340
20487
  queryParams.append('side', params.side);
20341
20488
  const endpoint = `/orders${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;
20342
- try {
20343
- return await this.makeRequest(endpoint);
20344
- }
20345
- catch (error) {
20346
- this.log(`Error getting orders: ${error}`, { type: 'error' });
20347
- throw error;
20348
- }
20489
+ return this.makeRequest(endpoint, 'GET', undefined, '', {
20490
+ action: 'Get orders',
20491
+ symbol: params.symbols?.join(','),
20492
+ });
20349
20493
  }
20350
20494
  async getAccountDetails() {
20351
- try {
20352
- return await this.makeRequest('/account');
20353
- }
20354
- catch (error) {
20355
- this.log(`Error getting account details: ${error}`, { type: 'error' });
20356
- throw error;
20357
- }
20495
+ return this.makeRequest('/account', 'GET', undefined, '', {
20496
+ action: 'Get account details',
20497
+ });
20358
20498
  }
20359
20499
  /**
20360
20500
  * Create a trailing stop order
@@ -20372,27 +20512,21 @@ class AlpacaTradingAPI {
20372
20512
  });
20373
20513
  const body = {
20374
20514
  symbol,
20375
- qty: Math.abs(qty),
20515
+ qty: Math.abs(qty).toString(),
20376
20516
  side,
20377
20517
  position_intent,
20378
20518
  order_class: 'simple',
20379
20519
  type: 'trailing_stop',
20380
- trail_percent: trailPercent100, // Already in decimal form (e.g., 4 for 4%)
20520
+ trail_percent: trailPercent100.toString(),
20381
20521
  time_in_force: 'gtc',
20382
20522
  };
20383
20523
  if (client_order_id !== undefined) {
20384
20524
  body.client_order_id = client_order_id;
20385
20525
  }
20386
- try {
20387
- return await this.makeRequest(`/orders`, 'POST', body);
20388
- }
20389
- catch (error) {
20390
- this.log(`Error creating trailing stop: ${error}`, {
20391
- symbol,
20392
- type: 'error',
20393
- });
20394
- throw error;
20395
- }
20526
+ return this.makeRequest('/orders', 'POST', body, '', {
20527
+ action: 'Create trailing stop order',
20528
+ symbol,
20529
+ });
20396
20530
  }
20397
20531
  /**
20398
20532
  * Create a stop order (stop or stop-limit)
@@ -20418,25 +20552,19 @@ class AlpacaTradingAPI {
20418
20552
  position_intent,
20419
20553
  order_class: 'simple',
20420
20554
  type: isStopLimit ? 'stop_limit' : 'stop',
20421
- stop_price: this.roundPriceForAlpaca(stopPrice),
20555
+ stop_price: this.roundPriceForAlpaca(stopPrice).toString(),
20422
20556
  time_in_force: 'gtc',
20423
20557
  };
20424
- if (isStopLimit) {
20425
- body.limit_price = this.roundPriceForAlpaca(limitPrice);
20558
+ if (limitPrice !== undefined) {
20559
+ body.limit_price = this.roundPriceForAlpaca(limitPrice).toString();
20426
20560
  }
20427
20561
  if (client_order_id !== undefined) {
20428
20562
  body.client_order_id = client_order_id;
20429
20563
  }
20430
- try {
20431
- return await this.makeRequest(`/orders`, 'POST', body);
20432
- }
20433
- catch (error) {
20434
- this.log(`Error creating ${orderType} order: ${error}`, {
20435
- symbol,
20436
- type: 'error',
20437
- });
20438
- throw error;
20439
- }
20564
+ return this.makeRequest('/orders', 'POST', body, '', {
20565
+ action: `Create ${orderType} order`,
20566
+ symbol,
20567
+ });
20440
20568
  }
20441
20569
  /**
20442
20570
  * Create a market order
@@ -20462,13 +20590,10 @@ class AlpacaTradingAPI {
20462
20590
  if (client_order_id !== undefined) {
20463
20591
  body.client_order_id = client_order_id;
20464
20592
  }
20465
- try {
20466
- return await this.makeRequest('/orders', 'POST', body);
20467
- }
20468
- catch (error) {
20469
- this.log(`Error creating market order: ${error}`, { type: 'error' });
20470
- throw error;
20471
- }
20593
+ return this.makeRequest('/orders', 'POST', body, '', {
20594
+ action: 'Create market order',
20595
+ symbol,
20596
+ });
20472
20597
  }
20473
20598
  /**
20474
20599
  * Create a Market on Open (MOO) order - executes in the opening auction
@@ -20502,13 +20627,10 @@ class AlpacaTradingAPI {
20502
20627
  if (client_order_id !== undefined) {
20503
20628
  body.client_order_id = client_order_id;
20504
20629
  }
20505
- try {
20506
- return await this.makeRequest('/orders', 'POST', body);
20507
- }
20508
- catch (error) {
20509
- this.log(`Error creating MOO order: ${error}`, { type: 'error' });
20510
- throw error;
20511
- }
20630
+ return this.makeRequest('/orders', 'POST', body, '', {
20631
+ action: 'Create market on open order',
20632
+ symbol,
20633
+ });
20512
20634
  }
20513
20635
  /**
20514
20636
  * Create a Market on Close (MOC) order - executes in the closing auction
@@ -20542,13 +20664,10 @@ class AlpacaTradingAPI {
20542
20664
  if (client_order_id !== undefined) {
20543
20665
  body.client_order_id = client_order_id;
20544
20666
  }
20545
- try {
20546
- return await this.makeRequest('/orders', 'POST', body);
20547
- }
20548
- catch (error) {
20549
- this.log(`Error creating MOC order: ${error}`, { type: 'error' });
20550
- throw error;
20551
- }
20667
+ return this.makeRequest('/orders', 'POST', body, '', {
20668
+ action: 'Create market on close order',
20669
+ symbol,
20670
+ });
20552
20671
  }
20553
20672
  /**
20554
20673
  * Create an OCO (One-Cancels-Other) order with take profit and stop loss
@@ -20574,32 +20693,29 @@ class AlpacaTradingAPI {
20574
20693
  position_intent,
20575
20694
  order_class: 'oco',
20576
20695
  type: 'limit',
20577
- limit_price: this.roundPriceForAlpaca(limitPrice),
20696
+ limit_price: this.roundPriceForAlpaca(limitPrice).toString(),
20578
20697
  time_in_force: 'gtc',
20579
20698
  take_profit: {
20580
- limit_price: this.roundPriceForAlpaca(takeProfitPrice),
20699
+ limit_price: this.roundPriceForAlpaca(takeProfitPrice).toString(),
20581
20700
  },
20582
20701
  stop_loss: {
20583
- stop_price: this.roundPriceForAlpaca(stopLossPrice),
20702
+ stop_price: this.roundPriceForAlpaca(stopLossPrice).toString(),
20584
20703
  },
20585
20704
  };
20586
20705
  // If stop loss limit price is provided, create stop-limit order
20587
20706
  if (stopLossLimitPrice !== undefined) {
20588
- body.stop_loss.limit_price = this.roundPriceForAlpaca(stopLossLimitPrice);
20707
+ body.stop_loss = {
20708
+ stop_price: this.roundPriceForAlpaca(stopLossPrice).toString(),
20709
+ limit_price: this.roundPriceForAlpaca(stopLossLimitPrice).toString(),
20710
+ };
20589
20711
  }
20590
20712
  if (client_order_id !== undefined) {
20591
20713
  body.client_order_id = client_order_id;
20592
20714
  }
20593
- try {
20594
- return await this.makeRequest(`/orders`, 'POST', body);
20595
- }
20596
- catch (error) {
20597
- this.log(`Error creating OCO order: ${error}`, {
20598
- symbol,
20599
- type: 'error',
20600
- });
20601
- throw error;
20602
- }
20715
+ return this.makeRequest('/orders', 'POST', body, '', {
20716
+ action: 'Create OCO order',
20717
+ symbol,
20718
+ });
20603
20719
  }
20604
20720
  /**
20605
20721
  * 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.
@@ -20607,35 +20723,26 @@ class AlpacaTradingAPI {
20607
20723
  * @returns the current trail percent
20608
20724
  */
20609
20725
  async getCurrentTrailPercent(symbol) {
20610
- try {
20611
- const orders = await this.getOrders({
20612
- status: 'open',
20613
- symbols: [symbol],
20726
+ const orders = await this.getOrders({
20727
+ status: 'open',
20728
+ symbols: [symbol],
20729
+ });
20730
+ const trailingStopOrder = orders.find((order) => order.type === 'trailing_stop' &&
20731
+ (order.position_intent === 'sell_to_close' || order.position_intent === 'buy_to_close'));
20732
+ if (!trailingStopOrder) {
20733
+ this.log(`No closing trailing stop order found for ${symbol}`, {
20734
+ symbol,
20614
20735
  });
20615
- const trailingStopOrder = orders.find((order) => order.type === 'trailing_stop' &&
20616
- (order.position_intent === 'sell_to_close' || order.position_intent === 'buy_to_close'));
20617
- if (!trailingStopOrder) {
20618
- this.log(`No closing trailing stop order found for ${symbol}`, {
20619
- symbol,
20620
- });
20621
- return null;
20622
- }
20623
- if (!trailingStopOrder.trail_percent) {
20624
- this.log(`Trailing stop order found for ${symbol} but no trail_percent value`, {
20625
- symbol,
20626
- });
20627
- return null;
20628
- }
20629
- const trailPercent = parseFloat(trailingStopOrder.trail_percent);
20630
- return trailPercent;
20736
+ return null;
20631
20737
  }
20632
- catch (error) {
20633
- this.log(`Error getting current trail percent: ${error}`, {
20738
+ if (!trailingStopOrder.trail_percent) {
20739
+ this.log(`Trailing stop order found for ${symbol} but no trail_percent value`, {
20634
20740
  symbol,
20635
- type: 'error',
20636
20741
  });
20637
- throw error;
20742
+ return null;
20638
20743
  }
20744
+ const trailPercent = parseFloat(trailingStopOrder.trail_percent);
20745
+ return trailPercent;
20639
20746
  }
20640
20747
  /**
20641
20748
  * Update the trail percent for a trailing stop order
@@ -20667,18 +20774,10 @@ class AlpacaTradingAPI {
20667
20774
  this.log(`Updating trailing stop for ${symbol} from ${currentTrailPercent}% to ${trailPercent100}%`, {
20668
20775
  symbol,
20669
20776
  });
20670
- try {
20671
- await this.makeRequest(`/orders/${trailingStopOrder.id}`, 'PATCH', {
20672
- trail: trailPercent100.toString(), // Changed from trail_percent to trail
20673
- });
20674
- }
20675
- catch (error) {
20676
- this.log(`Error updating trailing stop: ${error}`, {
20677
- symbol,
20678
- type: 'error',
20679
- });
20680
- throw error;
20681
- }
20777
+ await this.makeRequest(`/orders/${trailingStopOrder.id}`, 'PATCH', { trail: trailPercent100.toString() }, '', {
20778
+ action: 'Update trailing stop order',
20779
+ symbol,
20780
+ });
20682
20781
  }
20683
20782
  /**
20684
20783
  * Cancel all open orders
@@ -20686,10 +20785,16 @@ class AlpacaTradingAPI {
20686
20785
  async cancelAllOrders() {
20687
20786
  this.log(`Canceling all open orders`);
20688
20787
  try {
20689
- await this.makeRequest('/orders', 'DELETE');
20788
+ await this.makeRequest('/orders', 'DELETE', undefined, '', {
20789
+ action: 'Cancel all open orders',
20790
+ });
20690
20791
  }
20691
20792
  catch (error) {
20692
- this.log(`Error canceling all orders: ${error}`, { type: 'error' });
20793
+ if (!(error instanceof AlpacaRequestError)) {
20794
+ this.log(`Error canceling all orders: ${error instanceof Error ? error.message : `${error}`}`, {
20795
+ type: 'error',
20796
+ });
20797
+ }
20693
20798
  }
20694
20799
  }
20695
20800
  /**
@@ -20701,15 +20806,14 @@ class AlpacaTradingAPI {
20701
20806
  async cancelOrder(orderId) {
20702
20807
  this.log(`Attempting to cancel order ${orderId}`);
20703
20808
  try {
20704
- await this.makeRequest(`/orders/${orderId}`, 'DELETE');
20809
+ await this.makeRequest(`/orders/${orderId}`, 'DELETE', undefined, '', {
20810
+ action: `Cancel order ${orderId}`,
20811
+ });
20705
20812
  this.log(`Successfully canceled order ${orderId}`);
20706
20813
  }
20707
20814
  catch (error) {
20708
20815
  // If the error is a 422, it means the order is not cancelable
20709
- if (error instanceof Error && error.message.includes('422')) {
20710
- this.log(`Order ${orderId} is not cancelable`, {
20711
- type: 'error',
20712
- });
20816
+ if (error instanceof AlpacaRequestError && error.status === 422) {
20713
20817
  throw new Error(`Order ${orderId} is not cancelable`);
20714
20818
  }
20715
20819
  // Re-throw other errors
@@ -20744,13 +20848,10 @@ class AlpacaTradingAPI {
20744
20848
  if (client_order_id !== undefined) {
20745
20849
  body.client_order_id = client_order_id;
20746
20850
  }
20747
- try {
20748
- return await this.makeRequest('/orders', 'POST', body);
20749
- }
20750
- catch (error) {
20751
- this.log(`Error creating limit order: ${error}`, { type: 'error' });
20752
- throw error;
20753
- }
20851
+ return this.makeRequest('/orders', 'POST', body, '', {
20852
+ action: 'Create limit order',
20853
+ symbol,
20854
+ });
20754
20855
  }
20755
20856
  /**
20756
20857
  * Close all equities positions
@@ -20816,7 +20917,9 @@ class AlpacaTradingAPI {
20816
20917
  }
20817
20918
  }
20818
20919
  else {
20819
- await this.makeRequest('/positions', 'DELETE', undefined, options.cancel_orders ? '?cancel_orders=true' : '');
20920
+ await this.makeRequest('/positions', 'DELETE', undefined, options.cancel_orders ? '?cancel_orders=true' : '', {
20921
+ action: 'Close all positions',
20922
+ });
20820
20923
  }
20821
20924
  }
20822
20925
  /**
@@ -20895,7 +20998,9 @@ class AlpacaTradingAPI {
20895
20998
  queryParams.append('end', params.end);
20896
20999
  if (params.date_end)
20897
21000
  queryParams.append('date_end', params.date_end);
20898
- const response = await this.makeRequest(`/account/portfolio/history?${queryParams.toString()}`);
21001
+ const response = await this.makeRequest(`/account/portfolio/history?${queryParams.toString()}`, 'GET', undefined, '', {
21002
+ action: 'Get portfolio history',
21003
+ });
20899
21004
  return response;
20900
21005
  }
20901
21006
  /**
@@ -20986,7 +21091,10 @@ class AlpacaTradingAPI {
20986
21091
  this.log(`Fetching option contracts for ${params.underlying_symbols.join(', ')}`, {
20987
21092
  symbol: params.underlying_symbols.join(', '),
20988
21093
  });
20989
- const response = (await this.makeRequest(`/options/contracts?${queryParams.toString()}`));
21094
+ const response = await this.makeRequest(`/options/contracts?${queryParams.toString()}`, 'GET', undefined, '', {
21095
+ action: 'Get option contracts',
21096
+ symbol: params.underlying_symbols.join(', '),
21097
+ });
20990
21098
  this.log(`Found ${response.option_contracts.length} option contracts`, {
20991
21099
  symbol: params.underlying_symbols.join(', '),
20992
21100
  });
@@ -21001,7 +21109,10 @@ class AlpacaTradingAPI {
21001
21109
  this.log(`Fetching option contract details for ${symbolOrId}`, {
21002
21110
  symbol: symbolOrId,
21003
21111
  });
21004
- const response = (await this.makeRequest(`/options/contracts/${symbolOrId}`));
21112
+ const response = await this.makeRequest(`/options/contracts/${symbolOrId}`, 'GET', undefined, '', {
21113
+ action: 'Get option contract details',
21114
+ symbol: symbolOrId,
21115
+ });
21005
21116
  this.log(`Found option contract details for ${symbolOrId}: ${response.name}`, {
21006
21117
  symbol: symbolOrId,
21007
21118
  });
@@ -21019,10 +21130,10 @@ class AlpacaTradingAPI {
21019
21130
  */
21020
21131
  async createOptionOrder(symbol, qty, side, position_intent, type, limitPrice) {
21021
21132
  if (!Number.isInteger(qty) || qty <= 0) {
21022
- this.log('Quantity must be a positive whole number for option orders', { type: 'error' });
21133
+ this.log('Quantity must be a positive whole number for option orders', { symbol, type: 'error' });
21023
21134
  }
21024
21135
  if (type === 'limit' && limitPrice === undefined) {
21025
- this.log('Limit price is required for limit orders', { type: 'error' });
21136
+ this.log('Limit price is required for limit orders', { symbol, type: 'error' });
21026
21137
  }
21027
21138
  this.log(`Creating ${type} option order for ${symbol}: ${side} ${qty} contracts (${position_intent})${type === 'limit' ? ` at $${limitPrice?.toFixed(2)}` : ''}`, {
21028
21139
  symbol,
@@ -21040,7 +21151,10 @@ class AlpacaTradingAPI {
21040
21151
  if (type === 'limit' && limitPrice !== undefined) {
21041
21152
  orderData.limit_price = this.roundPriceForAlpaca(limitPrice).toString();
21042
21153
  }
21043
- return this.makeRequest('/orders', 'POST', orderData);
21154
+ return this.makeRequest('/orders', 'POST', orderData, '', {
21155
+ action: 'Create option order',
21156
+ symbol,
21157
+ });
21044
21158
  }
21045
21159
  /**
21046
21160
  * Create a multi-leg option order
@@ -21051,16 +21165,19 @@ class AlpacaTradingAPI {
21051
21165
  * @returns The created multi-leg order
21052
21166
  */
21053
21167
  async createMultiLegOptionOrder(legs, qty, type, limitPrice) {
21168
+ const legSymbols = legs.map((leg) => leg.symbol).join(', ');
21054
21169
  if (!Number.isInteger(qty) || qty <= 0) {
21055
- this.log('Quantity must be a positive whole number for option orders', { type: 'error' });
21170
+ this.log('Quantity must be a positive whole number for option orders', {
21171
+ symbol: legSymbols,
21172
+ type: 'error',
21173
+ });
21056
21174
  }
21057
21175
  if (type === 'limit' && limitPrice === undefined) {
21058
- this.log('Limit price is required for limit orders', { type: 'error' });
21176
+ this.log('Limit price is required for limit orders', { symbol: legSymbols, type: 'error' });
21059
21177
  }
21060
21178
  if (legs.length < 2) {
21061
- this.log('Multi-leg orders require at least 2 legs', { type: 'error' });
21179
+ this.log('Multi-leg orders require at least 2 legs', { symbol: legSymbols, type: 'error' });
21062
21180
  }
21063
- const legSymbols = legs.map((leg) => leg.symbol).join(', ');
21064
21181
  this.log(`Creating multi-leg ${type} option order with ${legs.length} legs (${legSymbols})${type === 'limit' ? ` at $${limitPrice?.toFixed(2)}` : ''}`, {
21065
21182
  symbol: legSymbols,
21066
21183
  });
@@ -21074,7 +21191,10 @@ class AlpacaTradingAPI {
21074
21191
  if (type === 'limit' && limitPrice !== undefined) {
21075
21192
  orderData.limit_price = this.roundPriceForAlpaca(limitPrice).toString();
21076
21193
  }
21077
- return this.makeRequest('/orders', 'POST', orderData);
21194
+ return this.makeRequest('/orders', 'POST', orderData, '', {
21195
+ action: 'Create multi-leg option order',
21196
+ symbol: legSymbols,
21197
+ });
21078
21198
  }
21079
21199
  /**
21080
21200
  * Exercise an option contract
@@ -21085,7 +21205,10 @@ class AlpacaTradingAPI {
21085
21205
  this.log(`Exercising option contract ${symbolOrContractId}`, {
21086
21206
  symbol: symbolOrContractId,
21087
21207
  });
21088
- return this.makeRequest(`/positions/${symbolOrContractId}/exercise`, 'POST');
21208
+ return this.makeRequest(`/positions/${symbolOrContractId}/exercise`, 'POST', undefined, '', {
21209
+ action: 'Exercise option contract',
21210
+ symbol: symbolOrContractId,
21211
+ });
21089
21212
  }
21090
21213
  /**
21091
21214
  * Get option positions
@@ -21121,7 +21244,9 @@ class AlpacaTradingAPI {
21121
21244
  queryParams.append('date', date);
21122
21245
  }
21123
21246
  this.log(`Fetching option activities${activityType ? ` of type ${activityType}` : ''}${date ? ` for date ${date}` : ''}`);
21124
- return this.makeRequest(`/account/activities?${queryParams.toString()}`);
21247
+ return this.makeRequest(`/account/activities?${queryParams.toString()}`, 'GET', undefined, '', {
21248
+ action: 'Get option activities',
21249
+ });
21125
21250
  }
21126
21251
  /**
21127
21252
  * Create a long call spread (buy lower strike call, sell higher strike call)
@@ -21219,13 +21344,7 @@ class AlpacaTradingAPI {
21219
21344
  position_intent: 'buy_to_open',
21220
21345
  },
21221
21346
  ];
21222
- try {
21223
- return await this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
21224
- }
21225
- catch (error) {
21226
- this.log(`Error creating iron condor: ${error}`, { type: 'error' });
21227
- throw error;
21228
- }
21347
+ return this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
21229
21348
  }
21230
21349
  /**
21231
21350
  * Create a covered call (sell call option against owned stock)
@@ -21241,13 +21360,7 @@ class AlpacaTradingAPI {
21241
21360
  });
21242
21361
  // For covered calls, we don't need to include the stock leg if we already own the shares
21243
21362
  // We just create a simple sell order for the call option
21244
- try {
21245
- return await this.createOptionOrder(callOptionSymbol, qty, 'sell', 'sell_to_open', 'limit', limitPrice);
21246
- }
21247
- catch (error) {
21248
- this.log(`Error creating covered call: ${error}`, { type: 'error' });
21249
- throw error;
21250
- }
21363
+ return this.createOptionOrder(callOptionSymbol, qty, 'sell', 'sell_to_open', 'limit', limitPrice);
21251
21364
  }
21252
21365
  /**
21253
21366
  * Roll an option position to a new expiration or strike
@@ -21282,13 +21395,7 @@ class AlpacaTradingAPI {
21282
21395
  position_intent: openPositionIntent,
21283
21396
  },
21284
21397
  ];
21285
- try {
21286
- return await this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
21287
- }
21288
- catch (error) {
21289
- this.log(`Error rolling option position: ${error}`, { type: 'error' });
21290
- throw error;
21291
- }
21398
+ return this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
21292
21399
  }
21293
21400
  /**
21294
21401
  * Get option chain for a specific underlying symbol and expiration date
@@ -21312,10 +21419,12 @@ class AlpacaTradingAPI {
21312
21419
  return response.option_contracts || [];
21313
21420
  }
21314
21421
  catch (error) {
21315
- this.log(`Failed to fetch option chain for ${underlyingSymbol}: ${error instanceof Error ? error.message : 'Unknown error'}`, {
21316
- type: 'error',
21317
- symbol: underlyingSymbol,
21318
- });
21422
+ if (!(error instanceof AlpacaRequestError)) {
21423
+ this.log(`Failed to fetch option chain for ${underlyingSymbol}: ${error instanceof Error ? error.message : `${error}`}`, {
21424
+ type: 'error',
21425
+ symbol: underlyingSymbol,
21426
+ });
21427
+ }
21319
21428
  return [];
21320
21429
  }
21321
21430
  }
@@ -21346,10 +21455,12 @@ class AlpacaTradingAPI {
21346
21455
  return Array.from(expirationDates).sort();
21347
21456
  }
21348
21457
  catch (error) {
21349
- this.log(`Failed to fetch expiration dates for ${underlyingSymbol}: ${error instanceof Error ? error.message : 'Unknown error'}`, {
21350
- type: 'error',
21351
- symbol: underlyingSymbol,
21352
- });
21458
+ if (!(error instanceof AlpacaRequestError)) {
21459
+ this.log(`Failed to fetch expiration dates for ${underlyingSymbol}: ${error instanceof Error ? error.message : `${error}`}`, {
21460
+ type: 'error',
21461
+ symbol: underlyingSymbol,
21462
+ });
21463
+ }
21353
21464
  return [];
21354
21465
  }
21355
21466
  }
@@ -21408,7 +21519,10 @@ class AlpacaTradingAPI {
21408
21519
  this.log(`Canceling open order for ${order.symbol}`, {
21409
21520
  symbol: order.symbol,
21410
21521
  });
21411
- await this.makeRequest(`/orders/${order.id}`, 'DELETE');
21522
+ await this.makeRequest(`/orders/${order.id}`, 'DELETE', undefined, '', {
21523
+ action: `Cancel option order ${order.id}`,
21524
+ symbol: order.symbol,
21525
+ });
21412
21526
  }
21413
21527
  }
21414
21528
  }
@@ -21431,13 +21545,7 @@ class AlpacaTradingAPI {
21431
21545
  const quantityToClose = qty || parseInt(position.qty);
21432
21546
  const side = position.side === 'long' ? 'sell' : 'buy';
21433
21547
  const positionIntent = side === 'sell' ? 'sell_to_close' : 'buy_to_close';
21434
- try {
21435
- return await this.createOptionOrder(symbol, quantityToClose, side, positionIntent, 'market');
21436
- }
21437
- catch (error) {
21438
- this.log(`Error closing option position: ${error}`, { type: 'error' });
21439
- throw error;
21440
- }
21548
+ return this.createOptionOrder(symbol, quantityToClose, side, positionIntent, 'market');
21441
21549
  }
21442
21550
  /**
21443
21551
  * Create a complete equities trade with optional stop loss and take profit
@@ -21572,16 +21680,10 @@ class AlpacaTradingAPI {
21572
21680
  this.log(logMessage, {
21573
21681
  symbol,
21574
21682
  });
21575
- try {
21576
- return await this.makeRequest('/orders', 'POST', orderData);
21577
- }
21578
- catch (error) {
21579
- this.log(`Error creating equities trade: ${error}`, {
21580
- symbol,
21581
- type: 'error',
21582
- });
21583
- throw error;
21584
- }
21683
+ return this.makeRequest('/orders', 'POST', orderData, '', {
21684
+ action: 'Create equities trade',
21685
+ symbol,
21686
+ });
21585
21687
  }
21586
21688
  }
21587
21689