@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.cjs CHANGED
@@ -1616,7 +1616,7 @@ const safeJSON = (text) => {
1616
1616
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
1617
1617
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1618
1618
 
1619
- const VERSION = '6.27.0'; // x-release-please-version
1619
+ const VERSION = '6.31.0'; // x-release-please-version
1620
1620
 
1621
1621
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
1622
1622
  const isRunningInBrowser = () => {
@@ -4860,7 +4860,7 @@ class Speech extends APIResource {
4860
4860
  * const speech = await client.audio.speech.create({
4861
4861
  * input: 'input',
4862
4862
  * model: 'string',
4863
- * voice: 'ash',
4863
+ * voice: 'string',
4864
4864
  * });
4865
4865
  *
4866
4866
  * const content = await speech.blob();
@@ -8141,7 +8141,7 @@ class Videos extends APIResource {
8141
8141
  * Create a new video generation job from a prompt and optional reference assets.
8142
8142
  */
8143
8143
  create(body, options) {
8144
- return this._client.post('/videos', maybeMultipartFormRequestOptions({ body, ...options }, this._client));
8144
+ return this._client.post('/videos', multipartFormRequestOptions({ body, ...options }, this._client));
8145
8145
  }
8146
8146
  /**
8147
8147
  * Fetch the latest metadata for a generated video.
@@ -8161,6 +8161,12 @@ class Videos extends APIResource {
8161
8161
  delete(videoID, options) {
8162
8162
  return this._client.delete(path `/videos/${videoID}`, options);
8163
8163
  }
8164
+ /**
8165
+ * Create a character from an uploaded video.
8166
+ */
8167
+ createCharacter(body, options) {
8168
+ return this._client.post('/videos/characters', multipartFormRequestOptions({ body, ...options }, this._client));
8169
+ }
8164
8170
  /**
8165
8171
  * Download the generated video bytes or a derived preview asset.
8166
8172
  *
@@ -8174,6 +8180,25 @@ class Videos extends APIResource {
8174
8180
  __binaryResponse: true,
8175
8181
  });
8176
8182
  }
8183
+ /**
8184
+ * Create a new video generation job by editing a source video or existing
8185
+ * generated video.
8186
+ */
8187
+ edit(body, options) {
8188
+ return this._client.post('/videos/edits', multipartFormRequestOptions({ body, ...options }, this._client));
8189
+ }
8190
+ /**
8191
+ * Create an extension of a completed video.
8192
+ */
8193
+ extend(body, options) {
8194
+ return this._client.post('/videos/extensions', multipartFormRequestOptions({ body, ...options }, this._client));
8195
+ }
8196
+ /**
8197
+ * Fetch a character.
8198
+ */
8199
+ getCharacter(characterID, options) {
8200
+ return this._client.get(path `/videos/characters/${characterID}`, options);
8201
+ }
8177
8202
  /**
8178
8203
  * Create a remix of a completed video using a refreshed prompt.
8179
8204
  */
@@ -8455,8 +8480,9 @@ class OpenAI {
8455
8480
  new URL(path)
8456
8481
  : new URL(baseURL + (baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path));
8457
8482
  const defaultQuery = this.defaultQuery();
8458
- if (!isEmptyObj$1(defaultQuery)) {
8459
- query = { ...defaultQuery, ...query };
8483
+ const pathQuery = Object.fromEntries(url.searchParams);
8484
+ if (!isEmptyObj$1(defaultQuery) || !isEmptyObj$1(pathQuery)) {
8485
+ query = { ...pathQuery, ...defaultQuery, ...query };
8460
8486
  }
8461
8487
  if (typeof query === 'object' && query && !Array.isArray(query)) {
8462
8488
  url.search = this.stringifyQuery(query);
@@ -19039,7 +19065,7 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
19039
19065
  */
19040
19066
  async getHistoricalBars(params) {
19041
19067
  const symbols = params.symbols;
19042
- symbols.join(',');
19068
+ const symbolsStr = symbols.join(',');
19043
19069
  let allBars = {};
19044
19070
  let pageToken = null;
19045
19071
  let hasMorePages = true;
@@ -19050,7 +19076,7 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
19050
19076
  symbols.forEach((symbol) => {
19051
19077
  allBars[symbol] = [];
19052
19078
  });
19053
- log(`Starting historical bars fetch for ${symbols.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`);
19079
+ log(`Starting ${params.timeframe}bars fetch for ${symbols.length} symbols (${symbolsStr}), ${params.start || 'no start'} to ${params.end || 'no end'})`, { symbol: symbolsStr });
19054
19080
  while (hasMorePages) {
19055
19081
  pageCount++;
19056
19082
  const requestParams = {
@@ -19061,7 +19087,7 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
19061
19087
  };
19062
19088
  const response = await this.makeRequest('/stocks/bars', 'GET', requestParams);
19063
19089
  if (!response.bars) {
19064
- log(`No bars data found in response for ${symbols.length} symbols`, { type: 'warn' });
19090
+ log(`No bars data found in response for ${symbols.length} symbols`, { symbol: symbolsStr, type: 'warn' });
19065
19091
  break;
19066
19092
  }
19067
19093
  // Track currency from first response
@@ -19095,7 +19121,7 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
19095
19121
  const dateRangeStr = earliestTimestamp && latestTimestamp
19096
19122
  ? `${new Date(earliestTimestamp).toLocaleDateString('en-US', { timeZone: 'America/New_York' })} to ${new Date(latestTimestamp).toLocaleDateString('en-US', { timeZone: 'America/New_York' })}`
19097
19123
  : 'unknown range';
19098
- log(`Page ${pageCount}: Fetched ${pageBarsCount.toLocaleString()} bars (total: ${totalBarsCount.toLocaleString()}) for ${symbols.length} symbols, date range: ${dateRangeStr}${hasMorePages ? ', more pages available' : ', complete'}`, { type: 'debug' });
19124
+ 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' });
19099
19125
  // Prevent infinite loops
19100
19126
  if (pageCount > 1000) {
19101
19127
  log(`Stopping pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
@@ -19106,7 +19132,7 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
19106
19132
  const symbolCounts = Object.entries(allBars)
19107
19133
  .map(([symbol, bars]) => `${symbol}: ${bars.length}`)
19108
19134
  .join(', ');
19109
- log(`Bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages for ${symbols.length} symbols (${symbolCounts})`);
19135
+ log(`${params.timeframe} bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages for ${symbols.length} symbols (${symbolCounts})`, { symbol: symbolsStr });
19110
19136
  return {
19111
19137
  bars: allBars,
19112
19138
  next_page_token: null, // Always null since we fetch all pages
@@ -19958,6 +19984,26 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
19958
19984
  const marketDataAPI = AlpacaMarketDataAPI.getInstance();
19959
19985
 
19960
19986
  const limitPriceSlippagePercent100 = 0.1; // 0.1%
19987
+ class AlpacaRequestError extends Error {
19988
+ method;
19989
+ status;
19990
+ url;
19991
+ responseCode;
19992
+ responseDetails;
19993
+ rawResponse;
19994
+ symbol;
19995
+ constructor(params) {
19996
+ super(params.message);
19997
+ this.name = 'AlpacaRequestError';
19998
+ this.method = params.method;
19999
+ this.status = params.status ?? null;
20000
+ this.url = params.url;
20001
+ this.responseCode = params.responseCode ?? null;
20002
+ this.responseDetails = params.responseDetails ?? null;
20003
+ this.rawResponse = params.rawResponse ?? null;
20004
+ this.symbol = params.symbol;
20005
+ }
20006
+ }
19961
20007
  /**
19962
20008
  Websocket example
19963
20009
  const alpacaAPI = createAlpacaTradingAPI(credentials); // type AlpacaCredentials
@@ -20041,6 +20087,75 @@ class AlpacaTradingAPI {
20041
20087
  roundPriceForAlpaca = (price) => {
20042
20088
  return price >= 1 ? Math.round(price * 100) / 100 : Math.round(price * 10000) / 10000;
20043
20089
  };
20090
+ isJsonObject(value) {
20091
+ return value !== null && !Array.isArray(value) && typeof value === 'object';
20092
+ }
20093
+ parseAlpacaAPIErrorResponse(rawResponse) {
20094
+ if (rawResponse.trim() === '') {
20095
+ return null;
20096
+ }
20097
+ try {
20098
+ const parsedValue = JSON.parse(rawResponse);
20099
+ if (!this.isJsonObject(parsedValue)) {
20100
+ return null;
20101
+ }
20102
+ const code = parsedValue['code'];
20103
+ const message = parsedValue['message'];
20104
+ const symbol = parsedValue['symbol'];
20105
+ if (typeof code !== 'number' || typeof message !== 'string') {
20106
+ return null;
20107
+ }
20108
+ if (symbol !== undefined && typeof symbol !== 'string') {
20109
+ return null;
20110
+ }
20111
+ return parsedValue;
20112
+ }
20113
+ catch {
20114
+ return null;
20115
+ }
20116
+ }
20117
+ formatJsonValueForLog(value) {
20118
+ if (value === undefined) {
20119
+ return 'undefined';
20120
+ }
20121
+ if (Array.isArray(value)) {
20122
+ return value.map((entry) => this.formatJsonValueForLog(entry)).join(', ');
20123
+ }
20124
+ if (value === null) {
20125
+ return 'null';
20126
+ }
20127
+ if (typeof value === 'object') {
20128
+ return JSON.stringify(value);
20129
+ }
20130
+ return `${value}`;
20131
+ }
20132
+ formatAlpacaAPIErrorDetails(errorResponse) {
20133
+ return Object.entries(errorResponse)
20134
+ .filter(([key]) => key !== 'code' && key !== 'message' && key !== 'symbol')
20135
+ .map(([key, value]) => `${key}=${this.formatJsonValueForLog(value)}`)
20136
+ .join(', ');
20137
+ }
20138
+ formatRequestAction(requestContext) {
20139
+ return requestContext?.action ? `${requestContext.action} failed` : 'Alpaca request failed';
20140
+ }
20141
+ buildAlpacaAPIErrorMessage(params) {
20142
+ const { requestContext, status, url, errorResponse, rawResponse } = params;
20143
+ const action = this.formatRequestAction(requestContext);
20144
+ const message = errorResponse?.message ?? (rawResponse || 'No error body returned');
20145
+ const responseCode = errorResponse?.code ? ` (code ${errorResponse.code})` : '';
20146
+ const detailSummary = errorResponse ? this.formatAlpacaAPIErrorDetails(errorResponse) : '';
20147
+ const detailText = detailSummary !== ''
20148
+ ? ` Details: ${detailSummary}.`
20149
+ : rawResponse.trim() !== '' && errorResponse === null
20150
+ ? ` Response: ${rawResponse}.`
20151
+ : '';
20152
+ return `${action}: Alpaca API ${status}${responseCode}: ${message}.${detailText} Url: ${url}`;
20153
+ }
20154
+ buildRequestExecutionErrorMessage(params) {
20155
+ const { requestContext, url, errorMessage } = params;
20156
+ const action = this.formatRequestAction(requestContext);
20157
+ return `${action}: ${errorMessage}. Url: ${url}`;
20158
+ }
20044
20159
  handleAuthMessage(data) {
20045
20160
  if (data.status === 'authorized') {
20046
20161
  this.authenticated = true;
@@ -20268,7 +20383,7 @@ class AlpacaTradingAPI {
20268
20383
  this.ws?.on('message', handleListenResponse);
20269
20384
  });
20270
20385
  }
20271
- async makeRequest(endpoint, method = 'GET', body, queryString = '') {
20386
+ async makeRequest(endpoint, method = 'GET', body, queryString = '', requestContext) {
20272
20387
  const url = `${this.apiBaseUrl}${endpoint}${queryString}`;
20273
20388
  try {
20274
20389
  const response = await fetch(url, {
@@ -20277,9 +20392,27 @@ class AlpacaTradingAPI {
20277
20392
  body: body ? JSON.stringify(body) : undefined,
20278
20393
  });
20279
20394
  if (!response.ok) {
20280
- const errorText = await response.text();
20281
- this.log(`Alpaca API error (${response.status}): ${errorText}`, { type: 'error' });
20282
- throw new Error(`Alpaca API error (${response.status}): ${errorText}`);
20395
+ const rawResponse = await response.text();
20396
+ const errorResponse = this.parseAlpacaAPIErrorResponse(rawResponse);
20397
+ const symbol = requestContext?.symbol ?? errorResponse?.symbol;
20398
+ const error = new AlpacaRequestError({
20399
+ message: this.buildAlpacaAPIErrorMessage({
20400
+ requestContext,
20401
+ status: response.status,
20402
+ url,
20403
+ errorResponse,
20404
+ rawResponse,
20405
+ }),
20406
+ method,
20407
+ status: response.status,
20408
+ url,
20409
+ responseCode: errorResponse?.code ?? null,
20410
+ responseDetails: errorResponse,
20411
+ rawResponse,
20412
+ symbol,
20413
+ });
20414
+ this.log(error.message, { symbol, type: 'error' });
20415
+ throw error;
20283
20416
  }
20284
20417
  // Handle responses with no content (e.g., 204 No Content)
20285
20418
  if (response.status === 204 || response.headers.get('content-length') === '0') {
@@ -20287,23 +20420,37 @@ class AlpacaTradingAPI {
20287
20420
  }
20288
20421
  const contentType = response.headers.get('content-type');
20289
20422
  if (contentType && contentType.includes('application/json')) {
20290
- return await response.json();
20423
+ return (await response.json());
20291
20424
  }
20292
20425
  // For non-JSON responses, return the text content
20293
20426
  const textContent = await response.text();
20294
- return textContent || null;
20427
+ return (textContent || null);
20295
20428
  }
20296
- catch (err) {
20297
- const error = err;
20298
- this.log(`Error in makeRequest: ${error.message}. Url: ${url}`, {
20299
- source: 'AlpacaAPI',
20300
- type: 'error',
20429
+ catch (error) {
20430
+ if (error instanceof AlpacaRequestError) {
20431
+ throw error;
20432
+ }
20433
+ const normalizedError = error instanceof Error ? error : new Error(`${error}`);
20434
+ const symbol = requestContext?.symbol;
20435
+ const requestError = new AlpacaRequestError({
20436
+ message: this.buildRequestExecutionErrorMessage({
20437
+ requestContext,
20438
+ url,
20439
+ errorMessage: normalizedError.message,
20440
+ }),
20441
+ method,
20442
+ url,
20443
+ rawResponse: null,
20444
+ symbol,
20301
20445
  });
20302
- throw error;
20446
+ this.log(requestError.message, { symbol, type: 'error' });
20447
+ throw requestError;
20303
20448
  }
20304
20449
  }
20305
20450
  async getPositions(assetClass) {
20306
- const positions = (await this.makeRequest('/positions'));
20451
+ const positions = await this.makeRequest('/positions', 'GET', undefined, '', {
20452
+ action: 'Get positions',
20453
+ });
20307
20454
  if (assetClass) {
20308
20455
  return positions.filter((position) => position.asset_class === assetClass);
20309
20456
  }
@@ -20341,22 +20488,15 @@ class AlpacaTradingAPI {
20341
20488
  if (params.side)
20342
20489
  queryParams.append('side', params.side);
20343
20490
  const endpoint = `/orders${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;
20344
- try {
20345
- return await this.makeRequest(endpoint);
20346
- }
20347
- catch (error) {
20348
- this.log(`Error getting orders: ${error}`, { type: 'error' });
20349
- throw error;
20350
- }
20491
+ return this.makeRequest(endpoint, 'GET', undefined, '', {
20492
+ action: 'Get orders',
20493
+ symbol: params.symbols?.join(','),
20494
+ });
20351
20495
  }
20352
20496
  async getAccountDetails() {
20353
- try {
20354
- return await this.makeRequest('/account');
20355
- }
20356
- catch (error) {
20357
- this.log(`Error getting account details: ${error}`, { type: 'error' });
20358
- throw error;
20359
- }
20497
+ return this.makeRequest('/account', 'GET', undefined, '', {
20498
+ action: 'Get account details',
20499
+ });
20360
20500
  }
20361
20501
  /**
20362
20502
  * Create a trailing stop order
@@ -20374,27 +20514,21 @@ class AlpacaTradingAPI {
20374
20514
  });
20375
20515
  const body = {
20376
20516
  symbol,
20377
- qty: Math.abs(qty),
20517
+ qty: Math.abs(qty).toString(),
20378
20518
  side,
20379
20519
  position_intent,
20380
20520
  order_class: 'simple',
20381
20521
  type: 'trailing_stop',
20382
- trail_percent: trailPercent100, // Already in decimal form (e.g., 4 for 4%)
20522
+ trail_percent: trailPercent100.toString(),
20383
20523
  time_in_force: 'gtc',
20384
20524
  };
20385
20525
  if (client_order_id !== undefined) {
20386
20526
  body.client_order_id = client_order_id;
20387
20527
  }
20388
- try {
20389
- return await this.makeRequest(`/orders`, 'POST', body);
20390
- }
20391
- catch (error) {
20392
- this.log(`Error creating trailing stop: ${error}`, {
20393
- symbol,
20394
- type: 'error',
20395
- });
20396
- throw error;
20397
- }
20528
+ return this.makeRequest('/orders', 'POST', body, '', {
20529
+ action: 'Create trailing stop order',
20530
+ symbol,
20531
+ });
20398
20532
  }
20399
20533
  /**
20400
20534
  * Create a stop order (stop or stop-limit)
@@ -20420,25 +20554,19 @@ class AlpacaTradingAPI {
20420
20554
  position_intent,
20421
20555
  order_class: 'simple',
20422
20556
  type: isStopLimit ? 'stop_limit' : 'stop',
20423
- stop_price: this.roundPriceForAlpaca(stopPrice),
20557
+ stop_price: this.roundPriceForAlpaca(stopPrice).toString(),
20424
20558
  time_in_force: 'gtc',
20425
20559
  };
20426
- if (isStopLimit) {
20427
- body.limit_price = this.roundPriceForAlpaca(limitPrice);
20560
+ if (limitPrice !== undefined) {
20561
+ body.limit_price = this.roundPriceForAlpaca(limitPrice).toString();
20428
20562
  }
20429
20563
  if (client_order_id !== undefined) {
20430
20564
  body.client_order_id = client_order_id;
20431
20565
  }
20432
- try {
20433
- return await this.makeRequest(`/orders`, 'POST', body);
20434
- }
20435
- catch (error) {
20436
- this.log(`Error creating ${orderType} order: ${error}`, {
20437
- symbol,
20438
- type: 'error',
20439
- });
20440
- throw error;
20441
- }
20566
+ return this.makeRequest('/orders', 'POST', body, '', {
20567
+ action: `Create ${orderType} order`,
20568
+ symbol,
20569
+ });
20442
20570
  }
20443
20571
  /**
20444
20572
  * Create a market order
@@ -20464,13 +20592,10 @@ class AlpacaTradingAPI {
20464
20592
  if (client_order_id !== undefined) {
20465
20593
  body.client_order_id = client_order_id;
20466
20594
  }
20467
- try {
20468
- return await this.makeRequest('/orders', 'POST', body);
20469
- }
20470
- catch (error) {
20471
- this.log(`Error creating market order: ${error}`, { type: 'error' });
20472
- throw error;
20473
- }
20595
+ return this.makeRequest('/orders', 'POST', body, '', {
20596
+ action: 'Create market order',
20597
+ symbol,
20598
+ });
20474
20599
  }
20475
20600
  /**
20476
20601
  * Create a Market on Open (MOO) order - executes in the opening auction
@@ -20504,13 +20629,10 @@ class AlpacaTradingAPI {
20504
20629
  if (client_order_id !== undefined) {
20505
20630
  body.client_order_id = client_order_id;
20506
20631
  }
20507
- try {
20508
- return await this.makeRequest('/orders', 'POST', body);
20509
- }
20510
- catch (error) {
20511
- this.log(`Error creating MOO order: ${error}`, { type: 'error' });
20512
- throw error;
20513
- }
20632
+ return this.makeRequest('/orders', 'POST', body, '', {
20633
+ action: 'Create market on open order',
20634
+ symbol,
20635
+ });
20514
20636
  }
20515
20637
  /**
20516
20638
  * Create a Market on Close (MOC) order - executes in the closing auction
@@ -20544,13 +20666,10 @@ class AlpacaTradingAPI {
20544
20666
  if (client_order_id !== undefined) {
20545
20667
  body.client_order_id = client_order_id;
20546
20668
  }
20547
- try {
20548
- return await this.makeRequest('/orders', 'POST', body);
20549
- }
20550
- catch (error) {
20551
- this.log(`Error creating MOC order: ${error}`, { type: 'error' });
20552
- throw error;
20553
- }
20669
+ return this.makeRequest('/orders', 'POST', body, '', {
20670
+ action: 'Create market on close order',
20671
+ symbol,
20672
+ });
20554
20673
  }
20555
20674
  /**
20556
20675
  * Create an OCO (One-Cancels-Other) order with take profit and stop loss
@@ -20576,32 +20695,29 @@ class AlpacaTradingAPI {
20576
20695
  position_intent,
20577
20696
  order_class: 'oco',
20578
20697
  type: 'limit',
20579
- limit_price: this.roundPriceForAlpaca(limitPrice),
20698
+ limit_price: this.roundPriceForAlpaca(limitPrice).toString(),
20580
20699
  time_in_force: 'gtc',
20581
20700
  take_profit: {
20582
- limit_price: this.roundPriceForAlpaca(takeProfitPrice),
20701
+ limit_price: this.roundPriceForAlpaca(takeProfitPrice).toString(),
20583
20702
  },
20584
20703
  stop_loss: {
20585
- stop_price: this.roundPriceForAlpaca(stopLossPrice),
20704
+ stop_price: this.roundPriceForAlpaca(stopLossPrice).toString(),
20586
20705
  },
20587
20706
  };
20588
20707
  // If stop loss limit price is provided, create stop-limit order
20589
20708
  if (stopLossLimitPrice !== undefined) {
20590
- body.stop_loss.limit_price = this.roundPriceForAlpaca(stopLossLimitPrice);
20709
+ body.stop_loss = {
20710
+ stop_price: this.roundPriceForAlpaca(stopLossPrice).toString(),
20711
+ limit_price: this.roundPriceForAlpaca(stopLossLimitPrice).toString(),
20712
+ };
20591
20713
  }
20592
20714
  if (client_order_id !== undefined) {
20593
20715
  body.client_order_id = client_order_id;
20594
20716
  }
20595
- try {
20596
- return await this.makeRequest(`/orders`, 'POST', body);
20597
- }
20598
- catch (error) {
20599
- this.log(`Error creating OCO order: ${error}`, {
20600
- symbol,
20601
- type: 'error',
20602
- });
20603
- throw error;
20604
- }
20717
+ return this.makeRequest('/orders', 'POST', body, '', {
20718
+ action: 'Create OCO order',
20719
+ symbol,
20720
+ });
20605
20721
  }
20606
20722
  /**
20607
20723
  * 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.
@@ -20609,35 +20725,26 @@ class AlpacaTradingAPI {
20609
20725
  * @returns the current trail percent
20610
20726
  */
20611
20727
  async getCurrentTrailPercent(symbol) {
20612
- try {
20613
- const orders = await this.getOrders({
20614
- status: 'open',
20615
- symbols: [symbol],
20728
+ const orders = await this.getOrders({
20729
+ status: 'open',
20730
+ symbols: [symbol],
20731
+ });
20732
+ const trailingStopOrder = orders.find((order) => order.type === 'trailing_stop' &&
20733
+ (order.position_intent === 'sell_to_close' || order.position_intent === 'buy_to_close'));
20734
+ if (!trailingStopOrder) {
20735
+ this.log(`No closing trailing stop order found for ${symbol}`, {
20736
+ symbol,
20616
20737
  });
20617
- const trailingStopOrder = orders.find((order) => order.type === 'trailing_stop' &&
20618
- (order.position_intent === 'sell_to_close' || order.position_intent === 'buy_to_close'));
20619
- if (!trailingStopOrder) {
20620
- this.log(`No closing trailing stop order found for ${symbol}`, {
20621
- symbol,
20622
- });
20623
- return null;
20624
- }
20625
- if (!trailingStopOrder.trail_percent) {
20626
- this.log(`Trailing stop order found for ${symbol} but no trail_percent value`, {
20627
- symbol,
20628
- });
20629
- return null;
20630
- }
20631
- const trailPercent = parseFloat(trailingStopOrder.trail_percent);
20632
- return trailPercent;
20738
+ return null;
20633
20739
  }
20634
- catch (error) {
20635
- this.log(`Error getting current trail percent: ${error}`, {
20740
+ if (!trailingStopOrder.trail_percent) {
20741
+ this.log(`Trailing stop order found for ${symbol} but no trail_percent value`, {
20636
20742
  symbol,
20637
- type: 'error',
20638
20743
  });
20639
- throw error;
20744
+ return null;
20640
20745
  }
20746
+ const trailPercent = parseFloat(trailingStopOrder.trail_percent);
20747
+ return trailPercent;
20641
20748
  }
20642
20749
  /**
20643
20750
  * Update the trail percent for a trailing stop order
@@ -20669,18 +20776,10 @@ class AlpacaTradingAPI {
20669
20776
  this.log(`Updating trailing stop for ${symbol} from ${currentTrailPercent}% to ${trailPercent100}%`, {
20670
20777
  symbol,
20671
20778
  });
20672
- try {
20673
- await this.makeRequest(`/orders/${trailingStopOrder.id}`, 'PATCH', {
20674
- trail: trailPercent100.toString(), // Changed from trail_percent to trail
20675
- });
20676
- }
20677
- catch (error) {
20678
- this.log(`Error updating trailing stop: ${error}`, {
20679
- symbol,
20680
- type: 'error',
20681
- });
20682
- throw error;
20683
- }
20779
+ await this.makeRequest(`/orders/${trailingStopOrder.id}`, 'PATCH', { trail: trailPercent100.toString() }, '', {
20780
+ action: 'Update trailing stop order',
20781
+ symbol,
20782
+ });
20684
20783
  }
20685
20784
  /**
20686
20785
  * Cancel all open orders
@@ -20688,10 +20787,16 @@ class AlpacaTradingAPI {
20688
20787
  async cancelAllOrders() {
20689
20788
  this.log(`Canceling all open orders`);
20690
20789
  try {
20691
- await this.makeRequest('/orders', 'DELETE');
20790
+ await this.makeRequest('/orders', 'DELETE', undefined, '', {
20791
+ action: 'Cancel all open orders',
20792
+ });
20692
20793
  }
20693
20794
  catch (error) {
20694
- this.log(`Error canceling all orders: ${error}`, { type: 'error' });
20795
+ if (!(error instanceof AlpacaRequestError)) {
20796
+ this.log(`Error canceling all orders: ${error instanceof Error ? error.message : `${error}`}`, {
20797
+ type: 'error',
20798
+ });
20799
+ }
20695
20800
  }
20696
20801
  }
20697
20802
  /**
@@ -20703,15 +20808,14 @@ class AlpacaTradingAPI {
20703
20808
  async cancelOrder(orderId) {
20704
20809
  this.log(`Attempting to cancel order ${orderId}`);
20705
20810
  try {
20706
- await this.makeRequest(`/orders/${orderId}`, 'DELETE');
20811
+ await this.makeRequest(`/orders/${orderId}`, 'DELETE', undefined, '', {
20812
+ action: `Cancel order ${orderId}`,
20813
+ });
20707
20814
  this.log(`Successfully canceled order ${orderId}`);
20708
20815
  }
20709
20816
  catch (error) {
20710
20817
  // If the error is a 422, it means the order is not cancelable
20711
- if (error instanceof Error && error.message.includes('422')) {
20712
- this.log(`Order ${orderId} is not cancelable`, {
20713
- type: 'error',
20714
- });
20818
+ if (error instanceof AlpacaRequestError && error.status === 422) {
20715
20819
  throw new Error(`Order ${orderId} is not cancelable`);
20716
20820
  }
20717
20821
  // Re-throw other errors
@@ -20746,13 +20850,10 @@ class AlpacaTradingAPI {
20746
20850
  if (client_order_id !== undefined) {
20747
20851
  body.client_order_id = client_order_id;
20748
20852
  }
20749
- try {
20750
- return await this.makeRequest('/orders', 'POST', body);
20751
- }
20752
- catch (error) {
20753
- this.log(`Error creating limit order: ${error}`, { type: 'error' });
20754
- throw error;
20755
- }
20853
+ return this.makeRequest('/orders', 'POST', body, '', {
20854
+ action: 'Create limit order',
20855
+ symbol,
20856
+ });
20756
20857
  }
20757
20858
  /**
20758
20859
  * Close all equities positions
@@ -20818,7 +20919,9 @@ class AlpacaTradingAPI {
20818
20919
  }
20819
20920
  }
20820
20921
  else {
20821
- await this.makeRequest('/positions', 'DELETE', undefined, options.cancel_orders ? '?cancel_orders=true' : '');
20922
+ await this.makeRequest('/positions', 'DELETE', undefined, options.cancel_orders ? '?cancel_orders=true' : '', {
20923
+ action: 'Close all positions',
20924
+ });
20822
20925
  }
20823
20926
  }
20824
20927
  /**
@@ -20897,7 +21000,9 @@ class AlpacaTradingAPI {
20897
21000
  queryParams.append('end', params.end);
20898
21001
  if (params.date_end)
20899
21002
  queryParams.append('date_end', params.date_end);
20900
- const response = await this.makeRequest(`/account/portfolio/history?${queryParams.toString()}`);
21003
+ const response = await this.makeRequest(`/account/portfolio/history?${queryParams.toString()}`, 'GET', undefined, '', {
21004
+ action: 'Get portfolio history',
21005
+ });
20901
21006
  return response;
20902
21007
  }
20903
21008
  /**
@@ -20988,7 +21093,10 @@ class AlpacaTradingAPI {
20988
21093
  this.log(`Fetching option contracts for ${params.underlying_symbols.join(', ')}`, {
20989
21094
  symbol: params.underlying_symbols.join(', '),
20990
21095
  });
20991
- const response = (await this.makeRequest(`/options/contracts?${queryParams.toString()}`));
21096
+ const response = await this.makeRequest(`/options/contracts?${queryParams.toString()}`, 'GET', undefined, '', {
21097
+ action: 'Get option contracts',
21098
+ symbol: params.underlying_symbols.join(', '),
21099
+ });
20992
21100
  this.log(`Found ${response.option_contracts.length} option contracts`, {
20993
21101
  symbol: params.underlying_symbols.join(', '),
20994
21102
  });
@@ -21003,7 +21111,10 @@ class AlpacaTradingAPI {
21003
21111
  this.log(`Fetching option contract details for ${symbolOrId}`, {
21004
21112
  symbol: symbolOrId,
21005
21113
  });
21006
- const response = (await this.makeRequest(`/options/contracts/${symbolOrId}`));
21114
+ const response = await this.makeRequest(`/options/contracts/${symbolOrId}`, 'GET', undefined, '', {
21115
+ action: 'Get option contract details',
21116
+ symbol: symbolOrId,
21117
+ });
21007
21118
  this.log(`Found option contract details for ${symbolOrId}: ${response.name}`, {
21008
21119
  symbol: symbolOrId,
21009
21120
  });
@@ -21021,10 +21132,10 @@ class AlpacaTradingAPI {
21021
21132
  */
21022
21133
  async createOptionOrder(symbol, qty, side, position_intent, type, limitPrice) {
21023
21134
  if (!Number.isInteger(qty) || qty <= 0) {
21024
- this.log('Quantity must be a positive whole number for option orders', { type: 'error' });
21135
+ this.log('Quantity must be a positive whole number for option orders', { symbol, type: 'error' });
21025
21136
  }
21026
21137
  if (type === 'limit' && limitPrice === undefined) {
21027
- this.log('Limit price is required for limit orders', { type: 'error' });
21138
+ this.log('Limit price is required for limit orders', { symbol, type: 'error' });
21028
21139
  }
21029
21140
  this.log(`Creating ${type} option order for ${symbol}: ${side} ${qty} contracts (${position_intent})${type === 'limit' ? ` at $${limitPrice?.toFixed(2)}` : ''}`, {
21030
21141
  symbol,
@@ -21042,7 +21153,10 @@ class AlpacaTradingAPI {
21042
21153
  if (type === 'limit' && limitPrice !== undefined) {
21043
21154
  orderData.limit_price = this.roundPriceForAlpaca(limitPrice).toString();
21044
21155
  }
21045
- return this.makeRequest('/orders', 'POST', orderData);
21156
+ return this.makeRequest('/orders', 'POST', orderData, '', {
21157
+ action: 'Create option order',
21158
+ symbol,
21159
+ });
21046
21160
  }
21047
21161
  /**
21048
21162
  * Create a multi-leg option order
@@ -21053,16 +21167,19 @@ class AlpacaTradingAPI {
21053
21167
  * @returns The created multi-leg order
21054
21168
  */
21055
21169
  async createMultiLegOptionOrder(legs, qty, type, limitPrice) {
21170
+ const legSymbols = legs.map((leg) => leg.symbol).join(', ');
21056
21171
  if (!Number.isInteger(qty) || qty <= 0) {
21057
- this.log('Quantity must be a positive whole number for option orders', { type: 'error' });
21172
+ this.log('Quantity must be a positive whole number for option orders', {
21173
+ symbol: legSymbols,
21174
+ type: 'error',
21175
+ });
21058
21176
  }
21059
21177
  if (type === 'limit' && limitPrice === undefined) {
21060
- this.log('Limit price is required for limit orders', { type: 'error' });
21178
+ this.log('Limit price is required for limit orders', { symbol: legSymbols, type: 'error' });
21061
21179
  }
21062
21180
  if (legs.length < 2) {
21063
- this.log('Multi-leg orders require at least 2 legs', { type: 'error' });
21181
+ this.log('Multi-leg orders require at least 2 legs', { symbol: legSymbols, type: 'error' });
21064
21182
  }
21065
- const legSymbols = legs.map((leg) => leg.symbol).join(', ');
21066
21183
  this.log(`Creating multi-leg ${type} option order with ${legs.length} legs (${legSymbols})${type === 'limit' ? ` at $${limitPrice?.toFixed(2)}` : ''}`, {
21067
21184
  symbol: legSymbols,
21068
21185
  });
@@ -21076,7 +21193,10 @@ class AlpacaTradingAPI {
21076
21193
  if (type === 'limit' && limitPrice !== undefined) {
21077
21194
  orderData.limit_price = this.roundPriceForAlpaca(limitPrice).toString();
21078
21195
  }
21079
- return this.makeRequest('/orders', 'POST', orderData);
21196
+ return this.makeRequest('/orders', 'POST', orderData, '', {
21197
+ action: 'Create multi-leg option order',
21198
+ symbol: legSymbols,
21199
+ });
21080
21200
  }
21081
21201
  /**
21082
21202
  * Exercise an option contract
@@ -21087,7 +21207,10 @@ class AlpacaTradingAPI {
21087
21207
  this.log(`Exercising option contract ${symbolOrContractId}`, {
21088
21208
  symbol: symbolOrContractId,
21089
21209
  });
21090
- return this.makeRequest(`/positions/${symbolOrContractId}/exercise`, 'POST');
21210
+ return this.makeRequest(`/positions/${symbolOrContractId}/exercise`, 'POST', undefined, '', {
21211
+ action: 'Exercise option contract',
21212
+ symbol: symbolOrContractId,
21213
+ });
21091
21214
  }
21092
21215
  /**
21093
21216
  * Get option positions
@@ -21123,7 +21246,9 @@ class AlpacaTradingAPI {
21123
21246
  queryParams.append('date', date);
21124
21247
  }
21125
21248
  this.log(`Fetching option activities${activityType ? ` of type ${activityType}` : ''}${date ? ` for date ${date}` : ''}`);
21126
- return this.makeRequest(`/account/activities?${queryParams.toString()}`);
21249
+ return this.makeRequest(`/account/activities?${queryParams.toString()}`, 'GET', undefined, '', {
21250
+ action: 'Get option activities',
21251
+ });
21127
21252
  }
21128
21253
  /**
21129
21254
  * Create a long call spread (buy lower strike call, sell higher strike call)
@@ -21221,13 +21346,7 @@ class AlpacaTradingAPI {
21221
21346
  position_intent: 'buy_to_open',
21222
21347
  },
21223
21348
  ];
21224
- try {
21225
- return await this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
21226
- }
21227
- catch (error) {
21228
- this.log(`Error creating iron condor: ${error}`, { type: 'error' });
21229
- throw error;
21230
- }
21349
+ return this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
21231
21350
  }
21232
21351
  /**
21233
21352
  * Create a covered call (sell call option against owned stock)
@@ -21243,13 +21362,7 @@ class AlpacaTradingAPI {
21243
21362
  });
21244
21363
  // For covered calls, we don't need to include the stock leg if we already own the shares
21245
21364
  // We just create a simple sell order for the call option
21246
- try {
21247
- return await this.createOptionOrder(callOptionSymbol, qty, 'sell', 'sell_to_open', 'limit', limitPrice);
21248
- }
21249
- catch (error) {
21250
- this.log(`Error creating covered call: ${error}`, { type: 'error' });
21251
- throw error;
21252
- }
21365
+ return this.createOptionOrder(callOptionSymbol, qty, 'sell', 'sell_to_open', 'limit', limitPrice);
21253
21366
  }
21254
21367
  /**
21255
21368
  * Roll an option position to a new expiration or strike
@@ -21284,13 +21397,7 @@ class AlpacaTradingAPI {
21284
21397
  position_intent: openPositionIntent,
21285
21398
  },
21286
21399
  ];
21287
- try {
21288
- return await this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
21289
- }
21290
- catch (error) {
21291
- this.log(`Error rolling option position: ${error}`, { type: 'error' });
21292
- throw error;
21293
- }
21400
+ return this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
21294
21401
  }
21295
21402
  /**
21296
21403
  * Get option chain for a specific underlying symbol and expiration date
@@ -21314,10 +21421,12 @@ class AlpacaTradingAPI {
21314
21421
  return response.option_contracts || [];
21315
21422
  }
21316
21423
  catch (error) {
21317
- this.log(`Failed to fetch option chain for ${underlyingSymbol}: ${error instanceof Error ? error.message : 'Unknown error'}`, {
21318
- type: 'error',
21319
- symbol: underlyingSymbol,
21320
- });
21424
+ if (!(error instanceof AlpacaRequestError)) {
21425
+ this.log(`Failed to fetch option chain for ${underlyingSymbol}: ${error instanceof Error ? error.message : `${error}`}`, {
21426
+ type: 'error',
21427
+ symbol: underlyingSymbol,
21428
+ });
21429
+ }
21321
21430
  return [];
21322
21431
  }
21323
21432
  }
@@ -21348,10 +21457,12 @@ class AlpacaTradingAPI {
21348
21457
  return Array.from(expirationDates).sort();
21349
21458
  }
21350
21459
  catch (error) {
21351
- this.log(`Failed to fetch expiration dates for ${underlyingSymbol}: ${error instanceof Error ? error.message : 'Unknown error'}`, {
21352
- type: 'error',
21353
- symbol: underlyingSymbol,
21354
- });
21460
+ if (!(error instanceof AlpacaRequestError)) {
21461
+ this.log(`Failed to fetch expiration dates for ${underlyingSymbol}: ${error instanceof Error ? error.message : `${error}`}`, {
21462
+ type: 'error',
21463
+ symbol: underlyingSymbol,
21464
+ });
21465
+ }
21355
21466
  return [];
21356
21467
  }
21357
21468
  }
@@ -21410,7 +21521,10 @@ class AlpacaTradingAPI {
21410
21521
  this.log(`Canceling open order for ${order.symbol}`, {
21411
21522
  symbol: order.symbol,
21412
21523
  });
21413
- await this.makeRequest(`/orders/${order.id}`, 'DELETE');
21524
+ await this.makeRequest(`/orders/${order.id}`, 'DELETE', undefined, '', {
21525
+ action: `Cancel option order ${order.id}`,
21526
+ symbol: order.symbol,
21527
+ });
21414
21528
  }
21415
21529
  }
21416
21530
  }
@@ -21433,13 +21547,7 @@ class AlpacaTradingAPI {
21433
21547
  const quantityToClose = qty || parseInt(position.qty);
21434
21548
  const side = position.side === 'long' ? 'sell' : 'buy';
21435
21549
  const positionIntent = side === 'sell' ? 'sell_to_close' : 'buy_to_close';
21436
- try {
21437
- return await this.createOptionOrder(symbol, quantityToClose, side, positionIntent, 'market');
21438
- }
21439
- catch (error) {
21440
- this.log(`Error closing option position: ${error}`, { type: 'error' });
21441
- throw error;
21442
- }
21550
+ return this.createOptionOrder(symbol, quantityToClose, side, positionIntent, 'market');
21443
21551
  }
21444
21552
  /**
21445
21553
  * Create a complete equities trade with optional stop loss and take profit
@@ -21574,16 +21682,10 @@ class AlpacaTradingAPI {
21574
21682
  this.log(logMessage, {
21575
21683
  symbol,
21576
21684
  });
21577
- try {
21578
- return await this.makeRequest('/orders', 'POST', orderData);
21579
- }
21580
- catch (error) {
21581
- this.log(`Error creating equities trade: ${error}`, {
21582
- symbol,
21583
- type: 'error',
21584
- });
21585
- throw error;
21586
- }
21685
+ return this.makeRequest('/orders', 'POST', orderData, '', {
21686
+ action: 'Create equities trade',
21687
+ symbol,
21688
+ });
21587
21689
  }
21588
21690
  }
21589
21691