@tradejs/node 3.1.27 → 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.
package/dist/registry.js CHANGED
@@ -932,6 +932,7 @@ var createStrategyRegistryState = () => ({
932
932
  strategyManifestsMap: /* @__PURE__ */ new Map(),
933
933
  strategyEntriesMap: /* @__PURE__ */ new Map(),
934
934
  strategySourcesMap: /* @__PURE__ */ new Map(),
935
+ strategyModuleResolutions: /* @__PURE__ */ new Map(),
935
936
  pluginsLoadPromise: null
936
937
  });
937
938
  var registryStateByProjectRoot = sharedStrategyRegistry.registryStateByProjectRoot;
@@ -993,7 +994,7 @@ var validateStrategyEntries = (moduleName, entries, state) => {
993
994
  const strategyName = entry?.manifest?.name;
994
995
  if (typeof strategyName !== "string" || !strategyName.trim()) {
995
996
  issues.push(`${entryPath}: manifest.name is required`);
996
- } else if (names.has(strategyName) || state.strategyEntriesMap.has(strategyName)) {
997
+ } else if (names.has(strategyName) || state?.strategyEntriesMap.has(strategyName)) {
997
998
  issues.push(`${entryPath}: duplicate strategy ${strategyName}`);
998
999
  } else {
999
1000
  names.add(strategyName);
@@ -1055,6 +1056,72 @@ var importStrategyPluginModule = async (moduleName, cwd = getTradejsProjectCwd()
1055
1056
  moduleName
1056
1057
  );
1057
1058
  };
1059
+ var resolveStrategyModuleEntry = async ({
1060
+ strategyName,
1061
+ moduleName,
1062
+ cwd
1063
+ }) => {
1064
+ const { projectRoot, state } = getStrategyRegistryState(cwd);
1065
+ const source = moduleName.trim();
1066
+ const resolvedModuleName = resolvePluginModuleSpecifier(source, projectRoot);
1067
+ const cacheKey = `${resolvedModuleName}\0${strategyName}`;
1068
+ const cached = state.strategyModuleResolutions.get(cacheKey);
1069
+ if (cached) return cached;
1070
+ const resolution = (async () => {
1071
+ const moduleExport = await importStrategyPluginModule(
1072
+ resolvedModuleName,
1073
+ projectRoot
1074
+ );
1075
+ const pluginDefinition = extractStrategyPluginDefinition(moduleExport);
1076
+ if (!pluginDefinition) {
1077
+ throw new Error(`${source}: export { strategyEntries } is missing`);
1078
+ }
1079
+ const issues = validateStrategyEntries(
1080
+ source,
1081
+ pluginDefinition.strategyEntries
1082
+ );
1083
+ if (issues.length > 0) {
1084
+ throw new Error(
1085
+ ["Invalid TradeJS strategy module:", ...issues].join("\n")
1086
+ );
1087
+ }
1088
+ const entry = pluginDefinition.strategyEntries.find(
1089
+ (candidate) => candidate.manifest.name === strategyName
1090
+ );
1091
+ if (!entry) {
1092
+ throw new Error(`${source}: strategy ${strategyName} is missing`);
1093
+ }
1094
+ const factory = sharedStrategyRegistry.strategyRuntimeFactory;
1095
+ if (!factory) {
1096
+ throw new Error("Strategy runtime factory is not configured");
1097
+ }
1098
+ const manifestsByName = new Map(
1099
+ pluginDefinition.strategyEntries.map(
1100
+ (candidate) => [candidate.manifest.name, candidate.manifest]
1101
+ )
1102
+ );
1103
+ return {
1104
+ entry,
1105
+ creator: factory({
1106
+ strategyName,
1107
+ defaults: entry.defaults,
1108
+ createCore: entry.createCore,
1109
+ manifest: entry.manifest,
1110
+ detectorKey: entry.detectorKey,
1111
+ detectorNoSignalSkipReason: entry.detectorNoSignalSkipReason,
1112
+ resolveRegisteredManifest: (name) => manifestsByName.get(name) ?? state.strategyManifestsMap.get(name)
1113
+ }),
1114
+ source
1115
+ };
1116
+ })();
1117
+ state.strategyModuleResolutions.set(cacheKey, resolution);
1118
+ try {
1119
+ return await resolution;
1120
+ } catch (error) {
1121
+ state.strategyModuleResolutions.delete(cacheKey);
1122
+ throw error;
1123
+ }
1124
+ };
1058
1125
  var ensureStrategyPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
1059
1126
  const { projectRoot, state } = getStrategyRegistryState(cwd);
1060
1127
  if (!state.pluginsLoadPromise) {
@@ -1133,7 +1200,11 @@ var ensureStrategyPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
1133
1200
  await state.pluginsLoadPromise;
1134
1201
  };
1135
1202
  var ensureIndicatorPluginsLoaded = async (cwd = getTradejsProjectCwd()) => ensureStrategyPluginsLoaded(cwd);
1136
- var getStrategyCreator = async (name, cwd = getTradejsProjectCwd()) => {
1203
+ var getStrategyCreator = async (name, cwd = getTradejsProjectCwd(), moduleName) => {
1204
+ if (moduleName?.trim()) {
1205
+ await ensureStrategyPluginsLoaded(cwd);
1206
+ return (await resolveStrategyModuleEntry({ strategyName: name, moduleName, cwd })).creator;
1207
+ }
1137
1208
  await ensureStrategyPluginsLoaded(cwd);
1138
1209
  const { state } = getStrategyRegistryState(cwd);
1139
1210
  return state.strategyCreators.get(name);
@@ -2393,9 +2464,40 @@ var DEFAULT_MAX_AGE_BY_INTERVAL = {
2393
2464
  "15m": 30 * 6e4,
2394
2465
  "1h": 2 * 60 * 6e4
2395
2466
  };
2467
+ var MARKET_CONTEXT_PRELOAD_CHUNK_MS = 7 * 24 * 60 * 6e4;
2396
2468
  var binanceMarketContextUnavailable = false;
2397
2469
  var referenceRowsCache = /* @__PURE__ */ new Map();
2398
2470
  var breadthCache = /* @__PURE__ */ new Map();
2471
+ var BREADTH_COMMON_NUMERIC_FIELDS = [
2472
+ "symbolsCount",
2473
+ "advancers",
2474
+ "decliners",
2475
+ "unchanged",
2476
+ "advanceDeclineRatio",
2477
+ "pctAboveMa20",
2478
+ "pctAboveMa50",
2479
+ "equalWeightedReturn",
2480
+ "volumeWeightedReturn",
2481
+ "dispersion"
2482
+ ];
2483
+ var BREADTH_REGIME_NUMERIC_FIELDS = [
2484
+ "btcReturn1h",
2485
+ "btcReturn4h",
2486
+ "btcReturn24h",
2487
+ "altBasketReturn1h",
2488
+ "altBasketReturn4h",
2489
+ "altBasketReturn24h",
2490
+ "btcVsAltReturn1h",
2491
+ "btcVsAltReturn4h",
2492
+ "btcVsAltReturn24h",
2493
+ "btcTurnoverShare1h",
2494
+ "btcTurnoverShare24h",
2495
+ "btcTurnoverShareChange24h",
2496
+ "altVolToBtcVol24h",
2497
+ "altDispersion24h"
2498
+ ];
2499
+ var preloadedTradeFlowBySymbol = null;
2500
+ var preloadedBreadthByUniverse = null;
2399
2501
  var parseEnabledFlag = (value, env) => {
2400
2502
  const normalized = String(value ?? "").trim().toLowerCase();
2401
2503
  if (!normalized)
@@ -2431,6 +2533,100 @@ var hasBaseContext = (signal) => Boolean(
2431
2533
  signal.additionalIndicators?.baseContext && typeof signal.additionalIndicators.baseContext === "object" && !Array.isArray(signal.additionalIndicators.baseContext)
2432
2534
  );
2433
2535
  var isBinanceMarketContextEnabled = (env) => parseEnabledFlag(process.env.BINANCE_MARKET_CONTEXT_ENABLED, env);
2536
+ var latestIndexAtOrBefore = (timestamps, timestamp) => {
2537
+ if (!timestamps?.length) return -1;
2538
+ let low = 0;
2539
+ let high = timestamps.length - 1;
2540
+ let match = -1;
2541
+ while (low <= high) {
2542
+ const middle = Math.floor((low + high) / 2);
2543
+ const rowTimestamp = timestamps[middle];
2544
+ if (rowTimestamp == null) break;
2545
+ if (rowTimestamp <= timestamp) {
2546
+ match = middle;
2547
+ low = middle + 1;
2548
+ } else {
2549
+ high = middle - 1;
2550
+ }
2551
+ }
2552
+ return match;
2553
+ };
2554
+ var latestChunkAtOrBefore = (chunks, timestamp) => {
2555
+ if (!chunks?.length) return null;
2556
+ let low = 0;
2557
+ let high = chunks.length - 1;
2558
+ let match = null;
2559
+ while (low <= high) {
2560
+ const middle = Math.floor((low + high) / 2);
2561
+ const chunk = chunks[middle];
2562
+ const firstTimestamp = chunk?.timestamps[0];
2563
+ if (!chunk || firstTimestamp == null) break;
2564
+ if (firstTimestamp <= timestamp) {
2565
+ match = chunk;
2566
+ low = middle + 1;
2567
+ } else {
2568
+ high = middle - 1;
2569
+ }
2570
+ }
2571
+ return match;
2572
+ };
2573
+ var unpackedNumber = (value) => value == null || Number.isNaN(value) ? null : value;
2574
+ var unpackTradeFlowRow = (chunks, timestamp) => {
2575
+ const rows = latestChunkAtOrBefore(chunks, timestamp);
2576
+ const index = latestIndexAtOrBefore(rows?.timestamps, timestamp);
2577
+ if (!rows || index < 0) return null;
2578
+ return {
2579
+ symbol: rows.symbol,
2580
+ interval: rows.interval,
2581
+ ts: new Date(rows.timestamps[index]),
2582
+ trades: unpackedNumber(rows.columns.trades[index]) ?? 0,
2583
+ buyBaseVolume: unpackedNumber(rows.columns.buyBaseVolume[index]),
2584
+ sellBaseVolume: unpackedNumber(rows.columns.sellBaseVolume[index]),
2585
+ buyQuoteVolume: unpackedNumber(rows.columns.buyQuoteVolume[index]),
2586
+ sellQuoteVolume: unpackedNumber(rows.columns.sellQuoteVolume[index]),
2587
+ netBaseDelta: unpackedNumber(rows.columns.netBaseDelta[index]),
2588
+ netQuoteDelta: unpackedNumber(rows.columns.netQuoteDelta[index]),
2589
+ buyPressurePct: unpackedNumber(rows.columns.buyPressurePct[index])
2590
+ };
2591
+ };
2592
+ var unpackBreadthRow = (chunks, timestamp) => {
2593
+ const rows = latestChunkAtOrBefore(chunks, timestamp);
2594
+ const index = latestIndexAtOrBefore(rows?.timestamps, timestamp);
2595
+ if (!rows || index < 0) return null;
2596
+ const commonNumeric = Object.fromEntries(
2597
+ BREADTH_COMMON_NUMERIC_FIELDS.map((field) => [
2598
+ field,
2599
+ unpackedNumber(rows.commonColumns[field][index])
2600
+ ])
2601
+ );
2602
+ const regimeNumeric = Object.fromEntries(
2603
+ BREADTH_REGIME_NUMERIC_FIELDS.map((field) => [
2604
+ field,
2605
+ unpackedNumber(rows.regimeColumns?.[field][index])
2606
+ ])
2607
+ );
2608
+ return {
2609
+ universe: rows.universe,
2610
+ interval: rows.interval,
2611
+ ts: new Date(rows.timestamps[index]),
2612
+ ...commonNumeric,
2613
+ ...regimeNumeric,
2614
+ symbolsCount: commonNumeric.symbolsCount ?? 0,
2615
+ advancers: commonNumeric.advancers ?? 0,
2616
+ decliners: commonNumeric.decliners ?? 0,
2617
+ unchanged: commonNumeric.unchanged ?? 0,
2618
+ btcAltRegime: rows.regimes?.[index] ?? null
2619
+ };
2620
+ };
2621
+ var toAsOfRow = (row, timestamp, maxAgeMs) => {
2622
+ if (!row) return null;
2623
+ const ageMs = timestamp - row.ts.getTime();
2624
+ return {
2625
+ ...row,
2626
+ ageMs: Number.isFinite(ageMs) ? ageMs : null,
2627
+ stale: !Number.isFinite(ageMs) || ageMs < 0 || ageMs > maxAgeMs
2628
+ };
2629
+ };
2434
2630
  var toTradeFlowContext = (row, interval) => row ? {
2435
2631
  source: "binance_agg_trades",
2436
2632
  interval,
@@ -2453,6 +2649,24 @@ var getCachedReferenceRows = ({
2453
2649
  maxAgeMs,
2454
2650
  abortSignal
2455
2651
  }) => {
2652
+ if (preloadedTradeFlowBySymbol) {
2653
+ return Promise.resolve(
2654
+ referenceSymbols.map((symbol) => ({
2655
+ symbol,
2656
+ tradeFlow: toTradeFlowContext(
2657
+ toAsOfRow(
2658
+ unpackTradeFlowRow(
2659
+ preloadedTradeFlowBySymbol?.get(symbol),
2660
+ timestamp
2661
+ ),
2662
+ timestamp,
2663
+ maxAgeMs
2664
+ ),
2665
+ interval
2666
+ )
2667
+ }))
2668
+ );
2669
+ }
2456
2670
  const key = `${referenceSymbols.join(",")}:${interval}:${timestamp}:${maxAgeMs}`;
2457
2671
  const cached = referenceRowsCache.get(key);
2458
2672
  if (cached) return cached;
@@ -2482,6 +2696,18 @@ var getCachedBreadth = ({
2482
2696
  maxAgeMs,
2483
2697
  abortSignal
2484
2698
  }) => {
2699
+ if (preloadedBreadthByUniverse) {
2700
+ return Promise.resolve(
2701
+ toAsOfRow(
2702
+ unpackBreadthRow(
2703
+ preloadedBreadthByUniverse.get(breadthUniverse),
2704
+ timestamp
2705
+ ),
2706
+ timestamp,
2707
+ maxAgeMs
2708
+ )
2709
+ );
2710
+ }
2485
2711
  const key = `${breadthUniverse}:${interval}:${timestamp}:${maxAgeMs}`;
2486
2712
  const cached = breadthCache.get(key);
2487
2713
  if (cached) return cached;
@@ -2688,12 +2914,20 @@ var SOURCE_EXCHANGE_LIQUIDITY = "coinmarketcap_exchange_liquidity";
2688
2914
  var SOURCE_FEAR_GREED = "coinmarketcap_fear_greed";
2689
2915
  var SOURCE_INDEX = "coinmarketcap_index";
2690
2916
  var DAY_MS = 864e5;
2917
+ var MAX_TIMESTAMP_CACHE_ENTRIES = 512;
2691
2918
  var coinMarketCapContextUnavailable = false;
2692
2919
  var globalContextCache = /* @__PURE__ */ new Map();
2693
2920
  var referenceContextCache = /* @__PURE__ */ new Map();
2694
2921
  var exchangeLiquidityContextCache = /* @__PURE__ */ new Map();
2695
2922
  var fearGreedContextCache = /* @__PURE__ */ new Map();
2696
2923
  var indexContextCache = /* @__PURE__ */ new Map();
2924
+ var setBoundedCache = (cache, key, value) => {
2925
+ if (cache.size >= MAX_TIMESTAMP_CACHE_ENTRIES) {
2926
+ const oldestKey = cache.keys().next().value;
2927
+ if (oldestKey != null) cache.delete(oldestKey);
2928
+ }
2929
+ cache.set(key, value);
2930
+ };
2697
2931
  var parseEnabledFlag2 = (value, env) => {
2698
2932
  const normalized = String(value ?? "").trim().toLowerCase();
2699
2933
  if (!normalized) {
@@ -2800,7 +3034,7 @@ var getCachedGlobalContext = ({
2800
3034
  maxAgeMs,
2801
3035
  ...abortSignal ? { signal: abortSignal } : {}
2802
3036
  });
2803
- globalContextCache.set(key, promise);
3037
+ setBoundedCache(globalContextCache, key, promise);
2804
3038
  void promise.catch(() => globalContextCache.delete(key));
2805
3039
  return promise;
2806
3040
  };
@@ -2820,7 +3054,7 @@ var getCachedReferenceContexts = ({
2820
3054
  maxAgeMs,
2821
3055
  ...abortSignal ? { signal: abortSignal } : {}
2822
3056
  });
2823
- referenceContextCache.set(key, promise);
3057
+ setBoundedCache(referenceContextCache, key, promise);
2824
3058
  void promise.catch(() => referenceContextCache.delete(key));
2825
3059
  return promise;
2826
3060
  };
@@ -2839,7 +3073,7 @@ var getCachedExchangeLiquidityContext = ({
2839
3073
  maxAgeMs,
2840
3074
  ...abortSignal ? { signal: abortSignal } : {}
2841
3075
  });
2842
- exchangeLiquidityContextCache.set(key, promise);
3076
+ setBoundedCache(exchangeLiquidityContextCache, key, promise);
2843
3077
  void promise.catch(() => exchangeLiquidityContextCache.delete(key));
2844
3078
  return promise;
2845
3079
  };
@@ -2858,7 +3092,7 @@ var getCachedFearGreedContext = ({
2858
3092
  maxAgeMs,
2859
3093
  ...abortSignal ? { signal: abortSignal } : {}
2860
3094
  });
2861
- fearGreedContextCache.set(key, promise);
3095
+ setBoundedCache(fearGreedContextCache, key, promise);
2862
3096
  void promise.catch(() => fearGreedContextCache.delete(key));
2863
3097
  return promise;
2864
3098
  };
@@ -2878,7 +3112,7 @@ var getCachedIndexContexts = ({
2878
3112
  maxAgeMs,
2879
3113
  ...abortSignal ? { signal: abortSignal } : {}
2880
3114
  });
2881
- indexContextCache.set(key, promise);
3115
+ setBoundedCache(indexContextCache, key, promise);
2882
3116
  void promise.catch(() => indexContextCache.delete(key));
2883
3117
  return promise;
2884
3118
  };
@@ -3688,7 +3922,7 @@ var signalIntervalToMarketInterval2 = (value) => {
3688
3922
  var hasBaseContext3 = (signal) => Boolean(
3689
3923
  signal.additionalIndicators?.baseContext && typeof signal.additionalIndicators.baseContext === "object" && !Array.isArray(signal.additionalIndicators.baseContext)
3690
3924
  );
3691
- var setBoundedCache = (key, value) => {
3925
+ var setBoundedCache2 = (key, value) => {
3692
3926
  if (contextCache.size >= MAX_CACHE_ENTRIES) {
3693
3927
  const oldestKey = contextCache.keys().next().value;
3694
3928
  if (oldestKey != null) contextCache.delete(oldestKey);
@@ -3935,7 +4169,7 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
3935
4169
  whaleRegistryFingerprint: whales.fingerprint,
3936
4170
  ...params.abortSignal ? { signal: params.abortSignal } : {}
3937
4171
  });
3938
- setBoundedCache(cacheKey, pending);
4172
+ setBoundedCache2(cacheKey, pending);
3939
4173
  }
3940
4174
  row = await pending;
3941
4175
  }
@@ -5070,20 +5304,23 @@ var buildHookCtx = ({
5070
5304
  strategyConfig,
5071
5305
  env,
5072
5306
  isConfigFromBacktest
5073
- }) => ({
5074
- connector,
5075
- strategyName,
5076
- userName,
5077
- symbol,
5078
- ...universe ? { universe } : {},
5079
- ...assetClass ? { assetClass } : {},
5080
- ...accountId ? { accountId } : {},
5081
- ...deploymentId ? { deploymentId } : {},
5082
- ...policyProfileId ? { policyProfileId } : {},
5083
- strategyConfig,
5084
- env,
5085
- isConfigFromBacktest
5086
- });
5307
+ }) => {
5308
+ const context = {
5309
+ connector,
5310
+ strategyName,
5311
+ userName,
5312
+ symbol
5313
+ };
5314
+ if (universe) context.universe = universe;
5315
+ if (assetClass) context.assetClass = assetClass;
5316
+ if (accountId) context.accountId = accountId;
5317
+ if (deploymentId) context.deploymentId = deploymentId;
5318
+ if (policyProfileId) context.policyProfileId = policyProfileId;
5319
+ context.strategyConfig = strategyConfig;
5320
+ context.env = env;
5321
+ context.isConfigFromBacktest = isConfigFromBacktest;
5322
+ return context;
5323
+ };
5087
5324
  var shouldRecordRuntimeJournal = ({
5088
5325
  env,
5089
5326
  config
package/dist/registry.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import "./chunk-XHNJ757K.mjs";
1
+ import "./chunk-VQSGH7YA.mjs";
2
2
  import {
3
3
  ensureIndicatorPluginsLoaded,
4
4
  ensureStrategyPluginsLoaded,
@@ -12,7 +12,7 @@ import {
12
12
  registerStrategyEntries,
13
13
  resetStrategyRegistryCache,
14
14
  strategies
15
- } from "./chunk-IKRSNRP6.mjs";
15
+ } from "./chunk-UITD6LG6.mjs";
16
16
  import "./chunk-LZDXRXIU.mjs";
17
17
  import "./chunk-Y6FXYEAI.mjs";
18
18
  export {