@adaptic/utils 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +204 -180
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +204 -180
- package/dist/index.mjs.map +1 -1
- package/dist/test.js +33 -9
- package/dist/test.js.map +1 -1
- package/dist/types/alpaca/legacy/account.d.ts +4 -4
- package/dist/types/alpaca/legacy/account.d.ts.map +1 -1
- package/dist/types/alpaca/legacy/auth.d.ts +2 -2
- package/dist/types/alpaca-market-data-api.d.ts +2 -0
- package/dist/types/alpaca-market-data-api.d.ts.map +1 -1
- package/dist/types/crypto.d.ts +2 -2
- package/dist/types/crypto.d.ts.map +1 -1
- package/dist/types/performance-metrics.d.ts +2 -2
- package/dist/types/performance-metrics.d.ts.map +1 -1
- package/dist/types/types/alpaca-types.d.ts +6 -6
- package/dist/types/types/alpaca-types.d.ts.map +1 -1
- package/dist/types/types/metrics-types.d.ts +1 -1
- package/dist/types/types/metrics-types.d.ts.map +1 -1
- package/dist/types/utils/auth-validator.d.ts +9 -4
- package/dist/types/utils/auth-validator.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -300,23 +300,42 @@ const fetchAssetOverview = async (symbol) => {
|
|
|
300
300
|
/**
|
|
301
301
|
* Validates Alpaca API credentials
|
|
302
302
|
* @param auth - Authentication object containing API key and secret
|
|
303
|
-
* @
|
|
303
|
+
* @param options - Validation options
|
|
304
|
+
* @param options.throwOnMissing - If false, missing credentials will log a warning instead of throwing (default: true)
|
|
305
|
+
* @throws {Error} If credentials are invalid (when throwOnMissing is true)
|
|
306
|
+
* @returns {boolean} True if credentials are valid, false if missing (when throwOnMissing is false)
|
|
304
307
|
*/
|
|
305
|
-
function validateAlpacaCredentials(auth) {
|
|
308
|
+
function validateAlpacaCredentials(auth, options = { throwOnMissing: true }) {
|
|
309
|
+
const { throwOnMissing = true } = options;
|
|
310
|
+
// Check for missing or empty API key
|
|
306
311
|
if (!auth.apiKey ||
|
|
307
312
|
typeof auth.apiKey !== "string" ||
|
|
308
313
|
auth.apiKey.trim().length === 0) {
|
|
309
|
-
|
|
314
|
+
if (throwOnMissing) {
|
|
315
|
+
throw new Error("Invalid Alpaca API key: must be a non-empty string");
|
|
316
|
+
}
|
|
317
|
+
console.warn("[AlpacaAPI] API key not configured. Market data features will be unavailable.");
|
|
318
|
+
return false;
|
|
310
319
|
}
|
|
320
|
+
// Check for missing or empty API secret
|
|
311
321
|
if (!auth.apiSecret ||
|
|
312
322
|
typeof auth.apiSecret !== "string" ||
|
|
313
323
|
auth.apiSecret.trim().length === 0) {
|
|
314
|
-
|
|
324
|
+
if (throwOnMissing) {
|
|
325
|
+
throw new Error("Invalid Alpaca API secret: must be a non-empty string");
|
|
326
|
+
}
|
|
327
|
+
console.warn("[AlpacaAPI] API secret not configured. Market data features will be unavailable.");
|
|
328
|
+
return false;
|
|
315
329
|
}
|
|
316
330
|
// Alpaca keys are typically 20+ characters
|
|
317
331
|
if (auth.apiKey.length < 10) {
|
|
318
|
-
|
|
332
|
+
if (throwOnMissing) {
|
|
333
|
+
throw new Error("Alpaca API key appears to be too short");
|
|
334
|
+
}
|
|
335
|
+
console.warn("[AlpacaAPI] API key appears to be too short.");
|
|
336
|
+
return false;
|
|
319
337
|
}
|
|
338
|
+
return true;
|
|
320
339
|
}
|
|
321
340
|
/**
|
|
322
341
|
* Validates Polygon API key
|
|
@@ -349,21 +368,21 @@ function validateAlphaVantageApiKey(apiKey) {
|
|
|
349
368
|
async function validateAuth(auth) {
|
|
350
369
|
if (auth.adapticAccountId) {
|
|
351
370
|
const client = await getSharedApolloClient();
|
|
352
|
-
const
|
|
371
|
+
const brokerageAccount = (await adaptic$1.brokerageAccount.get({
|
|
353
372
|
id: auth.adapticAccountId,
|
|
354
373
|
}, client));
|
|
355
|
-
if (!
|
|
374
|
+
if (!brokerageAccount || !brokerageAccount.apiKey || !brokerageAccount.apiSecret) {
|
|
356
375
|
throw new Error("Alpaca account not found or incomplete");
|
|
357
376
|
}
|
|
358
377
|
validateAlpacaCredentials({
|
|
359
|
-
apiKey:
|
|
360
|
-
apiSecret:
|
|
361
|
-
isPaper:
|
|
378
|
+
apiKey: brokerageAccount.apiKey,
|
|
379
|
+
apiSecret: brokerageAccount.apiSecret,
|
|
380
|
+
isPaper: brokerageAccount.type === "PAPER",
|
|
362
381
|
});
|
|
363
382
|
return {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
type:
|
|
383
|
+
apiKey: brokerageAccount.apiKey,
|
|
384
|
+
apiSecret: brokerageAccount.apiSecret,
|
|
385
|
+
type: brokerageAccount.type,
|
|
367
386
|
};
|
|
368
387
|
}
|
|
369
388
|
else if (auth.alpacaApiKey && auth.alpacaApiSecret) {
|
|
@@ -372,8 +391,8 @@ async function validateAuth(auth) {
|
|
|
372
391
|
apiKey: auth.alpacaApiKey,
|
|
373
392
|
apiSecret: auth.alpacaApiSecret});
|
|
374
393
|
return {
|
|
375
|
-
|
|
376
|
-
|
|
394
|
+
apiKey: auth.alpacaApiKey,
|
|
395
|
+
apiSecret: auth.alpacaApiSecret,
|
|
377
396
|
type: accountType,
|
|
378
397
|
};
|
|
379
398
|
}
|
|
@@ -544,11 +563,11 @@ const ORDER_CHUNK_SIZE = 500;
|
|
|
544
563
|
async function makeRequest(auth, params) {
|
|
545
564
|
const { endpoint, method, body, queryString, apiBaseUrl } = params;
|
|
546
565
|
try {
|
|
547
|
-
const {
|
|
566
|
+
const { apiKey, apiSecret, type } = await validateAuth(auth);
|
|
548
567
|
const apiBaseUrlInner = apiBaseUrl
|
|
549
568
|
? apiBaseUrl
|
|
550
569
|
: getTradingApiUrl(type);
|
|
551
|
-
if (!
|
|
570
|
+
if (!apiKey || !apiSecret) {
|
|
552
571
|
throw new Error("No valid Alpaca authentication found. Please provide either auth object or set ALPACA_API_KEY and ALPACA_API_SECRET environment variables.");
|
|
553
572
|
}
|
|
554
573
|
const url = `${apiBaseUrlInner}${endpoint}${queryString || ""}`;
|
|
@@ -559,8 +578,8 @@ async function makeRequest(auth, params) {
|
|
|
559
578
|
const fetchOptions = {
|
|
560
579
|
method,
|
|
561
580
|
headers: {
|
|
562
|
-
"APCA-API-KEY-ID":
|
|
563
|
-
"APCA-API-SECRET-KEY":
|
|
581
|
+
"APCA-API-KEY-ID": apiKey,
|
|
582
|
+
"APCA-API-SECRET-KEY": apiSecret,
|
|
564
583
|
},
|
|
565
584
|
};
|
|
566
585
|
// Only add Content-Type and body for non-GET/HEAD requests that have a body
|
|
@@ -604,13 +623,13 @@ async function makeRequest(auth, params) {
|
|
|
604
623
|
*/
|
|
605
624
|
async function createOrder$1(auth, params) {
|
|
606
625
|
try {
|
|
607
|
-
const {
|
|
626
|
+
const { apiKey, apiSecret, type } = await validateAuth(auth);
|
|
608
627
|
const apiBaseUrl = getTradingApiUrl(type);
|
|
609
628
|
const response = await fetch(`${apiBaseUrl}/v2/orders`, {
|
|
610
629
|
method: "POST",
|
|
611
630
|
headers: {
|
|
612
|
-
"APCA-API-KEY-ID":
|
|
613
|
-
"APCA-API-SECRET-KEY":
|
|
631
|
+
"APCA-API-KEY-ID": apiKey,
|
|
632
|
+
"APCA-API-SECRET-KEY": apiSecret,
|
|
614
633
|
"Content-Type": "application/json",
|
|
615
634
|
},
|
|
616
635
|
body: JSON.stringify(params),
|
|
@@ -635,7 +654,7 @@ async function createOrder$1(auth, params) {
|
|
|
635
654
|
*/
|
|
636
655
|
async function getOrders$1(auth, params = {}) {
|
|
637
656
|
try {
|
|
638
|
-
const {
|
|
657
|
+
const { apiKey, apiSecret, type } = await validateAuth(auth);
|
|
639
658
|
const apiBaseUrl = getTradingApiUrl(type);
|
|
640
659
|
const allOrders = [];
|
|
641
660
|
let currentUntil = params.until ? params.until : new Date().toISOString();
|
|
@@ -658,8 +677,8 @@ async function getOrders$1(auth, params = {}) {
|
|
|
658
677
|
const response = await fetch(`${apiBaseUrl}/v2/orders?${queryParams}`, {
|
|
659
678
|
method: "GET",
|
|
660
679
|
headers: {
|
|
661
|
-
"APCA-API-KEY-ID":
|
|
662
|
-
"APCA-API-SECRET-KEY":
|
|
680
|
+
"APCA-API-KEY-ID": apiKey,
|
|
681
|
+
"APCA-API-SECRET-KEY": apiSecret,
|
|
663
682
|
},
|
|
664
683
|
signal: createTimeoutSignal(DEFAULT_TIMEOUTS.ALPACA_API),
|
|
665
684
|
});
|
|
@@ -691,13 +710,13 @@ async function getOrders$1(auth, params = {}) {
|
|
|
691
710
|
*/
|
|
692
711
|
async function cancelAllOrders$1(auth) {
|
|
693
712
|
try {
|
|
694
|
-
const {
|
|
713
|
+
const { apiKey, apiSecret, type } = await validateAuth(auth);
|
|
695
714
|
const apiBaseUrl = getTradingApiUrl(type);
|
|
696
715
|
const response = await fetch(`${apiBaseUrl}/v2/orders`, {
|
|
697
716
|
method: "DELETE",
|
|
698
717
|
headers: {
|
|
699
|
-
"APCA-API-KEY-ID":
|
|
700
|
-
"APCA-API-SECRET-KEY":
|
|
718
|
+
"APCA-API-KEY-ID": apiKey,
|
|
719
|
+
"APCA-API-SECRET-KEY": apiSecret,
|
|
701
720
|
},
|
|
702
721
|
signal: createTimeoutSignal(DEFAULT_TIMEOUTS.ALPACA_API),
|
|
703
722
|
});
|
|
@@ -721,7 +740,7 @@ async function cancelAllOrders$1(auth) {
|
|
|
721
740
|
*/
|
|
722
741
|
async function getOrder$1(auth, orderId, nested) {
|
|
723
742
|
try {
|
|
724
|
-
const {
|
|
743
|
+
const { apiKey, apiSecret, type } = await validateAuth(auth);
|
|
725
744
|
const apiBaseUrl = getTradingApiUrl(type);
|
|
726
745
|
const queryParams = new URLSearchParams();
|
|
727
746
|
if (nested)
|
|
@@ -729,8 +748,8 @@ async function getOrder$1(auth, orderId, nested) {
|
|
|
729
748
|
const response = await fetch(`${apiBaseUrl}/v2/orders/${orderId}?${queryParams}`, {
|
|
730
749
|
method: "GET",
|
|
731
750
|
headers: {
|
|
732
|
-
"APCA-API-KEY-ID":
|
|
733
|
-
"APCA-API-SECRET-KEY":
|
|
751
|
+
"APCA-API-KEY-ID": apiKey,
|
|
752
|
+
"APCA-API-SECRET-KEY": apiSecret,
|
|
734
753
|
},
|
|
735
754
|
signal: createTimeoutSignal(DEFAULT_TIMEOUTS.ALPACA_API),
|
|
736
755
|
});
|
|
@@ -754,13 +773,13 @@ async function getOrder$1(auth, orderId, nested) {
|
|
|
754
773
|
*/
|
|
755
774
|
async function replaceOrder$1(auth, orderId, params) {
|
|
756
775
|
try {
|
|
757
|
-
const {
|
|
776
|
+
const { apiKey, apiSecret, type } = await validateAuth(auth);
|
|
758
777
|
const apiBaseUrl = getTradingApiUrl(type);
|
|
759
778
|
const response = await fetch(`${apiBaseUrl}/v2/orders/${orderId}`, {
|
|
760
779
|
method: "PATCH",
|
|
761
780
|
headers: {
|
|
762
|
-
"APCA-API-KEY-ID":
|
|
763
|
-
"APCA-API-SECRET-KEY":
|
|
781
|
+
"APCA-API-KEY-ID": apiKey,
|
|
782
|
+
"APCA-API-SECRET-KEY": apiSecret,
|
|
764
783
|
"Content-Type": "application/json",
|
|
765
784
|
accept: "application/json",
|
|
766
785
|
},
|
|
@@ -786,13 +805,13 @@ async function replaceOrder$1(auth, orderId, params) {
|
|
|
786
805
|
*/
|
|
787
806
|
async function cancelOrder$1(auth, orderId) {
|
|
788
807
|
try {
|
|
789
|
-
const {
|
|
808
|
+
const { apiKey, apiSecret, type } = await validateAuth(auth);
|
|
790
809
|
const apiBaseUrl = getTradingApiUrl(type);
|
|
791
810
|
const response = await fetch(`${apiBaseUrl}/v2/orders/${orderId}`, {
|
|
792
811
|
method: "DELETE",
|
|
793
812
|
headers: {
|
|
794
|
-
"APCA-API-KEY-ID":
|
|
795
|
-
"APCA-API-SECRET-KEY":
|
|
813
|
+
"APCA-API-KEY-ID": apiKey,
|
|
814
|
+
"APCA-API-SECRET-KEY": apiSecret,
|
|
796
815
|
},
|
|
797
816
|
signal: createTimeoutSignal(DEFAULT_TIMEOUTS.ALPACA_API),
|
|
798
817
|
});
|
|
@@ -1374,14 +1393,14 @@ async function fetchNews$1(symbols, params) {
|
|
|
1374
1393
|
}
|
|
1375
1394
|
else if (mergedParams.auth.adapticAccountId) {
|
|
1376
1395
|
const client = await getSharedApolloClient();
|
|
1377
|
-
const
|
|
1396
|
+
const brokerageAccount = (await adaptic$1.brokerageAccount.get({
|
|
1378
1397
|
id: mergedParams.auth.adapticAccountId,
|
|
1379
1398
|
}, client));
|
|
1380
|
-
if (!
|
|
1399
|
+
if (!brokerageAccount || !brokerageAccount.apiKey || !brokerageAccount.apiSecret) {
|
|
1381
1400
|
throw new Error("Alpaca account not found or incomplete");
|
|
1382
1401
|
}
|
|
1383
|
-
APIKey =
|
|
1384
|
-
APISecret =
|
|
1402
|
+
APIKey = brokerageAccount.apiKey;
|
|
1403
|
+
APISecret = brokerageAccount.apiSecret;
|
|
1385
1404
|
}
|
|
1386
1405
|
}
|
|
1387
1406
|
else {
|
|
@@ -1474,14 +1493,14 @@ async function fetchNews$1(symbols, params) {
|
|
|
1474
1493
|
*/
|
|
1475
1494
|
async function fetchAllPositions(auth) {
|
|
1476
1495
|
try {
|
|
1477
|
-
const {
|
|
1496
|
+
const { apiKey, apiSecret, type } = await validateAuth(auth);
|
|
1478
1497
|
const apiBaseUrl = getTradingApiUrl(type);
|
|
1479
1498
|
const apiUrl = `${apiBaseUrl}/v2/positions`;
|
|
1480
1499
|
const response = await fetch(apiUrl, {
|
|
1481
1500
|
method: "GET",
|
|
1482
1501
|
headers: {
|
|
1483
|
-
"APCA-API-KEY-ID":
|
|
1484
|
-
"APCA-API-SECRET-KEY":
|
|
1502
|
+
"APCA-API-KEY-ID": apiKey,
|
|
1503
|
+
"APCA-API-SECRET-KEY": apiSecret,
|
|
1485
1504
|
"Content-Type": "application/json",
|
|
1486
1505
|
},
|
|
1487
1506
|
signal: createTimeoutSignal(DEFAULT_TIMEOUTS.ALPACA_API),
|
|
@@ -1505,13 +1524,13 @@ async function fetchAllPositions(auth) {
|
|
|
1505
1524
|
*/
|
|
1506
1525
|
async function fetchPosition(auth, symbolOrAssetId) {
|
|
1507
1526
|
try {
|
|
1508
|
-
const {
|
|
1527
|
+
const { apiKey, apiSecret, type } = await validateAuth(auth);
|
|
1509
1528
|
const apiBaseUrl = getTradingApiUrl(type);
|
|
1510
1529
|
const response = await fetch(`${apiBaseUrl}/v2/positions/${symbolOrAssetId}`, {
|
|
1511
1530
|
method: "GET",
|
|
1512
1531
|
headers: {
|
|
1513
|
-
"APCA-API-KEY-ID":
|
|
1514
|
-
"APCA-API-SECRET-KEY":
|
|
1532
|
+
"APCA-API-KEY-ID": apiKey,
|
|
1533
|
+
"APCA-API-SECRET-KEY": apiSecret,
|
|
1515
1534
|
"Content-Type": "application/json",
|
|
1516
1535
|
},
|
|
1517
1536
|
signal: createTimeoutSignal(DEFAULT_TIMEOUTS.ALPACA_API),
|
|
@@ -1545,7 +1564,7 @@ async function fetchPosition(auth, symbolOrAssetId) {
|
|
|
1545
1564
|
*/
|
|
1546
1565
|
async function closePosition$1(auth, symbolOrAssetId, params) {
|
|
1547
1566
|
try {
|
|
1548
|
-
const {
|
|
1567
|
+
const { apiKey, apiSecret, type } = await validateAuth(auth);
|
|
1549
1568
|
const apiBaseUrl = getTradingApiUrl(type);
|
|
1550
1569
|
const useLimitOrder = params?.useLimitOrder ?? false;
|
|
1551
1570
|
const cancelOrdersFlag = params?.cancelOrders ?? true;
|
|
@@ -1628,8 +1647,8 @@ async function closePosition$1(auth, symbolOrAssetId, params) {
|
|
|
1628
1647
|
const response = await fetch(url, {
|
|
1629
1648
|
method: "DELETE",
|
|
1630
1649
|
headers: {
|
|
1631
|
-
"APCA-API-KEY-ID":
|
|
1632
|
-
"APCA-API-SECRET-KEY":
|
|
1650
|
+
"APCA-API-KEY-ID": apiKey,
|
|
1651
|
+
"APCA-API-SECRET-KEY": apiSecret,
|
|
1633
1652
|
},
|
|
1634
1653
|
});
|
|
1635
1654
|
if (!response.ok) {
|
|
@@ -1820,20 +1839,20 @@ async function closeAllPositionsAfterHours$1(auth, params = { cancel_orders: tru
|
|
|
1820
1839
|
* @param props - The properties for fetching account details
|
|
1821
1840
|
* @returns The account details
|
|
1822
1841
|
*/
|
|
1823
|
-
async function fetchAccountDetails({ accountId, client,
|
|
1824
|
-
let
|
|
1825
|
-
if (!
|
|
1842
|
+
async function fetchAccountDetails({ accountId, client, brokerageAccount, auth, }) {
|
|
1843
|
+
let brokerageAccountObj = brokerageAccount ? brokerageAccount : null;
|
|
1844
|
+
if (!brokerageAccountObj && auth) {
|
|
1826
1845
|
const validatedAuth = await validateAuth(auth);
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1846
|
+
brokerageAccountObj = {
|
|
1847
|
+
apiKey: validatedAuth.apiKey,
|
|
1848
|
+
apiSecret: validatedAuth.apiSecret,
|
|
1830
1849
|
type: validatedAuth.type,
|
|
1831
1850
|
};
|
|
1832
1851
|
}
|
|
1833
|
-
if (!
|
|
1852
|
+
if (!brokerageAccountObj) {
|
|
1834
1853
|
try {
|
|
1835
1854
|
const apolloClient = client || (await getSharedApolloClient());
|
|
1836
|
-
|
|
1855
|
+
brokerageAccountObj = (await adaptic$1.brokerageAccount.get({
|
|
1837
1856
|
id: accountId,
|
|
1838
1857
|
}, apolloClient));
|
|
1839
1858
|
}
|
|
@@ -1842,19 +1861,19 @@ async function fetchAccountDetails({ accountId, client, alpacaAccount, auth, })
|
|
|
1842
1861
|
throw error;
|
|
1843
1862
|
}
|
|
1844
1863
|
}
|
|
1845
|
-
if (!
|
|
1846
|
-
!
|
|
1847
|
-
!
|
|
1864
|
+
if (!brokerageAccountObj ||
|
|
1865
|
+
!brokerageAccountObj.apiKey ||
|
|
1866
|
+
!brokerageAccountObj.apiSecret) {
|
|
1848
1867
|
throw new Error("[fetchAccountDetails] Alpaca account not found or incomplete");
|
|
1849
1868
|
}
|
|
1850
|
-
const {
|
|
1869
|
+
const { apiKey, apiSecret, type } = brokerageAccountObj;
|
|
1851
1870
|
const apiUrl = `${getTradingApiUrl(type)}/account`;
|
|
1852
1871
|
try {
|
|
1853
1872
|
const response = await fetch(apiUrl, {
|
|
1854
1873
|
method: "GET",
|
|
1855
1874
|
headers: {
|
|
1856
|
-
"APCA-API-KEY-ID":
|
|
1857
|
-
"APCA-API-SECRET-KEY":
|
|
1875
|
+
"APCA-API-KEY-ID": apiKey,
|
|
1876
|
+
"APCA-API-SECRET-KEY": apiSecret,
|
|
1858
1877
|
"Content-Type": "application/json",
|
|
1859
1878
|
},
|
|
1860
1879
|
signal: createTimeoutSignal(DEFAULT_TIMEOUTS.ALPACA_API),
|
|
@@ -1875,12 +1894,12 @@ async function fetchAccountDetails({ accountId, client, alpacaAccount, auth, })
|
|
|
1875
1894
|
* @param props - The properties for fetching portfolio history
|
|
1876
1895
|
* @returns The portfolio history
|
|
1877
1896
|
*/
|
|
1878
|
-
async function fetchPortfolioHistory({ params, accountId, client,
|
|
1879
|
-
let
|
|
1880
|
-
if (!
|
|
1897
|
+
async function fetchPortfolioHistory({ params, accountId, client, brokerageAccount, }) {
|
|
1898
|
+
let brokerageAccountObj = brokerageAccount ? brokerageAccount : null;
|
|
1899
|
+
if (!brokerageAccountObj) {
|
|
1881
1900
|
try {
|
|
1882
1901
|
const apolloClient = client || (await getSharedApolloClient());
|
|
1883
|
-
|
|
1902
|
+
brokerageAccountObj = (await adaptic$1.brokerageAccount.get({
|
|
1884
1903
|
id: accountId,
|
|
1885
1904
|
}, apolloClient));
|
|
1886
1905
|
}
|
|
@@ -1889,12 +1908,12 @@ async function fetchPortfolioHistory({ params, accountId, client, alpacaAccount,
|
|
|
1889
1908
|
throw error;
|
|
1890
1909
|
}
|
|
1891
1910
|
}
|
|
1892
|
-
if (!
|
|
1893
|
-
!
|
|
1894
|
-
!
|
|
1911
|
+
if (!brokerageAccountObj ||
|
|
1912
|
+
!brokerageAccountObj.apiKey ||
|
|
1913
|
+
!brokerageAccountObj.apiSecret) {
|
|
1895
1914
|
throw new Error("[fetchPortfolioHistory] Alpaca account not found or incomplete");
|
|
1896
1915
|
}
|
|
1897
|
-
const {
|
|
1916
|
+
const { apiKey, apiSecret, type } = brokerageAccountObj;
|
|
1898
1917
|
const apiBaseUrl = getTradingApiUrl(type);
|
|
1899
1918
|
const apiUrl = `${apiBaseUrl}/v2/account/portfolio/history`;
|
|
1900
1919
|
const { start, end, period } = params;
|
|
@@ -1920,8 +1939,8 @@ async function fetchPortfolioHistory({ params, accountId, client, alpacaAccount,
|
|
|
1920
1939
|
const response = await fetch(fullUrl, {
|
|
1921
1940
|
method: "GET",
|
|
1922
1941
|
headers: {
|
|
1923
|
-
"APCA-API-KEY-ID":
|
|
1924
|
-
"APCA-API-SECRET-KEY":
|
|
1942
|
+
"APCA-API-KEY-ID": apiKey,
|
|
1943
|
+
"APCA-API-SECRET-KEY": apiSecret,
|
|
1925
1944
|
"Content-Type": "application/json",
|
|
1926
1945
|
},
|
|
1927
1946
|
signal: createTimeoutSignal(DEFAULT_TIMEOUTS.ALPACA_API),
|
|
@@ -1948,31 +1967,31 @@ async function getConfiguration(account) {
|
|
|
1948
1967
|
if (!account) {
|
|
1949
1968
|
throw new Error(`Account is missing.`);
|
|
1950
1969
|
}
|
|
1951
|
-
const {
|
|
1952
|
-
if (!
|
|
1953
|
-
throw new Error("
|
|
1970
|
+
const { apiKey, apiSecret } = account;
|
|
1971
|
+
if (!apiKey || !apiSecret) {
|
|
1972
|
+
throw new Error("Account apiKey or apiSecret is missing.");
|
|
1954
1973
|
}
|
|
1955
1974
|
const apiUrl = getTradingApiUrl(account.type);
|
|
1956
1975
|
const client = await getSharedApolloClient();
|
|
1957
|
-
const [alpacaResponse,
|
|
1976
|
+
const [alpacaResponse, freshBrokerageAccount] = await Promise.all([
|
|
1958
1977
|
fetch(`${apiUrl}/account/configurations`, {
|
|
1959
1978
|
method: "GET",
|
|
1960
1979
|
headers: {
|
|
1961
|
-
"APCA-API-KEY-ID":
|
|
1962
|
-
"APCA-API-SECRET-KEY":
|
|
1980
|
+
"APCA-API-KEY-ID": apiKey,
|
|
1981
|
+
"APCA-API-SECRET-KEY": apiSecret,
|
|
1963
1982
|
accept: "application/json",
|
|
1964
1983
|
},
|
|
1965
1984
|
}),
|
|
1966
|
-
adaptic$1.
|
|
1985
|
+
adaptic$1.brokerageAccount.get({ id: account.id }, client),
|
|
1967
1986
|
]);
|
|
1968
1987
|
if (!alpacaResponse.ok) {
|
|
1969
1988
|
throw new Error(`Failed to fetch account configuration: ${alpacaResponse.statusText}`);
|
|
1970
1989
|
}
|
|
1971
|
-
if (!
|
|
1990
|
+
if (!freshBrokerageAccount) {
|
|
1972
1991
|
throw new Error("Failed to get Alpaca Account from @adaptic/backend-legacy.");
|
|
1973
1992
|
}
|
|
1974
1993
|
const dataFromAlpaca = (await alpacaResponse.json());
|
|
1975
|
-
const accountWithAllocation =
|
|
1994
|
+
const accountWithAllocation = freshBrokerageAccount;
|
|
1976
1995
|
const allocationData = accountWithAllocation.allocation || {
|
|
1977
1996
|
stocks: 70,
|
|
1978
1997
|
options: 0,
|
|
@@ -1983,26 +2002,26 @@ async function getConfiguration(account) {
|
|
|
1983
2002
|
};
|
|
1984
2003
|
const combinedConfig = {
|
|
1985
2004
|
...dataFromAlpaca,
|
|
1986
|
-
marketOpen:
|
|
1987
|
-
realTime:
|
|
1988
|
-
tradeAllocationPct:
|
|
1989
|
-
minPercentageChange:
|
|
1990
|
-
volumeThreshold:
|
|
1991
|
-
cryptoTradingEnabled:
|
|
1992
|
-
cryptoTradingPairs:
|
|
1993
|
-
cryptoTradeAllocationPct:
|
|
2005
|
+
marketOpen: freshBrokerageAccount.marketOpen,
|
|
2006
|
+
realTime: freshBrokerageAccount.realTime,
|
|
2007
|
+
tradeAllocationPct: freshBrokerageAccount.tradeAllocationPct,
|
|
2008
|
+
minPercentageChange: freshBrokerageAccount.minPercentageChange,
|
|
2009
|
+
volumeThreshold: freshBrokerageAccount.volumeThreshold,
|
|
2010
|
+
cryptoTradingEnabled: freshBrokerageAccount.cryptoTradingEnabled ?? false,
|
|
2011
|
+
cryptoTradingPairs: freshBrokerageAccount.cryptoTradingPairs ?? [],
|
|
2012
|
+
cryptoTradeAllocationPct: freshBrokerageAccount.cryptoTradeAllocationPct ?? 5.0,
|
|
1994
2013
|
autoAllocation: accountWithAllocation.autoAllocation ?? false,
|
|
1995
2014
|
allocation: allocationData,
|
|
1996
|
-
enablePortfolioTrailingStop:
|
|
1997
|
-
portfolioTrailPercent:
|
|
1998
|
-
portfolioProfitThresholdPercent:
|
|
1999
|
-
reducedPortfolioTrailPercent:
|
|
2000
|
-
defaultTrailingStopPercentage100:
|
|
2001
|
-
firstTrailReductionThreshold100:
|
|
2002
|
-
secondTrailReductionThreshold100:
|
|
2003
|
-
firstReducedTrailPercentage100:
|
|
2004
|
-
secondReducedTrailPercentage100:
|
|
2005
|
-
minimumPriceChangePercent100:
|
|
2015
|
+
enablePortfolioTrailingStop: freshBrokerageAccount.enablePortfolioTrailingStop,
|
|
2016
|
+
portfolioTrailPercent: freshBrokerageAccount.portfolioTrailPercent,
|
|
2017
|
+
portfolioProfitThresholdPercent: freshBrokerageAccount.portfolioProfitThresholdPercent,
|
|
2018
|
+
reducedPortfolioTrailPercent: freshBrokerageAccount.reducedPortfolioTrailPercent,
|
|
2019
|
+
defaultTrailingStopPercentage100: freshBrokerageAccount.defaultTrailingStopPercentage100 ?? 4.0,
|
|
2020
|
+
firstTrailReductionThreshold100: freshBrokerageAccount.firstTrailReductionThreshold100 ?? 2.0,
|
|
2021
|
+
secondTrailReductionThreshold100: freshBrokerageAccount.secondTrailReductionThreshold100 ?? 5.0,
|
|
2022
|
+
firstReducedTrailPercentage100: freshBrokerageAccount.firstReducedTrailPercentage100 ?? 1.0,
|
|
2023
|
+
secondReducedTrailPercentage100: freshBrokerageAccount.secondReducedTrailPercentage100 ?? 0.5,
|
|
2024
|
+
minimumPriceChangePercent100: freshBrokerageAccount.minimumPriceChangePercent100 ?? 0.5,
|
|
2006
2025
|
};
|
|
2007
2026
|
return combinedConfig;
|
|
2008
2027
|
}
|
|
@@ -2023,9 +2042,9 @@ async function updateConfiguration(user, account, updatedConfig) {
|
|
|
2023
2042
|
if (!account) {
|
|
2024
2043
|
throw new Error(`Account is missing.`);
|
|
2025
2044
|
}
|
|
2026
|
-
const {
|
|
2027
|
-
if (!
|
|
2028
|
-
throw new Error("
|
|
2045
|
+
const { apiKey, apiSecret } = account;
|
|
2046
|
+
if (!apiKey || !apiSecret) {
|
|
2047
|
+
throw new Error("Account apiKey or apiSecret is missing.");
|
|
2029
2048
|
}
|
|
2030
2049
|
const apiUrl = getTradingApiUrl(account.type);
|
|
2031
2050
|
// Prepare the config object for Alpaca by removing DB-only fields
|
|
@@ -2053,8 +2072,8 @@ async function updateConfiguration(user, account, updatedConfig) {
|
|
|
2053
2072
|
const alpacaUpdatePromise = fetch(`${apiUrl}/account/configurations`, {
|
|
2054
2073
|
method: "PATCH",
|
|
2055
2074
|
headers: {
|
|
2056
|
-
"APCA-API-KEY-ID":
|
|
2057
|
-
"APCA-API-SECRET-KEY":
|
|
2075
|
+
"APCA-API-KEY-ID": apiKey,
|
|
2076
|
+
"APCA-API-SECRET-KEY": apiSecret,
|
|
2058
2077
|
"Content-Type": "application/json",
|
|
2059
2078
|
accept: "application/json",
|
|
2060
2079
|
},
|
|
@@ -2075,10 +2094,10 @@ async function updateConfiguration(user, account, updatedConfig) {
|
|
|
2075
2094
|
if (account.allocation) {
|
|
2076
2095
|
allocUpdatePromise = adaptic$1.allocation.update({
|
|
2077
2096
|
id: account.allocation.id,
|
|
2078
|
-
|
|
2097
|
+
brokerageAccount: {
|
|
2079
2098
|
id: account.id,
|
|
2080
2099
|
},
|
|
2081
|
-
|
|
2100
|
+
brokerageAccountId: account.id,
|
|
2082
2101
|
stocks: updatedConfig.allocation.stocks ?? 0,
|
|
2083
2102
|
options: updatedConfig.allocation.options ?? 0,
|
|
2084
2103
|
futures: updatedConfig.allocation.futures ?? 0,
|
|
@@ -2095,14 +2114,14 @@ async function updateConfiguration(user, account, updatedConfig) {
|
|
|
2095
2114
|
etfs: updatedConfig.allocation.etfs ?? 0,
|
|
2096
2115
|
forex: updatedConfig.allocation.forex ?? 0,
|
|
2097
2116
|
crypto: updatedConfig.allocation.crypto ?? 0,
|
|
2098
|
-
|
|
2117
|
+
brokerageAccount: {
|
|
2099
2118
|
id: account.id,
|
|
2100
2119
|
},
|
|
2101
|
-
|
|
2120
|
+
brokerageAccountId: account.id,
|
|
2102
2121
|
}, client);
|
|
2103
2122
|
}
|
|
2104
2123
|
}
|
|
2105
|
-
const adapticUpdatePromise = adaptic$1.
|
|
2124
|
+
const adapticUpdatePromise = adaptic$1.brokerageAccount.update({
|
|
2106
2125
|
id: account.id,
|
|
2107
2126
|
user: {
|
|
2108
2127
|
id: user.id,
|
|
@@ -2129,7 +2148,7 @@ async function updateConfiguration(user, account, updatedConfig) {
|
|
|
2129
2148
|
secondReducedTrailPercentage100: updatedConfig.secondReducedTrailPercentage100 ?? 0,
|
|
2130
2149
|
minimumPriceChangePercent100: updatedConfig.minimumPriceChangePercent100 ?? 0,
|
|
2131
2150
|
}, client);
|
|
2132
|
-
const [alpacaResponse,
|
|
2151
|
+
const [alpacaResponse, updatedBrokerageAccount, updatedAllocation] = await Promise.all([
|
|
2133
2152
|
alpacaUpdatePromise,
|
|
2134
2153
|
adapticUpdatePromise,
|
|
2135
2154
|
allocUpdatePromise,
|
|
@@ -2149,10 +2168,10 @@ async function updateConfiguration(user, account, updatedConfig) {
|
|
|
2149
2168
|
throw new Error(`Failed to update account config at Alpaca: ${alpacaResponse.statusText}`);
|
|
2150
2169
|
}
|
|
2151
2170
|
const alpacaData = (await alpacaResponse.json());
|
|
2152
|
-
if (!
|
|
2171
|
+
if (!updatedBrokerageAccount) {
|
|
2153
2172
|
throw new Error("Failed to update Alpaca Account in @adaptic/backend-legacy.");
|
|
2154
2173
|
}
|
|
2155
|
-
const updatedAccountWithAllocation =
|
|
2174
|
+
const updatedAccountWithAllocation = updatedBrokerageAccount;
|
|
2156
2175
|
const selectedAllocation = (updatedConfig.allocation ||
|
|
2157
2176
|
updatedAllocation ||
|
|
2158
2177
|
updatedAccountWithAllocation.allocation);
|
|
@@ -2162,26 +2181,26 @@ async function updateConfiguration(user, account, updatedConfig) {
|
|
|
2162
2181
|
getLogger().info("Final allocation:", selectedAllocation);
|
|
2163
2182
|
const finalConfig = {
|
|
2164
2183
|
...alpacaData,
|
|
2165
|
-
marketOpen:
|
|
2166
|
-
realTime:
|
|
2167
|
-
tradeAllocationPct:
|
|
2168
|
-
minPercentageChange:
|
|
2169
|
-
volumeThreshold:
|
|
2170
|
-
cryptoTradingEnabled:
|
|
2171
|
-
cryptoTradingPairs:
|
|
2172
|
-
cryptoTradeAllocationPct:
|
|
2184
|
+
marketOpen: updatedBrokerageAccount.marketOpen,
|
|
2185
|
+
realTime: updatedBrokerageAccount.realTime,
|
|
2186
|
+
tradeAllocationPct: updatedBrokerageAccount.tradeAllocationPct,
|
|
2187
|
+
minPercentageChange: updatedBrokerageAccount.minPercentageChange,
|
|
2188
|
+
volumeThreshold: updatedBrokerageAccount.volumeThreshold,
|
|
2189
|
+
cryptoTradingEnabled: updatedBrokerageAccount.cryptoTradingEnabled,
|
|
2190
|
+
cryptoTradingPairs: updatedBrokerageAccount.cryptoTradingPairs,
|
|
2191
|
+
cryptoTradeAllocationPct: updatedBrokerageAccount.cryptoTradeAllocationPct,
|
|
2173
2192
|
autoAllocation: updatedAccountWithAllocation.autoAllocation,
|
|
2174
2193
|
allocation: selectedAllocation,
|
|
2175
|
-
enablePortfolioTrailingStop:
|
|
2176
|
-
portfolioTrailPercent:
|
|
2177
|
-
portfolioProfitThresholdPercent:
|
|
2178
|
-
reducedPortfolioTrailPercent:
|
|
2179
|
-
defaultTrailingStopPercentage100:
|
|
2180
|
-
firstTrailReductionThreshold100:
|
|
2181
|
-
secondTrailReductionThreshold100:
|
|
2182
|
-
firstReducedTrailPercentage100:
|
|
2183
|
-
secondReducedTrailPercentage100:
|
|
2184
|
-
minimumPriceChangePercent100:
|
|
2194
|
+
enablePortfolioTrailingStop: updatedBrokerageAccount.enablePortfolioTrailingStop,
|
|
2195
|
+
portfolioTrailPercent: updatedBrokerageAccount.portfolioTrailPercent,
|
|
2196
|
+
portfolioProfitThresholdPercent: updatedBrokerageAccount.portfolioProfitThresholdPercent,
|
|
2197
|
+
reducedPortfolioTrailPercent: updatedBrokerageAccount.reducedPortfolioTrailPercent,
|
|
2198
|
+
defaultTrailingStopPercentage100: updatedBrokerageAccount.defaultTrailingStopPercentage100,
|
|
2199
|
+
firstTrailReductionThreshold100: updatedBrokerageAccount.firstTrailReductionThreshold100,
|
|
2200
|
+
secondTrailReductionThreshold100: updatedBrokerageAccount.secondTrailReductionThreshold100,
|
|
2201
|
+
firstReducedTrailPercentage100: updatedBrokerageAccount.firstReducedTrailPercentage100,
|
|
2202
|
+
secondReducedTrailPercentage100: updatedBrokerageAccount.secondReducedTrailPercentage100,
|
|
2203
|
+
minimumPriceChangePercent100: updatedBrokerageAccount.minimumPriceChangePercent100,
|
|
2185
2204
|
};
|
|
2186
2205
|
return finalConfig;
|
|
2187
2206
|
}
|
|
@@ -2199,15 +2218,15 @@ async function updateConfiguration(user, account, updatedConfig) {
|
|
|
2199
2218
|
*/
|
|
2200
2219
|
async function getAsset(auth, symbolOrAssetId) {
|
|
2201
2220
|
try {
|
|
2202
|
-
const {
|
|
2221
|
+
const { apiKey, apiSecret, type } = await validateAuth(auth);
|
|
2203
2222
|
const apiBaseUrl = getTradingApiUrl(type);
|
|
2204
2223
|
// Use encodeURIComponent to handle special characters in symbols (e.g., BTC/USDT)
|
|
2205
2224
|
const encodedSymbolOrAssetId = encodeURIComponent(symbolOrAssetId);
|
|
2206
2225
|
const response = await fetch(`${apiBaseUrl}/v2/assets/${encodedSymbolOrAssetId}`, {
|
|
2207
2226
|
method: "GET",
|
|
2208
2227
|
headers: {
|
|
2209
|
-
"APCA-API-KEY-ID":
|
|
2210
|
-
"APCA-API-SECRET-KEY":
|
|
2228
|
+
"APCA-API-KEY-ID": apiKey,
|
|
2229
|
+
"APCA-API-SECRET-KEY": apiSecret,
|
|
2211
2230
|
"Content-Type": "application/json",
|
|
2212
2231
|
},
|
|
2213
2232
|
signal: createTimeoutSignal(DEFAULT_TIMEOUTS.ALPACA_API),
|
|
@@ -3264,24 +3283,24 @@ async function calculateTotalReturnYTD(portfolioHistory) {
|
|
|
3264
3283
|
* Calculates the expense ratio for a given Alpaca account.
|
|
3265
3284
|
* @param accountId - The ID of the Alpaca account.
|
|
3266
3285
|
* @param client - The Apollo client instance.
|
|
3267
|
-
* @param
|
|
3286
|
+
* @param brokerageAccount - The Alpaca account object.
|
|
3268
3287
|
* @returns A promise that resolves to a string representing the expense ratio in percentage format.
|
|
3269
3288
|
*/
|
|
3270
|
-
async function calculateExpenseRatio$1({ accountId, client,
|
|
3271
|
-
if (!accountId && !
|
|
3289
|
+
async function calculateExpenseRatio$1({ accountId, client, brokerageAccount, }) {
|
|
3290
|
+
if (!accountId && !brokerageAccount && !client) {
|
|
3272
3291
|
getLogger().warn("Missing account ID or client to calculate expense ratio.");
|
|
3273
3292
|
return "N/A";
|
|
3274
3293
|
}
|
|
3275
|
-
let
|
|
3294
|
+
let brokerageAccountId = accountId || (brokerageAccount && brokerageAccount.id) || "";
|
|
3276
3295
|
let accountDetails;
|
|
3277
|
-
if (!
|
|
3296
|
+
if (!brokerageAccountId) {
|
|
3278
3297
|
getLogger().warn("Invalid account ID.");
|
|
3279
3298
|
return "N/A";
|
|
3280
3299
|
}
|
|
3281
|
-
if (
|
|
3300
|
+
if (brokerageAccount) {
|
|
3282
3301
|
// Use Alpaca account object to get accountDetails
|
|
3283
3302
|
accountDetails = (await fetchAccountDetails({
|
|
3284
|
-
|
|
3303
|
+
brokerageAccount: brokerageAccount,
|
|
3285
3304
|
}));
|
|
3286
3305
|
if (!accountDetails) {
|
|
3287
3306
|
getLogger().warn("Failed to fetch account details inside calculateExpenseRatio.");
|
|
@@ -3320,24 +3339,24 @@ async function getPortfolioExpensesFromYourSystem(accountId) {
|
|
|
3320
3339
|
* Calculates the liquidity ratio for a given Alpaca account.
|
|
3321
3340
|
* @param accountId - The ID of the Alpaca account.
|
|
3322
3341
|
* @param client - The Apollo client instance.
|
|
3323
|
-
* @param
|
|
3342
|
+
* @param brokerageAccount - The Alpaca account object.
|
|
3324
3343
|
* @returns A promise that resolves to a string representing the liquidity ratio in the format "1:ratio".
|
|
3325
3344
|
*/
|
|
3326
|
-
async function calculateLiquidityRatio({ accountId, client,
|
|
3327
|
-
if (!accountId && !
|
|
3345
|
+
async function calculateLiquidityRatio({ accountId, client, brokerageAccount, }) {
|
|
3346
|
+
if (!accountId && !brokerageAccount && !client) {
|
|
3328
3347
|
getLogger().warn("Missing account ID or client to calculateLiquidityRatio.");
|
|
3329
3348
|
return "N/A";
|
|
3330
3349
|
}
|
|
3331
|
-
let
|
|
3350
|
+
let brokerageAccountId = accountId || (brokerageAccount && brokerageAccount.id) || "";
|
|
3332
3351
|
let accountDetails;
|
|
3333
|
-
if (!
|
|
3352
|
+
if (!brokerageAccountId) {
|
|
3334
3353
|
getLogger().warn("Invalid account ID.");
|
|
3335
3354
|
return "N/A";
|
|
3336
3355
|
}
|
|
3337
|
-
if (
|
|
3356
|
+
if (brokerageAccount) {
|
|
3338
3357
|
// Use Alpaca account object to get accountDetails
|
|
3339
3358
|
accountDetails = (await fetchAccountDetails({
|
|
3340
|
-
|
|
3359
|
+
brokerageAccount: brokerageAccount,
|
|
3341
3360
|
}));
|
|
3342
3361
|
if (!accountDetails) {
|
|
3343
3362
|
getLogger().warn("Failed to fetch account details inside calculateLiquidityRatio.");
|
|
@@ -3965,11 +3984,11 @@ async function calculateInformationRatio$1(portfolioHistory, benchmarkBars) {
|
|
|
3965
3984
|
* @param params - The parameters for fetching performance metrics.
|
|
3966
3985
|
* @param client - The Apollo client instance.
|
|
3967
3986
|
* @param accountId - The ID of the Alpaca account.
|
|
3968
|
-
* @param
|
|
3987
|
+
* @param brokerageAccount - The Alpaca account object.
|
|
3969
3988
|
* @returns A promise that resolves to an object containing various performance metrics.
|
|
3970
3989
|
* @throws Will throw an error if required parameters are missing or if fetching fails.
|
|
3971
3990
|
*/
|
|
3972
|
-
async function fetchPerformanceMetrics({ params, client, accountId,
|
|
3991
|
+
async function fetchPerformanceMetrics({ params, client, accountId, brokerageAccount, }) {
|
|
3973
3992
|
// Default response for error cases
|
|
3974
3993
|
const defaultMetrics = {
|
|
3975
3994
|
totalReturnYTD: "N/A",
|
|
@@ -3992,12 +4011,12 @@ async function fetchPerformanceMetrics({ params, client, accountId, alpacaAccoun
|
|
|
3992
4011
|
throw new Error("Missing required timeframe or period parameters");
|
|
3993
4012
|
}
|
|
3994
4013
|
// Obtain Alpaca account
|
|
3995
|
-
let
|
|
3996
|
-
if (!
|
|
4014
|
+
let brokerageAccountObj = brokerageAccount ? brokerageAccount : null;
|
|
4015
|
+
if (!brokerageAccountObj && accountId) {
|
|
3997
4016
|
try {
|
|
3998
4017
|
// Use provided client or get the shared client
|
|
3999
4018
|
const apolloClient = client || (await getSharedApolloClient());
|
|
4000
|
-
|
|
4019
|
+
brokerageAccountObj = (await adaptic$1.brokerageAccount.get({
|
|
4001
4020
|
id: accountId,
|
|
4002
4021
|
}, apolloClient));
|
|
4003
4022
|
}
|
|
@@ -4007,9 +4026,9 @@ async function fetchPerformanceMetrics({ params, client, accountId, alpacaAccoun
|
|
|
4007
4026
|
}
|
|
4008
4027
|
}
|
|
4009
4028
|
// Validate Alpaca account
|
|
4010
|
-
if (!
|
|
4011
|
-
!
|
|
4012
|
-
!
|
|
4029
|
+
if (!brokerageAccountObj ||
|
|
4030
|
+
!brokerageAccountObj.apiKey ||
|
|
4031
|
+
!brokerageAccountObj.apiSecret) {
|
|
4013
4032
|
throw new Error("Alpaca account not found or credentials missing");
|
|
4014
4033
|
}
|
|
4015
4034
|
// Fetch portfolio history with structured error handling
|
|
@@ -4017,7 +4036,7 @@ async function fetchPerformanceMetrics({ params, client, accountId, alpacaAccoun
|
|
|
4017
4036
|
try {
|
|
4018
4037
|
portfolioHistory = await fetchPortfolioHistory({
|
|
4019
4038
|
params: params,
|
|
4020
|
-
|
|
4039
|
+
brokerageAccount: brokerageAccountObj,
|
|
4021
4040
|
});
|
|
4022
4041
|
}
|
|
4023
4042
|
catch (error) {
|
|
@@ -4067,10 +4086,10 @@ async function fetchPerformanceMetrics({ params, client, accountId, alpacaAccoun
|
|
|
4067
4086
|
calculateInformationRatio$1(portfolioHistory, benchmarkBars),
|
|
4068
4087
|
calculateRiskAdjustedReturn$1(portfolioHistory),
|
|
4069
4088
|
calculateLiquidityRatio({
|
|
4070
|
-
|
|
4089
|
+
brokerageAccount: brokerageAccountObj,
|
|
4071
4090
|
}),
|
|
4072
4091
|
calculateExpenseRatio$1({
|
|
4073
|
-
|
|
4092
|
+
brokerageAccount: brokerageAccountObj,
|
|
4074
4093
|
}),
|
|
4075
4094
|
getDividendYield(),
|
|
4076
4095
|
calculateMaxDrawdown$1(portfolioHistory.equity),
|
|
@@ -4290,15 +4309,15 @@ const timeDiffString = (milliseconds) => {
|
|
|
4290
4309
|
};
|
|
4291
4310
|
|
|
4292
4311
|
// price-utils.ts
|
|
4293
|
-
const calculateFees = async (action, trade,
|
|
4312
|
+
const calculateFees = async (action, trade, brokerageAccount) => {
|
|
4294
4313
|
let fee = 0;
|
|
4295
4314
|
const alpacaOrderId = action.alpacaOrderId;
|
|
4296
4315
|
if (!alpacaOrderId)
|
|
4297
4316
|
return fee;
|
|
4298
4317
|
const order = await getOrder$1({
|
|
4299
|
-
adapticAccountId: trade.
|
|
4300
|
-
alpacaApiKey:
|
|
4301
|
-
alpacaApiSecret:
|
|
4318
|
+
adapticAccountId: trade.brokerageAccountId,
|
|
4319
|
+
alpacaApiKey: brokerageAccount.apiKey,
|
|
4320
|
+
alpacaApiSecret: brokerageAccount.apiSecret,
|
|
4302
4321
|
}, alpacaOrderId);
|
|
4303
4322
|
if (!order)
|
|
4304
4323
|
return fee;
|
|
@@ -4336,13 +4355,13 @@ const calculateFees = async (action, trade, alpacaAccount) => {
|
|
|
4336
4355
|
};
|
|
4337
4356
|
const computeTotalFees = async (trade) => {
|
|
4338
4357
|
let totalFees = 0;
|
|
4339
|
-
// fetch alpaca account details using adaptic.
|
|
4340
|
-
const
|
|
4341
|
-
id: trade.
|
|
4358
|
+
// fetch alpaca account details using adaptic.brokerageAccount.get({id: trade.brokerageAccountId})
|
|
4359
|
+
const brokerageAccount = (await adaptic$1.brokerageAccount.get({
|
|
4360
|
+
id: trade.brokerageAccountId,
|
|
4342
4361
|
}));
|
|
4343
|
-
if (!
|
|
4362
|
+
if (!brokerageAccount)
|
|
4344
4363
|
return totalFees;
|
|
4345
|
-
const feePromises = trade?.actions?.map((action) => calculateFees(action, trade,
|
|
4364
|
+
const feePromises = trade?.actions?.map((action) => calculateFees(action, trade, brokerageAccount));
|
|
4346
4365
|
const fees = await Promise.all(feePromises || []);
|
|
4347
4366
|
totalFees = fees.reduce((acc, fee) => acc + fee, 0);
|
|
4348
4367
|
return totalFees;
|
|
@@ -6089,7 +6108,7 @@ async function fetchBars(params) {
|
|
|
6089
6108
|
*/
|
|
6090
6109
|
async function fetchNews(params, auth) {
|
|
6091
6110
|
const { symbol, start = new Date(Date.now() - 24 * 60 * 60 * 1000), sort = "desc", includeContent = false, limit = 1000, } = params;
|
|
6092
|
-
if (!auth.
|
|
6111
|
+
if (!auth.alpacaApiKey || !auth.alpacaApiSecret) {
|
|
6093
6112
|
throw new Error("Alpaca API key and secret are required");
|
|
6094
6113
|
}
|
|
6095
6114
|
if (!symbol) {
|
|
@@ -6157,7 +6176,7 @@ async function fetchNews(params, auth) {
|
|
|
6157
6176
|
*/
|
|
6158
6177
|
async function fetchLatestTrades(params, auth) {
|
|
6159
6178
|
const { symbols, loc = "us" } = params;
|
|
6160
|
-
if (!auth.
|
|
6179
|
+
if (!auth.alpacaApiKey || !auth.alpacaApiSecret) {
|
|
6161
6180
|
throw new Error("Alpaca API key and secret are required");
|
|
6162
6181
|
}
|
|
6163
6182
|
if (!symbols || symbols.length === 0) {
|
|
@@ -6173,8 +6192,8 @@ async function fetchLatestTrades(params, auth) {
|
|
|
6173
6192
|
return withRetry(async () => {
|
|
6174
6193
|
const response = await fetch(url, {
|
|
6175
6194
|
headers: {
|
|
6176
|
-
"APCA-API-KEY-ID": auth.
|
|
6177
|
-
"APCA-API-SECRET-KEY": auth.
|
|
6195
|
+
"APCA-API-KEY-ID": auth.alpacaApiKey,
|
|
6196
|
+
"APCA-API-SECRET-KEY": auth.alpacaApiSecret,
|
|
6178
6197
|
},
|
|
6179
6198
|
signal: createTimeoutSignal(DEFAULT_TIMEOUTS.ALPACA_API),
|
|
6180
6199
|
});
|
|
@@ -6200,7 +6219,7 @@ async function fetchLatestTrades(params, auth) {
|
|
|
6200
6219
|
*/
|
|
6201
6220
|
async function fetchLatestQuotes(params, auth) {
|
|
6202
6221
|
const { symbols, loc = "us" } = params;
|
|
6203
|
-
if (!auth.
|
|
6222
|
+
if (!auth.alpacaApiKey || !auth.alpacaApiSecret) {
|
|
6204
6223
|
throw new Error("Alpaca API key and secret are required");
|
|
6205
6224
|
}
|
|
6206
6225
|
if (!symbols || symbols.length === 0) {
|
|
@@ -6216,8 +6235,8 @@ async function fetchLatestQuotes(params, auth) {
|
|
|
6216
6235
|
return withRetry(async () => {
|
|
6217
6236
|
const response = await fetch(url, {
|
|
6218
6237
|
headers: {
|
|
6219
|
-
"APCA-API-KEY-ID": auth.
|
|
6220
|
-
"APCA-API-SECRET-KEY": auth.
|
|
6238
|
+
"APCA-API-KEY-ID": auth.alpacaApiKey,
|
|
6239
|
+
"APCA-API-SECRET-KEY": auth.alpacaApiSecret,
|
|
6221
6240
|
},
|
|
6222
6241
|
signal: createTimeoutSignal(DEFAULT_TIMEOUTS.ALPACA_API),
|
|
6223
6242
|
});
|
|
@@ -12263,6 +12282,8 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12263
12282
|
dataURL;
|
|
12264
12283
|
apiURL;
|
|
12265
12284
|
v1beta1url;
|
|
12285
|
+
/** Whether API credentials are valid and available. False during build time when env vars are missing. */
|
|
12286
|
+
credentialsValid = false;
|
|
12266
12287
|
stockStreamUrl = getStockStreamUrl("PRODUCTION"); // production values
|
|
12267
12288
|
optionStreamUrl = getOptionsStreamUrl("PRODUCTION"); // production values
|
|
12268
12289
|
cryptoStreamUrl = getCryptoStreamUrl("PRODUCTION"); // production values
|
|
@@ -12318,13 +12339,16 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12318
12339
|
constructor() {
|
|
12319
12340
|
super();
|
|
12320
12341
|
// Validate credentials from environment variables before initializing
|
|
12321
|
-
|
|
12322
|
-
|
|
12323
|
-
|
|
12342
|
+
// Use throwOnMissing: false to allow initialization during build time
|
|
12343
|
+
// when env vars are not available. Features will be unavailable until
|
|
12344
|
+
// credentials are provided at runtime.
|
|
12345
|
+
const apiKey = process.env.ALPACA_API_KEY || "";
|
|
12346
|
+
const apiSecret = process.env.ALPACA_SECRET_KEY || "";
|
|
12347
|
+
this.credentialsValid = validateAlpacaCredentials({
|
|
12324
12348
|
apiKey,
|
|
12325
12349
|
apiSecret,
|
|
12326
12350
|
isPaper: process.env.ALPACA_ACCOUNT_TYPE === "PAPER",
|
|
12327
|
-
});
|
|
12351
|
+
}, { throwOnMissing: false });
|
|
12328
12352
|
this.dataURL = MARKET_DATA_API.STOCKS;
|
|
12329
12353
|
this.apiURL =
|
|
12330
12354
|
process.env.ALPACA_ACCOUNT_TYPE === "PAPER"
|