@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.mjs CHANGED
@@ -1146,6 +1146,7 @@ function isOpenRouterModel(model) {
1146
1146
  const openRouterModels = [
1147
1147
  'openai/gpt-5',
1148
1148
  'openai/gpt-5-mini',
1149
+ 'openai/gpt-5.4-mini',
1149
1150
  'openai/gpt-5-nano',
1150
1151
  'openai/gpt-5.1',
1151
1152
  'openai/gpt-5.4',
@@ -1614,7 +1615,7 @@ const safeJSON = (text) => {
1614
1615
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
1615
1616
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1616
1617
 
1617
- const VERSION = '6.27.0'; // x-release-please-version
1618
+ const VERSION = '6.32.0'; // x-release-please-version
1618
1619
 
1619
1620
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
1620
1621
  const isRunningInBrowser = () => {
@@ -4858,7 +4859,7 @@ class Speech extends APIResource {
4858
4859
  * const speech = await client.audio.speech.create({
4859
4860
  * input: 'input',
4860
4861
  * model: 'string',
4861
- * voice: 'ash',
4862
+ * voice: 'string',
4862
4863
  * });
4863
4864
  *
4864
4865
  * const content = await speech.blob();
@@ -8139,7 +8140,7 @@ class Videos extends APIResource {
8139
8140
  * Create a new video generation job from a prompt and optional reference assets.
8140
8141
  */
8141
8142
  create(body, options) {
8142
- return this._client.post('/videos', maybeMultipartFormRequestOptions({ body, ...options }, this._client));
8143
+ return this._client.post('/videos', multipartFormRequestOptions({ body, ...options }, this._client));
8143
8144
  }
8144
8145
  /**
8145
8146
  * Fetch the latest metadata for a generated video.
@@ -8159,6 +8160,12 @@ class Videos extends APIResource {
8159
8160
  delete(videoID, options) {
8160
8161
  return this._client.delete(path `/videos/${videoID}`, options);
8161
8162
  }
8163
+ /**
8164
+ * Create a character from an uploaded video.
8165
+ */
8166
+ createCharacter(body, options) {
8167
+ return this._client.post('/videos/characters', multipartFormRequestOptions({ body, ...options }, this._client));
8168
+ }
8162
8169
  /**
8163
8170
  * Download the generated video bytes or a derived preview asset.
8164
8171
  *
@@ -8172,6 +8179,25 @@ class Videos extends APIResource {
8172
8179
  __binaryResponse: true,
8173
8180
  });
8174
8181
  }
8182
+ /**
8183
+ * Create a new video generation job by editing a source video or existing
8184
+ * generated video.
8185
+ */
8186
+ edit(body, options) {
8187
+ return this._client.post('/videos/edits', multipartFormRequestOptions({ body, ...options }, this._client));
8188
+ }
8189
+ /**
8190
+ * Create an extension of a completed video.
8191
+ */
8192
+ extend(body, options) {
8193
+ return this._client.post('/videos/extensions', multipartFormRequestOptions({ body, ...options }, this._client));
8194
+ }
8195
+ /**
8196
+ * Fetch a character.
8197
+ */
8198
+ getCharacter(characterID, options) {
8199
+ return this._client.get(path `/videos/characters/${characterID}`, options);
8200
+ }
8175
8201
  /**
8176
8202
  * Create a remix of a completed video using a refreshed prompt.
8177
8203
  */
@@ -8453,8 +8479,9 @@ class OpenAI {
8453
8479
  new URL(path)
8454
8480
  : new URL(baseURL + (baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path));
8455
8481
  const defaultQuery = this.defaultQuery();
8456
- if (!isEmptyObj$1(defaultQuery)) {
8457
- query = { ...defaultQuery, ...query };
8482
+ const pathQuery = Object.fromEntries(url.searchParams);
8483
+ if (!isEmptyObj$1(defaultQuery) || !isEmptyObj$1(pathQuery)) {
8484
+ query = { ...pathQuery, ...defaultQuery, ...query };
8458
8485
  }
8459
8486
  if (typeof query === 'object' && query && !Array.isArray(query)) {
8460
8487
  url.search = this.stringifyQuery(query);
@@ -11674,6 +11701,11 @@ const openAiModelCosts = {
11674
11701
  cacheHitCost: 0.025 / 1_000_000,
11675
11702
  outputCost: 2 / 1_000_000,
11676
11703
  },
11704
+ 'gpt-5.4-mini': {
11705
+ inputCost: 0.25 / 1_000_000,
11706
+ cacheHitCost: 0.025 / 1_000_000,
11707
+ outputCost: 2 / 1_000_000,
11708
+ },
11677
11709
  'gpt-5-nano': {
11678
11710
  inputCost: 0.05 / 1_000_000,
11679
11711
  cacheHitCost: 0.005 / 1_000_000,
@@ -12184,6 +12216,7 @@ const isSupportedModel = (model) => {
12184
12216
  'gpt-4.1-nano',
12185
12217
  'gpt-5',
12186
12218
  'gpt-5-mini',
12219
+ 'gpt-5.4-mini',
12187
12220
  'gpt-5-nano',
12188
12221
  'gpt-5.1',
12189
12222
  'gpt-5.4',
@@ -12212,6 +12245,7 @@ function supportsTemperature(model) {
12212
12245
  'o3',
12213
12246
  'gpt-5',
12214
12247
  'gpt-5-mini',
12248
+ 'gpt-5.4-mini',
12215
12249
  'gpt-5-nano',
12216
12250
  'gpt-5.1',
12217
12251
  'gpt-5.4',
@@ -12241,6 +12275,7 @@ function isGPT5Model(model) {
12241
12275
  const gpt5Models = [
12242
12276
  'gpt-5',
12243
12277
  'gpt-5-mini',
12278
+ 'gpt-5.4-mini',
12244
12279
  'gpt-5-nano',
12245
12280
  'gpt-5.1',
12246
12281
  'gpt-5.4',
@@ -12550,6 +12585,7 @@ const MULTIMODAL_VISION_MODELS = new Set([
12550
12585
  'gpt-4o',
12551
12586
  'gpt-5',
12552
12587
  'gpt-5-mini',
12588
+ 'gpt-5.4-mini',
12553
12589
  'gpt-5-nano',
12554
12590
  'gpt-5.1',
12555
12591
  'gpt-5.4',
@@ -19037,7 +19073,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
19037
19073
  */
19038
19074
  async getHistoricalBars(params) {
19039
19075
  const symbols = params.symbols;
19040
- symbols.join(',');
19076
+ const symbolsStr = symbols.join(',');
19041
19077
  let allBars = {};
19042
19078
  let pageToken = null;
19043
19079
  let hasMorePages = true;
@@ -19048,7 +19084,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
19048
19084
  symbols.forEach((symbol) => {
19049
19085
  allBars[symbol] = [];
19050
19086
  });
19051
- log(`Starting historical bars fetch for ${symbols.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`);
19087
+ log(`Starting ${params.timeframe}bars fetch for ${symbols.length} symbols (${symbolsStr}), ${params.start || 'no start'} to ${params.end || 'no end'})`, { symbol: symbolsStr });
19052
19088
  while (hasMorePages) {
19053
19089
  pageCount++;
19054
19090
  const requestParams = {
@@ -19059,7 +19095,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
19059
19095
  };
19060
19096
  const response = await this.makeRequest('/stocks/bars', 'GET', requestParams);
19061
19097
  if (!response.bars) {
19062
- log(`No bars data found in response for ${symbols.length} symbols`, { type: 'warn' });
19098
+ log(`No bars data found in response for ${symbols.length} symbols`, { symbol: symbolsStr, type: 'warn' });
19063
19099
  break;
19064
19100
  }
19065
19101
  // Track currency from first response
@@ -19093,7 +19129,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
19093
19129
  const dateRangeStr = earliestTimestamp && latestTimestamp
19094
19130
  ? `${new Date(earliestTimestamp).toLocaleDateString('en-US', { timeZone: 'America/New_York' })} to ${new Date(latestTimestamp).toLocaleDateString('en-US', { timeZone: 'America/New_York' })}`
19095
19131
  : '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' });
19132
+ 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
19133
  // Prevent infinite loops
19098
19134
  if (pageCount > 1000) {
19099
19135
  log(`Stopping pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
@@ -19104,7 +19140,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
19104
19140
  const symbolCounts = Object.entries(allBars)
19105
19141
  .map(([symbol, bars]) => `${symbol}: ${bars.length}`)
19106
19142
  .join(', ');
19107
- log(`Bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages for ${symbols.length} symbols (${symbolCounts})`);
19143
+ log(`${params.timeframe} bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages for ${symbols.length} symbols (${symbolCounts})`, { symbol: symbolsStr });
19108
19144
  return {
19109
19145
  bars: allBars,
19110
19146
  next_page_token: null, // Always null since we fetch all pages
@@ -19956,6 +19992,26 @@ class AlpacaMarketDataAPI extends EventEmitter {
19956
19992
  const marketDataAPI = AlpacaMarketDataAPI.getInstance();
19957
19993
 
19958
19994
  const limitPriceSlippagePercent100 = 0.1; // 0.1%
19995
+ class AlpacaRequestError extends Error {
19996
+ method;
19997
+ status;
19998
+ url;
19999
+ responseCode;
20000
+ responseDetails;
20001
+ rawResponse;
20002
+ symbol;
20003
+ constructor(params) {
20004
+ super(params.message);
20005
+ this.name = 'AlpacaRequestError';
20006
+ this.method = params.method;
20007
+ this.status = params.status ?? null;
20008
+ this.url = params.url;
20009
+ this.responseCode = params.responseCode ?? null;
20010
+ this.responseDetails = params.responseDetails ?? null;
20011
+ this.rawResponse = params.rawResponse ?? null;
20012
+ this.symbol = params.symbol;
20013
+ }
20014
+ }
19959
20015
  /**
19960
20016
  Websocket example
19961
20017
  const alpacaAPI = createAlpacaTradingAPI(credentials); // type AlpacaCredentials
@@ -20039,6 +20095,75 @@ class AlpacaTradingAPI {
20039
20095
  roundPriceForAlpaca = (price) => {
20040
20096
  return price >= 1 ? Math.round(price * 100) / 100 : Math.round(price * 10000) / 10000;
20041
20097
  };
20098
+ isJsonObject(value) {
20099
+ return value !== null && !Array.isArray(value) && typeof value === 'object';
20100
+ }
20101
+ parseAlpacaAPIErrorResponse(rawResponse) {
20102
+ if (rawResponse.trim() === '') {
20103
+ return null;
20104
+ }
20105
+ try {
20106
+ const parsedValue = JSON.parse(rawResponse);
20107
+ if (!this.isJsonObject(parsedValue)) {
20108
+ return null;
20109
+ }
20110
+ const code = parsedValue['code'];
20111
+ const message = parsedValue['message'];
20112
+ const symbol = parsedValue['symbol'];
20113
+ if (typeof code !== 'number' || typeof message !== 'string') {
20114
+ return null;
20115
+ }
20116
+ if (symbol !== undefined && typeof symbol !== 'string') {
20117
+ return null;
20118
+ }
20119
+ return parsedValue;
20120
+ }
20121
+ catch {
20122
+ return null;
20123
+ }
20124
+ }
20125
+ formatJsonValueForLog(value) {
20126
+ if (value === undefined) {
20127
+ return 'undefined';
20128
+ }
20129
+ if (Array.isArray(value)) {
20130
+ return value.map((entry) => this.formatJsonValueForLog(entry)).join(', ');
20131
+ }
20132
+ if (value === null) {
20133
+ return 'null';
20134
+ }
20135
+ if (typeof value === 'object') {
20136
+ return JSON.stringify(value);
20137
+ }
20138
+ return `${value}`;
20139
+ }
20140
+ formatAlpacaAPIErrorDetails(errorResponse) {
20141
+ return Object.entries(errorResponse)
20142
+ .filter(([key]) => key !== 'code' && key !== 'message' && key !== 'symbol')
20143
+ .map(([key, value]) => `${key}=${this.formatJsonValueForLog(value)}`)
20144
+ .join(', ');
20145
+ }
20146
+ formatRequestAction(requestContext) {
20147
+ return requestContext?.action ? `${requestContext.action} failed` : 'Alpaca request failed';
20148
+ }
20149
+ buildAlpacaAPIErrorMessage(params) {
20150
+ const { requestContext, status, url, errorResponse, rawResponse } = params;
20151
+ const action = this.formatRequestAction(requestContext);
20152
+ const message = errorResponse?.message ?? (rawResponse || 'No error body returned');
20153
+ const responseCode = errorResponse?.code ? ` (code ${errorResponse.code})` : '';
20154
+ const detailSummary = errorResponse ? this.formatAlpacaAPIErrorDetails(errorResponse) : '';
20155
+ const detailText = detailSummary !== ''
20156
+ ? ` Details: ${detailSummary}.`
20157
+ : rawResponse.trim() !== '' && errorResponse === null
20158
+ ? ` Response: ${rawResponse}.`
20159
+ : '';
20160
+ return `${action}: Alpaca API ${status}${responseCode}: ${message}.${detailText} Url: ${url}`;
20161
+ }
20162
+ buildRequestExecutionErrorMessage(params) {
20163
+ const { requestContext, url, errorMessage } = params;
20164
+ const action = this.formatRequestAction(requestContext);
20165
+ return `${action}: ${errorMessage}. Url: ${url}`;
20166
+ }
20042
20167
  handleAuthMessage(data) {
20043
20168
  if (data.status === 'authorized') {
20044
20169
  this.authenticated = true;
@@ -20266,7 +20391,7 @@ class AlpacaTradingAPI {
20266
20391
  this.ws?.on('message', handleListenResponse);
20267
20392
  });
20268
20393
  }
20269
- async makeRequest(endpoint, method = 'GET', body, queryString = '') {
20394
+ async makeRequest(endpoint, method = 'GET', body, queryString = '', requestContext) {
20270
20395
  const url = `${this.apiBaseUrl}${endpoint}${queryString}`;
20271
20396
  try {
20272
20397
  const response = await fetch(url, {
@@ -20275,9 +20400,27 @@ class AlpacaTradingAPI {
20275
20400
  body: body ? JSON.stringify(body) : undefined,
20276
20401
  });
20277
20402
  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}`);
20403
+ const rawResponse = await response.text();
20404
+ const errorResponse = this.parseAlpacaAPIErrorResponse(rawResponse);
20405
+ const symbol = requestContext?.symbol ?? errorResponse?.symbol;
20406
+ const error = new AlpacaRequestError({
20407
+ message: this.buildAlpacaAPIErrorMessage({
20408
+ requestContext,
20409
+ status: response.status,
20410
+ url,
20411
+ errorResponse,
20412
+ rawResponse,
20413
+ }),
20414
+ method,
20415
+ status: response.status,
20416
+ url,
20417
+ responseCode: errorResponse?.code ?? null,
20418
+ responseDetails: errorResponse,
20419
+ rawResponse,
20420
+ symbol,
20421
+ });
20422
+ this.log(error.message, { symbol, type: 'error' });
20423
+ throw error;
20281
20424
  }
20282
20425
  // Handle responses with no content (e.g., 204 No Content)
20283
20426
  if (response.status === 204 || response.headers.get('content-length') === '0') {
@@ -20285,23 +20428,37 @@ class AlpacaTradingAPI {
20285
20428
  }
20286
20429
  const contentType = response.headers.get('content-type');
20287
20430
  if (contentType && contentType.includes('application/json')) {
20288
- return await response.json();
20431
+ return (await response.json());
20289
20432
  }
20290
20433
  // For non-JSON responses, return the text content
20291
20434
  const textContent = await response.text();
20292
- return textContent || null;
20435
+ return (textContent || null);
20293
20436
  }
20294
- catch (err) {
20295
- const error = err;
20296
- this.log(`Error in makeRequest: ${error.message}. Url: ${url}`, {
20297
- source: 'AlpacaAPI',
20298
- type: 'error',
20437
+ catch (error) {
20438
+ if (error instanceof AlpacaRequestError) {
20439
+ throw error;
20440
+ }
20441
+ const normalizedError = error instanceof Error ? error : new Error(`${error}`);
20442
+ const symbol = requestContext?.symbol;
20443
+ const requestError = new AlpacaRequestError({
20444
+ message: this.buildRequestExecutionErrorMessage({
20445
+ requestContext,
20446
+ url,
20447
+ errorMessage: normalizedError.message,
20448
+ }),
20449
+ method,
20450
+ url,
20451
+ rawResponse: null,
20452
+ symbol,
20299
20453
  });
20300
- throw error;
20454
+ this.log(requestError.message, { symbol, type: 'error' });
20455
+ throw requestError;
20301
20456
  }
20302
20457
  }
20303
20458
  async getPositions(assetClass) {
20304
- const positions = (await this.makeRequest('/positions'));
20459
+ const positions = await this.makeRequest('/positions', 'GET', undefined, '', {
20460
+ action: 'Get positions',
20461
+ });
20305
20462
  if (assetClass) {
20306
20463
  return positions.filter((position) => position.asset_class === assetClass);
20307
20464
  }
@@ -20339,22 +20496,15 @@ class AlpacaTradingAPI {
20339
20496
  if (params.side)
20340
20497
  queryParams.append('side', params.side);
20341
20498
  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
- }
20499
+ return this.makeRequest(endpoint, 'GET', undefined, '', {
20500
+ action: 'Get orders',
20501
+ symbol: params.symbols?.join(','),
20502
+ });
20349
20503
  }
20350
20504
  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
- }
20505
+ return this.makeRequest('/account', 'GET', undefined, '', {
20506
+ action: 'Get account details',
20507
+ });
20358
20508
  }
20359
20509
  /**
20360
20510
  * Create a trailing stop order
@@ -20372,27 +20522,21 @@ class AlpacaTradingAPI {
20372
20522
  });
20373
20523
  const body = {
20374
20524
  symbol,
20375
- qty: Math.abs(qty),
20525
+ qty: Math.abs(qty).toString(),
20376
20526
  side,
20377
20527
  position_intent,
20378
20528
  order_class: 'simple',
20379
20529
  type: 'trailing_stop',
20380
- trail_percent: trailPercent100, // Already in decimal form (e.g., 4 for 4%)
20530
+ trail_percent: trailPercent100.toString(),
20381
20531
  time_in_force: 'gtc',
20382
20532
  };
20383
20533
  if (client_order_id !== undefined) {
20384
20534
  body.client_order_id = client_order_id;
20385
20535
  }
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
- }
20536
+ return this.makeRequest('/orders', 'POST', body, '', {
20537
+ action: 'Create trailing stop order',
20538
+ symbol,
20539
+ });
20396
20540
  }
20397
20541
  /**
20398
20542
  * Create a stop order (stop or stop-limit)
@@ -20418,25 +20562,19 @@ class AlpacaTradingAPI {
20418
20562
  position_intent,
20419
20563
  order_class: 'simple',
20420
20564
  type: isStopLimit ? 'stop_limit' : 'stop',
20421
- stop_price: this.roundPriceForAlpaca(stopPrice),
20565
+ stop_price: this.roundPriceForAlpaca(stopPrice).toString(),
20422
20566
  time_in_force: 'gtc',
20423
20567
  };
20424
- if (isStopLimit) {
20425
- body.limit_price = this.roundPriceForAlpaca(limitPrice);
20568
+ if (limitPrice !== undefined) {
20569
+ body.limit_price = this.roundPriceForAlpaca(limitPrice).toString();
20426
20570
  }
20427
20571
  if (client_order_id !== undefined) {
20428
20572
  body.client_order_id = client_order_id;
20429
20573
  }
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
- }
20574
+ return this.makeRequest('/orders', 'POST', body, '', {
20575
+ action: `Create ${orderType} order`,
20576
+ symbol,
20577
+ });
20440
20578
  }
20441
20579
  /**
20442
20580
  * Create a market order
@@ -20462,13 +20600,10 @@ class AlpacaTradingAPI {
20462
20600
  if (client_order_id !== undefined) {
20463
20601
  body.client_order_id = client_order_id;
20464
20602
  }
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
- }
20603
+ return this.makeRequest('/orders', 'POST', body, '', {
20604
+ action: 'Create market order',
20605
+ symbol,
20606
+ });
20472
20607
  }
20473
20608
  /**
20474
20609
  * Create a Market on Open (MOO) order - executes in the opening auction
@@ -20502,13 +20637,10 @@ class AlpacaTradingAPI {
20502
20637
  if (client_order_id !== undefined) {
20503
20638
  body.client_order_id = client_order_id;
20504
20639
  }
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
- }
20640
+ return this.makeRequest('/orders', 'POST', body, '', {
20641
+ action: 'Create market on open order',
20642
+ symbol,
20643
+ });
20512
20644
  }
20513
20645
  /**
20514
20646
  * Create a Market on Close (MOC) order - executes in the closing auction
@@ -20542,13 +20674,10 @@ class AlpacaTradingAPI {
20542
20674
  if (client_order_id !== undefined) {
20543
20675
  body.client_order_id = client_order_id;
20544
20676
  }
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
- }
20677
+ return this.makeRequest('/orders', 'POST', body, '', {
20678
+ action: 'Create market on close order',
20679
+ symbol,
20680
+ });
20552
20681
  }
20553
20682
  /**
20554
20683
  * Create an OCO (One-Cancels-Other) order with take profit and stop loss
@@ -20574,32 +20703,29 @@ class AlpacaTradingAPI {
20574
20703
  position_intent,
20575
20704
  order_class: 'oco',
20576
20705
  type: 'limit',
20577
- limit_price: this.roundPriceForAlpaca(limitPrice),
20706
+ limit_price: this.roundPriceForAlpaca(limitPrice).toString(),
20578
20707
  time_in_force: 'gtc',
20579
20708
  take_profit: {
20580
- limit_price: this.roundPriceForAlpaca(takeProfitPrice),
20709
+ limit_price: this.roundPriceForAlpaca(takeProfitPrice).toString(),
20581
20710
  },
20582
20711
  stop_loss: {
20583
- stop_price: this.roundPriceForAlpaca(stopLossPrice),
20712
+ stop_price: this.roundPriceForAlpaca(stopLossPrice).toString(),
20584
20713
  },
20585
20714
  };
20586
20715
  // If stop loss limit price is provided, create stop-limit order
20587
20716
  if (stopLossLimitPrice !== undefined) {
20588
- body.stop_loss.limit_price = this.roundPriceForAlpaca(stopLossLimitPrice);
20717
+ body.stop_loss = {
20718
+ stop_price: this.roundPriceForAlpaca(stopLossPrice).toString(),
20719
+ limit_price: this.roundPriceForAlpaca(stopLossLimitPrice).toString(),
20720
+ };
20589
20721
  }
20590
20722
  if (client_order_id !== undefined) {
20591
20723
  body.client_order_id = client_order_id;
20592
20724
  }
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
- }
20725
+ return this.makeRequest('/orders', 'POST', body, '', {
20726
+ action: 'Create OCO order',
20727
+ symbol,
20728
+ });
20603
20729
  }
20604
20730
  /**
20605
20731
  * 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 +20733,26 @@ class AlpacaTradingAPI {
20607
20733
  * @returns the current trail percent
20608
20734
  */
20609
20735
  async getCurrentTrailPercent(symbol) {
20610
- try {
20611
- const orders = await this.getOrders({
20612
- status: 'open',
20613
- symbols: [symbol],
20736
+ const orders = await this.getOrders({
20737
+ status: 'open',
20738
+ symbols: [symbol],
20739
+ });
20740
+ const trailingStopOrder = orders.find((order) => order.type === 'trailing_stop' &&
20741
+ (order.position_intent === 'sell_to_close' || order.position_intent === 'buy_to_close'));
20742
+ if (!trailingStopOrder) {
20743
+ this.log(`No closing trailing stop order found for ${symbol}`, {
20744
+ symbol,
20614
20745
  });
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;
20746
+ return null;
20631
20747
  }
20632
- catch (error) {
20633
- this.log(`Error getting current trail percent: ${error}`, {
20748
+ if (!trailingStopOrder.trail_percent) {
20749
+ this.log(`Trailing stop order found for ${symbol} but no trail_percent value`, {
20634
20750
  symbol,
20635
- type: 'error',
20636
20751
  });
20637
- throw error;
20752
+ return null;
20638
20753
  }
20754
+ const trailPercent = parseFloat(trailingStopOrder.trail_percent);
20755
+ return trailPercent;
20639
20756
  }
20640
20757
  /**
20641
20758
  * Update the trail percent for a trailing stop order
@@ -20667,18 +20784,10 @@ class AlpacaTradingAPI {
20667
20784
  this.log(`Updating trailing stop for ${symbol} from ${currentTrailPercent}% to ${trailPercent100}%`, {
20668
20785
  symbol,
20669
20786
  });
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
- }
20787
+ await this.makeRequest(`/orders/${trailingStopOrder.id}`, 'PATCH', { trail: trailPercent100.toString() }, '', {
20788
+ action: 'Update trailing stop order',
20789
+ symbol,
20790
+ });
20682
20791
  }
20683
20792
  /**
20684
20793
  * Cancel all open orders
@@ -20686,10 +20795,16 @@ class AlpacaTradingAPI {
20686
20795
  async cancelAllOrders() {
20687
20796
  this.log(`Canceling all open orders`);
20688
20797
  try {
20689
- await this.makeRequest('/orders', 'DELETE');
20798
+ await this.makeRequest('/orders', 'DELETE', undefined, '', {
20799
+ action: 'Cancel all open orders',
20800
+ });
20690
20801
  }
20691
20802
  catch (error) {
20692
- this.log(`Error canceling all orders: ${error}`, { type: 'error' });
20803
+ if (!(error instanceof AlpacaRequestError)) {
20804
+ this.log(`Error canceling all orders: ${error instanceof Error ? error.message : `${error}`}`, {
20805
+ type: 'error',
20806
+ });
20807
+ }
20693
20808
  }
20694
20809
  }
20695
20810
  /**
@@ -20701,15 +20816,14 @@ class AlpacaTradingAPI {
20701
20816
  async cancelOrder(orderId) {
20702
20817
  this.log(`Attempting to cancel order ${orderId}`);
20703
20818
  try {
20704
- await this.makeRequest(`/orders/${orderId}`, 'DELETE');
20819
+ await this.makeRequest(`/orders/${orderId}`, 'DELETE', undefined, '', {
20820
+ action: `Cancel order ${orderId}`,
20821
+ });
20705
20822
  this.log(`Successfully canceled order ${orderId}`);
20706
20823
  }
20707
20824
  catch (error) {
20708
20825
  // 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
- });
20826
+ if (error instanceof AlpacaRequestError && error.status === 422) {
20713
20827
  throw new Error(`Order ${orderId} is not cancelable`);
20714
20828
  }
20715
20829
  // Re-throw other errors
@@ -20744,13 +20858,10 @@ class AlpacaTradingAPI {
20744
20858
  if (client_order_id !== undefined) {
20745
20859
  body.client_order_id = client_order_id;
20746
20860
  }
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
- }
20861
+ return this.makeRequest('/orders', 'POST', body, '', {
20862
+ action: 'Create limit order',
20863
+ symbol,
20864
+ });
20754
20865
  }
20755
20866
  /**
20756
20867
  * Close all equities positions
@@ -20816,7 +20927,9 @@ class AlpacaTradingAPI {
20816
20927
  }
20817
20928
  }
20818
20929
  else {
20819
- await this.makeRequest('/positions', 'DELETE', undefined, options.cancel_orders ? '?cancel_orders=true' : '');
20930
+ await this.makeRequest('/positions', 'DELETE', undefined, options.cancel_orders ? '?cancel_orders=true' : '', {
20931
+ action: 'Close all positions',
20932
+ });
20820
20933
  }
20821
20934
  }
20822
20935
  /**
@@ -20895,7 +21008,9 @@ class AlpacaTradingAPI {
20895
21008
  queryParams.append('end', params.end);
20896
21009
  if (params.date_end)
20897
21010
  queryParams.append('date_end', params.date_end);
20898
- const response = await this.makeRequest(`/account/portfolio/history?${queryParams.toString()}`);
21011
+ const response = await this.makeRequest(`/account/portfolio/history?${queryParams.toString()}`, 'GET', undefined, '', {
21012
+ action: 'Get portfolio history',
21013
+ });
20899
21014
  return response;
20900
21015
  }
20901
21016
  /**
@@ -20986,7 +21101,10 @@ class AlpacaTradingAPI {
20986
21101
  this.log(`Fetching option contracts for ${params.underlying_symbols.join(', ')}`, {
20987
21102
  symbol: params.underlying_symbols.join(', '),
20988
21103
  });
20989
- const response = (await this.makeRequest(`/options/contracts?${queryParams.toString()}`));
21104
+ const response = await this.makeRequest(`/options/contracts?${queryParams.toString()}`, 'GET', undefined, '', {
21105
+ action: 'Get option contracts',
21106
+ symbol: params.underlying_symbols.join(', '),
21107
+ });
20990
21108
  this.log(`Found ${response.option_contracts.length} option contracts`, {
20991
21109
  symbol: params.underlying_symbols.join(', '),
20992
21110
  });
@@ -21001,7 +21119,10 @@ class AlpacaTradingAPI {
21001
21119
  this.log(`Fetching option contract details for ${symbolOrId}`, {
21002
21120
  symbol: symbolOrId,
21003
21121
  });
21004
- const response = (await this.makeRequest(`/options/contracts/${symbolOrId}`));
21122
+ const response = await this.makeRequest(`/options/contracts/${symbolOrId}`, 'GET', undefined, '', {
21123
+ action: 'Get option contract details',
21124
+ symbol: symbolOrId,
21125
+ });
21005
21126
  this.log(`Found option contract details for ${symbolOrId}: ${response.name}`, {
21006
21127
  symbol: symbolOrId,
21007
21128
  });
@@ -21019,10 +21140,10 @@ class AlpacaTradingAPI {
21019
21140
  */
21020
21141
  async createOptionOrder(symbol, qty, side, position_intent, type, limitPrice) {
21021
21142
  if (!Number.isInteger(qty) || qty <= 0) {
21022
- this.log('Quantity must be a positive whole number for option orders', { type: 'error' });
21143
+ this.log('Quantity must be a positive whole number for option orders', { symbol, type: 'error' });
21023
21144
  }
21024
21145
  if (type === 'limit' && limitPrice === undefined) {
21025
- this.log('Limit price is required for limit orders', { type: 'error' });
21146
+ this.log('Limit price is required for limit orders', { symbol, type: 'error' });
21026
21147
  }
21027
21148
  this.log(`Creating ${type} option order for ${symbol}: ${side} ${qty} contracts (${position_intent})${type === 'limit' ? ` at $${limitPrice?.toFixed(2)}` : ''}`, {
21028
21149
  symbol,
@@ -21040,7 +21161,10 @@ class AlpacaTradingAPI {
21040
21161
  if (type === 'limit' && limitPrice !== undefined) {
21041
21162
  orderData.limit_price = this.roundPriceForAlpaca(limitPrice).toString();
21042
21163
  }
21043
- return this.makeRequest('/orders', 'POST', orderData);
21164
+ return this.makeRequest('/orders', 'POST', orderData, '', {
21165
+ action: 'Create option order',
21166
+ symbol,
21167
+ });
21044
21168
  }
21045
21169
  /**
21046
21170
  * Create a multi-leg option order
@@ -21051,16 +21175,19 @@ class AlpacaTradingAPI {
21051
21175
  * @returns The created multi-leg order
21052
21176
  */
21053
21177
  async createMultiLegOptionOrder(legs, qty, type, limitPrice) {
21178
+ const legSymbols = legs.map((leg) => leg.symbol).join(', ');
21054
21179
  if (!Number.isInteger(qty) || qty <= 0) {
21055
- this.log('Quantity must be a positive whole number for option orders', { type: 'error' });
21180
+ this.log('Quantity must be a positive whole number for option orders', {
21181
+ symbol: legSymbols,
21182
+ type: 'error',
21183
+ });
21056
21184
  }
21057
21185
  if (type === 'limit' && limitPrice === undefined) {
21058
- this.log('Limit price is required for limit orders', { type: 'error' });
21186
+ this.log('Limit price is required for limit orders', { symbol: legSymbols, type: 'error' });
21059
21187
  }
21060
21188
  if (legs.length < 2) {
21061
- this.log('Multi-leg orders require at least 2 legs', { type: 'error' });
21189
+ this.log('Multi-leg orders require at least 2 legs', { symbol: legSymbols, type: 'error' });
21062
21190
  }
21063
- const legSymbols = legs.map((leg) => leg.symbol).join(', ');
21064
21191
  this.log(`Creating multi-leg ${type} option order with ${legs.length} legs (${legSymbols})${type === 'limit' ? ` at $${limitPrice?.toFixed(2)}` : ''}`, {
21065
21192
  symbol: legSymbols,
21066
21193
  });
@@ -21074,7 +21201,10 @@ class AlpacaTradingAPI {
21074
21201
  if (type === 'limit' && limitPrice !== undefined) {
21075
21202
  orderData.limit_price = this.roundPriceForAlpaca(limitPrice).toString();
21076
21203
  }
21077
- return this.makeRequest('/orders', 'POST', orderData);
21204
+ return this.makeRequest('/orders', 'POST', orderData, '', {
21205
+ action: 'Create multi-leg option order',
21206
+ symbol: legSymbols,
21207
+ });
21078
21208
  }
21079
21209
  /**
21080
21210
  * Exercise an option contract
@@ -21085,7 +21215,10 @@ class AlpacaTradingAPI {
21085
21215
  this.log(`Exercising option contract ${symbolOrContractId}`, {
21086
21216
  symbol: symbolOrContractId,
21087
21217
  });
21088
- return this.makeRequest(`/positions/${symbolOrContractId}/exercise`, 'POST');
21218
+ return this.makeRequest(`/positions/${symbolOrContractId}/exercise`, 'POST', undefined, '', {
21219
+ action: 'Exercise option contract',
21220
+ symbol: symbolOrContractId,
21221
+ });
21089
21222
  }
21090
21223
  /**
21091
21224
  * Get option positions
@@ -21121,7 +21254,9 @@ class AlpacaTradingAPI {
21121
21254
  queryParams.append('date', date);
21122
21255
  }
21123
21256
  this.log(`Fetching option activities${activityType ? ` of type ${activityType}` : ''}${date ? ` for date ${date}` : ''}`);
21124
- return this.makeRequest(`/account/activities?${queryParams.toString()}`);
21257
+ return this.makeRequest(`/account/activities?${queryParams.toString()}`, 'GET', undefined, '', {
21258
+ action: 'Get option activities',
21259
+ });
21125
21260
  }
21126
21261
  /**
21127
21262
  * Create a long call spread (buy lower strike call, sell higher strike call)
@@ -21219,13 +21354,7 @@ class AlpacaTradingAPI {
21219
21354
  position_intent: 'buy_to_open',
21220
21355
  },
21221
21356
  ];
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
- }
21357
+ return this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
21229
21358
  }
21230
21359
  /**
21231
21360
  * Create a covered call (sell call option against owned stock)
@@ -21241,13 +21370,7 @@ class AlpacaTradingAPI {
21241
21370
  });
21242
21371
  // For covered calls, we don't need to include the stock leg if we already own the shares
21243
21372
  // 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
- }
21373
+ return this.createOptionOrder(callOptionSymbol, qty, 'sell', 'sell_to_open', 'limit', limitPrice);
21251
21374
  }
21252
21375
  /**
21253
21376
  * Roll an option position to a new expiration or strike
@@ -21282,13 +21405,7 @@ class AlpacaTradingAPI {
21282
21405
  position_intent: openPositionIntent,
21283
21406
  },
21284
21407
  ];
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
- }
21408
+ return this.createMultiLegOptionOrder(legs, qty, 'limit', limitPrice);
21292
21409
  }
21293
21410
  /**
21294
21411
  * Get option chain for a specific underlying symbol and expiration date
@@ -21312,10 +21429,12 @@ class AlpacaTradingAPI {
21312
21429
  return response.option_contracts || [];
21313
21430
  }
21314
21431
  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
- });
21432
+ if (!(error instanceof AlpacaRequestError)) {
21433
+ this.log(`Failed to fetch option chain for ${underlyingSymbol}: ${error instanceof Error ? error.message : `${error}`}`, {
21434
+ type: 'error',
21435
+ symbol: underlyingSymbol,
21436
+ });
21437
+ }
21319
21438
  return [];
21320
21439
  }
21321
21440
  }
@@ -21346,10 +21465,12 @@ class AlpacaTradingAPI {
21346
21465
  return Array.from(expirationDates).sort();
21347
21466
  }
21348
21467
  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
- });
21468
+ if (!(error instanceof AlpacaRequestError)) {
21469
+ this.log(`Failed to fetch expiration dates for ${underlyingSymbol}: ${error instanceof Error ? error.message : `${error}`}`, {
21470
+ type: 'error',
21471
+ symbol: underlyingSymbol,
21472
+ });
21473
+ }
21353
21474
  return [];
21354
21475
  }
21355
21476
  }
@@ -21408,7 +21529,10 @@ class AlpacaTradingAPI {
21408
21529
  this.log(`Canceling open order for ${order.symbol}`, {
21409
21530
  symbol: order.symbol,
21410
21531
  });
21411
- await this.makeRequest(`/orders/${order.id}`, 'DELETE');
21532
+ await this.makeRequest(`/orders/${order.id}`, 'DELETE', undefined, '', {
21533
+ action: `Cancel option order ${order.id}`,
21534
+ symbol: order.symbol,
21535
+ });
21412
21536
  }
21413
21537
  }
21414
21538
  }
@@ -21431,13 +21555,7 @@ class AlpacaTradingAPI {
21431
21555
  const quantityToClose = qty || parseInt(position.qty);
21432
21556
  const side = position.side === 'long' ? 'sell' : 'buy';
21433
21557
  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
- }
21558
+ return this.createOptionOrder(symbol, quantityToClose, side, positionIntent, 'market');
21441
21559
  }
21442
21560
  /**
21443
21561
  * Create a complete equities trade with optional stop loss and take profit
@@ -21572,16 +21690,10 @@ class AlpacaTradingAPI {
21572
21690
  this.log(logMessage, {
21573
21691
  symbol,
21574
21692
  });
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
- }
21693
+ return this.makeRequest('/orders', 'POST', orderData, '', {
21694
+ action: 'Create equities trade',
21695
+ symbol,
21696
+ });
21585
21697
  }
21586
21698
  }
21587
21699