@adaptic/utils 0.0.978 → 0.0.980

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -3799,6 +3799,419 @@ class AlpacaMarketDataAPI extends EventEmitter {
3799
3799
  // Export the singleton instance
3800
3800
  const marketDataAPI = AlpacaMarketDataAPI.getInstance();
3801
3801
 
3802
+ const DEFAULT_RETRY_CONFIG = {
3803
+ maxRetries: 3,
3804
+ baseDelayMs: 1000,
3805
+ maxDelayMs: 30000,
3806
+ retryableStatusCodes: [429, 500, 502, 503, 504],
3807
+ retryOnNetworkError: true,
3808
+ };
3809
+ /**
3810
+ * Node.js / undici / system error codes that represent transient network
3811
+ * conditions. Present on `error.code` for net/http/dns/undici errors.
3812
+ */
3813
+ const RETRYABLE_ERROR_CODES = new Set([
3814
+ "ETIMEDOUT",
3815
+ "ESOCKETTIMEDOUT",
3816
+ "ECONNRESET",
3817
+ "ECONNREFUSED",
3818
+ "EHOSTUNREACH",
3819
+ "ENETUNREACH",
3820
+ "EAI_AGAIN",
3821
+ "EPIPE",
3822
+ "ECONNABORTED",
3823
+ "ENOTFOUND",
3824
+ "UND_ERR_CONNECT_TIMEOUT",
3825
+ "UND_ERR_HEADERS_TIMEOUT",
3826
+ "UND_ERR_BODY_TIMEOUT",
3827
+ "UND_ERR_SOCKET",
3828
+ "UND_ERR_CLOSED",
3829
+ "UND_ERR_REQ_CONTENT_LENGTH_MISMATCH",
3830
+ ]);
3831
+ /**
3832
+ * Error constructor names / `error.name` values that indicate transient
3833
+ * abort / timeout conditions.
3834
+ */
3835
+ const RETRYABLE_ERROR_NAMES = new Set([
3836
+ "AbortError",
3837
+ "TimeoutError",
3838
+ "FetchError",
3839
+ "RequestTimeoutError",
3840
+ "ConnectTimeoutError",
3841
+ "HeadersTimeoutError",
3842
+ "BodyTimeoutError",
3843
+ ]);
3844
+ /**
3845
+ * Message-pattern fallback for libraries that discard error codes/names but
3846
+ * preserve text (e.g., some Apollo/axios wrappers).
3847
+ */
3848
+ const RETRYABLE_MESSAGE_PATTERNS = [
3849
+ /aborted/i,
3850
+ /timeout/i,
3851
+ /timed out/i,
3852
+ /network error/i,
3853
+ /socket hang up/i,
3854
+ /connection (reset|refused|closed)/i,
3855
+ /ECONNRESET/,
3856
+ /ETIMEDOUT/,
3857
+ /ECONNREFUSED/,
3858
+ /EAI_AGAIN/,
3859
+ /UND_ERR_/,
3860
+ ];
3861
+ /**
3862
+ * Walks the `error.cause` chain (capped to avoid cycles) and tests whether
3863
+ * any link along the chain looks like a transient network error. Modern APIs
3864
+ * (undici, fetch, Apollo Client 3.8+) wrap the root network failure as a
3865
+ * `.cause`, so the surface `Error` may report a generic message while the
3866
+ * actionable signal lives one or more levels deeper.
3867
+ *
3868
+ * Exported for use by downstream consumers (engine services, per-call catch
3869
+ * blocks, application-level loggers) that need to demote recoverable
3870
+ * transient errors from ERROR to WARN. Aligns the whole stack on a single
3871
+ * canonical classifier so MassiveAPI, AlpacaAPI, and application-layer
3872
+ * retry handlers all treat the same network blips identically.
3873
+ */
3874
+ function isTransientNetworkError(error) {
3875
+ const MAX_CAUSE_DEPTH = 6;
3876
+ let current = error;
3877
+ for (let depth = 0; depth < MAX_CAUSE_DEPTH && current; depth++) {
3878
+ if (current instanceof Error || typeof current === "object") {
3879
+ const err = current;
3880
+ if (typeof err.name === "string" && RETRYABLE_ERROR_NAMES.has(err.name)) {
3881
+ return true;
3882
+ }
3883
+ if (typeof err.code === "string" && RETRYABLE_ERROR_CODES.has(err.code)) {
3884
+ return true;
3885
+ }
3886
+ if (typeof err.message === "string") {
3887
+ for (const pattern of RETRYABLE_MESSAGE_PATTERNS) {
3888
+ if (pattern.test(err.message)) {
3889
+ return true;
3890
+ }
3891
+ }
3892
+ }
3893
+ current = err.cause;
3894
+ }
3895
+ else {
3896
+ break;
3897
+ }
3898
+ }
3899
+ return false;
3900
+ }
3901
+ /**
3902
+ * Analyzes an error and determines if it's retryable.
3903
+ * @param error - The error to analyze
3904
+ * @param response - Optional Response object for HTTP errors
3905
+ * @param config - Retry configuration
3906
+ * @returns Structured error details
3907
+ */
3908
+ function analyzeError(error, response, config) {
3909
+ // Handle Response objects with error status codes
3910
+ if (response && !response.ok) {
3911
+ const status = response.status;
3912
+ // Rate limit errors - always retryable
3913
+ if (status === 429) {
3914
+ const retryAfterHeader = response.headers.get("Retry-After");
3915
+ const retryAfter = retryAfterHeader
3916
+ ? parseInt(retryAfterHeader, 10) * 1000
3917
+ : undefined;
3918
+ return {
3919
+ type: "RATE_LIMIT",
3920
+ reason: "Rate limit exceeded",
3921
+ status,
3922
+ retryAfter,
3923
+ isRetryable: true,
3924
+ };
3925
+ }
3926
+ // Authentication errors - never retry
3927
+ if (status === 401 || status === 403) {
3928
+ return {
3929
+ type: "AUTH_ERROR",
3930
+ reason: status === 401
3931
+ ? "Authentication failed - invalid credentials"
3932
+ : "Access forbidden - insufficient permissions",
3933
+ status,
3934
+ isRetryable: false,
3935
+ };
3936
+ }
3937
+ // Server errors - check if in retryable list
3938
+ if (status >= 500 && status < 600) {
3939
+ return {
3940
+ type: "SERVER_ERROR",
3941
+ reason: `Server error (${status})`,
3942
+ status,
3943
+ isRetryable: config.retryableStatusCodes.includes(status),
3944
+ };
3945
+ }
3946
+ // Other client errors - never retry
3947
+ if (status >= 400 && status < 500) {
3948
+ return {
3949
+ type: "CLIENT_ERROR",
3950
+ reason: `Client error (${status})`,
3951
+ status,
3952
+ isRetryable: false,
3953
+ };
3954
+ }
3955
+ }
3956
+ // Handle network errors (TypeError from fetch API)
3957
+ if (error instanceof TypeError && error.message.includes("fetch")) {
3958
+ return {
3959
+ type: "NETWORK_ERROR",
3960
+ reason: "Network connectivity issue",
3961
+ status: null,
3962
+ isRetryable: config.retryOnNetworkError,
3963
+ };
3964
+ }
3965
+ // Handle transient network conditions: AbortError, TimeoutError,
3966
+ // Node/undici error codes (ETIMEDOUT, ECONNRESET, UND_ERR_*), and
3967
+ // wrapped failures exposed via error.cause. This catches the broad class
3968
+ // of infrastructure flakes that the TypeError-only check above misses.
3969
+ if (isTransientNetworkError(error)) {
3970
+ const reason = error instanceof Error ? error.message : "Transient network error";
3971
+ return {
3972
+ type: "NETWORK_ERROR",
3973
+ reason,
3974
+ status: null,
3975
+ isRetryable: config.retryOnNetworkError,
3976
+ };
3977
+ }
3978
+ // Handle error objects with messages
3979
+ if (error instanceof Error) {
3980
+ // Parse error messages that might contain status information
3981
+ if (error.message.includes("429") || error.message.includes("RATE_LIMIT")) {
3982
+ const match = error.message.match(/RATE_LIMIT: 429:(\d+)/);
3983
+ const retryAfter = match ? parseInt(match[1], 10) : undefined;
3984
+ return {
3985
+ type: "RATE_LIMIT",
3986
+ reason: "Rate limit exceeded",
3987
+ status: 429,
3988
+ retryAfter,
3989
+ isRetryable: true,
3990
+ };
3991
+ }
3992
+ if (error.message.includes("401") ||
3993
+ error.message.includes("403") ||
3994
+ error.message.includes("AUTH_ERROR")) {
3995
+ const status = error.message.includes("401") ? 401 : 403;
3996
+ return {
3997
+ type: "AUTH_ERROR",
3998
+ reason: `Authentication error (${status})`,
3999
+ status,
4000
+ isRetryable: false,
4001
+ };
4002
+ }
4003
+ if (error.message.includes("SERVER_ERROR") ||
4004
+ error.message.match(/50[0-9]/)) {
4005
+ const statusMatch = error.message.match(/50[0-9]/);
4006
+ const status = statusMatch ? parseInt(statusMatch[0], 10) : 500;
4007
+ return {
4008
+ type: "SERVER_ERROR",
4009
+ reason: `Server error (${status})`,
4010
+ status,
4011
+ isRetryable: config.retryableStatusCodes.includes(status),
4012
+ };
4013
+ }
4014
+ if (error.message.includes("network") ||
4015
+ error.message.includes("NETWORK_ERROR")) {
4016
+ return {
4017
+ type: "NETWORK_ERROR",
4018
+ reason: error.message,
4019
+ status: null,
4020
+ isRetryable: config.retryOnNetworkError,
4021
+ };
4022
+ }
4023
+ }
4024
+ // Unknown error - not retryable by default for safety
4025
+ return {
4026
+ type: "UNKNOWN",
4027
+ reason: error instanceof Error ? error.message : String(error),
4028
+ status: null,
4029
+ isRetryable: false,
4030
+ };
4031
+ }
4032
+ /**
4033
+ * Calculates the delay before the next retry attempt using exponential backoff with jitter.
4034
+ * @param attempt - Current attempt number (1-indexed)
4035
+ * @param baseDelay - Base delay in milliseconds
4036
+ * @param maxDelay - Maximum delay in milliseconds
4037
+ * @returns Delay in milliseconds
4038
+ */
4039
+ function calculateBackoff(attempt, baseDelay, maxDelay) {
4040
+ // Exponential backoff: baseDelay * 2^(attempt-1)
4041
+ const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);
4042
+ // Cap at maxDelay
4043
+ const cappedDelay = Math.min(exponentialDelay, maxDelay);
4044
+ // Add jitter (random value between 0% and 25% of the delay)
4045
+ const jitter = Math.random() * cappedDelay * 0.25;
4046
+ return Math.floor(cappedDelay + jitter);
4047
+ }
4048
+ /**
4049
+ * Wraps an async function with retry logic and exponential backoff.
4050
+ *
4051
+ * This utility handles transient errors in external API calls by automatically retrying
4052
+ * failed requests with intelligent backoff strategies. It respects rate limit headers,
4053
+ * fails fast on non-retryable errors, and provides detailed logging.
4054
+ *
4055
+ * @template T - The return type of the wrapped function
4056
+ * @param fn - The async function to wrap with retry logic
4057
+ * @param config - Retry configuration (merged with defaults)
4058
+ * @param label - A descriptive label for logging (e.g., 'Massive.fetchTickerInfo')
4059
+ * @returns A promise that resolves to the function's return value
4060
+ * @throws The last error encountered if all retries are exhausted
4061
+ *
4062
+ * @example
4063
+ * ```typescript
4064
+ * // Basic usage with defaults
4065
+ * const data = await withRetry(
4066
+ * async () => fetch('https://api.example.com/data'),
4067
+ * {},
4068
+ * 'ExampleAPI.fetchData'
4069
+ * );
4070
+ *
4071
+ * // Custom configuration for rate-limited API
4072
+ * const result = await withRetry(
4073
+ * async () => alphaVantageAPI.getQuote(symbol),
4074
+ * {
4075
+ * maxRetries: 5,
4076
+ * baseDelayMs: 5000,
4077
+ * maxDelayMs: 60000,
4078
+ * onRetry: (attempt, error) => {
4079
+ * getLogger().info(`Retry ${attempt} after error:`, error);
4080
+ * }
4081
+ * },
4082
+ * 'AlphaVantage.getQuote'
4083
+ * );
4084
+ * ```
4085
+ */
4086
+ async function withRetry(fn, config = {}, label = "unknown") {
4087
+ const fullConfig = { ...DEFAULT_RETRY_CONFIG, ...config };
4088
+ let lastError;
4089
+ for (let attempt = 1; attempt <= fullConfig.maxRetries; attempt++) {
4090
+ try {
4091
+ const result = await fn();
4092
+ // If we succeeded after retries, log it
4093
+ if (attempt > 1) {
4094
+ getLogger().info(`[${label}] Succeeded on attempt ${attempt}/${fullConfig.maxRetries}`);
4095
+ }
4096
+ return result;
4097
+ }
4098
+ catch (error) {
4099
+ lastError = error;
4100
+ // If this is the last attempt, throw the error.
4101
+ // Transient network classes (undici/fetch timeouts, ECONNRESET,
4102
+ // AbortError, etc.) are self-healing at the upstream retry layer —
4103
+ // the caller re-invokes on the next refresh/poll tick. Logging them
4104
+ // at ERROR produces alert noise that does not represent actionable
4105
+ // failures. Demote the transient class to WARN with a recovery hint;
4106
+ // reserve ERROR for non-transient final failures (auth, schema,
4107
+ // contract violations, unknown classes).
4108
+ if (attempt === fullConfig.maxRetries) {
4109
+ const isTransient = isTransientNetworkError(error);
4110
+ const logMeta = {
4111
+ error: error instanceof Error ? error.message : String(error),
4112
+ attempts: fullConfig.maxRetries,
4113
+ timestamp: new Date().toISOString(),
4114
+ ...(isTransient
4115
+ ? {
4116
+ transient: true,
4117
+ recoveryHint: "Upstream caller should retry on next cycle",
4118
+ }
4119
+ : {}),
4120
+ };
4121
+ if (isTransient) {
4122
+ getLogger().warn(`[${label}] Failed after ${fullConfig.maxRetries} attempts (transient)`, logMeta);
4123
+ }
4124
+ else {
4125
+ getLogger().error(`[${label}] Failed after ${fullConfig.maxRetries} attempts`, logMeta);
4126
+ }
4127
+ throw error;
4128
+ }
4129
+ // Analyze the error to determine if we should retry
4130
+ const response = error instanceof Response ? error : null;
4131
+ const errorDetails = analyzeError(error, response, fullConfig);
4132
+ // If error is not retryable, fail immediately
4133
+ if (!errorDetails.isRetryable) {
4134
+ getLogger().error(`[${label}] Non-retryable error (${errorDetails.type})`, {
4135
+ reason: errorDetails.reason,
4136
+ status: errorDetails.status,
4137
+ timestamp: new Date().toISOString(),
4138
+ });
4139
+ throw error;
4140
+ }
4141
+ // Calculate delay for next retry
4142
+ let delayMs;
4143
+ if (errorDetails.type === "RATE_LIMIT" && errorDetails.retryAfter) {
4144
+ // Use Retry-After header if available
4145
+ delayMs = errorDetails.retryAfter;
4146
+ }
4147
+ else if (errorDetails.type === "RATE_LIMIT") {
4148
+ // For rate limits without Retry-After, use a longer minimum delay
4149
+ delayMs = Math.max(calculateBackoff(attempt, fullConfig.baseDelayMs, fullConfig.maxDelayMs), 5000);
4150
+ }
4151
+ else {
4152
+ // Standard exponential backoff with jitter
4153
+ delayMs = calculateBackoff(attempt, fullConfig.baseDelayMs, fullConfig.maxDelayMs);
4154
+ }
4155
+ // Log the retry attempt
4156
+ getLogger().warn(`[${label}] Attempt ${attempt}/${fullConfig.maxRetries} failed: ${errorDetails.reason}. Retrying in ${delayMs}ms...`, {
4157
+ attemptNumber: attempt,
4158
+ totalRetries: fullConfig.maxRetries,
4159
+ errorType: errorDetails.type,
4160
+ httpStatus: errorDetails.status,
4161
+ retryDelay: delayMs,
4162
+ timestamp: new Date().toISOString(),
4163
+ });
4164
+ // Call the optional retry callback
4165
+ if (fullConfig.onRetry) {
4166
+ fullConfig.onRetry(attempt, error);
4167
+ }
4168
+ // Wait before retrying
4169
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
4170
+ }
4171
+ }
4172
+ // This should never be reached due to the throw in the last attempt,
4173
+ // but TypeScript needs this to satisfy the return type
4174
+ throw lastError;
4175
+ }
4176
+ /**
4177
+ * API-specific retry configurations for different external services.
4178
+ * These configurations are tuned based on each API's rate limits and characteristics.
4179
+ */
4180
+ const API_RETRY_CONFIGS = {
4181
+ /** Massive.com API - 5 requests/second rate limit */
4182
+ MASSIVE: {
4183
+ maxRetries: 3,
4184
+ baseDelayMs: 1000,
4185
+ maxDelayMs: 30000,
4186
+ retryableStatusCodes: [429, 500, 502, 503, 504],
4187
+ retryOnNetworkError: true,
4188
+ },
4189
+ /** Alpha Vantage API - 5 requests/minute rate limit (more strict) */
4190
+ ALPHA_VANTAGE: {
4191
+ maxRetries: 5,
4192
+ baseDelayMs: 5000,
4193
+ maxDelayMs: 60000,
4194
+ retryableStatusCodes: [429, 500, 502, 503, 504],
4195
+ retryOnNetworkError: true,
4196
+ },
4197
+ /** Alpaca API - generally reliable, shorter retry window */
4198
+ ALPACA: {
4199
+ maxRetries: 3,
4200
+ baseDelayMs: 1000,
4201
+ maxDelayMs: 30000,
4202
+ retryableStatusCodes: [429, 500, 502, 503, 504],
4203
+ retryOnNetworkError: true,
4204
+ },
4205
+ /** Generic crypto API configuration */
4206
+ CRYPTO: {
4207
+ maxRetries: 3,
4208
+ baseDelayMs: 1000,
4209
+ maxDelayMs: 30000,
4210
+ retryableStatusCodes: [429, 500, 502, 503, 504],
4211
+ retryOnNetworkError: true,
4212
+ },
4213
+ };
4214
+
3802
4215
  const limitPriceSlippagePercent100 = 0.1; // 0.1%
3803
4216
  /**
3804
4217
  Websocket example
@@ -4109,9 +4522,20 @@ class AlpacaTradingAPI {
4109
4522
  }
4110
4523
  catch (err) {
4111
4524
  const error = err;
4525
+ const isTransient = isTransientNetworkError(err);
4526
+ // Transient fetch failures (timeouts, connection resets, undici
4527
+ // aborts) are recoverable at the caller's next cycle; log at WARN
4528
+ // and annotate for observability filters. Non-transient errors
4529
+ // (4xx/auth/schema) stay at ERROR for operator attention.
4112
4530
  this.log(`Error in makeRequest: ${error.message}. Url: ${url}`, {
4113
4531
  source: "AlpacaAPI",
4114
- type: "error",
4532
+ type: isTransient ? "warn" : "error",
4533
+ metadata: isTransient
4534
+ ? {
4535
+ transient: true,
4536
+ recoveryHint: "Upstream caller should retry on next cycle",
4537
+ }
4538
+ : undefined,
4115
4539
  });
4116
4540
  throw error;
4117
4541
  }
@@ -5595,392 +6019,6 @@ async function createLimitOrder(auth, params = {
5595
6019
  });
5596
6020
  }
5597
6021
 
5598
- const DEFAULT_RETRY_CONFIG = {
5599
- maxRetries: 3,
5600
- baseDelayMs: 1000,
5601
- maxDelayMs: 30000,
5602
- retryableStatusCodes: [429, 500, 502, 503, 504],
5603
- retryOnNetworkError: true,
5604
- };
5605
- /**
5606
- * Node.js / undici / system error codes that represent transient network
5607
- * conditions. Present on `error.code` for net/http/dns/undici errors.
5608
- */
5609
- const RETRYABLE_ERROR_CODES = new Set([
5610
- "ETIMEDOUT",
5611
- "ESOCKETTIMEDOUT",
5612
- "ECONNRESET",
5613
- "ECONNREFUSED",
5614
- "EHOSTUNREACH",
5615
- "ENETUNREACH",
5616
- "EAI_AGAIN",
5617
- "EPIPE",
5618
- "ECONNABORTED",
5619
- "ENOTFOUND",
5620
- "UND_ERR_CONNECT_TIMEOUT",
5621
- "UND_ERR_HEADERS_TIMEOUT",
5622
- "UND_ERR_BODY_TIMEOUT",
5623
- "UND_ERR_SOCKET",
5624
- "UND_ERR_CLOSED",
5625
- "UND_ERR_REQ_CONTENT_LENGTH_MISMATCH",
5626
- ]);
5627
- /**
5628
- * Error constructor names / `error.name` values that indicate transient
5629
- * abort / timeout conditions.
5630
- */
5631
- const RETRYABLE_ERROR_NAMES = new Set([
5632
- "AbortError",
5633
- "TimeoutError",
5634
- "FetchError",
5635
- "RequestTimeoutError",
5636
- "ConnectTimeoutError",
5637
- "HeadersTimeoutError",
5638
- "BodyTimeoutError",
5639
- ]);
5640
- /**
5641
- * Message-pattern fallback for libraries that discard error codes/names but
5642
- * preserve text (e.g., some Apollo/axios wrappers).
5643
- */
5644
- const RETRYABLE_MESSAGE_PATTERNS = [
5645
- /aborted/i,
5646
- /timeout/i,
5647
- /timed out/i,
5648
- /network error/i,
5649
- /socket hang up/i,
5650
- /connection (reset|refused|closed)/i,
5651
- /ECONNRESET/,
5652
- /ETIMEDOUT/,
5653
- /ECONNREFUSED/,
5654
- /EAI_AGAIN/,
5655
- /UND_ERR_/,
5656
- ];
5657
- /**
5658
- * Walks the `error.cause` chain (capped to avoid cycles) and tests whether
5659
- * any link along the chain looks like a transient network error. Modern APIs
5660
- * (undici, fetch, Apollo Client 3.8+) wrap the root network failure as a
5661
- * `.cause`, so the surface `Error` may report a generic message while the
5662
- * actionable signal lives one or more levels deeper.
5663
- */
5664
- function isTransientNetworkError(error) {
5665
- const MAX_CAUSE_DEPTH = 6;
5666
- let current = error;
5667
- for (let depth = 0; depth < MAX_CAUSE_DEPTH && current; depth++) {
5668
- if (current instanceof Error || typeof current === "object") {
5669
- const err = current;
5670
- if (typeof err.name === "string" && RETRYABLE_ERROR_NAMES.has(err.name)) {
5671
- return true;
5672
- }
5673
- if (typeof err.code === "string" && RETRYABLE_ERROR_CODES.has(err.code)) {
5674
- return true;
5675
- }
5676
- if (typeof err.message === "string") {
5677
- for (const pattern of RETRYABLE_MESSAGE_PATTERNS) {
5678
- if (pattern.test(err.message)) {
5679
- return true;
5680
- }
5681
- }
5682
- }
5683
- current = err.cause;
5684
- }
5685
- else {
5686
- break;
5687
- }
5688
- }
5689
- return false;
5690
- }
5691
- /**
5692
- * Analyzes an error and determines if it's retryable.
5693
- * @param error - The error to analyze
5694
- * @param response - Optional Response object for HTTP errors
5695
- * @param config - Retry configuration
5696
- * @returns Structured error details
5697
- */
5698
- function analyzeError(error, response, config) {
5699
- // Handle Response objects with error status codes
5700
- if (response && !response.ok) {
5701
- const status = response.status;
5702
- // Rate limit errors - always retryable
5703
- if (status === 429) {
5704
- const retryAfterHeader = response.headers.get("Retry-After");
5705
- const retryAfter = retryAfterHeader
5706
- ? parseInt(retryAfterHeader, 10) * 1000
5707
- : undefined;
5708
- return {
5709
- type: "RATE_LIMIT",
5710
- reason: "Rate limit exceeded",
5711
- status,
5712
- retryAfter,
5713
- isRetryable: true,
5714
- };
5715
- }
5716
- // Authentication errors - never retry
5717
- if (status === 401 || status === 403) {
5718
- return {
5719
- type: "AUTH_ERROR",
5720
- reason: status === 401
5721
- ? "Authentication failed - invalid credentials"
5722
- : "Access forbidden - insufficient permissions",
5723
- status,
5724
- isRetryable: false,
5725
- };
5726
- }
5727
- // Server errors - check if in retryable list
5728
- if (status >= 500 && status < 600) {
5729
- return {
5730
- type: "SERVER_ERROR",
5731
- reason: `Server error (${status})`,
5732
- status,
5733
- isRetryable: config.retryableStatusCodes.includes(status),
5734
- };
5735
- }
5736
- // Other client errors - never retry
5737
- if (status >= 400 && status < 500) {
5738
- return {
5739
- type: "CLIENT_ERROR",
5740
- reason: `Client error (${status})`,
5741
- status,
5742
- isRetryable: false,
5743
- };
5744
- }
5745
- }
5746
- // Handle network errors (TypeError from fetch API)
5747
- if (error instanceof TypeError && error.message.includes("fetch")) {
5748
- return {
5749
- type: "NETWORK_ERROR",
5750
- reason: "Network connectivity issue",
5751
- status: null,
5752
- isRetryable: config.retryOnNetworkError,
5753
- };
5754
- }
5755
- // Handle transient network conditions: AbortError, TimeoutError,
5756
- // Node/undici error codes (ETIMEDOUT, ECONNRESET, UND_ERR_*), and
5757
- // wrapped failures exposed via error.cause. This catches the broad class
5758
- // of infrastructure flakes that the TypeError-only check above misses.
5759
- if (isTransientNetworkError(error)) {
5760
- const reason = error instanceof Error ? error.message : "Transient network error";
5761
- return {
5762
- type: "NETWORK_ERROR",
5763
- reason,
5764
- status: null,
5765
- isRetryable: config.retryOnNetworkError,
5766
- };
5767
- }
5768
- // Handle error objects with messages
5769
- if (error instanceof Error) {
5770
- // Parse error messages that might contain status information
5771
- if (error.message.includes("429") || error.message.includes("RATE_LIMIT")) {
5772
- const match = error.message.match(/RATE_LIMIT: 429:(\d+)/);
5773
- const retryAfter = match ? parseInt(match[1], 10) : undefined;
5774
- return {
5775
- type: "RATE_LIMIT",
5776
- reason: "Rate limit exceeded",
5777
- status: 429,
5778
- retryAfter,
5779
- isRetryable: true,
5780
- };
5781
- }
5782
- if (error.message.includes("401") ||
5783
- error.message.includes("403") ||
5784
- error.message.includes("AUTH_ERROR")) {
5785
- const status = error.message.includes("401") ? 401 : 403;
5786
- return {
5787
- type: "AUTH_ERROR",
5788
- reason: `Authentication error (${status})`,
5789
- status,
5790
- isRetryable: false,
5791
- };
5792
- }
5793
- if (error.message.includes("SERVER_ERROR") ||
5794
- error.message.match(/50[0-9]/)) {
5795
- const statusMatch = error.message.match(/50[0-9]/);
5796
- const status = statusMatch ? parseInt(statusMatch[0], 10) : 500;
5797
- return {
5798
- type: "SERVER_ERROR",
5799
- reason: `Server error (${status})`,
5800
- status,
5801
- isRetryable: config.retryableStatusCodes.includes(status),
5802
- };
5803
- }
5804
- if (error.message.includes("network") ||
5805
- error.message.includes("NETWORK_ERROR")) {
5806
- return {
5807
- type: "NETWORK_ERROR",
5808
- reason: error.message,
5809
- status: null,
5810
- isRetryable: config.retryOnNetworkError,
5811
- };
5812
- }
5813
- }
5814
- // Unknown error - not retryable by default for safety
5815
- return {
5816
- type: "UNKNOWN",
5817
- reason: error instanceof Error ? error.message : String(error),
5818
- status: null,
5819
- isRetryable: false,
5820
- };
5821
- }
5822
- /**
5823
- * Calculates the delay before the next retry attempt using exponential backoff with jitter.
5824
- * @param attempt - Current attempt number (1-indexed)
5825
- * @param baseDelay - Base delay in milliseconds
5826
- * @param maxDelay - Maximum delay in milliseconds
5827
- * @returns Delay in milliseconds
5828
- */
5829
- function calculateBackoff(attempt, baseDelay, maxDelay) {
5830
- // Exponential backoff: baseDelay * 2^(attempt-1)
5831
- const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);
5832
- // Cap at maxDelay
5833
- const cappedDelay = Math.min(exponentialDelay, maxDelay);
5834
- // Add jitter (random value between 0% and 25% of the delay)
5835
- const jitter = Math.random() * cappedDelay * 0.25;
5836
- return Math.floor(cappedDelay + jitter);
5837
- }
5838
- /**
5839
- * Wraps an async function with retry logic and exponential backoff.
5840
- *
5841
- * This utility handles transient errors in external API calls by automatically retrying
5842
- * failed requests with intelligent backoff strategies. It respects rate limit headers,
5843
- * fails fast on non-retryable errors, and provides detailed logging.
5844
- *
5845
- * @template T - The return type of the wrapped function
5846
- * @param fn - The async function to wrap with retry logic
5847
- * @param config - Retry configuration (merged with defaults)
5848
- * @param label - A descriptive label for logging (e.g., 'Massive.fetchTickerInfo')
5849
- * @returns A promise that resolves to the function's return value
5850
- * @throws The last error encountered if all retries are exhausted
5851
- *
5852
- * @example
5853
- * ```typescript
5854
- * // Basic usage with defaults
5855
- * const data = await withRetry(
5856
- * async () => fetch('https://api.example.com/data'),
5857
- * {},
5858
- * 'ExampleAPI.fetchData'
5859
- * );
5860
- *
5861
- * // Custom configuration for rate-limited API
5862
- * const result = await withRetry(
5863
- * async () => alphaVantageAPI.getQuote(symbol),
5864
- * {
5865
- * maxRetries: 5,
5866
- * baseDelayMs: 5000,
5867
- * maxDelayMs: 60000,
5868
- * onRetry: (attempt, error) => {
5869
- * getLogger().info(`Retry ${attempt} after error:`, error);
5870
- * }
5871
- * },
5872
- * 'AlphaVantage.getQuote'
5873
- * );
5874
- * ```
5875
- */
5876
- async function withRetry(fn, config = {}, label = "unknown") {
5877
- const fullConfig = { ...DEFAULT_RETRY_CONFIG, ...config };
5878
- let lastError;
5879
- for (let attempt = 1; attempt <= fullConfig.maxRetries; attempt++) {
5880
- try {
5881
- const result = await fn();
5882
- // If we succeeded after retries, log it
5883
- if (attempt > 1) {
5884
- getLogger().info(`[${label}] Succeeded on attempt ${attempt}/${fullConfig.maxRetries}`);
5885
- }
5886
- return result;
5887
- }
5888
- catch (error) {
5889
- lastError = error;
5890
- // If this is the last attempt, throw the error
5891
- if (attempt === fullConfig.maxRetries) {
5892
- getLogger().error(`[${label}] Failed after ${fullConfig.maxRetries} attempts`, {
5893
- error: error instanceof Error ? error.message : String(error),
5894
- timestamp: new Date().toISOString(),
5895
- });
5896
- throw error;
5897
- }
5898
- // Analyze the error to determine if we should retry
5899
- const response = error instanceof Response ? error : null;
5900
- const errorDetails = analyzeError(error, response, fullConfig);
5901
- // If error is not retryable, fail immediately
5902
- if (!errorDetails.isRetryable) {
5903
- getLogger().error(`[${label}] Non-retryable error (${errorDetails.type})`, {
5904
- reason: errorDetails.reason,
5905
- status: errorDetails.status,
5906
- timestamp: new Date().toISOString(),
5907
- });
5908
- throw error;
5909
- }
5910
- // Calculate delay for next retry
5911
- let delayMs;
5912
- if (errorDetails.type === "RATE_LIMIT" && errorDetails.retryAfter) {
5913
- // Use Retry-After header if available
5914
- delayMs = errorDetails.retryAfter;
5915
- }
5916
- else if (errorDetails.type === "RATE_LIMIT") {
5917
- // For rate limits without Retry-After, use a longer minimum delay
5918
- delayMs = Math.max(calculateBackoff(attempt, fullConfig.baseDelayMs, fullConfig.maxDelayMs), 5000);
5919
- }
5920
- else {
5921
- // Standard exponential backoff with jitter
5922
- delayMs = calculateBackoff(attempt, fullConfig.baseDelayMs, fullConfig.maxDelayMs);
5923
- }
5924
- // Log the retry attempt
5925
- getLogger().warn(`[${label}] Attempt ${attempt}/${fullConfig.maxRetries} failed: ${errorDetails.reason}. Retrying in ${delayMs}ms...`, {
5926
- attemptNumber: attempt,
5927
- totalRetries: fullConfig.maxRetries,
5928
- errorType: errorDetails.type,
5929
- httpStatus: errorDetails.status,
5930
- retryDelay: delayMs,
5931
- timestamp: new Date().toISOString(),
5932
- });
5933
- // Call the optional retry callback
5934
- if (fullConfig.onRetry) {
5935
- fullConfig.onRetry(attempt, error);
5936
- }
5937
- // Wait before retrying
5938
- await new Promise((resolve) => setTimeout(resolve, delayMs));
5939
- }
5940
- }
5941
- // This should never be reached due to the throw in the last attempt,
5942
- // but TypeScript needs this to satisfy the return type
5943
- throw lastError;
5944
- }
5945
- /**
5946
- * API-specific retry configurations for different external services.
5947
- * These configurations are tuned based on each API's rate limits and characteristics.
5948
- */
5949
- const API_RETRY_CONFIGS = {
5950
- /** Massive.com API - 5 requests/second rate limit */
5951
- MASSIVE: {
5952
- maxRetries: 3,
5953
- baseDelayMs: 1000,
5954
- maxDelayMs: 30000,
5955
- retryableStatusCodes: [429, 500, 502, 503, 504],
5956
- retryOnNetworkError: true,
5957
- },
5958
- /** Alpha Vantage API - 5 requests/minute rate limit (more strict) */
5959
- ALPHA_VANTAGE: {
5960
- maxRetries: 5,
5961
- baseDelayMs: 5000,
5962
- maxDelayMs: 60000,
5963
- retryableStatusCodes: [429, 500, 502, 503, 504],
5964
- retryOnNetworkError: true,
5965
- },
5966
- /** Alpaca API - generally reliable, shorter retry window */
5967
- ALPACA: {
5968
- maxRetries: 3,
5969
- baseDelayMs: 1000,
5970
- maxDelayMs: 30000,
5971
- retryableStatusCodes: [429, 500, 502, 503, 504],
5972
- retryOnNetworkError: true,
5973
- },
5974
- /** Generic crypto API configuration */
5975
- CRYPTO: {
5976
- maxRetries: 3,
5977
- baseDelayMs: 1000,
5978
- maxDelayMs: 30000,
5979
- retryableStatusCodes: [429, 500, 502, 503, 504],
5980
- retryOnNetworkError: true,
5981
- },
5982
- };
5983
-
5984
6022
  // Utility function for debug logging
5985
6023
  /**
5986
6024
  * Debug logging utility that respects environment debug flags.
@@ -8507,19 +8545,37 @@ const fetchPrices = async (params, options) => {
8507
8545
  catch (error) {
8508
8546
  const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
8509
8547
  const contextualMessage = `Error fetching price data for ${ticker}`;
8510
- getLogger().error(`${contextualMessage}: ${errorMessage}`, {
8548
+ const isTransient = isTransientNetworkError(error);
8549
+ const errorType = error instanceof Error && error.message.includes("AUTH_ERROR")
8550
+ ? "AUTH_ERROR"
8551
+ : error instanceof Error && error.message.includes("RATE_LIMIT")
8552
+ ? "RATE_LIMIT"
8553
+ : isTransient
8554
+ ? "NETWORK_ERROR"
8555
+ : "UNKNOWN";
8556
+ // Transient network failures (undici timeouts, socket resets, DNS
8557
+ // blips) are recoverable at the caller's next cycle — demote to
8558
+ // WARN to avoid alert noise. Non-transient classes (auth, rate
8559
+ // limits that have exhausted retries, unknown schema errors) stay
8560
+ // at ERROR because they require operator attention.
8561
+ const logMeta = {
8511
8562
  ticker,
8512
- errorType: error instanceof Error && error.message.includes("AUTH_ERROR")
8513
- ? "AUTH_ERROR"
8514
- : error instanceof Error && error.message.includes("RATE_LIMIT")
8515
- ? "RATE_LIMIT"
8516
- : error instanceof Error &&
8517
- error.message.includes("NETWORK_ERROR")
8518
- ? "NETWORK_ERROR"
8519
- : "UNKNOWN",
8563
+ errorType,
8520
8564
  source: "MassiveAPI.fetchPrices",
8521
8565
  timestamp: new Date().toISOString(),
8522
- });
8566
+ ...(isTransient
8567
+ ? {
8568
+ transient: true,
8569
+ recoveryHint: "Upstream caller should retry on next cycle",
8570
+ }
8571
+ : {}),
8572
+ };
8573
+ if (isTransient) {
8574
+ getLogger().warn(`${contextualMessage}: ${errorMessage}`, logMeta);
8575
+ }
8576
+ else {
8577
+ getLogger().error(`${contextualMessage}: ${errorMessage}`, logMeta);
8578
+ }
8523
8579
  throw new Error(`${contextualMessage}: ${errorMessage}`);
8524
8580
  }
8525
8581
  });
@@ -68384,5 +68440,5 @@ const adaptic = {
68384
68440
  };
68385
68441
  const adptc = adaptic;
68386
68442
 
68387
- export { API_RETRY_CONFIGS, AVNewsArticleSchema, AVNewsResponseSchema, AdapticUtilsError, AlpacaAccountDetailsSchema, AlpacaApiError, AlpacaBarSchema, AlpacaClient, AlpacaCryptoBarsResponseSchema, AlpacaHistoricalBarsResponseSchema, AlpacaLatestBarsResponseSchema, AlpacaLatestQuotesResponseSchema, AlpacaLatestTradesResponseSchema, AlpacaMarketDataAPI, AlpacaNewsArticleSchema, AlpacaNewsResponseSchema, AlpacaOrderSchema, AlpacaOrdersArraySchema, AlpacaPortfolioHistoryResponseSchema, AlpacaPositionSchema, AlpacaPositionsArraySchema, AlpacaQuoteSchema, AlpacaTradeSchema, AlpacaTradingAPI, AlphaVantageError, AlphaVantageQuoteResponseSchema, AssetAllocationEngine, AuthenticationError, AutonomyMode, BTC_PAIRS, BarError, CryptoDataError, CryptoOrderError, DEFAULT_CACHE_OPTIONS, DEFAULT_TIMEOUTS, DEFAULT_TRADING_POLICY, DataFormatError, DecisionMemoryOutcome, DecisionOutcome, DecisionRecordStatus, HttpClientError, HttpServerError, KEEP_ALIVE_DEFAULTS, LlmProvider, MARKET_DATA_API, MassiveAggregatesResponseSchema, MassiveApiError, MassiveDailyOpenCloseSchema, MassiveErrorResponseSchema, MassiveGroupedDailyResponseSchema, MassiveLastTradeResponseSchema, MassiveTickerDetailsResponseSchema, MassiveTickerInfoSchema, MassiveTradeSchema as MassiveTradeZodSchema, MassiveTradesResponseSchema, NetworkError, NewsError, OptionStrategyError, OptionsDataError, OverlaySeverity, OverlayStatus, OverlayType, QuoteError, RateLimitError, RawMassivePriceDataSchema, StampedeProtectedCache, TRADING_API, TimeoutError, TokenBucketRateLimiter, TradeError, TrailingStopValidationError, USDC_PAIRS, USDT_PAIRS, USD_PAIRS, ValidationError, ValidationResponseError, WEBSOCKET_STREAMS, WebSocketError, account, adaptic, adptc, alpaca, analyzeBars, approximateImpliedVolatility, bracketOrders, buildOCCSymbol, buildOptionSymbol, buyCryptoNotional, buyToClose, buyToOpen, buyWithStopLoss, buyWithTrailingStop, calculateMoneyness, calculateOrderValue, calculatePeriodPerformance, calculatePutCallRatio, calculateTotalFilledValue, cancelAllCryptoOrders, cancelOCOOrder, cancelOTOOrder, cancelTrailingStop, cancelTrailingStopsForSymbol, checkTradingEligibility, clearClientCache, clock, closeAllOptionPositions, closeOptionPosition, createAlpacaClient, createAlpacaMarketDataAPI, createAlpacaTradingAPI, createBracketOrder, createButterflySpread, createClientFromEnv, createCoveredCall, createCryptoLimitOrder, createCryptoMarketOrder, createCryptoOrder, createCryptoStopLimitOrder, createCryptoStopOrder, createExecutorFromTradingAPI, createIronCondor$1 as createIronCondor, createIronCondor as createIronCondorAdvanced, createMultiLegOptionOrder, createOCOOrder, createOTOOrder, createOptionOrder, createPortfolioTrailingStops, createProtectiveBracket, createStampedeProtectedCache, createStraddle$1 as createStraddle, createStraddle as createStraddleAdvanced, createStrangle$1 as createStrangle, createStrangle as createStrangleAdvanced, createStreamManager, createTimeoutSignal, createTrailingStop, createVerticalSpread$1 as createVerticalSpread, createVerticalSpread as createVerticalSpreadAdvanced, entryWithPercentStopLoss, exerciseOption, extractGreeks, filterByExpiration, filterByStrike, filterByType, filterOrdersByDateRange, findATMOptions, findATMStrikes, findNearestExpiration, findOptionsByDelta, formatOrderForLog, formatOrderSummary, generateOptimalAllocation, getAccountConfiguration, getAccountDetails, getAccountSummary, getAgentPoolStatus, getAllOrders, getAlpacaCalendar, getAlpacaClock, getAverageDailyVolume, getBars, getBuyingPower, getCrypto24HourChange, getCryptoBars, getCryptoDailyPrices, getCryptoPairsByQuote, getCryptoPrice, getCryptoSnapshots, getCryptoSpread, getCryptoStreamUrl, getCryptoTrades, getCurrentPrice, getCurrentPrices, getDailyPrices, getDailyReturns, getDaysToExpiration, getDefaultRiskProfile, getEquityCurve, getExpirationDates, getFilledOrders, getGroupedOptionChain, getHistoricalOptionsBars, getHistoricalTrades, getIntradayPrices, getLatestBars, getLatestCryptoQuotes, getLatestCryptoTrades, getLatestNews, getLatestOptionsQuotes, getLatestOptionsTrades, getLatestQuote, getLatestQuotes, getLatestTrade, getLatestTrades, getLogger, getMarginInfo, getNews, getNewsForSymbols, getOCOOrderStatus, getOTOOrderStatus, getOpenCryptoOrders, getOpenOrders$1 as getOpenOrdersQuery, getOpenTrailingStops, getOptionChain, getOptionContract, getOptionContracts, getOptionSpread, getOptionsChain, getOptionsSnapshots, getOptionsStreamUrl, getOptionsTradingLevel, getOrderHistory, getOrdersBySymbol, getPDTStatus, getPopularCryptoPairs, getPortfolioHistory, getPreviousClose, getPriceRange, getSpread, getSpreads, getStockStreamUrl, getStrikePrices, getSupportedCryptoPairs, getSymbolSentiment, getTimeout, getTradeVolume, getTradingApiUrl, getTradingWebSocketUrl, getTrailingStopHWM, groupOrdersByStatus, groupOrdersBySymbol, hasActiveTrailingStop, hasGoodLiquidity as hasOptionLiquidity, hasGoodLiquidity$1 as hasStockLiquidity, hasSufficientVolume, httpAgent, httpsAgent, isContractTradable, isCryptoPair, isExpiringWithin, isMarginAccount, isOptionOrderCancelable, isOptionOrderTerminal, isOrderFillable, isOrderFilled, isOrderOpen, isOrderTerminal$1 as isOrderTerminalStatus, isSupportedCryptoPair, index$1 as legacyApi, limitBuyWithTakeProfit, ocoOrders, orderUtils, otoOrders, paginate, paginateAll, parseOCCSymbol, protectLongPosition, protectShortPosition, rateLimiters, resetLogger, rollOptionPosition, roundPriceForAlpaca$3 as roundPriceForAlpaca, roundPriceForAlpacaNumber, safeValidateResponse, searchNews, sellAllCrypto, sellCryptoNotional, sellToClose, sellToOpen, setLogger, shortWithStopLoss, sortOrdersByDate, index as tradingPolicy, trailingStops, updateAccountConfiguration, updateTrailingStop, validateAlpacaCredentials, validateAlphaVantageApiKey, validateMassiveApiKey$1 as validateMassiveApiKey, validateMultiLegOrder, validateResponse, verifyFetchKeepAlive, waitForOrderFill, withRetry, withTimeout };
68443
+ export { API_RETRY_CONFIGS, AVNewsArticleSchema, AVNewsResponseSchema, AdapticUtilsError, AlpacaAccountDetailsSchema, AlpacaApiError, AlpacaBarSchema, AlpacaClient, AlpacaCryptoBarsResponseSchema, AlpacaHistoricalBarsResponseSchema, AlpacaLatestBarsResponseSchema, AlpacaLatestQuotesResponseSchema, AlpacaLatestTradesResponseSchema, AlpacaMarketDataAPI, AlpacaNewsArticleSchema, AlpacaNewsResponseSchema, AlpacaOrderSchema, AlpacaOrdersArraySchema, AlpacaPortfolioHistoryResponseSchema, AlpacaPositionSchema, AlpacaPositionsArraySchema, AlpacaQuoteSchema, AlpacaTradeSchema, AlpacaTradingAPI, AlphaVantageError, AlphaVantageQuoteResponseSchema, AssetAllocationEngine, AuthenticationError, AutonomyMode, BTC_PAIRS, BarError, CryptoDataError, CryptoOrderError, DEFAULT_CACHE_OPTIONS, DEFAULT_TIMEOUTS, DEFAULT_TRADING_POLICY, DataFormatError, DecisionMemoryOutcome, DecisionOutcome, DecisionRecordStatus, HttpClientError, HttpServerError, KEEP_ALIVE_DEFAULTS, LlmProvider, MARKET_DATA_API, MassiveAggregatesResponseSchema, MassiveApiError, MassiveDailyOpenCloseSchema, MassiveErrorResponseSchema, MassiveGroupedDailyResponseSchema, MassiveLastTradeResponseSchema, MassiveTickerDetailsResponseSchema, MassiveTickerInfoSchema, MassiveTradeSchema as MassiveTradeZodSchema, MassiveTradesResponseSchema, NetworkError, NewsError, OptionStrategyError, OptionsDataError, OverlaySeverity, OverlayStatus, OverlayType, QuoteError, RateLimitError, RawMassivePriceDataSchema, StampedeProtectedCache, TRADING_API, TimeoutError, TokenBucketRateLimiter, TradeError, TrailingStopValidationError, USDC_PAIRS, USDT_PAIRS, USD_PAIRS, ValidationError, ValidationResponseError, WEBSOCKET_STREAMS, WebSocketError, account, adaptic, adptc, alpaca, analyzeBars, approximateImpliedVolatility, bracketOrders, buildOCCSymbol, buildOptionSymbol, buyCryptoNotional, buyToClose, buyToOpen, buyWithStopLoss, buyWithTrailingStop, calculateMoneyness, calculateOrderValue, calculatePeriodPerformance, calculatePutCallRatio, calculateTotalFilledValue, cancelAllCryptoOrders, cancelOCOOrder, cancelOTOOrder, cancelTrailingStop, cancelTrailingStopsForSymbol, checkTradingEligibility, clearClientCache, clock, closeAllOptionPositions, closeOptionPosition, createAlpacaClient, createAlpacaMarketDataAPI, createAlpacaTradingAPI, createBracketOrder, createButterflySpread, createClientFromEnv, createCoveredCall, createCryptoLimitOrder, createCryptoMarketOrder, createCryptoOrder, createCryptoStopLimitOrder, createCryptoStopOrder, createExecutorFromTradingAPI, createIronCondor$1 as createIronCondor, createIronCondor as createIronCondorAdvanced, createMultiLegOptionOrder, createOCOOrder, createOTOOrder, createOptionOrder, createPortfolioTrailingStops, createProtectiveBracket, createStampedeProtectedCache, createStraddle$1 as createStraddle, createStraddle as createStraddleAdvanced, createStrangle$1 as createStrangle, createStrangle as createStrangleAdvanced, createStreamManager, createTimeoutSignal, createTrailingStop, createVerticalSpread$1 as createVerticalSpread, createVerticalSpread as createVerticalSpreadAdvanced, entryWithPercentStopLoss, exerciseOption, extractGreeks, filterByExpiration, filterByStrike, filterByType, filterOrdersByDateRange, findATMOptions, findATMStrikes, findNearestExpiration, findOptionsByDelta, formatOrderForLog, formatOrderSummary, generateOptimalAllocation, getAccountConfiguration, getAccountDetails, getAccountSummary, getAgentPoolStatus, getAllOrders, getAlpacaCalendar, getAlpacaClock, getAverageDailyVolume, getBars, getBuyingPower, getCrypto24HourChange, getCryptoBars, getCryptoDailyPrices, getCryptoPairsByQuote, getCryptoPrice, getCryptoSnapshots, getCryptoSpread, getCryptoStreamUrl, getCryptoTrades, getCurrentPrice, getCurrentPrices, getDailyPrices, getDailyReturns, getDaysToExpiration, getDefaultRiskProfile, getEquityCurve, getExpirationDates, getFilledOrders, getGroupedOptionChain, getHistoricalOptionsBars, getHistoricalTrades, getIntradayPrices, getLatestBars, getLatestCryptoQuotes, getLatestCryptoTrades, getLatestNews, getLatestOptionsQuotes, getLatestOptionsTrades, getLatestQuote, getLatestQuotes, getLatestTrade, getLatestTrades, getLogger, getMarginInfo, getNews, getNewsForSymbols, getOCOOrderStatus, getOTOOrderStatus, getOpenCryptoOrders, getOpenOrders$1 as getOpenOrdersQuery, getOpenTrailingStops, getOptionChain, getOptionContract, getOptionContracts, getOptionSpread, getOptionsChain, getOptionsSnapshots, getOptionsStreamUrl, getOptionsTradingLevel, getOrderHistory, getOrdersBySymbol, getPDTStatus, getPopularCryptoPairs, getPortfolioHistory, getPreviousClose, getPriceRange, getSpread, getSpreads, getStockStreamUrl, getStrikePrices, getSupportedCryptoPairs, getSymbolSentiment, getTimeout, getTradeVolume, getTradingApiUrl, getTradingWebSocketUrl, getTrailingStopHWM, groupOrdersByStatus, groupOrdersBySymbol, hasActiveTrailingStop, hasGoodLiquidity as hasOptionLiquidity, hasGoodLiquidity$1 as hasStockLiquidity, hasSufficientVolume, httpAgent, httpsAgent, isContractTradable, isCryptoPair, isExpiringWithin, isMarginAccount, isOptionOrderCancelable, isOptionOrderTerminal, isOrderFillable, isOrderFilled, isOrderOpen, isOrderTerminal$1 as isOrderTerminalStatus, isSupportedCryptoPair, isTransientNetworkError, index$1 as legacyApi, limitBuyWithTakeProfit, ocoOrders, orderUtils, otoOrders, paginate, paginateAll, parseOCCSymbol, protectLongPosition, protectShortPosition, rateLimiters, resetLogger, rollOptionPosition, roundPriceForAlpaca$3 as roundPriceForAlpaca, roundPriceForAlpacaNumber, safeValidateResponse, searchNews, sellAllCrypto, sellCryptoNotional, sellToClose, sellToOpen, setLogger, shortWithStopLoss, sortOrdersByDate, index as tradingPolicy, trailingStops, updateAccountConfiguration, updateTrailingStop, validateAlpacaCredentials, validateAlphaVantageApiKey, validateMassiveApiKey$1 as validateMassiveApiKey, validateMultiLegOrder, validateResponse, verifyFetchKeepAlive, waitForOrderFill, withRetry, withTimeout };
68388
68444
  //# sourceMappingURL=index.mjs.map