@tradejs/node 3.1.27-beta.250 → 3.1.28-beta.251

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.
@@ -1006,7 +1006,8 @@ var buildExchangeFallbackRuntimeTrades = ({
1006
1006
  openPositions,
1007
1007
  strategyNames,
1008
1008
  existingTrades,
1009
- endTime
1009
+ endTime,
1010
+ scope
1010
1011
  }) => {
1011
1012
  if (!entryRows.length && !closedPnlRows.length) return [];
1012
1013
  const strategyNameByOrderId = new Map(
@@ -1061,6 +1062,7 @@ var buildExchangeFallbackRuntimeTrades = ({
1061
1062
  const entryPrice = typeof entry.entryPrice === "number" && Number.isFinite(entry.entryPrice) ? entry.entryPrice : typeof closed?.entryPrice === "number" && Number.isFinite(closed.entryPrice) ? closed.entryPrice : null;
1062
1063
  if (entryPrice == null) return null;
1063
1064
  return {
1065
+ ...scope,
1064
1066
  orderId: runtimeOrderId,
1065
1067
  strategy,
1066
1068
  symbol: entry.symbol,
@@ -1103,6 +1105,7 @@ var buildExchangeFallbackRuntimeTrades = ({
1103
1105
  return null;
1104
1106
  }
1105
1107
  return {
1108
+ ...scope,
1106
1109
  orderId: runtimeOrderId,
1107
1110
  strategy,
1108
1111
  symbol: row.symbol,
@@ -1933,6 +1936,7 @@ var createStrategyRegistryState = () => ({
1933
1936
  strategyManifestsMap: /* @__PURE__ */ new Map(),
1934
1937
  strategyEntriesMap: /* @__PURE__ */ new Map(),
1935
1938
  strategySourcesMap: /* @__PURE__ */ new Map(),
1939
+ strategyModuleResolutions: /* @__PURE__ */ new Map(),
1936
1940
  pluginsLoadPromise: null
1937
1941
  });
1938
1942
  var registryStateByProjectRoot2 = sharedStrategyRegistry.registryStateByProjectRoot;
@@ -1994,7 +1998,7 @@ var validateStrategyEntries = (moduleName, entries, state) => {
1994
1998
  const strategyName = entry?.manifest?.name;
1995
1999
  if (typeof strategyName !== "string" || !strategyName.trim()) {
1996
2000
  issues.push(`${entryPath}: manifest.name is required`);
1997
- } else if (names.has(strategyName) || state.strategyEntriesMap.has(strategyName)) {
2001
+ } else if (names.has(strategyName) || state?.strategyEntriesMap.has(strategyName)) {
1998
2002
  issues.push(`${entryPath}: duplicate strategy ${strategyName}`);
1999
2003
  } else {
2000
2004
  names.add(strategyName);
@@ -2056,6 +2060,72 @@ var importStrategyPluginModule = async (moduleName, cwd = getTradejsProjectCwd()
2056
2060
  moduleName
2057
2061
  );
2058
2062
  };
2063
+ var resolveStrategyModuleEntry = async ({
2064
+ strategyName,
2065
+ moduleName,
2066
+ cwd
2067
+ }) => {
2068
+ const { projectRoot, state } = getStrategyRegistryState(cwd);
2069
+ const source = moduleName.trim();
2070
+ const resolvedModuleName = resolvePluginModuleSpecifier(source, projectRoot);
2071
+ const cacheKey = `${resolvedModuleName}\0${strategyName}`;
2072
+ const cached = state.strategyModuleResolutions.get(cacheKey);
2073
+ if (cached) return cached;
2074
+ const resolution = (async () => {
2075
+ const moduleExport = await importStrategyPluginModule(
2076
+ resolvedModuleName,
2077
+ projectRoot
2078
+ );
2079
+ const pluginDefinition = extractStrategyPluginDefinition(moduleExport);
2080
+ if (!pluginDefinition) {
2081
+ throw new Error(`${source}: export { strategyEntries } is missing`);
2082
+ }
2083
+ const issues = validateStrategyEntries(
2084
+ source,
2085
+ pluginDefinition.strategyEntries
2086
+ );
2087
+ if (issues.length > 0) {
2088
+ throw new Error(
2089
+ ["Invalid TradeJS strategy module:", ...issues].join("\n")
2090
+ );
2091
+ }
2092
+ const entry = pluginDefinition.strategyEntries.find(
2093
+ (candidate) => candidate.manifest.name === strategyName
2094
+ );
2095
+ if (!entry) {
2096
+ throw new Error(`${source}: strategy ${strategyName} is missing`);
2097
+ }
2098
+ const factory = sharedStrategyRegistry.strategyRuntimeFactory;
2099
+ if (!factory) {
2100
+ throw new Error("Strategy runtime factory is not configured");
2101
+ }
2102
+ const manifestsByName = new Map(
2103
+ pluginDefinition.strategyEntries.map(
2104
+ (candidate) => [candidate.manifest.name, candidate.manifest]
2105
+ )
2106
+ );
2107
+ return {
2108
+ entry,
2109
+ creator: factory({
2110
+ strategyName,
2111
+ defaults: entry.defaults,
2112
+ createCore: entry.createCore,
2113
+ manifest: entry.manifest,
2114
+ detectorKey: entry.detectorKey,
2115
+ detectorNoSignalSkipReason: entry.detectorNoSignalSkipReason,
2116
+ resolveRegisteredManifest: (name) => manifestsByName.get(name) ?? state.strategyManifestsMap.get(name)
2117
+ }),
2118
+ source
2119
+ };
2120
+ })();
2121
+ state.strategyModuleResolutions.set(cacheKey, resolution);
2122
+ try {
2123
+ return await resolution;
2124
+ } catch (error) {
2125
+ state.strategyModuleResolutions.delete(cacheKey);
2126
+ throw error;
2127
+ }
2128
+ };
2059
2129
  var ensureStrategyPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
2060
2130
  const { projectRoot, state } = getStrategyRegistryState(cwd);
2061
2131
  if (!state.pluginsLoadPromise) {
@@ -2133,17 +2203,29 @@ var ensureStrategyPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
2133
2203
  }
2134
2204
  await state.pluginsLoadPromise;
2135
2205
  };
2136
- var getStrategyCreator = async (name, cwd = getTradejsProjectCwd()) => {
2206
+ var getStrategyCreator = async (name, cwd = getTradejsProjectCwd(), moduleName) => {
2207
+ if (moduleName?.trim()) {
2208
+ await ensureStrategyPluginsLoaded(cwd);
2209
+ return (await resolveStrategyModuleEntry({ strategyName: name, moduleName, cwd })).creator;
2210
+ }
2137
2211
  await ensureStrategyPluginsLoaded(cwd);
2138
2212
  const { state } = getStrategyRegistryState(cwd);
2139
2213
  return state.strategyCreators.get(name);
2140
2214
  };
2141
- var getStrategyEntry = async (name, cwd = getTradejsProjectCwd()) => {
2215
+ var getStrategyEntry = async (name, cwd = getTradejsProjectCwd(), moduleName) => {
2216
+ if (moduleName?.trim()) {
2217
+ await ensureStrategyPluginsLoaded(cwd);
2218
+ return (await resolveStrategyModuleEntry({ strategyName: name, moduleName, cwd })).entry;
2219
+ }
2142
2220
  await ensureStrategyPluginsLoaded(cwd);
2143
2221
  const { state } = getStrategyRegistryState(cwd);
2144
2222
  return state.strategyEntriesMap.get(name);
2145
2223
  };
2146
- var getStrategyPluginSource = async (name, cwd = getTradejsProjectCwd()) => {
2224
+ var getStrategyPluginSource = async (name, cwd = getTradejsProjectCwd(), moduleName) => {
2225
+ if (moduleName?.trim()) {
2226
+ await ensureStrategyPluginsLoaded(cwd);
2227
+ return (await resolveStrategyModuleEntry({ strategyName: name, moduleName, cwd })).source;
2228
+ }
2147
2229
  await ensureStrategyPluginsLoaded(cwd);
2148
2230
  const { state } = getStrategyRegistryState(cwd);
2149
2231
  return state.strategySourcesMap.get(name);
@@ -3362,9 +3444,40 @@ var DEFAULT_MAX_AGE_BY_INTERVAL = {
3362
3444
  "15m": 30 * 6e4,
3363
3445
  "1h": 2 * 60 * 6e4
3364
3446
  };
3447
+ var MARKET_CONTEXT_PRELOAD_CHUNK_MS = 7 * 24 * 60 * 6e4;
3365
3448
  var binanceMarketContextUnavailable = false;
3366
3449
  var referenceRowsCache = /* @__PURE__ */ new Map();
3367
3450
  var breadthCache = /* @__PURE__ */ new Map();
3451
+ var BREADTH_COMMON_NUMERIC_FIELDS = [
3452
+ "symbolsCount",
3453
+ "advancers",
3454
+ "decliners",
3455
+ "unchanged",
3456
+ "advanceDeclineRatio",
3457
+ "pctAboveMa20",
3458
+ "pctAboveMa50",
3459
+ "equalWeightedReturn",
3460
+ "volumeWeightedReturn",
3461
+ "dispersion"
3462
+ ];
3463
+ var BREADTH_REGIME_NUMERIC_FIELDS = [
3464
+ "btcReturn1h",
3465
+ "btcReturn4h",
3466
+ "btcReturn24h",
3467
+ "altBasketReturn1h",
3468
+ "altBasketReturn4h",
3469
+ "altBasketReturn24h",
3470
+ "btcVsAltReturn1h",
3471
+ "btcVsAltReturn4h",
3472
+ "btcVsAltReturn24h",
3473
+ "btcTurnoverShare1h",
3474
+ "btcTurnoverShare24h",
3475
+ "btcTurnoverShareChange24h",
3476
+ "altVolToBtcVol24h",
3477
+ "altDispersion24h"
3478
+ ];
3479
+ var preloadedTradeFlowBySymbol = null;
3480
+ var preloadedBreadthByUniverse = null;
3368
3481
  var parseEnabledFlag = (value, env) => {
3369
3482
  const normalized = String(value ?? "").trim().toLowerCase();
3370
3483
  if (!normalized)
@@ -3400,6 +3513,100 @@ var hasBaseContext = (signal) => Boolean(
3400
3513
  signal.additionalIndicators?.baseContext && typeof signal.additionalIndicators.baseContext === "object" && !Array.isArray(signal.additionalIndicators.baseContext)
3401
3514
  );
3402
3515
  var isBinanceMarketContextEnabled = (env) => parseEnabledFlag(process.env.BINANCE_MARKET_CONTEXT_ENABLED, env);
3516
+ var latestIndexAtOrBefore = (timestamps, timestamp) => {
3517
+ if (!timestamps?.length) return -1;
3518
+ let low = 0;
3519
+ let high = timestamps.length - 1;
3520
+ let match = -1;
3521
+ while (low <= high) {
3522
+ const middle = Math.floor((low + high) / 2);
3523
+ const rowTimestamp = timestamps[middle];
3524
+ if (rowTimestamp == null) break;
3525
+ if (rowTimestamp <= timestamp) {
3526
+ match = middle;
3527
+ low = middle + 1;
3528
+ } else {
3529
+ high = middle - 1;
3530
+ }
3531
+ }
3532
+ return match;
3533
+ };
3534
+ var latestChunkAtOrBefore = (chunks, timestamp) => {
3535
+ if (!chunks?.length) return null;
3536
+ let low = 0;
3537
+ let high = chunks.length - 1;
3538
+ let match = null;
3539
+ while (low <= high) {
3540
+ const middle = Math.floor((low + high) / 2);
3541
+ const chunk = chunks[middle];
3542
+ const firstTimestamp = chunk?.timestamps[0];
3543
+ if (!chunk || firstTimestamp == null) break;
3544
+ if (firstTimestamp <= timestamp) {
3545
+ match = chunk;
3546
+ low = middle + 1;
3547
+ } else {
3548
+ high = middle - 1;
3549
+ }
3550
+ }
3551
+ return match;
3552
+ };
3553
+ var unpackedNumber = (value) => value == null || Number.isNaN(value) ? null : value;
3554
+ var unpackTradeFlowRow = (chunks, timestamp) => {
3555
+ const rows = latestChunkAtOrBefore(chunks, timestamp);
3556
+ const index = latestIndexAtOrBefore(rows?.timestamps, timestamp);
3557
+ if (!rows || index < 0) return null;
3558
+ return {
3559
+ symbol: rows.symbol,
3560
+ interval: rows.interval,
3561
+ ts: new Date(rows.timestamps[index]),
3562
+ trades: unpackedNumber(rows.columns.trades[index]) ?? 0,
3563
+ buyBaseVolume: unpackedNumber(rows.columns.buyBaseVolume[index]),
3564
+ sellBaseVolume: unpackedNumber(rows.columns.sellBaseVolume[index]),
3565
+ buyQuoteVolume: unpackedNumber(rows.columns.buyQuoteVolume[index]),
3566
+ sellQuoteVolume: unpackedNumber(rows.columns.sellQuoteVolume[index]),
3567
+ netBaseDelta: unpackedNumber(rows.columns.netBaseDelta[index]),
3568
+ netQuoteDelta: unpackedNumber(rows.columns.netQuoteDelta[index]),
3569
+ buyPressurePct: unpackedNumber(rows.columns.buyPressurePct[index])
3570
+ };
3571
+ };
3572
+ var unpackBreadthRow = (chunks, timestamp) => {
3573
+ const rows = latestChunkAtOrBefore(chunks, timestamp);
3574
+ const index = latestIndexAtOrBefore(rows?.timestamps, timestamp);
3575
+ if (!rows || index < 0) return null;
3576
+ const commonNumeric = Object.fromEntries(
3577
+ BREADTH_COMMON_NUMERIC_FIELDS.map((field) => [
3578
+ field,
3579
+ unpackedNumber(rows.commonColumns[field][index])
3580
+ ])
3581
+ );
3582
+ const regimeNumeric = Object.fromEntries(
3583
+ BREADTH_REGIME_NUMERIC_FIELDS.map((field) => [
3584
+ field,
3585
+ unpackedNumber(rows.regimeColumns?.[field][index])
3586
+ ])
3587
+ );
3588
+ return {
3589
+ universe: rows.universe,
3590
+ interval: rows.interval,
3591
+ ts: new Date(rows.timestamps[index]),
3592
+ ...commonNumeric,
3593
+ ...regimeNumeric,
3594
+ symbolsCount: commonNumeric.symbolsCount ?? 0,
3595
+ advancers: commonNumeric.advancers ?? 0,
3596
+ decliners: commonNumeric.decliners ?? 0,
3597
+ unchanged: commonNumeric.unchanged ?? 0,
3598
+ btcAltRegime: rows.regimes?.[index] ?? null
3599
+ };
3600
+ };
3601
+ var toAsOfRow = (row, timestamp, maxAgeMs) => {
3602
+ if (!row) return null;
3603
+ const ageMs = timestamp - row.ts.getTime();
3604
+ return {
3605
+ ...row,
3606
+ ageMs: Number.isFinite(ageMs) ? ageMs : null,
3607
+ stale: !Number.isFinite(ageMs) || ageMs < 0 || ageMs > maxAgeMs
3608
+ };
3609
+ };
3403
3610
  var toTradeFlowContext = (row, interval) => row ? {
3404
3611
  source: "binance_agg_trades",
3405
3612
  interval,
@@ -3422,6 +3629,24 @@ var getCachedReferenceRows = ({
3422
3629
  maxAgeMs,
3423
3630
  abortSignal
3424
3631
  }) => {
3632
+ if (preloadedTradeFlowBySymbol) {
3633
+ return Promise.resolve(
3634
+ referenceSymbols.map((symbol) => ({
3635
+ symbol,
3636
+ tradeFlow: toTradeFlowContext(
3637
+ toAsOfRow(
3638
+ unpackTradeFlowRow(
3639
+ preloadedTradeFlowBySymbol?.get(symbol),
3640
+ timestamp
3641
+ ),
3642
+ timestamp,
3643
+ maxAgeMs
3644
+ ),
3645
+ interval
3646
+ )
3647
+ }))
3648
+ );
3649
+ }
3425
3650
  const key = `${referenceSymbols.join(",")}:${interval}:${timestamp}:${maxAgeMs}`;
3426
3651
  const cached = referenceRowsCache.get(key);
3427
3652
  if (cached) return cached;
@@ -3451,6 +3676,18 @@ var getCachedBreadth = ({
3451
3676
  maxAgeMs,
3452
3677
  abortSignal
3453
3678
  }) => {
3679
+ if (preloadedBreadthByUniverse) {
3680
+ return Promise.resolve(
3681
+ toAsOfRow(
3682
+ unpackBreadthRow(
3683
+ preloadedBreadthByUniverse.get(breadthUniverse),
3684
+ timestamp
3685
+ ),
3686
+ timestamp,
3687
+ maxAgeMs
3688
+ )
3689
+ );
3690
+ }
3454
3691
  const key = `${breadthUniverse}:${interval}:${timestamp}:${maxAgeMs}`;
3455
3692
  const cached = breadthCache.get(key);
3456
3693
  if (cached) return cached;
@@ -3657,12 +3894,20 @@ var SOURCE_EXCHANGE_LIQUIDITY = "coinmarketcap_exchange_liquidity";
3657
3894
  var SOURCE_FEAR_GREED = "coinmarketcap_fear_greed";
3658
3895
  var SOURCE_INDEX = "coinmarketcap_index";
3659
3896
  var DAY_MS = 864e5;
3897
+ var MAX_TIMESTAMP_CACHE_ENTRIES = 512;
3660
3898
  var coinMarketCapContextUnavailable = false;
3661
3899
  var globalContextCache = /* @__PURE__ */ new Map();
3662
3900
  var referenceContextCache = /* @__PURE__ */ new Map();
3663
3901
  var exchangeLiquidityContextCache = /* @__PURE__ */ new Map();
3664
3902
  var fearGreedContextCache = /* @__PURE__ */ new Map();
3665
3903
  var indexContextCache = /* @__PURE__ */ new Map();
3904
+ var setBoundedCache = (cache, key, value) => {
3905
+ if (cache.size >= MAX_TIMESTAMP_CACHE_ENTRIES) {
3906
+ const oldestKey = cache.keys().next().value;
3907
+ if (oldestKey != null) cache.delete(oldestKey);
3908
+ }
3909
+ cache.set(key, value);
3910
+ };
3666
3911
  var parseEnabledFlag2 = (value, env) => {
3667
3912
  const normalized = String(value ?? "").trim().toLowerCase();
3668
3913
  if (!normalized) {
@@ -3769,7 +4014,7 @@ var getCachedGlobalContext = ({
3769
4014
  maxAgeMs,
3770
4015
  ...abortSignal ? { signal: abortSignal } : {}
3771
4016
  });
3772
- globalContextCache.set(key, promise);
4017
+ setBoundedCache(globalContextCache, key, promise);
3773
4018
  void promise.catch(() => globalContextCache.delete(key));
3774
4019
  return promise;
3775
4020
  };
@@ -3789,7 +4034,7 @@ var getCachedReferenceContexts = ({
3789
4034
  maxAgeMs,
3790
4035
  ...abortSignal ? { signal: abortSignal } : {}
3791
4036
  });
3792
- referenceContextCache.set(key, promise);
4037
+ setBoundedCache(referenceContextCache, key, promise);
3793
4038
  void promise.catch(() => referenceContextCache.delete(key));
3794
4039
  return promise;
3795
4040
  };
@@ -3808,7 +4053,7 @@ var getCachedExchangeLiquidityContext = ({
3808
4053
  maxAgeMs,
3809
4054
  ...abortSignal ? { signal: abortSignal } : {}
3810
4055
  });
3811
- exchangeLiquidityContextCache.set(key, promise);
4056
+ setBoundedCache(exchangeLiquidityContextCache, key, promise);
3812
4057
  void promise.catch(() => exchangeLiquidityContextCache.delete(key));
3813
4058
  return promise;
3814
4059
  };
@@ -3827,7 +4072,7 @@ var getCachedFearGreedContext = ({
3827
4072
  maxAgeMs,
3828
4073
  ...abortSignal ? { signal: abortSignal } : {}
3829
4074
  });
3830
- fearGreedContextCache.set(key, promise);
4075
+ setBoundedCache(fearGreedContextCache, key, promise);
3831
4076
  void promise.catch(() => fearGreedContextCache.delete(key));
3832
4077
  return promise;
3833
4078
  };
@@ -3847,7 +4092,7 @@ var getCachedIndexContexts = ({
3847
4092
  maxAgeMs,
3848
4093
  ...abortSignal ? { signal: abortSignal } : {}
3849
4094
  });
3850
- indexContextCache.set(key, promise);
4095
+ setBoundedCache(indexContextCache, key, promise);
3851
4096
  void promise.catch(() => indexContextCache.delete(key));
3852
4097
  return promise;
3853
4098
  };
@@ -4657,7 +4902,7 @@ var signalIntervalToMarketInterval2 = (value) => {
4657
4902
  var hasBaseContext3 = (signal) => Boolean(
4658
4903
  signal.additionalIndicators?.baseContext && typeof signal.additionalIndicators.baseContext === "object" && !Array.isArray(signal.additionalIndicators.baseContext)
4659
4904
  );
4660
- var setBoundedCache = (key, value) => {
4905
+ var setBoundedCache2 = (key, value) => {
4661
4906
  if (contextCache.size >= MAX_CACHE_ENTRIES) {
4662
4907
  const oldestKey = contextCache.keys().next().value;
4663
4908
  if (oldestKey != null) contextCache.delete(oldestKey);
@@ -4904,7 +5149,7 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
4904
5149
  whaleRegistryFingerprint: whales.fingerprint,
4905
5150
  ...params.abortSignal ? { signal: params.abortSignal } : {}
4906
5151
  });
4907
- setBoundedCache(cacheKey, pending);
5152
+ setBoundedCache2(cacheKey, pending);
4908
5153
  }
4909
5154
  row = await pending;
4910
5155
  }
@@ -6039,20 +6284,23 @@ var buildHookCtx = ({
6039
6284
  strategyConfig,
6040
6285
  env,
6041
6286
  isConfigFromBacktest
6042
- }) => ({
6043
- connector,
6044
- strategyName,
6045
- userName,
6046
- symbol,
6047
- ...universe ? { universe } : {},
6048
- ...assetClass ? { assetClass } : {},
6049
- ...accountId ? { accountId } : {},
6050
- ...deploymentId ? { deploymentId } : {},
6051
- ...policyProfileId ? { policyProfileId } : {},
6052
- strategyConfig,
6053
- env,
6054
- isConfigFromBacktest
6055
- });
6287
+ }) => {
6288
+ const context = {
6289
+ connector,
6290
+ strategyName,
6291
+ userName,
6292
+ symbol
6293
+ };
6294
+ if (universe) context.universe = universe;
6295
+ if (assetClass) context.assetClass = assetClass;
6296
+ if (accountId) context.accountId = accountId;
6297
+ if (deploymentId) context.deploymentId = deploymentId;
6298
+ if (policyProfileId) context.policyProfileId = policyProfileId;
6299
+ context.strategyConfig = strategyConfig;
6300
+ context.env = env;
6301
+ context.isConfigFromBacktest = isConfigFromBacktest;
6302
+ return context;
6303
+ };
6056
6304
  var shouldRecordRuntimeJournal = ({
6057
6305
  env,
6058
6306
  config
@@ -7397,7 +7645,13 @@ var DEPLOYMENT_KEYS = /* @__PURE__ */ new Set([
7397
7645
  "assetClasses",
7398
7646
  "tickers"
7399
7647
  ]);
7400
- var STRATEGY_KEYS = /* @__PURE__ */ new Set(["generation", "enabled", "selection", "config"]);
7648
+ var STRATEGY_KEYS = /* @__PURE__ */ new Set([
7649
+ "module",
7650
+ "generation",
7651
+ "enabled",
7652
+ "selection",
7653
+ "config"
7654
+ ]);
7401
7655
  var SELECTION_KEYS = /* @__PURE__ */ new Set(["tickers"]);
7402
7656
  var FORBIDDEN_CONFIG_KEYS = /* @__PURE__ */ new Set([
7403
7657
  "ACCOUNT_ID",
@@ -7469,6 +7723,39 @@ var readRuntimePackageManifest = async (projectRoot) => {
7469
7723
  }
7470
7724
  return value;
7471
7725
  };
7726
+ var parseNpmAliasReference = (value) => {
7727
+ if (typeof value !== "string" || !value.startsWith("npm:")) return null;
7728
+ const target = value.slice("npm:".length);
7729
+ const versionSeparator = target.lastIndexOf("@");
7730
+ if (versionSeparator <= 0 || versionSeparator === target.length - 1) {
7731
+ return null;
7732
+ }
7733
+ return {
7734
+ packageName: target.slice(0, versionSeparator),
7735
+ version: target.slice(versionSeparator + 1)
7736
+ };
7737
+ };
7738
+ var verifyInstalledPackageName = async ({
7739
+ projectRoot,
7740
+ packageName,
7741
+ installedName,
7742
+ installedVersion,
7743
+ projectPackage
7744
+ }) => {
7745
+ if (installedName === void 0 || installedName === packageName) return;
7746
+ if (projectPackage || typeof installedName !== "string") {
7747
+ throw new Error("name is invalid");
7748
+ }
7749
+ const projectManifest = JSON.parse(
7750
+ await (0, import_promises.readFile)(import_node_path.default.join(projectRoot, "package.json"), "utf8")
7751
+ );
7752
+ const alias = parseNpmAliasReference(
7753
+ projectManifest.dependencies?.[packageName]
7754
+ );
7755
+ if (alias?.packageName !== installedName || alias.version !== installedVersion) {
7756
+ throw new Error("name or alias target is invalid");
7757
+ }
7758
+ };
7472
7759
  var readInstalledPackageMetadata = async ({
7473
7760
  projectRoot,
7474
7761
  packageName,
@@ -7482,9 +7769,16 @@ var readInstalledPackageMetadata = async ({
7482
7769
  );
7483
7770
  try {
7484
7771
  const packageJson = JSON.parse(await (0, import_promises.readFile)(packageJsonPath, "utf8"));
7485
- if (packageJson.name !== void 0 && packageJson.name !== packageName || typeof packageJson.version !== "string" || !packageJson.version.trim()) {
7486
- throw new Error("name or version is invalid");
7772
+ if (typeof packageJson.version !== "string" || !packageJson.version.trim()) {
7773
+ throw new Error("version is invalid");
7487
7774
  }
7775
+ await verifyInstalledPackageName({
7776
+ projectRoot,
7777
+ packageName,
7778
+ installedName: packageJson.name,
7779
+ installedVersion: packageJson.version,
7780
+ projectPackage
7781
+ });
7488
7782
  const dependencyNames = [
7489
7783
  ...isRecord(packageJson.dependencies) ? Object.keys(packageJson.dependencies) : [],
7490
7784
  ...isRecord(packageJson.peerDependencies) ? Object.keys(packageJson.peerDependencies) : []
@@ -7579,7 +7873,7 @@ var verifyDeploymentDeclaration = (deploymentId, value) => {
7579
7873
  for (const [strategyName, strategyValue] of Object.entries(
7580
7874
  value.strategies
7581
7875
  )) {
7582
- if (!strategyName.trim() || !isRecord(strategyValue) || Object.keys(strategyValue).some((key) => !STRATEGY_KEYS.has(key)) || strategyValue.generation !== void 0 && (typeof strategyValue.generation !== "string" || !strategyValue.generation.trim()) || typeof strategyValue.enabled !== "boolean" || !verifyStrategySelection(strategyValue.selection) || !isRecord(strategyValue.config) || Object.keys(strategyValue.config).some(
7876
+ if (!strategyName.trim() || !isRecord(strategyValue) || Object.keys(strategyValue).some((key) => !STRATEGY_KEYS.has(key)) || strategyValue.generation !== void 0 && (typeof strategyValue.generation !== "string" || !strategyValue.generation.trim()) || strategyValue.module !== void 0 && (typeof strategyValue.module !== "string" || !strategyValue.module.trim()) || typeof strategyValue.enabled !== "boolean" || !verifyStrategySelection(strategyValue.selection) || !isRecord(strategyValue.config) || Object.keys(strategyValue.config).some(
7583
7877
  (key) => FORBIDDEN_CONFIG_KEYS.has(key)
7584
7878
  ) || !INTERVALS.has(String(strategyValue.config.INTERVAL)) || !["crypto", "tradfi"].includes(String(strategyValue.config.UNIVERSE))) {
7585
7879
  throw new Error(
@@ -7613,9 +7907,9 @@ var resolveStrategyComposition = async ({
7613
7907
  packageManifest
7614
7908
  }) => {
7615
7909
  const [strategyEntry, strategyCreator, pluginSource] = await Promise.all([
7616
- getStrategyEntry(strategyName, projectRoot),
7617
- getStrategyCreator(strategyName, projectRoot),
7618
- getStrategyPluginSource(strategyName, projectRoot)
7910
+ getStrategyEntry(strategyName, projectRoot, declaration.module),
7911
+ getStrategyCreator(strategyName, projectRoot, declaration.module),
7912
+ getStrategyPluginSource(strategyName, projectRoot, declaration.module)
7619
7913
  ]);
7620
7914
  if (!strategyEntry || !strategyCreator) {
7621
7915
  throw new Error(`Unknown strategy: ${strategyName}`);
@@ -7665,6 +7959,7 @@ var resolveStrategyComposition = async ({
7665
7959
  });
7666
7960
  return {
7667
7961
  strategyName,
7962
+ strategyModule: pluginSource ?? strategyPackage.name,
7668
7963
  strategyRevision: computeStrategyRevision({
7669
7964
  strategyName,
7670
7965
  strategyPackage: strategyPackage.name,
@@ -7851,6 +8146,32 @@ var MIN_HOURS = 6;
7851
8146
  var MAX_HOURS = 24 * 90;
7852
8147
  var BYBIT_MAX_TIME_RANGE_MS = 7 * 24 * 60 * 60 * 1e3 - 1e3;
7853
8148
  var EXCHANGE_REQUEST_TIMEOUT_MS = 15e3;
8149
+ var groupRuntimeTradesByIdentity = (trades, identities) => {
8150
+ const grouped = /* @__PURE__ */ new Map();
8151
+ for (const trade of trades) {
8152
+ const matches = [...identities.entries()].filter(([, identity]) => {
8153
+ if (trade.strategy !== identity.strategyName || String(trade.interval) !== String(identity.interval)) {
8154
+ return false;
8155
+ }
8156
+ if (trade.deploymentId && trade.deploymentId !== identity.deploymentId) {
8157
+ return false;
8158
+ }
8159
+ if (trade.accountId && trade.accountId !== identity.accountId) {
8160
+ return false;
8161
+ }
8162
+ if (trade.universe && trade.universe !== identity.universe) {
8163
+ return false;
8164
+ }
8165
+ return true;
8166
+ });
8167
+ if (matches.length !== 1) continue;
8168
+ const [runtimeKey] = matches[0];
8169
+ const bucket = grouped.get(runtimeKey) ?? [];
8170
+ bucket.push(trade);
8171
+ grouped.set(runtimeKey, bucket);
8172
+ }
8173
+ return grouped;
8174
+ };
7854
8175
  var coerceHours = (value) => {
7855
8176
  const parsed = Number(value ?? Number.NaN);
7856
8177
  if (!Number.isFinite(parsed)) {
@@ -8181,6 +8502,7 @@ var loadRuntimeDashboard = async ({
8181
8502
  const syncedTrades = [...unsyncedTrades, ...syncedConnectorTrades];
8182
8503
  const fallbackTrades = [];
8183
8504
  for (const {
8505
+ connector,
8184
8506
  strategyNames,
8185
8507
  entryRows,
8186
8508
  closedPnlRows,
@@ -8193,7 +8515,12 @@ var loadRuntimeDashboard = async ({
8193
8515
  openPositions: openPositionsSnapshot.positions,
8194
8516
  strategyNames,
8195
8517
  existingTrades: [...syncedTrades, ...fallbackTrades],
8196
- endTime
8518
+ endTime,
8519
+ scope: {
8520
+ accountId: connector.accountId,
8521
+ deploymentId: connector.deploymentId,
8522
+ universe: connector.universe
8523
+ }
8197
8524
  })
8198
8525
  );
8199
8526
  }
@@ -8236,6 +8563,9 @@ var loadRuntimeDashboard = async ({
8236
8563
  });
8237
8564
  identityByKey.set(runtimeKey, {
8238
8565
  strategyName: resolvedStrategy.strategyName,
8566
+ strategyModule: resolvedStrategy.strategyModule,
8567
+ strategyPackage: resolvedStrategy.strategyPackage,
8568
+ strategyPackageVersion: resolvedStrategy.strategyPackageVersion,
8239
8569
  configId: strategyConfigId,
8240
8570
  strategyRevision: resolvedStrategy.strategyRevision,
8241
8571
  controlState: resolvedStrategy.controlState,
@@ -8244,6 +8574,8 @@ var loadRuntimeDashboard = async ({
8244
8574
  accountId: deployment.accountId,
8245
8575
  accountLabel: accountsById.get(deployment.accountId)?.label,
8246
8576
  deploymentId: deployment.id,
8577
+ deploymentLabel: deployment.label,
8578
+ ...resolvedStrategy.generation ? { generation: resolvedStrategy.generation } : {},
8247
8579
  policyProfileId: strategyPolicyProfileId,
8248
8580
  enabled: resolvedStrategy.controlState !== "entries_paused",
8249
8581
  config: resolvedStrategy.strategyConfig,
@@ -8252,12 +8584,16 @@ var loadRuntimeDashboard = async ({
8252
8584
  });
8253
8585
  }
8254
8586
  }
8587
+ const tradesByRuntimeKey = groupRuntimeTradesByIdentity(
8588
+ allTrades,
8589
+ identityByKey
8590
+ );
8255
8591
  const strategies2 = await Promise.all(
8256
8592
  [...identityByKey.entries()].map(async ([runtimeKey, identity]) => {
8257
8593
  const { strategyName } = identity;
8258
- const strategyTrades = allTrades.filter(
8259
- (trade) => trade.strategy === strategyName && String(trade.interval) === String(identity.interval)
8260
- ).sort((left, right) => right.entryTimestamp - left.entryTimestamp);
8594
+ const strategyTrades = (tradesByRuntimeKey.get(runtimeKey) ?? []).sort(
8595
+ (left, right) => right.entryTimestamp - left.entryTimestamp
8596
+ );
8261
8597
  const orders = strategyTrades.sort((left, right) => {
8262
8598
  const leftDate = left.exitTimestamp ?? left.entryTimestamp;
8263
8599
  const rightDate = right.exitTimestamp ?? right.entryTimestamp;
@@ -8272,14 +8608,19 @@ var loadRuntimeDashboard = async ({
8272
8608
  return {
8273
8609
  runtimeKey,
8274
8610
  strategyName,
8611
+ strategyModule: identity.strategyModule,
8612
+ strategyPackage: identity.strategyPackage,
8613
+ strategyPackageVersion: identity.strategyPackageVersion,
8275
8614
  configId: identity.configId,
8276
8615
  strategyRevision: identity.strategyRevision,
8616
+ ...identity.generation ? { generation: identity.generation } : {},
8277
8617
  controlState: identity.controlState,
8278
8618
  interval: identity.interval,
8279
8619
  universe: identity.universe,
8280
8620
  accountId: identity.accountId,
8281
8621
  accountLabel: identity.accountLabel,
8282
8622
  deploymentId: identity.deploymentId,
8623
+ deploymentLabel: identity.deploymentLabel,
8283
8624
  ...identity.selection ? { selection: identity.selection } : {},
8284
8625
  policyProfileId: identity.policyProfileId,
8285
8626
  connected: identity.connected,