@discomedia/utils 1.0.63 → 1.0.65

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.
Files changed (32) hide show
  1. package/dist/index-frontend.cjs +41 -5
  2. package/dist/index-frontend.cjs.map +1 -1
  3. package/dist/index-frontend.mjs +41 -5
  4. package/dist/index-frontend.mjs.map +1 -1
  5. package/dist/index.cjs +320 -208
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.mjs +320 -208
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/package.json +2 -2
  10. package/dist/test.js +364 -210
  11. package/dist/test.js.map +1 -1
  12. package/dist/types/alpaca-market-data-api.d.ts.map +1 -1
  13. package/dist/types/alpaca-trading-api.d.ts +8 -1
  14. package/dist/types/alpaca-trading-api.d.ts.map +1 -1
  15. package/dist/types/llm-config.d.ts.map +1 -1
  16. package/dist/types/llm-images.d.ts.map +1 -1
  17. package/dist/types/llm-openai.d.ts.map +1 -1
  18. package/dist/types/types/alpaca-types.d.ts +29 -0
  19. package/dist/types/types/alpaca-types.d.ts.map +1 -1
  20. package/dist/types/types/llm-types.d.ts +2 -2
  21. package/dist/types/types/llm-types.d.ts.map +1 -1
  22. package/dist/types-frontend/alpaca-market-data-api.d.ts.map +1 -1
  23. package/dist/types-frontend/alpaca-trading-api.d.ts +8 -1
  24. package/dist/types-frontend/alpaca-trading-api.d.ts.map +1 -1
  25. package/dist/types-frontend/llm-config.d.ts.map +1 -1
  26. package/dist/types-frontend/llm-images.d.ts.map +1 -1
  27. package/dist/types-frontend/llm-openai.d.ts.map +1 -1
  28. package/dist/types-frontend/types/alpaca-types.d.ts +29 -0
  29. package/dist/types-frontend/types/alpaca-types.d.ts.map +1 -1
  30. package/dist/types-frontend/types/llm-types.d.ts +2 -2
  31. package/dist/types-frontend/types/llm-types.d.ts.map +1 -1
  32. package/package.json +2 -2
package/dist/index.cjs CHANGED
@@ -1148,6 +1148,7 @@ function isOpenRouterModel(model) {
1148
1148
  const openRouterModels = [
1149
1149
  'openai/gpt-5',
1150
1150
  'openai/gpt-5-mini',
1151
+ 'openai/gpt-5.4-mini',
1151
1152
  'openai/gpt-5-nano',
1152
1153
  'openai/gpt-5.1',
1153
1154
  'openai/gpt-5.4',
@@ -1616,7 +1617,7 @@ const safeJSON = (text) => {
1616
1617
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
1617
1618
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1618
1619
 
1619
- const VERSION = '6.27.0'; // x-release-please-version
1620
+ const VERSION = '6.32.0'; // x-release-please-version
1620
1621
 
1621
1622
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
1622
1623
  const isRunningInBrowser = () => {
@@ -4860,7 +4861,7 @@ class Speech extends APIResource {
4860
4861
  * const speech = await client.audio.speech.create({
4861
4862
  * input: 'input',
4862
4863
  * model: 'string',
4863
- * voice: 'ash',
4864
+ * voice: 'string',
4864
4865
  * });
4865
4866
  *
4866
4867
  * const content = await speech.blob();
@@ -8141,7 +8142,7 @@ class Videos extends APIResource {
8141
8142
  * Create a new video generation job from a prompt and optional reference assets.
8142
8143
  */
8143
8144
  create(body, options) {
8144
- return this._client.post('/videos', maybeMultipartFormRequestOptions({ body, ...options }, this._client));
8145
+ return this._client.post('/videos', multipartFormRequestOptions({ body, ...options }, this._client));
8145
8146
  }
8146
8147
  /**
8147
8148
  * Fetch the latest metadata for a generated video.
@@ -8161,6 +8162,12 @@ class Videos extends APIResource {
8161
8162
  delete(videoID, options) {
8162
8163
  return this._client.delete(path `/videos/${videoID}`, options);
8163
8164
  }
8165
+ /**
8166
+ * Create a character from an uploaded video.
8167
+ */
8168
+ createCharacter(body, options) {
8169
+ return this._client.post('/videos/characters', multipartFormRequestOptions({ body, ...options }, this._client));
8170
+ }
8164
8171
  /**
8165
8172
  * Download the generated video bytes or a derived preview asset.
8166
8173
  *
@@ -8174,6 +8181,25 @@ class Videos extends APIResource {
8174
8181
  __binaryResponse: true,
8175
8182
  });
8176
8183
  }
8184
+ /**
8185
+ * Create a new video generation job by editing a source video or existing
8186
+ * generated video.
8187
+ */
8188
+ edit(body, options) {
8189
+ return this._client.post('/videos/edits', multipartFormRequestOptions({ body, ...options }, this._client));
8190
+ }
8191
+ /**
8192
+ * Create an extension of a completed video.
8193
+ */
8194
+ extend(body, options) {
8195
+ return this._client.post('/videos/extensions', multipartFormRequestOptions({ body, ...options }, this._client));
8196
+ }
8197
+ /**
8198
+ * Fetch a character.
8199
+ */
8200
+ getCharacter(characterID, options) {
8201
+ return this._client.get(path `/videos/characters/${characterID}`, options);
8202
+ }
8177
8203
  /**
8178
8204
  * Create a remix of a completed video using a refreshed prompt.
8179
8205
  */
@@ -8455,8 +8481,9 @@ class OpenAI {
8455
8481
  new URL(path)
8456
8482
  : new URL(baseURL + (baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path));
8457
8483
  const defaultQuery = this.defaultQuery();
8458
- if (!isEmptyObj$1(defaultQuery)) {
8459
- query = { ...defaultQuery, ...query };
8484
+ const pathQuery = Object.fromEntries(url.searchParams);
8485
+ if (!isEmptyObj$1(defaultQuery) || !isEmptyObj$1(pathQuery)) {
8486
+ query = { ...pathQuery, ...defaultQuery, ...query };
8460
8487
  }
8461
8488
  if (typeof query === 'object' && query && !Array.isArray(query)) {
8462
8489
  url.search = this.stringifyQuery(query);
@@ -11676,6 +11703,11 @@ const openAiModelCosts = {
11676
11703
  cacheHitCost: 0.025 / 1_000_000,
11677
11704
  outputCost: 2 / 1_000_000,
11678
11705
  },
11706
+ 'gpt-5.4-mini': {
11707
+ inputCost: 0.25 / 1_000_000,
11708
+ cacheHitCost: 0.025 / 1_000_000,
11709
+ outputCost: 2 / 1_000_000,
11710
+ },
11679
11711
  'gpt-5-nano': {
11680
11712
  inputCost: 0.05 / 1_000_000,
11681
11713
  cacheHitCost: 0.005 / 1_000_000,
@@ -12186,6 +12218,7 @@ const isSupportedModel = (model) => {
12186
12218
  'gpt-4.1-nano',
12187
12219
  'gpt-5',
12188
12220
  'gpt-5-mini',
12221
+ 'gpt-5.4-mini',
12189
12222
  'gpt-5-nano',
12190
12223
  'gpt-5.1',
12191
12224
  'gpt-5.4',
@@ -12214,6 +12247,7 @@ function supportsTemperature(model) {
12214
12247
  'o3',
12215
12248
  'gpt-5',
12216
12249
  'gpt-5-mini',
12250
+ 'gpt-5.4-mini',
12217
12251
  'gpt-5-nano',
12218
12252
  'gpt-5.1',
12219
12253
  'gpt-5.4',
@@ -12243,6 +12277,7 @@ function isGPT5Model(model) {
12243
12277
  const gpt5Models = [
12244
12278
  'gpt-5',
12245
12279
  'gpt-5-mini',
12280
+ 'gpt-5.4-mini',
12246
12281
  'gpt-5-nano',
12247
12282
  'gpt-5.1',
12248
12283
  'gpt-5.4',
@@ -12552,6 +12587,7 @@ const MULTIMODAL_VISION_MODELS = new Set([
12552
12587
  'gpt-4o',
12553
12588
  'gpt-5',
12554
12589
  'gpt-5-mini',
12590
+ 'gpt-5.4-mini',
12555
12591
  'gpt-5-nano',
12556
12592
  'gpt-5.1',
12557
12593
  'gpt-5.4',
@@ -19039,7 +19075,7 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
19039
19075
  */
19040
19076
  async getHistoricalBars(params) {
19041
19077
  const symbols = params.symbols;
19042
- symbols.join(',');
19078
+ const symbolsStr = symbols.join(',');
19043
19079
  let allBars = {};
19044
19080
  let pageToken = null;
19045
19081
  let hasMorePages = true;
@@ -19050,7 +19086,7 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
19050
19086
  symbols.forEach((symbol) => {
19051
19087
  allBars[symbol] = [];
19052
19088
  });
19053
- log(`Starting historical bars fetch for ${symbols.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`);
19089
+ log(`Starting ${params.timeframe}bars fetch for ${symbols.length} symbols (${symbolsStr}), ${params.start || 'no start'} to ${params.end || 'no end'})`, { symbol: symbolsStr });
19054
19090
  while (hasMorePages) {
19055
19091
  pageCount++;
19056
19092
  const requestParams = {
@@ -19061,7 +19097,7 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
19061
19097
  };
19062
19098
  const response = await this.makeRequest('/stocks/bars', 'GET', requestParams);
19063
19099
  if (!response.bars) {
19064
- log(`No bars data found in response for ${symbols.length} symbols`, { type: 'warn' });
19100
+ log(`No bars data found in response for ${symbols.length} symbols`, { symbol: symbolsStr, type: 'warn' });
19065
19101
  break;
19066
19102
  }
19067
19103
  // Track currency from first response
@@ -19095,7 +19131,7 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
19095
19131
  const dateRangeStr = earliestTimestamp && latestTimestamp
19096
19132
  ? `${new Date(earliestTimestamp).toLocaleDateString('en-US', { timeZone: 'America/New_York' })} to ${new Date(latestTimestamp).toLocaleDateString('en-US', { timeZone: 'America/New_York' })}`
19097
19133
  : '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' });
19134
+ 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
19135
  // Prevent infinite loops
19100
19136
  if (pageCount > 1000) {
19101
19137
  log(`Stopping pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
@@ -19106,7 +19142,7 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
19106
19142
  const symbolCounts = Object.entries(allBars)
19107
19143
  .map(([symbol, bars]) => `${symbol}: ${bars.length}`)
19108
19144
  .join(', ');
19109
- log(`Bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages for ${symbols.length} symbols (${symbolCounts})`);
19145
+ log(`${params.timeframe} bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages for ${symbols.length} symbols (${symbolCounts})`, { symbol: symbolsStr });
19110
19146
  return {
19111
19147
  bars: allBars,
19112
19148
  next_page_token: null, // Always null since we fetch all pages
@@ -19958,6 +19994,26 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
19958
19994
  const marketDataAPI = AlpacaMarketDataAPI.getInstance();
19959
19995
 
19960
19996
  const limitPriceSlippagePercent100 = 0.1; // 0.1%
19997
+ class AlpacaRequestError extends Error {
19998
+ method;
19999
+ status;
20000
+ url;
20001
+ responseCode;
20002
+ responseDetails;
20003
+ rawResponse;
20004
+ symbol;
20005
+ constructor(params) {
20006
+ super(params.message);
20007
+ this.name = 'AlpacaRequestError';
20008
+ this.method = params.method;
20009
+ this.status = params.status ?? null;
20010
+ this.url = params.url;
20011
+ this.responseCode = params.responseCode ?? null;
20012
+ this.responseDetails = params.responseDetails ?? null;
20013
+ this.rawResponse = params.rawResponse ?? null;
20014
+ this.symbol = params.symbol;
20015
+ }
20016
+ }
19961
20017
  /**
19962
20018
  Websocket example
19963
20019
  const alpacaAPI = createAlpacaTradingAPI(credentials); // type AlpacaCredentials
@@ -20041,6 +20097,75 @@ class AlpacaTradingAPI {
20041
20097
  roundPriceForAlpaca = (price) => {
20042
20098
  return price >= 1 ? Math.round(price * 100) / 100 : Math.round(price * 10000) / 10000;
20043
20099
  };
20100
+ isJsonObject(value) {
20101
+ return value !== null && !Array.isArray(value) && typeof value === 'object';
20102
+ }
20103
+ parseAlpacaAPIErrorResponse(rawResponse) {
20104
+ if (rawResponse.trim() === '') {
20105
+ return null;
20106
+ }
20107
+ try {
20108
+ const parsedValue = JSON.parse(rawResponse);
20109
+ if (!this.isJsonObject(parsedValue)) {
20110
+ return null;
20111
+ }
20112
+ const code = parsedValue['code'];
20113
+ const message = parsedValue['message'];
20114
+ const symbol = parsedValue['symbol'];
20115
+ if (typeof code !== 'number' || typeof message !== 'string') {
20116
+ return null;
20117
+ }
20118
+ if (symbol !== undefined && typeof symbol !== 'string') {
20119
+ return null;
20120
+ }
20121
+ return parsedValue;
20122
+ }
20123
+ catch {
20124
+ return null;
20125
+ }
20126
+ }
20127
+ formatJsonValueForLog(value) {
20128
+ if (value === undefined) {
20129
+ return 'undefined';
20130
+ }
20131
+ if (Array.isArray(value)) {
20132
+ return value.map((entry) => this.formatJsonValueForLog(entry)).join(', ');
20133
+ }
20134
+ if (value === null) {
20135
+ return 'null';
20136
+ }
20137
+ if (typeof value === 'object') {
20138
+ return JSON.stringify(value);
20139
+ }
20140
+ return `${value}`;
20141
+ }
20142
+ formatAlpacaAPIErrorDetails(errorResponse) {
20143
+ return Object.entries(errorResponse)
20144
+ .filter(([key]) => key !== 'code' && key !== 'message' && key !== 'symbol')
20145
+ .map(([key, value]) => `${key}=${this.formatJsonValueForLog(value)}`)
20146
+ .join(', ');
20147
+ }
20148
+ formatRequestAction(requestContext) {
20149
+ return requestContext?.action ? `${requestContext.action} failed` : 'Alpaca request failed';
20150
+ }
20151
+ buildAlpacaAPIErrorMessage(params) {
20152
+ const { requestContext, status, url, errorResponse, rawResponse } = params;
20153
+ const action = this.formatRequestAction(requestContext);
20154
+ const message = errorResponse?.message ?? (rawResponse || 'No error body returned');
20155
+ const responseCode = errorResponse?.code ? ` (code ${errorResponse.code})` : '';
20156
+ const detailSummary = errorResponse ? this.formatAlpacaAPIErrorDetails(errorResponse) : '';
20157
+ const detailText = detailSummary !== ''
20158
+ ? ` Details: ${detailSummary}.`
20159
+ : rawResponse.trim() !== '' && errorResponse === null
20160
+ ? ` Response: ${rawResponse}.`
20161
+ : '';
20162
+ return `${action}: Alpaca API ${status}${responseCode}: ${message}.${detailText} Url: ${url}`;
20163
+ }
20164
+ buildRequestExecutionErrorMessage(params) {
20165
+ const { requestContext, url, errorMessage } = params;
20166
+ const action = this.formatRequestAction(requestContext);
20167
+ return `${action}: ${errorMessage}. Url: ${url}`;
20168
+ }
20044
20169
  handleAuthMessage(data) {
20045
20170
  if (data.status === 'authorized') {
20046
20171
  this.authenticated = true;
@@ -20268,7 +20393,7 @@ class AlpacaTradingAPI {
20268
20393
  this.ws?.on('message', handleListenResponse);
20269
20394
  });
20270
20395
  }
20271
- async makeRequest(endpoint, method = 'GET', body, queryString = '') {
20396
+ async makeRequest(endpoint, method = 'GET', body, queryString = '', requestContext) {
20272
20397
  const url = `${this.apiBaseUrl}${endpoint}${queryString}`;
20273
20398
  try {
20274
20399
  const response = await fetch(url, {
@@ -20277,9 +20402,27 @@ class AlpacaTradingAPI {
20277
20402
  body: body ? JSON.stringify(body) : undefined,
20278
20403
  });
20279
20404
  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}`);
20405
+ const rawResponse = await response.text();
20406
+ const errorResponse = this.parseAlpacaAPIErrorResponse(rawResponse);
20407
+ const symbol = requestContext?.symbol ?? errorResponse?.symbol;
20408
+ const error = new AlpacaRequestError({
20409
+ message: this.buildAlpacaAPIErrorMessage({
20410
+ requestContext,
20411
+ status: response.status,
20412
+ url,
20413
+ errorResponse,
20414
+ rawResponse,
20415
+ }),
20416
+ method,
20417
+ status: response.status,
20418
+ url,
20419
+ responseCode: errorResponse?.code ?? null,
20420
+ responseDetails: errorResponse,
20421
+ rawResponse,
20422
+ symbol,
20423
+ });
20424
+ this.log(error.message, { symbol, type: 'error' });
20425
+ throw error;
20283
20426
  }
20284
20427
  // Handle responses with no content (e.g., 204 No Content)
20285
20428
  if (response.status === 204 || response.headers.get('content-length') === '0') {
@@ -20287,23 +20430,37 @@ class AlpacaTradingAPI {
20287
20430
  }
20288
20431
  const contentType = response.headers.get('content-type');
20289
20432
  if (contentType && contentType.includes('application/json')) {
20290
- return await response.json();
20433
+ return (await response.json());
20291
20434
  }
20292
20435
  // For non-JSON responses, return the text content
20293
20436
  const textContent = await response.text();
20294
- return textContent || null;
20437
+ return (textContent || null);
20295
20438
  }
20296
- catch (err) {
20297
- const error = err;
20298
- this.log(`Error in makeRequest: ${error.message}. Url: ${url}`, {
20299
- source: 'AlpacaAPI',
20300
- type: 'error',
20439
+ catch (error) {
20440
+ if (error instanceof AlpacaRequestError) {
20441
+ throw error;
20442
+ }
20443
+ const normalizedError = error instanceof Error ? error : new Error(`${error}`);
20444
+ const symbol = requestContext?.symbol;
20445
+ const requestError = new AlpacaRequestError({
20446
+ message: this.buildRequestExecutionErrorMessage({
20447
+ requestContext,
20448
+ url,
20449
+ errorMessage: normalizedError.message,
20450
+ }),
20451
+ method,
20452
+ url,
20453
+ rawResponse: null,
20454
+ symbol,
20301
20455
  });
20302
- throw error;
20456
+ this.log(requestError.message, { symbol, type: 'error' });
20457
+ throw requestError;
20303
20458
  }
20304
20459
  }
20305
20460
  async getPositions(assetClass) {
20306
- const positions = (await this.makeRequest('/positions'));
20461
+ const positions = await this.makeRequest('/positions', 'GET', undefined, '', {
20462
+ action: 'Get positions',
20463
+ });
20307
20464
  if (assetClass) {
20308
20465
  return positions.filter((position) => position.asset_class === assetClass);
20309
20466
  }
@@ -20341,22 +20498,15 @@ class AlpacaTradingAPI {
20341
20498
  if (params.side)
20342
20499
  queryParams.append('side', params.side);
20343
20500
  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
- }
20501
+ return this.makeRequest(endpoint, 'GET', undefined, '', {
20502
+ action: 'Get orders',
20503
+ symbol: params.symbols?.join(','),
20504
+ });
20351
20505
  }
20352
20506
  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
- }
20507
+ return this.makeRequest('/account', 'GET', undefined, '', {
20508
+ action: 'Get account details',
20509
+ });
20360
20510
  }
20361
20511
  /**
20362
20512
  * Create a trailing stop order
@@ -20374,27 +20524,21 @@ class AlpacaTradingAPI {
20374
20524
  });
20375
20525
  const body = {
20376
20526
  symbol,
20377
- qty: Math.abs(qty),
20527
+ qty: Math.abs(qty).toString(),
20378
20528
  side,
20379
20529
  position_intent,
20380
20530
  order_class: 'simple',
20381
20531
  type: 'trailing_stop',
20382
- trail_percent: trailPercent100, // Already in decimal form (e.g., 4 for 4%)
20532
+ trail_percent: trailPercent100.toString(),
20383
20533
  time_in_force: 'gtc',
20384
20534
  };
20385
20535
  if (client_order_id !== undefined) {
20386
20536
  body.client_order_id = client_order_id;
20387
20537
  }
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
- }
20538
+ return this.makeRequest('/orders', 'POST', body, '', {
20539
+ action: 'Create trailing stop order',
20540
+ symbol,
20541
+ });
20398
20542
  }
20399
20543
  /**
20400
20544
  * Create a stop order (stop or stop-limit)
@@ -20420,25 +20564,19 @@ class AlpacaTradingAPI {
20420
20564
  position_intent,
20421
20565
  order_class: 'simple',
20422
20566
  type: isStopLimit ? 'stop_limit' : 'stop',
20423
- stop_price: this.roundPriceForAlpaca(stopPrice),
20567
+ stop_price: this.roundPriceForAlpaca(stopPrice).toString(),
20424
20568
  time_in_force: 'gtc',
20425
20569
  };
20426
- if (isStopLimit) {
20427
- body.limit_price = this.roundPriceForAlpaca(limitPrice);
20570
+ if (limitPrice !== undefined) {
20571
+ body.limit_price = this.roundPriceForAlpaca(limitPrice).toString();
20428
20572
  }
20429
20573
  if (client_order_id !== undefined) {
20430
20574
  body.client_order_id = client_order_id;
20431
20575
  }
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
- }
20576
+ return this.makeRequest('/orders', 'POST', body, '', {
20577
+ action: `Create ${orderType} order`,
20578
+ symbol,
20579
+ });
20442
20580
  }
20443
20581
  /**
20444
20582
  * Create a market order
@@ -20464,13 +20602,10 @@ class AlpacaTradingAPI {
20464
20602
  if (client_order_id !== undefined) {
20465
20603
  body.client_order_id = client_order_id;
20466
20604
  }
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
- }
20605
+ return this.makeRequest('/orders', 'POST', body, '', {
20606
+ action: 'Create market order',
20607
+ symbol,
20608
+ });
20474
20609
  }
20475
20610
  /**
20476
20611
  * Create a Market on Open (MOO) order - executes in the opening auction
@@ -20504,13 +20639,10 @@ class AlpacaTradingAPI {
20504
20639
  if (client_order_id !== undefined) {
20505
20640
  body.client_order_id = client_order_id;
20506
20641
  }
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
- }
20642
+ return this.makeRequest('/orders', 'POST', body, '', {
20643
+ action: 'Create market on open order',
20644
+ symbol,
20645
+ });
20514
20646
  }
20515
20647
  /**
20516
20648
  * Create a Market on Close (MOC) order - executes in the closing auction
@@ -20544,13 +20676,10 @@ class AlpacaTradingAPI {
20544
20676
  if (client_order_id !== undefined) {
20545
20677
  body.client_order_id = client_order_id;
20546
20678
  }
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
- }
20679
+ return this.makeRequest('/orders', 'POST', body, '', {
20680
+ action: 'Create market on close order',
20681
+ symbol,
20682
+ });
20554
20683
  }
20555
20684
  /**
20556
20685
  * Create an OCO (One-Cancels-Other) order with take profit and stop loss
@@ -20576,32 +20705,29 @@ class AlpacaTradingAPI {
20576
20705
  position_intent,
20577
20706
  order_class: 'oco',
20578
20707
  type: 'limit',
20579
- limit_price: this.roundPriceForAlpaca(limitPrice),
20708
+ limit_price: this.roundPriceForAlpaca(limitPrice).toString(),
20580
20709
  time_in_force: 'gtc',
20581
20710
  take_profit: {
20582
- limit_price: this.roundPriceForAlpaca(takeProfitPrice),
20711
+ limit_price: this.roundPriceForAlpaca(takeProfitPrice).toString(),
20583
20712
  },
20584
20713
  stop_loss: {
20585
- stop_price: this.roundPriceForAlpaca(stopLossPrice),
20714
+ stop_price: this.roundPriceForAlpaca(stopLossPrice).toString(),
20586
20715
  },
20587
20716
  };
20588
20717
  // If stop loss limit price is provided, create stop-limit order
20589
20718
  if (stopLossLimitPrice !== undefined) {
20590
- body.stop_loss.limit_price = this.roundPriceForAlpaca(stopLossLimitPrice);
20719
+ body.stop_loss = {
20720
+ stop_price: this.roundPriceForAlpaca(stopLossPrice).toString(),
20721
+ limit_price: this.roundPriceForAlpaca(stopLossLimitPrice).toString(),
20722
+ };
20591
20723
  }
20592
20724
  if (client_order_id !== undefined) {
20593
20725
  body.client_order_id = client_order_id;
20594
20726
  }
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
- }
20727
+ return this.makeRequest('/orders', 'POST', body, '', {
20728
+ action: 'Create OCO order',
20729
+ symbol,
20730
+ });
20605
20731
  }
20606
20732
  /**
20607
20733
  * 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 +20735,26 @@ class AlpacaTradingAPI {
20609
20735
  * @returns the current trail percent
20610
20736
  */
20611
20737
  async getCurrentTrailPercent(symbol) {
20612
- try {
20613
- const orders = await this.getOrders({
20614
- status: 'open',
20615
- symbols: [symbol],
20738
+ const orders = await this.getOrders({
20739
+ status: 'open',
20740
+ symbols: [symbol],
20741
+ });
20742
+ const trailingStopOrder = orders.find((order) => order.type === 'trailing_stop' &&
20743
+ (order.position_intent === 'sell_to_close' || order.position_intent === 'buy_to_close'));
20744
+ if (!trailingStopOrder) {
20745
+ this.log(`No closing trailing stop order found for ${symbol}`, {
20746
+ symbol,
20616
20747
  });
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;
20748
+ return null;
20633
20749
  }
20634
- catch (error) {
20635
- this.log(`Error getting current trail percent: ${error}`, {
20750
+ if (!trailingStopOrder.trail_percent) {
20751
+ this.log(`Trailing stop order found for ${symbol} but no trail_percent value`, {
20636
20752
  symbol,
20637
- type: 'error',
20638
20753
  });
20639
- throw error;
20754
+ return null;
20640
20755
  }
20756
+ const trailPercent = parseFloat(trailingStopOrder.trail_percent);
20757
+ return trailPercent;
20641
20758
  }
20642
20759
  /**
20643
20760
  * Update the trail percent for a trailing stop order
@@ -20669,18 +20786,10 @@ class AlpacaTradingAPI {
20669
20786
  this.log(`Updating trailing stop for ${symbol} from ${currentTrailPercent}% to ${trailPercent100}%`, {
20670
20787
  symbol,
20671
20788
  });
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
- }
20789
+ await this.makeRequest(`/orders/${trailingStopOrder.id}`, 'PATCH', { trail: trailPercent100.toString() }, '', {
20790
+ action: 'Update trailing stop order',
20791
+ symbol,
20792
+ });
20684
20793
  }
20685
20794
  /**
20686
20795
  * Cancel all open orders
@@ -20688,10 +20797,16 @@ class AlpacaTradingAPI {
20688
20797
  async cancelAllOrders() {
20689
20798
  this.log(`Canceling all open orders`);
20690
20799
  try {
20691
- await this.makeRequest('/orders', 'DELETE');
20800
+ await this.makeRequest('/orders', 'DELETE', undefined, '', {
20801
+ action: 'Cancel all open orders',
20802
+ });
20692
20803
  }
20693
20804
  catch (error) {
20694
- this.log(`Error canceling all orders: ${error}`, { type: 'error' });
20805
+ if (!(error instanceof AlpacaRequestError)) {
20806
+ this.log(`Error canceling all orders: ${error instanceof Error ? error.message : `${error}`}`, {
20807
+ type: 'error',
20808
+ });
20809
+ }
20695
20810
  }
20696
20811
  }
20697
20812
  /**
@@ -20703,15 +20818,14 @@ class AlpacaTradingAPI {
20703
20818
  async cancelOrder(orderId) {
20704
20819
  this.log(`Attempting to cancel order ${orderId}`);
20705
20820
  try {
20706
- await this.makeRequest(`/orders/${orderId}`, 'DELETE');
20821
+ await this.makeRequest(`/orders/${orderId}`, 'DELETE', undefined, '', {
20822
+ action: `Cancel order ${orderId}`,
20823
+ });
20707
20824
  this.log(`Successfully canceled order ${orderId}`);
20708
20825
  }
20709
20826
  catch (error) {
20710
20827
  // 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
- });
20828
+ if (error instanceof AlpacaRequestError && error.status === 422) {
20715
20829
  throw new Error(`Order ${orderId} is not cancelable`);
20716
20830
  }
20717
20831
  // Re-throw other errors
@@ -20746,13 +20860,10 @@ class AlpacaTradingAPI {
20746
20860
  if (client_order_id !== undefined) {
20747
20861
  body.client_order_id = client_order_id;
20748
20862
  }
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
- }
20863
+ return this.makeRequest('/orders', 'POST', body, '', {
20864
+ action: 'Create limit order',
20865
+ symbol,
20866
+ });
20756
20867
  }
20757
20868
  /**
20758
20869
  * Close all equities positions
@@ -20818,7 +20929,9 @@ class AlpacaTradingAPI {
20818
20929
  }
20819
20930
  }
20820
20931
  else {
20821
- await this.makeRequest('/positions', 'DELETE', undefined, options.cancel_orders ? '?cancel_orders=true' : '');
20932
+ await this.makeRequest('/positions', 'DELETE', undefined, options.cancel_orders ? '?cancel_orders=true' : '', {
20933
+ action: 'Close all positions',
20934
+ });
20822
20935
  }
20823
20936
  }
20824
20937
  /**
@@ -20897,7 +21010,9 @@ class AlpacaTradingAPI {
20897
21010
  queryParams.append('end', params.end);
20898
21011
  if (params.date_end)
20899
21012
  queryParams.append('date_end', params.date_end);
20900
- const response = await this.makeRequest(`/account/portfolio/history?${queryParams.toString()}`);
21013
+ const response = await this.makeRequest(`/account/portfolio/history?${queryParams.toString()}`, 'GET', undefined, '', {
21014
+ action: 'Get portfolio history',
21015
+ });
20901
21016
  return response;
20902
21017
  }
20903
21018
  /**
@@ -20988,7 +21103,10 @@ class AlpacaTradingAPI {
20988
21103
  this.log(`Fetching option contracts for ${params.underlying_symbols.join(', ')}`, {
20989
21104
  symbol: params.underlying_symbols.join(', '),
20990
21105
  });
20991
- const response = (await this.makeRequest(`/options/contracts?${queryParams.toString()}`));
21106
+ const response = await this.makeRequest(`/options/contracts?${queryParams.toString()}`, 'GET', undefined, '', {
21107
+ action: 'Get option contracts',
21108
+ symbol: params.underlying_symbols.join(', '),
21109
+ });
20992
21110
  this.log(`Found ${response.option_contracts.length} option contracts`, {
20993
21111
  symbol: params.underlying_symbols.join(', '),
20994
21112
  });
@@ -21003,7 +21121,10 @@ class AlpacaTradingAPI {
21003
21121
  this.log(`Fetching option contract details for ${symbolOrId}`, {
21004
21122
  symbol: symbolOrId,
21005
21123
  });
21006
- const response = (await this.makeRequest(`/options/contracts/${symbolOrId}`));
21124
+ const response = await this.makeRequest(`/options/contracts/${symbolOrId}`, 'GET', undefined, '', {
21125
+ action: 'Get option contract details',
21126
+ symbol: symbolOrId,
21127
+ });
21007
21128
  this.log(`Found option contract details for ${symbolOrId}: ${response.name}`, {
21008
21129
  symbol: symbolOrId,
21009
21130
  });
@@ -21021,10 +21142,10 @@ class AlpacaTradingAPI {
21021
21142
  */
21022
21143
  async createOptionOrder(symbol, qty, side, position_intent, type, limitPrice) {
21023
21144
  if (!Number.isInteger(qty) || qty <= 0) {
21024
- this.log('Quantity must be a positive whole number for option orders', { type: 'error' });
21145
+ this.log('Quantity must be a positive whole number for option orders', { symbol, type: 'error' });
21025
21146
  }
21026
21147
  if (type === 'limit' && limitPrice === undefined) {
21027
- this.log('Limit price is required for limit orders', { type: 'error' });
21148
+ this.log('Limit price is required for limit orders', { symbol, type: 'error' });
21028
21149
  }
21029
21150
  this.log(`Creating ${type} option order for ${symbol}: ${side} ${qty} contracts (${position_intent})${type === 'limit' ? ` at $${limitPrice?.toFixed(2)}` : ''}`, {
21030
21151
  symbol,
@@ -21042,7 +21163,10 @@ class AlpacaTradingAPI {
21042
21163
  if (type === 'limit' && limitPrice !== undefined) {
21043
21164
  orderData.limit_price = this.roundPriceForAlpaca(limitPrice).toString();
21044
21165
  }
21045
- return this.makeRequest('/orders', 'POST', orderData);
21166
+ return this.makeRequest('/orders', 'POST', orderData, '', {
21167
+ action: 'Create option order',
21168
+ symbol,
21169
+ });
21046
21170
  }
21047
21171
  /**
21048
21172
  * Create a multi-leg option order
@@ -21053,16 +21177,19 @@ class AlpacaTradingAPI {
21053
21177
  * @returns The created multi-leg order
21054
21178
  */
21055
21179
  async createMultiLegOptionOrder(legs, qty, type, limitPrice) {
21180
+ const legSymbols = legs.map((leg) => leg.symbol).join(', ');
21056
21181
  if (!Number.isInteger(qty) || qty <= 0) {
21057
- this.log('Quantity must be a positive whole number for option orders', { type: 'error' });
21182
+ this.log('Quantity must be a positive whole number for option orders', {
21183
+ symbol: legSymbols,
21184
+ type: 'error',
21185
+ });
21058
21186
  }
21059
21187
  if (type === 'limit' && limitPrice === undefined) {
21060
- this.log('Limit price is required for limit orders', { type: 'error' });
21188
+ this.log('Limit price is required for limit orders', { symbol: legSymbols, type: 'error' });
21061
21189
  }
21062
21190
  if (legs.length < 2) {
21063
- this.log('Multi-leg orders require at least 2 legs', { type: 'error' });
21191
+ this.log('Multi-leg orders require at least 2 legs', { symbol: legSymbols, type: 'error' });
21064
21192
  }
21065
- const legSymbols = legs.map((leg) => leg.symbol).join(', ');
21066
21193
  this.log(`Creating multi-leg ${type} option order with ${legs.length} legs (${legSymbols})${type === 'limit' ? ` at $${limitPrice?.toFixed(2)}` : ''}`, {
21067
21194
  symbol: legSymbols,
21068
21195
  });
@@ -21076,7 +21203,10 @@ class AlpacaTradingAPI {
21076
21203
  if (type === 'limit' && limitPrice !== undefined) {
21077
21204
  orderData.limit_price = this.roundPriceForAlpaca(limitPrice).toString();
21078
21205
  }
21079
- return this.makeRequest('/orders', 'POST', orderData);
21206
+ return this.makeRequest('/orders', 'POST', orderData, '', {
21207
+ action: 'Create multi-leg option order',
21208
+ symbol: legSymbols,
21209
+ });
21080
21210
  }
21081
21211
  /**
21082
21212
  * Exercise an option contract
@@ -21087,7 +21217,10 @@ class AlpacaTradingAPI {
21087
21217
  this.log(`Exercising option contract ${symbolOrContractId}`, {
21088
21218
  symbol: symbolOrContractId,
21089
21219
  });
21090
- return this.makeRequest(`/positions/${symbolOrContractId}/exercise`, 'POST');
21220
+ return this.makeRequest(`/positions/${symbolOrContractId}/exercise`, 'POST', undefined, '', {
21221
+ action: 'Exercise option contract',
21222
+ symbol: symbolOrContractId,
21223
+ });
21091
21224
  }
21092
21225
  /**
21093
21226
  * Get option positions
@@ -21123,7 +21256,9 @@ class AlpacaTradingAPI {
21123
21256
  queryParams.append('date', date);
21124
21257
  }
21125
21258
  this.log(`Fetching option activities${activityType ? ` of type ${activityType}` : ''}${date ? ` for date ${date}` : ''}`);
21126
- return this.makeRequest(`/account/activities?${queryParams.toString()}`);
21259
+ return this.makeRequest(`/account/activities?${queryParams.toString()}`, 'GET', undefined, '', {
21260
+ action: 'Get option activities',
21261
+ });
21127
21262
  }
21128
21263
  /**
21129
21264
  * Create a long call spread (buy lower strike call, sell higher strike call)
@@ -21221,13 +21356,7 @@ class AlpacaTradingAPI {
21221
21356
  position_intent: 'buy_to_open',
21222
21357
  },
21223
21358
  ];
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
- }
21359
+ return this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
21231
21360
  }
21232
21361
  /**
21233
21362
  * Create a covered call (sell call option against owned stock)
@@ -21243,13 +21372,7 @@ class AlpacaTradingAPI {
21243
21372
  });
21244
21373
  // For covered calls, we don't need to include the stock leg if we already own the shares
21245
21374
  // 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
- }
21375
+ return this.createOptionOrder(callOptionSymbol, qty, 'sell', 'sell_to_open', 'limit', limitPrice);
21253
21376
  }
21254
21377
  /**
21255
21378
  * Roll an option position to a new expiration or strike
@@ -21284,13 +21407,7 @@ class AlpacaTradingAPI {
21284
21407
  position_intent: openPositionIntent,
21285
21408
  },
21286
21409
  ];
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
- }
21410
+ return this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
21294
21411
  }
21295
21412
  /**
21296
21413
  * Get option chain for a specific underlying symbol and expiration date
@@ -21314,10 +21431,12 @@ class AlpacaTradingAPI {
21314
21431
  return response.option_contracts || [];
21315
21432
  }
21316
21433
  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
- });
21434
+ if (!(error instanceof AlpacaRequestError)) {
21435
+ this.log(`Failed to fetch option chain for ${underlyingSymbol}: ${error instanceof Error ? error.message : `${error}`}`, {
21436
+ type: 'error',
21437
+ symbol: underlyingSymbol,
21438
+ });
21439
+ }
21321
21440
  return [];
21322
21441
  }
21323
21442
  }
@@ -21348,10 +21467,12 @@ class AlpacaTradingAPI {
21348
21467
  return Array.from(expirationDates).sort();
21349
21468
  }
21350
21469
  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
- });
21470
+ if (!(error instanceof AlpacaRequestError)) {
21471
+ this.log(`Failed to fetch expiration dates for ${underlyingSymbol}: ${error instanceof Error ? error.message : `${error}`}`, {
21472
+ type: 'error',
21473
+ symbol: underlyingSymbol,
21474
+ });
21475
+ }
21355
21476
  return [];
21356
21477
  }
21357
21478
  }
@@ -21410,7 +21531,10 @@ class AlpacaTradingAPI {
21410
21531
  this.log(`Canceling open order for ${order.symbol}`, {
21411
21532
  symbol: order.symbol,
21412
21533
  });
21413
- await this.makeRequest(`/orders/${order.id}`, 'DELETE');
21534
+ await this.makeRequest(`/orders/${order.id}`, 'DELETE', undefined, '', {
21535
+ action: `Cancel option order ${order.id}`,
21536
+ symbol: order.symbol,
21537
+ });
21414
21538
  }
21415
21539
  }
21416
21540
  }
@@ -21433,13 +21557,7 @@ class AlpacaTradingAPI {
21433
21557
  const quantityToClose = qty || parseInt(position.qty);
21434
21558
  const side = position.side === 'long' ? 'sell' : 'buy';
21435
21559
  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
- }
21560
+ return this.createOptionOrder(symbol, quantityToClose, side, positionIntent, 'market');
21443
21561
  }
21444
21562
  /**
21445
21563
  * Create a complete equities trade with optional stop loss and take profit
@@ -21574,16 +21692,10 @@ class AlpacaTradingAPI {
21574
21692
  this.log(logMessage, {
21575
21693
  symbol,
21576
21694
  });
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
- }
21695
+ return this.makeRequest('/orders', 'POST', orderData, '', {
21696
+ action: 'Create equities trade',
21697
+ symbol,
21698
+ });
21587
21699
  }
21588
21700
  }
21589
21701