@tradejs/node 3.1.29-beta.290 → 3.1.29-beta.292

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/cli.js CHANGED
@@ -81,869 +81,881 @@ var import_candles = require("@tradejs/infra/timescale/candles");
81
81
  var import_aiLanguages2 = require("@tradejs/core/aiLanguages");
82
82
  var import_redis = require("@tradejs/infra/redis");
83
83
 
84
- // src/aiShared.ts
85
- var MAX_AI_SERIES_POINTS = 5;
86
- var COMPACT_INDICATORS_SNAPSHOT_SYMBOL = /* @__PURE__ */ Symbol.for(
87
- "tradejs.indicators.compactSnapshot"
88
- );
89
- var COMPACT_INDICATORS_SNAPSHOT_KEY = "__tradejsCompactIndicatorsSnapshot";
90
- var trimSeriesDeep = (value) => {
91
- if (Array.isArray(value)) {
92
- const trimmed = value.slice(-MAX_AI_SERIES_POINTS);
93
- const isMatrix = trimmed.every((item) => Array.isArray(item));
94
- if (isMatrix) {
95
- return trimmed;
96
- }
97
- return trimmed.map(
98
- (item) => item && typeof item === "object" ? trimSeriesDeep(item) : item
99
- );
84
+ // src/strategyAdapters/ai.ts
85
+ var import_indicators2 = require("@tradejs/core/indicators");
86
+
87
+ // src/tradejsConfig.ts
88
+ var import_fs = __toESM(require("fs"));
89
+ var import_path = __toESM(require("path"));
90
+ var import_url = require("url");
91
+ var import_config = require("@tradejs/core/config");
92
+ var import_logger = require("@tradejs/infra/logger");
93
+ var CONFIG_FILE_NAMES = [
94
+ "tradejs.config.ts",
95
+ "tradejs.config.mts",
96
+ "tradejs.config.js",
97
+ "tradejs.config.mjs",
98
+ "tradejs.config.cjs"
99
+ ];
100
+ var TS_MODULE_RE = /\.(cts|mts|ts)$/i;
101
+ var cachedByCwd = /* @__PURE__ */ new Map();
102
+ var announcedConfigFile = /* @__PURE__ */ new Set();
103
+ var tsNodeRegistered = false;
104
+ var tsconfigPathsRegisteredByCwd = /* @__PURE__ */ new Set();
105
+ var tsconfigPathMatchersByCwd = /* @__PURE__ */ new Map();
106
+ var getTradejsProjectCwd = (cwd) => {
107
+ const explicit = String(cwd ?? "").trim();
108
+ if (explicit) {
109
+ return import_path.default.resolve(explicit);
100
110
  }
101
- if (value && typeof value === "object") {
102
- return Object.fromEntries(
103
- Object.entries(value).map(([key, nested]) => [
104
- key,
105
- trimSeriesDeep(nested)
106
- ])
107
- );
111
+ const fromEnv = String(process.env.PROJECT_CWD || "").trim();
112
+ if (fromEnv) {
113
+ return import_path.default.resolve(fromEnv);
108
114
  }
109
- return value;
115
+ return process.cwd();
110
116
  };
111
- var buildCompactAiIndicatorsSnapshot = (value) => {
112
- const compactSnapshot = value && typeof value === "object" ? value[COMPACT_INDICATORS_SNAPSHOT_SYMBOL] ?? value[COMPACT_INDICATORS_SNAPSHOT_KEY] : void 0;
113
- if (typeof compactSnapshot === "function") {
114
- return compactSnapshot({ limit: MAX_AI_SERIES_POINTS });
117
+ var normalizeConfig = (rawConfig) => {
118
+ if (!rawConfig || typeof rawConfig !== "object") {
119
+ return {};
115
120
  }
116
- return trimSeriesDeep(value);
121
+ const config = rawConfig;
122
+ const strategies2 = Array.isArray(config.strategies) ? config.strategies.map((value) => String(value || "").trim()).filter(Boolean) : [];
123
+ const indicators = Array.isArray(config.indicators) ? config.indicators.map((value) => String(value || "").trim()).filter(Boolean) : [];
124
+ const connectors = Array.isArray(config.connectors) ? config.connectors.map((value) => String(value || "").trim()).filter(Boolean) : [];
125
+ const hooks = (0, import_config.normalizeTradejsConfigHooks)(
126
+ config.hooks
127
+ );
128
+ const runtime = config.runtime && typeof config.runtime === "object" && !Array.isArray(config.runtime) ? config.runtime : void 0;
129
+ return {
130
+ strategies: strategies2,
131
+ indicators,
132
+ connectors,
133
+ ...hooks ? { hooks } : {},
134
+ ...runtime ? { runtime } : {}
135
+ };
117
136
  };
118
-
119
- // src/aiMarketContext.ts
120
- var toRecord = (value) => {
121
- if (!value || typeof value !== "object" || Array.isArray(value)) {
122
- return null;
137
+ var getNodeCreateRequire = () => {
138
+ const builtinModule = process.getBuiltinModule?.("module");
139
+ if (typeof builtinModule?.createRequire === "function") {
140
+ return builtinModule.createRequire;
123
141
  }
124
- return value;
142
+ throw new TypeError("module.createRequire is not available");
125
143
  };
126
- var toFiniteNumber = (value) => {
127
- const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
128
- return Number.isFinite(numeric) ? numeric : null;
144
+ var getRequireFn = (cwd = getTradejsProjectCwd()) => getNodeCreateRequire()(import_path.default.join(import_path.default.resolve(cwd), "__tradejs_loader__.js"));
145
+ var ensureTsNodeRegistered = async () => {
146
+ if (tsNodeRegistered) {
147
+ return;
148
+ }
149
+ const tsNodeModule = await import("ts-node");
150
+ const tsNode = tsNodeModule.default ?? tsNodeModule;
151
+ tsNode.register?.({
152
+ transpileOnly: true,
153
+ compilerOptions: {
154
+ module: "Node16",
155
+ moduleResolution: "node16"
156
+ }
157
+ });
158
+ tsNodeRegistered = true;
129
159
  };
130
- var roundTo = (value, decimals) => {
131
- const factor = 10 ** decimals;
132
- return Math.round(value * factor) / factor;
160
+ var ensureTsconfigPathsRegistered = async (cwd = getTradejsProjectCwd()) => {
161
+ const projectRoot = getTradejsProjectCwd(cwd);
162
+ if (tsconfigPathsRegisteredByCwd.has(projectRoot)) {
163
+ return;
164
+ }
165
+ const tsconfigPathsModule = await import("tsconfig-paths");
166
+ const loadConfig = tsconfigPathsModule.loadConfig;
167
+ const register = tsconfigPathsModule.register;
168
+ if (typeof loadConfig !== "function" || typeof register !== "function") {
169
+ return;
170
+ }
171
+ const loadedConfig = loadConfig(projectRoot);
172
+ if (loadedConfig.resultType !== "success") {
173
+ return;
174
+ }
175
+ register({
176
+ baseUrl: loadedConfig.absoluteBaseUrl,
177
+ paths: loadedConfig.paths,
178
+ addMatchAll: false
179
+ });
180
+ tsconfigPathsRegisteredByCwd.add(projectRoot);
133
181
  };
134
- var buildMissingSpreadContext = () => ({
135
- source: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
136
- indicatorKey: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
137
- available: false,
138
- value: null,
139
- zScore: null,
140
- bps: null,
141
- absBps: null,
142
- bias: null,
143
- severity: null
144
- });
145
- var buildSpreadContextFromSignal = (signal) => {
146
- const baseContext = toRecord(signal.additionalIndicators?.baseContext);
147
- const relative = toRecord(baseContext?.relative);
148
- const execution = toRecord(relative?.execution);
149
- const spread = toFiniteNumber(execution?.venueSpread);
150
- const zScore = toFiniteNumber(execution?.venueSpreadZScore);
151
- if (spread == null) {
152
- return buildMissingSpreadContext();
182
+ var resolveTsconfigPathModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
183
+ const projectRoot = getTradejsProjectCwd(cwd);
184
+ const cachedMatcher = tsconfigPathMatchersByCwd.get(projectRoot);
185
+ if (cachedMatcher) {
186
+ const resolved2 = cachedMatcher(moduleName);
187
+ return resolved2 || null;
153
188
  }
154
- const value = roundTo(spread, 8);
155
- const bps = roundTo(value * 1e4, 2);
156
- const absBps = Math.abs(bps);
157
- const bias = absBps < 1 ? "flat" : bps > 0 ? "coinbase_premium" : "binance_premium";
158
- const severity = absBps >= 20 ? "wide" : absBps >= 5 ? "elevated" : "normal";
159
- return {
160
- source: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
161
- indicatorKey: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
162
- available: true,
163
- value,
164
- zScore,
165
- bps,
166
- absBps,
167
- bias,
168
- severity
169
- };
189
+ const tsconfigPathsModule = await import("tsconfig-paths");
190
+ const loadConfig = tsconfigPathsModule.loadConfig;
191
+ const createMatchPath = tsconfigPathsModule.createMatchPath;
192
+ if (typeof loadConfig !== "function" || typeof createMatchPath !== "function") {
193
+ return null;
194
+ }
195
+ const loadedConfig = loadConfig(projectRoot);
196
+ if (loadedConfig.resultType !== "success") {
197
+ return null;
198
+ }
199
+ const matchPath = createMatchPath(
200
+ loadedConfig.absoluteBaseUrl,
201
+ loadedConfig.paths
202
+ );
203
+ const matcher = (requestedModule) => matchPath(requestedModule, void 0, import_fs.default.existsSync, [
204
+ ".ts",
205
+ ".tsx",
206
+ ".mts",
207
+ ".cts",
208
+ ".js",
209
+ ".jsx",
210
+ ".mjs",
211
+ ".cjs",
212
+ ".json"
213
+ ]) || "";
214
+ tsconfigPathMatchersByCwd.set(projectRoot, matcher);
215
+ const resolved = matcher(moduleName);
216
+ return resolved || null;
170
217
  };
171
- var buildTrueDeltaContextFromSignal = (signal) => {
172
- const baseContext = toRecord(signal.additionalIndicators?.baseContext);
173
- const participation = toRecord(baseContext?.participation);
174
- const delta = toRecord(participation?.delta);
175
- const source = String(delta?.source ?? "");
176
- const isTrueDeltaSource = source === "kline_taker_volume" || source === "agg_trades" || source === "trades";
177
- if (!delta || !isTrueDeltaSource) {
178
- return {
179
- source: source || null,
180
- available: false,
181
- buyPressurePct: null,
182
- buyVolume: null,
183
- sellVolume: null,
184
- netDelta: null,
185
- deltaPct: null,
186
- signedVolumeZScore: null
187
- };
218
+ var toImportSpecifier = (moduleName) => {
219
+ if (moduleName.startsWith("file://")) {
220
+ return moduleName;
188
221
  }
189
- return {
190
- source,
191
- available: true,
192
- buyPressurePct: toFiniteNumber(delta.buyPressurePct),
193
- buyVolume: toFiniteNumber(delta.buyVolume),
194
- sellVolume: toFiniteNumber(delta.sellVolume),
195
- netDelta: toFiniteNumber(delta.netDelta),
196
- deltaPct: toFiniteNumber(delta.deltaPct),
197
- signedVolumeZScore: toFiniteNumber(delta.signedVolumeZScore)
198
- };
222
+ if (import_path.default.isAbsolute(moduleName)) {
223
+ return (0, import_url.pathToFileURL)(moduleName).href;
224
+ }
225
+ return moduleName;
199
226
  };
200
- var buildTradeFlowContextFromSignal = (signal) => {
201
- const baseContext = toRecord(signal.additionalIndicators?.baseContext);
202
- const participation = toRecord(baseContext?.participation);
203
- const tradeFlow = toRecord(participation?.tradeFlow);
204
- if (!tradeFlow) {
205
- return {
206
- source: null,
207
- available: false,
208
- interval: null,
209
- stale: null,
210
- trades: null,
211
- buyPressurePct: null,
212
- netBaseDelta: null,
213
- netQuoteDelta: null
214
- };
227
+ var isTsModulePath = (moduleName) => TS_MODULE_RE.test(moduleName.split("?")[0]);
228
+ var isRelativeModulePath = (moduleName) => moduleName.startsWith("./") || moduleName.startsWith("../");
229
+ var isBareModuleSpecifier = (moduleName) => {
230
+ const normalized = String(moduleName ?? "").trim();
231
+ if (!normalized) {
232
+ return false;
215
233
  }
216
- return {
217
- source: String(tradeFlow.source ?? ""),
218
- available: true,
219
- interval: String(tradeFlow.interval ?? ""),
220
- stale: typeof tradeFlow.stale === "boolean" ? tradeFlow.stale : null,
221
- trades: toFiniteNumber(tradeFlow.trades),
222
- buyPressurePct: toFiniteNumber(tradeFlow.buyPressurePct),
223
- netBaseDelta: toFiniteNumber(tradeFlow.netBaseDelta),
224
- netQuoteDelta: toFiniteNumber(tradeFlow.netQuoteDelta)
225
- };
234
+ if (normalized.startsWith("file://") || import_path.default.isAbsolute(normalized) || isRelativeModulePath(normalized)) {
235
+ return false;
236
+ }
237
+ return true;
226
238
  };
227
- var buildMarketBreadthContextFromSignal = (signal) => {
228
- const baseContext = toRecord(signal.additionalIndicators?.baseContext);
229
- const relative = toRecord(baseContext?.relative);
230
- const breadth = toRecord(relative?.marketBreadth);
231
- if (!breadth) {
232
- return {
233
- source: null,
234
- available: false,
235
- universe: null,
236
- interval: null,
237
- stale: null,
238
- symbolsCount: null,
239
- advanceDeclineRatio: null,
240
- pctAboveMa20: null,
241
- pctAboveMa50: null,
242
- equalWeightedReturn: null,
243
- volumeWeightedReturn: null,
244
- dispersion: null
245
- };
239
+ var importConfigFile = async (configFilePath) => {
240
+ const ext = import_path.default.extname(configFilePath).toLowerCase();
241
+ const configFileUrl = `${toImportSpecifier(configFilePath)}?t=${Date.now()}`;
242
+ if (ext === ".ts" || ext === ".mts") {
243
+ const requireFn = getRequireFn(import_path.default.dirname(configFilePath));
244
+ await ensureTsNodeRegistered();
245
+ await ensureTsconfigPathsRegistered(import_path.default.dirname(configFilePath));
246
+ return requireFn(configFilePath);
246
247
  }
247
- return {
248
- source: String(breadth.source ?? ""),
249
- available: true,
250
- universe: String(breadth.universe ?? ""),
251
- interval: String(breadth.interval ?? ""),
252
- stale: typeof breadth.stale === "boolean" ? breadth.stale : null,
253
- symbolsCount: toFiniteNumber(breadth.symbolsCount),
254
- advanceDeclineRatio: toFiniteNumber(breadth.advanceDeclineRatio),
255
- pctAboveMa20: toFiniteNumber(breadth.pctAboveMa20),
256
- pctAboveMa50: toFiniteNumber(breadth.pctAboveMa50),
257
- equalWeightedReturn: toFiniteNumber(breadth.equalWeightedReturn),
258
- volumeWeightedReturn: toFiniteNumber(breadth.volumeWeightedReturn),
259
- dispersion: toFiniteNumber(breadth.dispersion)
260
- };
248
+ return import(
249
+ /* webpackIgnore: true */
250
+ configFileUrl
251
+ );
261
252
  };
262
- var buildMarketBreadthsContextFromSignal = (signal) => {
263
- const baseContext = toRecord(signal.additionalIndicators?.baseContext);
264
- const relative = toRecord(baseContext?.relative);
265
- const breadths = toRecord(relative?.marketBreadths);
266
- const build = (key) => {
267
- const breadth = toRecord(breadths?.[key]);
268
- if (!breadth) {
269
- return {
270
- source: null,
271
- available: false,
272
- universe: null,
273
- interval: null,
274
- stale: null,
275
- symbolsCount: null,
276
- advanceDeclineRatio: null,
277
- pctAboveMa20: null,
278
- pctAboveMa50: null,
279
- equalWeightedReturn: null,
280
- volumeWeightedReturn: null,
281
- dispersion: null
282
- };
253
+ var importTradejsModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
254
+ const normalized = String(moduleName ?? "").trim();
255
+ if (!normalized) {
256
+ return {};
257
+ }
258
+ let modulePath = normalized;
259
+ if (normalized.startsWith("file://")) {
260
+ try {
261
+ modulePath = (0, import_url.fileURLToPath)(normalized);
262
+ } catch {
263
+ modulePath = normalized;
283
264
  }
284
- return {
285
- source: String(breadth.source ?? ""),
286
- available: true,
287
- universe: String(breadth.universe ?? ""),
288
- interval: String(breadth.interval ?? ""),
289
- stale: typeof breadth.stale === "boolean" ? breadth.stale : null,
290
- symbolsCount: toFiniteNumber(breadth.symbolsCount),
291
- advanceDeclineRatio: toFiniteNumber(breadth.advanceDeclineRatio),
292
- pctAboveMa20: toFiniteNumber(breadth.pctAboveMa20),
293
- pctAboveMa50: toFiniteNumber(breadth.pctAboveMa50),
294
- equalWeightedReturn: toFiniteNumber(breadth.equalWeightedReturn),
295
- volumeWeightedReturn: toFiniteNumber(breadth.volumeWeightedReturn),
296
- dispersion: toFiniteNumber(breadth.dispersion)
297
- };
298
- };
299
- return {
300
- top5: build("top5"),
301
- top10: build("top10"),
302
- top30: build("top30"),
303
- top50: build("top50"),
304
- top100: build("top100")
305
- };
306
- };
307
- var buildTargetVsBtcContextFromSignal = (signal) => {
308
- const baseContext = toRecord(signal.additionalIndicators?.baseContext);
309
- const relative = toRecord(baseContext?.relative);
310
- const targetVsBtc = toRecord(relative?.targetVsBtc);
311
- if (!targetVsBtc) {
312
- return {
313
- source: null,
314
- available: false,
315
- ratioReturn1h: null,
316
- ratioReturn4h: null,
317
- ratioReturn24h: null,
318
- alphaVsBtc1h: null,
319
- alphaVsBtc4h: null,
320
- alphaVsBtc24h: null,
321
- betaToBtc20: null,
322
- correlationToBtc20: null,
323
- ratioTrend: null
324
- };
325
265
  }
326
- return {
327
- source: String(targetVsBtc.source ?? ""),
328
- available: true,
329
- ratioReturn1h: toFiniteNumber(targetVsBtc.ratioReturn1h),
330
- ratioReturn4h: toFiniteNumber(targetVsBtc.ratioReturn4h),
331
- ratioReturn24h: toFiniteNumber(targetVsBtc.ratioReturn24h),
332
- alphaVsBtc1h: toFiniteNumber(targetVsBtc.alphaVsBtc1h),
333
- alphaVsBtc4h: toFiniteNumber(targetVsBtc.alphaVsBtc4h),
334
- alphaVsBtc24h: toFiniteNumber(targetVsBtc.alphaVsBtc24h),
335
- betaToBtc20: toFiniteNumber(targetVsBtc.betaToBtc20),
336
- correlationToBtc20: toFiniteNumber(targetVsBtc.correlationToBtc20),
337
- ratioTrend: typeof targetVsBtc.ratioTrend === "string" ? targetVsBtc.ratioTrend : null
338
- };
266
+ const requireFn = getRequireFn(
267
+ import_path.default.isAbsolute(modulePath) ? import_path.default.dirname(modulePath) : cwd
268
+ );
269
+ if (isTsModulePath(modulePath)) {
270
+ await ensureTsNodeRegistered();
271
+ await ensureTsconfigPathsRegistered(cwd);
272
+ return requireFn(modulePath);
273
+ }
274
+ if (isBareModuleSpecifier(normalized)) {
275
+ await ensureTsconfigPathsRegistered(cwd);
276
+ try {
277
+ return requireFn(normalized);
278
+ } catch (error) {
279
+ const resolvedByTsconfig = await resolveTsconfigPathModule(
280
+ normalized,
281
+ cwd
282
+ );
283
+ if (resolvedByTsconfig && resolvedByTsconfig !== normalized) {
284
+ return requireFn(resolvedByTsconfig);
285
+ }
286
+ throw error;
287
+ }
288
+ }
289
+ try {
290
+ return await import(
291
+ /* webpackIgnore: true */
292
+ toImportSpecifier(normalized)
293
+ );
294
+ } catch (error) {
295
+ if (isTsModulePath(modulePath)) {
296
+ await ensureTsNodeRegistered();
297
+ await ensureTsconfigPathsRegistered(cwd);
298
+ return requireFn(modulePath);
299
+ }
300
+ throw error;
301
+ }
339
302
  };
340
- var buildBtcAltRegimeContextFromSignal = (signal) => {
341
- const baseContext = toRecord(signal.additionalIndicators?.baseContext);
342
- const relative = toRecord(baseContext?.relative);
343
- const btcAltRegime = toRecord(relative?.btcAltRegime);
344
- if (!btcAltRegime) {
345
- return {
346
- source: null,
347
- available: false,
348
- universe: null,
349
- interval: null,
350
- stale: null,
351
- regime: null,
352
- btcReturn24h: null,
353
- altBasketReturn24h: null,
354
- btcVsAltReturn24h: null,
355
- btcTurnoverShare24h: null,
356
- btcTurnoverShareChange24h: null,
357
- altVolToBtcVol24h: null,
358
- altDispersion24h: null
359
- };
303
+ var resolveExportedConfig = (moduleExports) => {
304
+ const candidate = moduleExports && typeof moduleExports === "object" && "default" in moduleExports ? moduleExports.default : moduleExports;
305
+ return normalizeConfig(candidate);
306
+ };
307
+ var findConfigFilePath = (cwd) => {
308
+ let currentDir = import_path.default.resolve(cwd);
309
+ while (true) {
310
+ for (const fileName of CONFIG_FILE_NAMES) {
311
+ const fullPath = import_path.default.join(currentDir, fileName);
312
+ if (import_fs.default.existsSync(fullPath) && import_fs.default.statSync(fullPath).isFile()) {
313
+ return fullPath;
314
+ }
315
+ }
316
+ const parentDir = import_path.default.dirname(currentDir);
317
+ if (parentDir === currentDir) {
318
+ return null;
319
+ }
320
+ currentDir = parentDir;
360
321
  }
361
- return {
362
- source: String(btcAltRegime.source ?? ""),
363
- available: true,
364
- universe: String(btcAltRegime.universe ?? ""),
365
- interval: String(btcAltRegime.interval ?? ""),
366
- stale: typeof btcAltRegime.stale === "boolean" ? btcAltRegime.stale : null,
367
- regime: typeof btcAltRegime.regime === "string" ? btcAltRegime.regime : null,
368
- btcReturn24h: toFiniteNumber(btcAltRegime.btcReturn24h),
369
- altBasketReturn24h: toFiniteNumber(btcAltRegime.altBasketReturn24h),
370
- btcVsAltReturn24h: toFiniteNumber(btcAltRegime.btcVsAltReturn24h),
371
- btcTurnoverShare24h: toFiniteNumber(btcAltRegime.btcTurnoverShare24h),
372
- btcTurnoverShareChange24h: toFiniteNumber(
373
- btcAltRegime.btcTurnoverShareChange24h
374
- ),
375
- altVolToBtcVol24h: toFiniteNumber(btcAltRegime.altVolToBtcVol24h),
376
- altDispersion24h: toFiniteNumber(btcAltRegime.altDispersion24h)
377
- };
378
322
  };
379
- var buildCmcGlobalContextFromSignal = (signal) => {
380
- const baseContext = toRecord(signal.additionalIndicators?.baseContext);
381
- const relative = toRecord(baseContext?.relative);
382
- const cmcGlobal = toRecord(relative?.cmcGlobal);
383
- if (!cmcGlobal) {
384
- return {
385
- source: null,
386
- available: false,
387
- interval: null,
388
- asOfTs: null,
389
- stale: null,
390
- totalMarketCapUsd: null,
391
- totalVolumeUsd: null,
392
- totalVolumeReportedUsd: null,
393
- altMarketCapUsd: null,
394
- altVolumeUsd: null,
395
- altVolumeReportedUsd: null,
396
- btcDominancePct: null,
397
- ethDominancePct: null,
398
- btcDominanceChange24hPct: null,
399
- ethDominanceChange24hPct: null,
400
- altMarketCapChange24hPct: null,
401
- altVolumeChange24hPct: null,
402
- activeCryptocurrencies: null,
403
- activeExchanges: null,
404
- activeMarketPairs: null,
405
- altLiquidityRegime: null
406
- };
323
+ var resolvePluginModuleSpecifier = (moduleName, cwd = getTradejsProjectCwd()) => {
324
+ const normalized = String(moduleName ?? "").trim();
325
+ if (!normalized) {
326
+ return "";
407
327
  }
408
- return {
409
- source: String(cmcGlobal.source ?? ""),
410
- available: true,
411
- interval: typeof cmcGlobal.interval === "string" ? cmcGlobal.interval : null,
412
- asOfTs: toFiniteNumber(cmcGlobal.asOfTs),
413
- stale: typeof cmcGlobal.stale === "boolean" ? cmcGlobal.stale : null,
414
- totalMarketCapUsd: toFiniteNumber(cmcGlobal.totalMarketCapUsd),
415
- totalVolumeUsd: toFiniteNumber(cmcGlobal.totalVolumeUsd),
416
- totalVolumeReportedUsd: toFiniteNumber(cmcGlobal.totalVolumeReportedUsd),
417
- altMarketCapUsd: toFiniteNumber(cmcGlobal.altMarketCapUsd),
418
- altVolumeUsd: toFiniteNumber(cmcGlobal.altVolumeUsd),
419
- altVolumeReportedUsd: toFiniteNumber(cmcGlobal.altVolumeReportedUsd),
420
- btcDominancePct: toFiniteNumber(cmcGlobal.btcDominancePct),
421
- ethDominancePct: toFiniteNumber(cmcGlobal.ethDominancePct),
422
- btcDominanceChange24hPct: toFiniteNumber(
423
- cmcGlobal.btcDominanceChange24hPct
424
- ),
425
- ethDominanceChange24hPct: toFiniteNumber(
426
- cmcGlobal.ethDominanceChange24hPct
427
- ),
428
- altMarketCapChange24hPct: toFiniteNumber(
429
- cmcGlobal.altMarketCapChange24hPct
430
- ),
431
- altVolumeChange24hPct: toFiniteNumber(cmcGlobal.altVolumeChange24hPct),
432
- activeCryptocurrencies: toFiniteNumber(cmcGlobal.activeCryptocurrencies),
433
- activeExchanges: toFiniteNumber(cmcGlobal.activeExchanges),
434
- activeMarketPairs: toFiniteNumber(cmcGlobal.activeMarketPairs),
435
- altLiquidityRegime: typeof cmcGlobal.altLiquidityRegime === "string" ? cmcGlobal.altLiquidityRegime : null
436
- };
328
+ if (normalized.startsWith("file://")) {
329
+ try {
330
+ return (0, import_url.fileURLToPath)(normalized);
331
+ } catch {
332
+ return normalized;
333
+ }
334
+ }
335
+ if (import_path.default.isAbsolute(normalized)) {
336
+ return normalized;
337
+ }
338
+ if (isRelativeModulePath(normalized)) {
339
+ return import_path.default.resolve(cwd, normalized);
340
+ }
341
+ return normalized;
437
342
  };
438
- var buildCmcReferenceAssetsContextFromSignal = (signal) => {
343
+ var loadTradejsConfig = async (cwd = getTradejsProjectCwd()) => {
344
+ const cached = cachedByCwd.get(cwd);
345
+ if (cached) {
346
+ return cached;
347
+ }
348
+ const configFilePath = findConfigFilePath(cwd);
349
+ if (!configFilePath) {
350
+ cachedByCwd.set(cwd, {});
351
+ return {};
352
+ }
353
+ try {
354
+ const moduleExports = await importConfigFile(configFilePath);
355
+ const config = resolveExportedConfig(moduleExports);
356
+ cachedByCwd.set(cwd, config);
357
+ if (!announcedConfigFile.has(configFilePath)) {
358
+ announcedConfigFile.add(configFilePath);
359
+ import_logger.logger.log("debug", "Loaded TradeJS config: %s", configFilePath);
360
+ }
361
+ return config;
362
+ } catch (error) {
363
+ import_logger.logger.log(
364
+ "warn",
365
+ "Failed to load TradeJS config from %s: %s",
366
+ configFilePath,
367
+ String(error)
368
+ );
369
+ cachedByCwd.set(cwd, {});
370
+ return {};
371
+ }
372
+ };
373
+
374
+ // src/strategyAdapters/ai.ts
375
+ var import_aiFeatures = require("@tradejs/core/aiFeatures");
376
+
377
+ // src/aiShared.ts
378
+ var MAX_AI_SERIES_POINTS = 5;
379
+ var COMPACT_INDICATORS_SNAPSHOT_SYMBOL = /* @__PURE__ */ Symbol.for(
380
+ "tradejs.indicators.compactSnapshot"
381
+ );
382
+ var COMPACT_INDICATORS_SNAPSHOT_KEY = "__tradejsCompactIndicatorsSnapshot";
383
+ var trimSeriesDeep = (value) => {
384
+ if (Array.isArray(value)) {
385
+ const trimmed = value.slice(-MAX_AI_SERIES_POINTS);
386
+ const isMatrix = trimmed.every((item) => Array.isArray(item));
387
+ if (isMatrix) {
388
+ return trimmed;
389
+ }
390
+ return trimmed.map(
391
+ (item) => item && typeof item === "object" ? trimSeriesDeep(item) : item
392
+ );
393
+ }
394
+ if (value && typeof value === "object") {
395
+ return Object.fromEntries(
396
+ Object.entries(value).map(([key, nested]) => [
397
+ key,
398
+ trimSeriesDeep(nested)
399
+ ])
400
+ );
401
+ }
402
+ return value;
403
+ };
404
+ var buildCompactAiIndicatorsSnapshot = (value) => {
405
+ const compactSnapshot = value && typeof value === "object" ? value[COMPACT_INDICATORS_SNAPSHOT_SYMBOL] ?? value[COMPACT_INDICATORS_SNAPSHOT_KEY] : void 0;
406
+ if (typeof compactSnapshot === "function") {
407
+ return compactSnapshot({ limit: MAX_AI_SERIES_POINTS });
408
+ }
409
+ return trimSeriesDeep(value);
410
+ };
411
+
412
+ // src/aiMarketContext.ts
413
+ var toRecord = (value) => {
414
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
415
+ return null;
416
+ }
417
+ return value;
418
+ };
419
+ var toFiniteNumber = (value) => {
420
+ const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
421
+ return Number.isFinite(numeric) ? numeric : null;
422
+ };
423
+ var roundTo = (value, decimals) => {
424
+ const factor = 10 ** decimals;
425
+ return Math.round(value * factor) / factor;
426
+ };
427
+ var buildMissingSpreadContext = () => ({
428
+ source: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
429
+ indicatorKey: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
430
+ available: false,
431
+ value: null,
432
+ zScore: null,
433
+ bps: null,
434
+ absBps: null,
435
+ bias: null,
436
+ severity: null
437
+ });
438
+ var buildSpreadContextFromSignal = (signal) => {
439
439
  const baseContext = toRecord(signal.additionalIndicators?.baseContext);
440
440
  const relative = toRecord(baseContext?.relative);
441
- const cmcReferenceAssets = toRecord(relative?.cmcReferenceAssets);
442
- if (!cmcReferenceAssets) {
443
- return {
444
- source: null,
445
- available: false,
446
- interval: null,
447
- asOfTs: null,
448
- stale: null,
449
- btcMarketCapUsd: null,
450
- ethMarketCapUsd: null,
451
- btcVolumeUsd: null,
452
- ethVolumeUsd: null,
453
- btcVolumeToMarketCap: null,
454
- ethVolumeToMarketCap: null,
455
- ethBtcMarketCapRatio: null,
456
- ethBtcMarketCapRatioChange24hPct: null,
457
- ethVsBtcVolumeRatio: null,
458
- referenceLiquidityRegime: null
459
- };
441
+ const execution = toRecord(relative?.execution);
442
+ const spread = toFiniteNumber(execution?.venueSpread);
443
+ const zScore = toFiniteNumber(execution?.venueSpreadZScore);
444
+ if (spread == null) {
445
+ return buildMissingSpreadContext();
460
446
  }
447
+ const value = roundTo(spread, 8);
448
+ const bps = roundTo(value * 1e4, 2);
449
+ const absBps = Math.abs(bps);
450
+ const bias = absBps < 1 ? "flat" : bps > 0 ? "coinbase_premium" : "binance_premium";
451
+ const severity = absBps >= 20 ? "wide" : absBps >= 5 ? "elevated" : "normal";
461
452
  return {
462
- source: String(cmcReferenceAssets.source ?? ""),
453
+ source: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
454
+ indicatorKey: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
463
455
  available: true,
464
- interval: typeof cmcReferenceAssets.interval === "string" ? cmcReferenceAssets.interval : null,
465
- asOfTs: toFiniteNumber(cmcReferenceAssets.asOfTs),
466
- stale: typeof cmcReferenceAssets.stale === "boolean" ? cmcReferenceAssets.stale : null,
467
- btcMarketCapUsd: toFiniteNumber(cmcReferenceAssets.btcMarketCapUsd),
468
- ethMarketCapUsd: toFiniteNumber(cmcReferenceAssets.ethMarketCapUsd),
469
- btcVolumeUsd: toFiniteNumber(cmcReferenceAssets.btcVolumeUsd),
470
- ethVolumeUsd: toFiniteNumber(cmcReferenceAssets.ethVolumeUsd),
471
- btcVolumeToMarketCap: toFiniteNumber(
472
- cmcReferenceAssets.btcVolumeToMarketCap
473
- ),
474
- ethVolumeToMarketCap: toFiniteNumber(
475
- cmcReferenceAssets.ethVolumeToMarketCap
476
- ),
477
- ethBtcMarketCapRatio: toFiniteNumber(
478
- cmcReferenceAssets.ethBtcMarketCapRatio
479
- ),
480
- ethBtcMarketCapRatioChange24hPct: toFiniteNumber(
481
- cmcReferenceAssets.ethBtcMarketCapRatioChange24hPct
482
- ),
483
- ethVsBtcVolumeRatio: toFiniteNumber(cmcReferenceAssets.ethVsBtcVolumeRatio),
484
- referenceLiquidityRegime: typeof cmcReferenceAssets.referenceLiquidityRegime === "string" ? cmcReferenceAssets.referenceLiquidityRegime : null
456
+ value,
457
+ zScore,
458
+ bps,
459
+ absBps,
460
+ bias,
461
+ severity
485
462
  };
486
463
  };
487
- var buildCmcExchangeLiquidityContextFromSignal = (signal) => {
464
+ var buildTrueDeltaContextFromSignal = (signal) => {
488
465
  const baseContext = toRecord(signal.additionalIndicators?.baseContext);
489
- const relative = toRecord(baseContext?.relative);
490
- const cmcExchangeLiquidity = toRecord(relative?.cmcExchangeLiquidity);
491
- if (!cmcExchangeLiquidity) {
466
+ const participation = toRecord(baseContext?.participation);
467
+ const delta = toRecord(participation?.delta);
468
+ const source = String(delta?.source ?? "");
469
+ const isTrueDeltaSource = source === "kline_taker_volume" || source === "agg_trades" || source === "trades";
470
+ if (!delta || !isTrueDeltaSource) {
492
471
  return {
493
- source: null,
472
+ source: source || null,
494
473
  available: false,
495
- interval: null,
496
- asOfTs: null,
497
- stale: null,
498
- exchangesCount: null,
499
- totalVolumeUsd: null,
500
- totalVolumeChange24hPct: null,
501
- binanceVolumeUsd: null,
502
- binanceVolumeShare: null,
503
- topExchangeVolumeShare: null,
504
- liquidityRegime: null
474
+ buyPressurePct: null,
475
+ buyVolume: null,
476
+ sellVolume: null,
477
+ netDelta: null,
478
+ deltaPct: null,
479
+ signedVolumeZScore: null
505
480
  };
506
481
  }
507
482
  return {
508
- source: String(cmcExchangeLiquidity.source ?? ""),
483
+ source,
509
484
  available: true,
510
- interval: typeof cmcExchangeLiquidity.interval === "string" ? cmcExchangeLiquidity.interval : null,
511
- asOfTs: toFiniteNumber(cmcExchangeLiquidity.asOfTs),
512
- stale: typeof cmcExchangeLiquidity.stale === "boolean" ? cmcExchangeLiquidity.stale : null,
513
- exchangesCount: toFiniteNumber(cmcExchangeLiquidity.exchangesCount),
514
- totalVolumeUsd: toFiniteNumber(cmcExchangeLiquidity.totalVolumeUsd),
515
- totalVolumeChange24hPct: toFiniteNumber(
516
- cmcExchangeLiquidity.totalVolumeChange24hPct
517
- ),
518
- binanceVolumeUsd: toFiniteNumber(cmcExchangeLiquidity.binanceVolumeUsd),
519
- binanceVolumeShare: toFiniteNumber(cmcExchangeLiquidity.binanceVolumeShare),
520
- topExchangeVolumeShare: toFiniteNumber(
521
- cmcExchangeLiquidity.topExchangeVolumeShare
522
- ),
523
- liquidityRegime: typeof cmcExchangeLiquidity.liquidityRegime === "string" ? cmcExchangeLiquidity.liquidityRegime : null
485
+ buyPressurePct: toFiniteNumber(delta.buyPressurePct),
486
+ buyVolume: toFiniteNumber(delta.buyVolume),
487
+ sellVolume: toFiniteNumber(delta.sellVolume),
488
+ netDelta: toFiniteNumber(delta.netDelta),
489
+ deltaPct: toFiniteNumber(delta.deltaPct),
490
+ signedVolumeZScore: toFiniteNumber(delta.signedVolumeZScore)
524
491
  };
525
492
  };
526
- var buildCmcFearGreedContextFromSignal = (signal) => {
493
+ var buildTradeFlowContextFromSignal = (signal) => {
527
494
  const baseContext = toRecord(signal.additionalIndicators?.baseContext);
528
- const relative = toRecord(baseContext?.relative);
529
- const cmcFearGreed = toRecord(relative?.cmcFearGreed);
530
- if (!cmcFearGreed) {
495
+ const participation = toRecord(baseContext?.participation);
496
+ const tradeFlow = toRecord(participation?.tradeFlow);
497
+ if (!tradeFlow) {
531
498
  return {
532
499
  source: null,
533
500
  available: false,
534
501
  interval: null,
535
- asOfTs: null,
536
502
  stale: null,
537
- value: null,
538
- valueChange24h: null,
539
- valueChange7d: null,
540
- classification: null,
541
- sentimentRegime: null
503
+ trades: null,
504
+ buyPressurePct: null,
505
+ netBaseDelta: null,
506
+ netQuoteDelta: null
542
507
  };
543
508
  }
544
509
  return {
545
- source: String(cmcFearGreed.source ?? ""),
510
+ source: String(tradeFlow.source ?? ""),
546
511
  available: true,
547
- interval: typeof cmcFearGreed.interval === "string" ? cmcFearGreed.interval : null,
548
- asOfTs: toFiniteNumber(cmcFearGreed.asOfTs),
549
- stale: typeof cmcFearGreed.stale === "boolean" ? cmcFearGreed.stale : null,
550
- value: toFiniteNumber(cmcFearGreed.value),
551
- valueChange24h: toFiniteNumber(cmcFearGreed.valueChange24h),
552
- valueChange7d: toFiniteNumber(cmcFearGreed.valueChange7d),
553
- classification: typeof cmcFearGreed.classification === "string" ? cmcFearGreed.classification : null,
554
- sentimentRegime: typeof cmcFearGreed.sentimentRegime === "string" ? cmcFearGreed.sentimentRegime : null
512
+ interval: String(tradeFlow.interval ?? ""),
513
+ stale: typeof tradeFlow.stale === "boolean" ? tradeFlow.stale : null,
514
+ trades: toFiniteNumber(tradeFlow.trades),
515
+ buyPressurePct: toFiniteNumber(tradeFlow.buyPressurePct),
516
+ netBaseDelta: toFiniteNumber(tradeFlow.netBaseDelta),
517
+ netQuoteDelta: toFiniteNumber(tradeFlow.netQuoteDelta)
555
518
  };
556
519
  };
557
- var buildCmcIndexesContextFromSignal = (signal) => {
520
+ var buildMarketBreadthContextFromSignal = (signal) => {
558
521
  const baseContext = toRecord(signal.additionalIndicators?.baseContext);
559
522
  const relative = toRecord(baseContext?.relative);
560
- const cmcIndexes = toRecord(relative?.cmcIndexes);
561
- if (!cmcIndexes) {
523
+ const breadth = toRecord(relative?.marketBreadth);
524
+ if (!breadth) {
562
525
  return {
563
526
  source: null,
564
527
  available: false,
528
+ universe: null,
565
529
  interval: null,
566
- asOfTs: null,
567
530
  stale: null,
568
- cmc100Value: null,
569
- cmc100Change24hPct: null,
570
- cmc100TopConstituentSymbol: null,
571
- cmc100TopConstituentWeightPct: null,
572
- cmc20Value: null,
573
- cmc20Change24hPct: null,
574
- cmc20TopConstituentSymbol: null,
575
- cmc20TopConstituentWeightPct: null,
576
- cmc20ToCmc100Ratio: null,
577
- cmc20ToCmc100RatioChange24hPct: null,
578
- indexRegime: null
531
+ symbolsCount: null,
532
+ advanceDeclineRatio: null,
533
+ pctAboveMa20: null,
534
+ pctAboveMa50: null,
535
+ equalWeightedReturn: null,
536
+ volumeWeightedReturn: null,
537
+ dispersion: null
579
538
  };
580
539
  }
581
540
  return {
582
- source: String(cmcIndexes.source ?? ""),
541
+ source: String(breadth.source ?? ""),
583
542
  available: true,
584
- interval: typeof cmcIndexes.interval === "string" ? cmcIndexes.interval : null,
585
- asOfTs: toFiniteNumber(cmcIndexes.asOfTs),
586
- stale: typeof cmcIndexes.stale === "boolean" ? cmcIndexes.stale : null,
587
- cmc100Value: toFiniteNumber(cmcIndexes.cmc100Value),
588
- cmc100Change24hPct: toFiniteNumber(cmcIndexes.cmc100Change24hPct),
589
- cmc100TopConstituentSymbol: typeof cmcIndexes.cmc100TopConstituentSymbol === "string" ? cmcIndexes.cmc100TopConstituentSymbol : null,
590
- cmc100TopConstituentWeightPct: toFiniteNumber(
591
- cmcIndexes.cmc100TopConstituentWeightPct
592
- ),
593
- cmc20Value: toFiniteNumber(cmcIndexes.cmc20Value),
594
- cmc20Change24hPct: toFiniteNumber(cmcIndexes.cmc20Change24hPct),
595
- cmc20TopConstituentSymbol: typeof cmcIndexes.cmc20TopConstituentSymbol === "string" ? cmcIndexes.cmc20TopConstituentSymbol : null,
596
- cmc20TopConstituentWeightPct: toFiniteNumber(
597
- cmcIndexes.cmc20TopConstituentWeightPct
598
- ),
599
- cmc20ToCmc100Ratio: toFiniteNumber(cmcIndexes.cmc20ToCmc100Ratio),
600
- cmc20ToCmc100RatioChange24hPct: toFiniteNumber(
601
- cmcIndexes.cmc20ToCmc100RatioChange24hPct
602
- ),
603
- indexRegime: typeof cmcIndexes.indexRegime === "string" ? cmcIndexes.indexRegime : null
543
+ universe: String(breadth.universe ?? ""),
544
+ interval: String(breadth.interval ?? ""),
545
+ stale: typeof breadth.stale === "boolean" ? breadth.stale : null,
546
+ symbolsCount: toFiniteNumber(breadth.symbolsCount),
547
+ advanceDeclineRatio: toFiniteNumber(breadth.advanceDeclineRatio),
548
+ pctAboveMa20: toFiniteNumber(breadth.pctAboveMa20),
549
+ pctAboveMa50: toFiniteNumber(breadth.pctAboveMa50),
550
+ equalWeightedReturn: toFiniteNumber(breadth.equalWeightedReturn),
551
+ volumeWeightedReturn: toFiniteNumber(breadth.volumeWeightedReturn),
552
+ dispersion: toFiniteNumber(breadth.dispersion)
604
553
  };
605
554
  };
606
- var buildReferenceTradeFlowContextFromSignal = (signal) => {
555
+ var buildMarketBreadthsContextFromSignal = (signal) => {
607
556
  const baseContext = toRecord(signal.additionalIndicators?.baseContext);
608
557
  const relative = toRecord(baseContext?.relative);
609
- const refs = toRecord(relative?.referenceTradeFlow);
610
- const primaryReferenceSymbol = typeof refs?.primaryReferenceSymbol === "string" ? refs.primaryReferenceSymbol : null;
611
- const tradeFlowBySymbol = toRecord(refs?.tradeFlowBySymbol);
612
- const primaryTradeFlow = primaryReferenceSymbol != null ? toRecord(tradeFlowBySymbol?.[primaryReferenceSymbol]) : null;
613
- if (!refs) {
558
+ const breadths = toRecord(relative?.marketBreadths);
559
+ const build = (key) => {
560
+ const breadth = toRecord(breadths?.[key]);
561
+ if (!breadth) {
562
+ return {
563
+ source: null,
564
+ available: false,
565
+ universe: null,
566
+ interval: null,
567
+ stale: null,
568
+ symbolsCount: null,
569
+ advanceDeclineRatio: null,
570
+ pctAboveMa20: null,
571
+ pctAboveMa50: null,
572
+ equalWeightedReturn: null,
573
+ volumeWeightedReturn: null,
574
+ dispersion: null
575
+ };
576
+ }
614
577
  return {
615
- source: null,
616
- available: false,
617
- primaryReferenceSymbol: null,
618
- referenceSymbols: [],
619
- primaryTradeFlowBuyPressurePct: null,
620
- primaryTradeFlowStale: null
578
+ source: String(breadth.source ?? ""),
579
+ available: true,
580
+ universe: String(breadth.universe ?? ""),
581
+ interval: String(breadth.interval ?? ""),
582
+ stale: typeof breadth.stale === "boolean" ? breadth.stale : null,
583
+ symbolsCount: toFiniteNumber(breadth.symbolsCount),
584
+ advanceDeclineRatio: toFiniteNumber(breadth.advanceDeclineRatio),
585
+ pctAboveMa20: toFiniteNumber(breadth.pctAboveMa20),
586
+ pctAboveMa50: toFiniteNumber(breadth.pctAboveMa50),
587
+ equalWeightedReturn: toFiniteNumber(breadth.equalWeightedReturn),
588
+ volumeWeightedReturn: toFiniteNumber(breadth.volumeWeightedReturn),
589
+ dispersion: toFiniteNumber(breadth.dispersion)
621
590
  };
622
- }
591
+ };
623
592
  return {
624
- source: String(refs.source ?? ""),
625
- available: true,
626
- primaryReferenceSymbol,
627
- referenceSymbols: Array.isArray(refs.referenceSymbols) ? refs.referenceSymbols.map(String) : [],
628
- primaryTradeFlowBuyPressurePct: toFiniteNumber(
629
- primaryTradeFlow?.buyPressurePct
630
- ),
631
- primaryTradeFlowStale: typeof primaryTradeFlow?.stale === "boolean" ? primaryTradeFlow.stale : null
593
+ top5: build("top5"),
594
+ top10: build("top10"),
595
+ top30: build("top30"),
596
+ top50: build("top50"),
597
+ top100: build("top100")
632
598
  };
633
599
  };
634
- var buildAiMarketContext = (signal) => ({
635
- execution: {
636
- binanceCoinbaseSpread: buildSpreadContextFromSignal(signal)
637
- },
638
- participation: {
639
- trueDelta: buildTrueDeltaContextFromSignal(signal),
640
- tradeFlow: buildTradeFlowContextFromSignal(signal)
641
- },
642
- relative: {
643
- marketBreadth: buildMarketBreadthContextFromSignal(signal),
644
- marketBreadths: buildMarketBreadthsContextFromSignal(signal),
645
- targetVsBtc: buildTargetVsBtcContextFromSignal(signal),
646
- btcAltRegime: buildBtcAltRegimeContextFromSignal(signal),
647
- cmcGlobal: buildCmcGlobalContextFromSignal(signal),
648
- cmcReferenceAssets: buildCmcReferenceAssetsContextFromSignal(signal),
649
- cmcExchangeLiquidity: buildCmcExchangeLiquidityContextFromSignal(signal),
650
- cmcFearGreed: buildCmcFearGreedContextFromSignal(signal),
651
- cmcIndexes: buildCmcIndexesContextFromSignal(signal),
652
- referenceTradeFlow: buildReferenceTradeFlowContextFromSignal(signal)
653
- }
654
- });
655
-
656
- // src/strategy/manifests.ts
657
- var import_indicators = require("@tradejs/core/indicators");
658
-
659
- // src/tradejsConfig.ts
660
- var import_fs = __toESM(require("fs"));
661
- var import_path = __toESM(require("path"));
662
- var import_url = require("url");
663
- var import_config = require("@tradejs/core/config");
664
- var import_logger = require("@tradejs/infra/logger");
665
- var CONFIG_FILE_NAMES = [
666
- "tradejs.config.ts",
667
- "tradejs.config.mts",
668
- "tradejs.config.js",
669
- "tradejs.config.mjs",
670
- "tradejs.config.cjs"
671
- ];
672
- var TS_MODULE_RE = /\.(cts|mts|ts)$/i;
673
- var cachedByCwd = /* @__PURE__ */ new Map();
674
- var announcedConfigFile = /* @__PURE__ */ new Set();
675
- var tsNodeRegistered = false;
676
- var tsconfigPathsRegisteredByCwd = /* @__PURE__ */ new Set();
677
- var tsconfigPathMatchersByCwd = /* @__PURE__ */ new Map();
678
- var getTradejsProjectCwd = (cwd) => {
679
- const explicit = String(cwd ?? "").trim();
680
- if (explicit) {
681
- return import_path.default.resolve(explicit);
682
- }
683
- const fromEnv = String(process.env.PROJECT_CWD || "").trim();
684
- if (fromEnv) {
685
- return import_path.default.resolve(fromEnv);
686
- }
687
- return process.cwd();
688
- };
689
- var normalizeConfig = (rawConfig) => {
690
- if (!rawConfig || typeof rawConfig !== "object") {
691
- return {};
692
- }
693
- const config = rawConfig;
694
- const strategies2 = Array.isArray(config.strategies) ? config.strategies.map((value) => String(value || "").trim()).filter(Boolean) : [];
695
- const indicators = Array.isArray(config.indicators) ? config.indicators.map((value) => String(value || "").trim()).filter(Boolean) : [];
696
- const connectors = Array.isArray(config.connectors) ? config.connectors.map((value) => String(value || "").trim()).filter(Boolean) : [];
697
- const hooks = (0, import_config.normalizeTradejsConfigHooks)(
698
- config.hooks
699
- );
700
- const runtime = config.runtime && typeof config.runtime === "object" && !Array.isArray(config.runtime) ? config.runtime : void 0;
600
+ var buildTargetVsReference = (signal, asset) => {
601
+ const base = toRecord(signal.additionalIndicators?.baseContext);
602
+ const relative = toRecord(base?.relative);
603
+ const target = toRecord(relative?.[`targetVs${asset}`]);
701
604
  return {
702
- strategies: strategies2,
703
- indicators,
704
- connectors,
705
- ...hooks ? { hooks } : {},
706
- ...runtime ? { runtime } : {}
605
+ source: target ? String(target.source ?? "") : null,
606
+ available: Boolean(target),
607
+ ratioReturn1h: toFiniteNumber(target?.ratioReturn1h),
608
+ ratioReturn4h: toFiniteNumber(target?.ratioReturn4h),
609
+ ratioReturn24h: toFiniteNumber(target?.ratioReturn24h),
610
+ alpha1h: toFiniteNumber(target?.[`alphaVs${asset}1h`]),
611
+ alpha4h: toFiniteNumber(target?.[`alphaVs${asset}4h`]),
612
+ alpha24h: toFiniteNumber(target?.[`alphaVs${asset}24h`]),
613
+ beta: toFiniteNumber(target?.[`betaTo${asset}20`]),
614
+ correlation: toFiniteNumber(target?.[`correlationTo${asset}20`]),
615
+ ratioTrend: typeof target?.ratioTrend === "string" ? target.ratioTrend : null
707
616
  };
708
617
  };
709
- var getNodeCreateRequire = () => {
710
- const builtinModule = process.getBuiltinModule?.("module");
711
- if (typeof builtinModule?.createRequire === "function") {
712
- return builtinModule.createRequire;
713
- }
714
- throw new TypeError("module.createRequire is not available");
715
- };
716
- var getRequireFn = (cwd = getTradejsProjectCwd()) => getNodeCreateRequire()(import_path.default.join(import_path.default.resolve(cwd), "__tradejs_loader__.js"));
717
- var ensureTsNodeRegistered = async () => {
718
- if (tsNodeRegistered) {
719
- return;
720
- }
721
- const tsNodeModule = await import("ts-node");
722
- const tsNode = tsNodeModule.default ?? tsNodeModule;
723
- tsNode.register?.({
724
- transpileOnly: true,
725
- compilerOptions: {
726
- module: "Node16",
727
- moduleResolution: "node16"
728
- }
729
- });
730
- tsNodeRegistered = true;
731
- };
732
- var ensureTsconfigPathsRegistered = async (cwd = getTradejsProjectCwd()) => {
733
- const projectRoot = getTradejsProjectCwd(cwd);
734
- if (tsconfigPathsRegisteredByCwd.has(projectRoot)) {
735
- return;
736
- }
737
- const tsconfigPathsModule = await import("tsconfig-paths");
738
- const loadConfig = tsconfigPathsModule.loadConfig;
739
- const register = tsconfigPathsModule.register;
740
- if (typeof loadConfig !== "function" || typeof register !== "function") {
741
- return;
742
- }
743
- const loadedConfig = loadConfig(projectRoot);
744
- if (loadedConfig.resultType !== "success") {
745
- return;
746
- }
747
- register({
748
- baseUrl: loadedConfig.absoluteBaseUrl,
749
- paths: loadedConfig.paths,
750
- addMatchAll: false
751
- });
752
- tsconfigPathsRegisteredByCwd.add(projectRoot);
753
- };
754
- var resolveTsconfigPathModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
755
- const projectRoot = getTradejsProjectCwd(cwd);
756
- const cachedMatcher = tsconfigPathMatchersByCwd.get(projectRoot);
757
- if (cachedMatcher) {
758
- const resolved2 = cachedMatcher(moduleName);
759
- return resolved2 || null;
760
- }
761
- const tsconfigPathsModule = await import("tsconfig-paths");
762
- const loadConfig = tsconfigPathsModule.loadConfig;
763
- const createMatchPath = tsconfigPathsModule.createMatchPath;
764
- if (typeof loadConfig !== "function" || typeof createMatchPath !== "function") {
765
- return null;
766
- }
767
- const loadedConfig = loadConfig(projectRoot);
768
- if (loadedConfig.resultType !== "success") {
769
- return null;
770
- }
771
- const matchPath = createMatchPath(
772
- loadedConfig.absoluteBaseUrl,
773
- loadedConfig.paths
774
- );
775
- const matcher = (requestedModule) => matchPath(requestedModule, void 0, import_fs.default.existsSync, [
776
- ".ts",
777
- ".tsx",
778
- ".mts",
779
- ".cts",
780
- ".js",
781
- ".jsx",
782
- ".mjs",
783
- ".cjs",
784
- ".json"
785
- ]) || "";
786
- tsconfigPathMatchersByCwd.set(projectRoot, matcher);
787
- const resolved = matcher(moduleName);
788
- return resolved || null;
789
- };
790
- var toImportSpecifier = (moduleName) => {
791
- if (moduleName.startsWith("file://")) {
792
- return moduleName;
793
- }
794
- if (import_path.default.isAbsolute(moduleName)) {
795
- return (0, import_url.pathToFileURL)(moduleName).href;
796
- }
797
- return moduleName;
618
+ var buildTargetVsBtcContextFromSignal = (signal) => {
619
+ const { alpha1h, alpha4h, alpha24h, beta, correlation, ...shared } = buildTargetVsReference(signal, "Btc");
620
+ return {
621
+ ...shared,
622
+ alphaVsBtc1h: alpha1h,
623
+ alphaVsBtc4h: alpha4h,
624
+ alphaVsBtc24h: alpha24h,
625
+ betaToBtc20: beta,
626
+ correlationToBtc20: correlation
627
+ };
798
628
  };
799
- var isTsModulePath = (moduleName) => TS_MODULE_RE.test(moduleName.split("?")[0]);
800
- var isRelativeModulePath = (moduleName) => moduleName.startsWith("./") || moduleName.startsWith("../");
801
- var isBareModuleSpecifier = (moduleName) => {
802
- const normalized = String(moduleName ?? "").trim();
803
- if (!normalized) {
804
- return false;
805
- }
806
- if (normalized.startsWith("file://") || import_path.default.isAbsolute(normalized) || isRelativeModulePath(normalized)) {
807
- return false;
808
- }
809
- return true;
629
+ var buildTargetVsEthContextFromSignal = (signal) => {
630
+ const { alpha1h, alpha4h, alpha24h, beta, correlation, ...shared } = buildTargetVsReference(signal, "Eth");
631
+ return {
632
+ ...shared,
633
+ alphaVsEth1h: alpha1h,
634
+ alphaVsEth4h: alpha4h,
635
+ alphaVsEth24h: alpha24h,
636
+ betaToEth20: beta,
637
+ correlationToEth20: correlation
638
+ };
810
639
  };
811
- var importConfigFile = async (configFilePath) => {
812
- const ext = import_path.default.extname(configFilePath).toLowerCase();
813
- const configFileUrl = `${toImportSpecifier(configFilePath)}?t=${Date.now()}`;
814
- if (ext === ".ts" || ext === ".mts") {
815
- const requireFn = getRequireFn(import_path.default.dirname(configFilePath));
816
- await ensureTsNodeRegistered();
817
- await ensureTsconfigPathsRegistered(import_path.default.dirname(configFilePath));
818
- return requireFn(configFilePath);
640
+ var buildBtcAltRegimeContextFromSignal = (signal) => {
641
+ const baseContext = toRecord(signal.additionalIndicators?.baseContext);
642
+ const relative = toRecord(baseContext?.relative);
643
+ const btcAltRegime = toRecord(relative?.btcAltRegime);
644
+ if (!btcAltRegime) {
645
+ return {
646
+ source: null,
647
+ available: false,
648
+ universe: null,
649
+ interval: null,
650
+ stale: null,
651
+ regime: null,
652
+ btcReturn24h: null,
653
+ altBasketReturn24h: null,
654
+ btcVsAltReturn24h: null,
655
+ btcTurnoverShare24h: null,
656
+ btcTurnoverShareChange24h: null,
657
+ altVolToBtcVol24h: null,
658
+ altDispersion24h: null
659
+ };
819
660
  }
820
- return import(
821
- /* webpackIgnore: true */
822
- configFileUrl
823
- );
661
+ return {
662
+ source: String(btcAltRegime.source ?? ""),
663
+ available: true,
664
+ universe: String(btcAltRegime.universe ?? ""),
665
+ interval: String(btcAltRegime.interval ?? ""),
666
+ stale: typeof btcAltRegime.stale === "boolean" ? btcAltRegime.stale : null,
667
+ regime: typeof btcAltRegime.regime === "string" ? btcAltRegime.regime : null,
668
+ btcReturn24h: toFiniteNumber(btcAltRegime.btcReturn24h),
669
+ altBasketReturn24h: toFiniteNumber(btcAltRegime.altBasketReturn24h),
670
+ btcVsAltReturn24h: toFiniteNumber(btcAltRegime.btcVsAltReturn24h),
671
+ btcTurnoverShare24h: toFiniteNumber(btcAltRegime.btcTurnoverShare24h),
672
+ btcTurnoverShareChange24h: toFiniteNumber(
673
+ btcAltRegime.btcTurnoverShareChange24h
674
+ ),
675
+ altVolToBtcVol24h: toFiniteNumber(btcAltRegime.altVolToBtcVol24h),
676
+ altDispersion24h: toFiniteNumber(btcAltRegime.altDispersion24h)
677
+ };
824
678
  };
825
- var importTradejsModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
826
- const normalized = String(moduleName ?? "").trim();
827
- if (!normalized) {
828
- return {};
829
- }
830
- let modulePath = normalized;
831
- if (normalized.startsWith("file://")) {
832
- try {
833
- modulePath = (0, import_url.fileURLToPath)(normalized);
834
- } catch {
835
- modulePath = normalized;
836
- }
837
- }
838
- const requireFn = getRequireFn(
839
- import_path.default.isAbsolute(modulePath) ? import_path.default.dirname(modulePath) : cwd
840
- );
841
- if (isTsModulePath(modulePath)) {
842
- await ensureTsNodeRegistered();
843
- await ensureTsconfigPathsRegistered(cwd);
844
- return requireFn(modulePath);
845
- }
846
- if (isBareModuleSpecifier(normalized)) {
847
- await ensureTsconfigPathsRegistered(cwd);
848
- try {
849
- return requireFn(normalized);
850
- } catch (error) {
851
- const resolvedByTsconfig = await resolveTsconfigPathModule(
852
- normalized,
853
- cwd
854
- );
855
- if (resolvedByTsconfig && resolvedByTsconfig !== normalized) {
856
- return requireFn(resolvedByTsconfig);
857
- }
858
- throw error;
859
- }
860
- }
861
- try {
862
- return await import(
863
- /* webpackIgnore: true */
864
- toImportSpecifier(normalized)
865
- );
866
- } catch (error) {
867
- if (isTsModulePath(modulePath)) {
868
- await ensureTsNodeRegistered();
869
- await ensureTsconfigPathsRegistered(cwd);
870
- return requireFn(modulePath);
871
- }
872
- throw error;
679
+ var buildCmcGlobalContextFromSignal = (signal) => {
680
+ const baseContext = toRecord(signal.additionalIndicators?.baseContext);
681
+ const relative = toRecord(baseContext?.relative);
682
+ const cmcGlobal = toRecord(relative?.cmcGlobal);
683
+ if (!cmcGlobal) {
684
+ return {
685
+ source: null,
686
+ available: false,
687
+ interval: null,
688
+ asOfTs: null,
689
+ stale: null,
690
+ totalMarketCapUsd: null,
691
+ totalVolumeUsd: null,
692
+ totalVolumeReportedUsd: null,
693
+ altMarketCapUsd: null,
694
+ altVolumeUsd: null,
695
+ altVolumeReportedUsd: null,
696
+ btcDominancePct: null,
697
+ ethDominancePct: null,
698
+ btcDominanceChange24hPct: null,
699
+ ethDominanceChange24hPct: null,
700
+ altMarketCapChange24hPct: null,
701
+ altVolumeChange24hPct: null,
702
+ activeCryptocurrencies: null,
703
+ activeExchanges: null,
704
+ activeMarketPairs: null,
705
+ altLiquidityRegime: null
706
+ };
873
707
  }
708
+ return {
709
+ source: String(cmcGlobal.source ?? ""),
710
+ available: true,
711
+ interval: typeof cmcGlobal.interval === "string" ? cmcGlobal.interval : null,
712
+ asOfTs: toFiniteNumber(cmcGlobal.asOfTs),
713
+ stale: typeof cmcGlobal.stale === "boolean" ? cmcGlobal.stale : null,
714
+ totalMarketCapUsd: toFiniteNumber(cmcGlobal.totalMarketCapUsd),
715
+ totalVolumeUsd: toFiniteNumber(cmcGlobal.totalVolumeUsd),
716
+ totalVolumeReportedUsd: toFiniteNumber(cmcGlobal.totalVolumeReportedUsd),
717
+ altMarketCapUsd: toFiniteNumber(cmcGlobal.altMarketCapUsd),
718
+ altVolumeUsd: toFiniteNumber(cmcGlobal.altVolumeUsd),
719
+ altVolumeReportedUsd: toFiniteNumber(cmcGlobal.altVolumeReportedUsd),
720
+ btcDominancePct: toFiniteNumber(cmcGlobal.btcDominancePct),
721
+ ethDominancePct: toFiniteNumber(cmcGlobal.ethDominancePct),
722
+ btcDominanceChange24hPct: toFiniteNumber(
723
+ cmcGlobal.btcDominanceChange24hPct
724
+ ),
725
+ ethDominanceChange24hPct: toFiniteNumber(
726
+ cmcGlobal.ethDominanceChange24hPct
727
+ ),
728
+ altMarketCapChange24hPct: toFiniteNumber(
729
+ cmcGlobal.altMarketCapChange24hPct
730
+ ),
731
+ altVolumeChange24hPct: toFiniteNumber(cmcGlobal.altVolumeChange24hPct),
732
+ activeCryptocurrencies: toFiniteNumber(cmcGlobal.activeCryptocurrencies),
733
+ activeExchanges: toFiniteNumber(cmcGlobal.activeExchanges),
734
+ activeMarketPairs: toFiniteNumber(cmcGlobal.activeMarketPairs),
735
+ altLiquidityRegime: typeof cmcGlobal.altLiquidityRegime === "string" ? cmcGlobal.altLiquidityRegime : null
736
+ };
874
737
  };
875
- var resolveExportedConfig = (moduleExports) => {
876
- const candidate = moduleExports && typeof moduleExports === "object" && "default" in moduleExports ? moduleExports.default : moduleExports;
877
- return normalizeConfig(candidate);
878
- };
879
- var findConfigFilePath = (cwd) => {
880
- let currentDir = import_path.default.resolve(cwd);
881
- while (true) {
882
- for (const fileName of CONFIG_FILE_NAMES) {
883
- const fullPath = import_path.default.join(currentDir, fileName);
884
- if (import_fs.default.existsSync(fullPath) && import_fs.default.statSync(fullPath).isFile()) {
885
- return fullPath;
886
- }
887
- }
888
- const parentDir = import_path.default.dirname(currentDir);
889
- if (parentDir === currentDir) {
890
- return null;
891
- }
892
- currentDir = parentDir;
738
+ var buildCmcReferenceAssetsContextFromSignal = (signal) => {
739
+ const baseContext = toRecord(signal.additionalIndicators?.baseContext);
740
+ const relative = toRecord(baseContext?.relative);
741
+ const cmcReferenceAssets = toRecord(relative?.cmcReferenceAssets);
742
+ if (!cmcReferenceAssets) {
743
+ return {
744
+ source: null,
745
+ available: false,
746
+ interval: null,
747
+ asOfTs: null,
748
+ stale: null,
749
+ btcMarketCapUsd: null,
750
+ ethMarketCapUsd: null,
751
+ btcVolumeUsd: null,
752
+ ethVolumeUsd: null,
753
+ btcVolumeToMarketCap: null,
754
+ ethVolumeToMarketCap: null,
755
+ ethBtcMarketCapRatio: null,
756
+ ethBtcMarketCapRatioChange24hPct: null,
757
+ ethVsBtcVolumeRatio: null,
758
+ referenceLiquidityRegime: null
759
+ };
893
760
  }
761
+ return {
762
+ source: String(cmcReferenceAssets.source ?? ""),
763
+ available: true,
764
+ interval: typeof cmcReferenceAssets.interval === "string" ? cmcReferenceAssets.interval : null,
765
+ asOfTs: toFiniteNumber(cmcReferenceAssets.asOfTs),
766
+ stale: typeof cmcReferenceAssets.stale === "boolean" ? cmcReferenceAssets.stale : null,
767
+ btcMarketCapUsd: toFiniteNumber(cmcReferenceAssets.btcMarketCapUsd),
768
+ ethMarketCapUsd: toFiniteNumber(cmcReferenceAssets.ethMarketCapUsd),
769
+ btcVolumeUsd: toFiniteNumber(cmcReferenceAssets.btcVolumeUsd),
770
+ ethVolumeUsd: toFiniteNumber(cmcReferenceAssets.ethVolumeUsd),
771
+ btcVolumeToMarketCap: toFiniteNumber(
772
+ cmcReferenceAssets.btcVolumeToMarketCap
773
+ ),
774
+ ethVolumeToMarketCap: toFiniteNumber(
775
+ cmcReferenceAssets.ethVolumeToMarketCap
776
+ ),
777
+ ethBtcMarketCapRatio: toFiniteNumber(
778
+ cmcReferenceAssets.ethBtcMarketCapRatio
779
+ ),
780
+ ethBtcMarketCapRatioChange24hPct: toFiniteNumber(
781
+ cmcReferenceAssets.ethBtcMarketCapRatioChange24hPct
782
+ ),
783
+ ethVsBtcVolumeRatio: toFiniteNumber(cmcReferenceAssets.ethVsBtcVolumeRatio),
784
+ referenceLiquidityRegime: typeof cmcReferenceAssets.referenceLiquidityRegime === "string" ? cmcReferenceAssets.referenceLiquidityRegime : null
785
+ };
894
786
  };
895
- var resolvePluginModuleSpecifier = (moduleName, cwd = getTradejsProjectCwd()) => {
896
- const normalized = String(moduleName ?? "").trim();
897
- if (!normalized) {
898
- return "";
899
- }
900
- if (normalized.startsWith("file://")) {
901
- try {
902
- return (0, import_url.fileURLToPath)(normalized);
903
- } catch {
904
- return normalized;
905
- }
906
- }
907
- if (import_path.default.isAbsolute(normalized)) {
908
- return normalized;
909
- }
910
- if (isRelativeModulePath(normalized)) {
911
- return import_path.default.resolve(cwd, normalized);
787
+ var buildCmcExchangeLiquidityContextFromSignal = (signal) => {
788
+ const baseContext = toRecord(signal.additionalIndicators?.baseContext);
789
+ const relative = toRecord(baseContext?.relative);
790
+ const cmcExchangeLiquidity = toRecord(relative?.cmcExchangeLiquidity);
791
+ if (!cmcExchangeLiquidity) {
792
+ return {
793
+ source: null,
794
+ available: false,
795
+ interval: null,
796
+ asOfTs: null,
797
+ stale: null,
798
+ exchangesCount: null,
799
+ totalVolumeUsd: null,
800
+ totalVolumeChange24hPct: null,
801
+ binanceVolumeUsd: null,
802
+ binanceVolumeShare: null,
803
+ topExchangeVolumeShare: null,
804
+ liquidityRegime: null
805
+ };
912
806
  }
913
- return normalized;
807
+ return {
808
+ source: String(cmcExchangeLiquidity.source ?? ""),
809
+ available: true,
810
+ interval: typeof cmcExchangeLiquidity.interval === "string" ? cmcExchangeLiquidity.interval : null,
811
+ asOfTs: toFiniteNumber(cmcExchangeLiquidity.asOfTs),
812
+ stale: typeof cmcExchangeLiquidity.stale === "boolean" ? cmcExchangeLiquidity.stale : null,
813
+ exchangesCount: toFiniteNumber(cmcExchangeLiquidity.exchangesCount),
814
+ totalVolumeUsd: toFiniteNumber(cmcExchangeLiquidity.totalVolumeUsd),
815
+ totalVolumeChange24hPct: toFiniteNumber(
816
+ cmcExchangeLiquidity.totalVolumeChange24hPct
817
+ ),
818
+ binanceVolumeUsd: toFiniteNumber(cmcExchangeLiquidity.binanceVolumeUsd),
819
+ binanceVolumeShare: toFiniteNumber(cmcExchangeLiquidity.binanceVolumeShare),
820
+ topExchangeVolumeShare: toFiniteNumber(
821
+ cmcExchangeLiquidity.topExchangeVolumeShare
822
+ ),
823
+ liquidityRegime: typeof cmcExchangeLiquidity.liquidityRegime === "string" ? cmcExchangeLiquidity.liquidityRegime : null
824
+ };
914
825
  };
915
- var loadTradejsConfig = async (cwd = getTradejsProjectCwd()) => {
916
- const cached = cachedByCwd.get(cwd);
917
- if (cached) {
918
- return cached;
826
+ var buildCmcFearGreedContextFromSignal = (signal) => {
827
+ const baseContext = toRecord(signal.additionalIndicators?.baseContext);
828
+ const relative = toRecord(baseContext?.relative);
829
+ const cmcFearGreed = toRecord(relative?.cmcFearGreed);
830
+ if (!cmcFearGreed) {
831
+ return {
832
+ source: null,
833
+ available: false,
834
+ interval: null,
835
+ asOfTs: null,
836
+ stale: null,
837
+ value: null,
838
+ valueChange24h: null,
839
+ valueChange7d: null,
840
+ classification: null,
841
+ sentimentRegime: null
842
+ };
919
843
  }
920
- const configFilePath = findConfigFilePath(cwd);
921
- if (!configFilePath) {
922
- cachedByCwd.set(cwd, {});
923
- return {};
844
+ return {
845
+ source: String(cmcFearGreed.source ?? ""),
846
+ available: true,
847
+ interval: typeof cmcFearGreed.interval === "string" ? cmcFearGreed.interval : null,
848
+ asOfTs: toFiniteNumber(cmcFearGreed.asOfTs),
849
+ stale: typeof cmcFearGreed.stale === "boolean" ? cmcFearGreed.stale : null,
850
+ value: toFiniteNumber(cmcFearGreed.value),
851
+ valueChange24h: toFiniteNumber(cmcFearGreed.valueChange24h),
852
+ valueChange7d: toFiniteNumber(cmcFearGreed.valueChange7d),
853
+ classification: typeof cmcFearGreed.classification === "string" ? cmcFearGreed.classification : null,
854
+ sentimentRegime: typeof cmcFearGreed.sentimentRegime === "string" ? cmcFearGreed.sentimentRegime : null
855
+ };
856
+ };
857
+ var buildCmcIndexesContextFromSignal = (signal) => {
858
+ const baseContext = toRecord(signal.additionalIndicators?.baseContext);
859
+ const relative = toRecord(baseContext?.relative);
860
+ const cmcIndexes = toRecord(relative?.cmcIndexes);
861
+ if (!cmcIndexes) {
862
+ return {
863
+ source: null,
864
+ available: false,
865
+ interval: null,
866
+ asOfTs: null,
867
+ stale: null,
868
+ cmc100Value: null,
869
+ cmc100Change24hPct: null,
870
+ cmc100TopConstituentSymbol: null,
871
+ cmc100TopConstituentWeightPct: null,
872
+ cmc20Value: null,
873
+ cmc20Change24hPct: null,
874
+ cmc20TopConstituentSymbol: null,
875
+ cmc20TopConstituentWeightPct: null,
876
+ cmc20ToCmc100Ratio: null,
877
+ cmc20ToCmc100RatioChange24hPct: null,
878
+ indexRegime: null
879
+ };
924
880
  }
925
- try {
926
- const moduleExports = await importConfigFile(configFilePath);
927
- const config = resolveExportedConfig(moduleExports);
928
- cachedByCwd.set(cwd, config);
929
- if (!announcedConfigFile.has(configFilePath)) {
930
- announcedConfigFile.add(configFilePath);
931
- import_logger.logger.log("debug", "Loaded TradeJS config: %s", configFilePath);
932
- }
933
- return config;
934
- } catch (error) {
935
- import_logger.logger.log(
936
- "warn",
937
- "Failed to load TradeJS config from %s: %s",
938
- configFilePath,
939
- String(error)
940
- );
941
- cachedByCwd.set(cwd, {});
942
- return {};
881
+ return {
882
+ source: String(cmcIndexes.source ?? ""),
883
+ available: true,
884
+ interval: typeof cmcIndexes.interval === "string" ? cmcIndexes.interval : null,
885
+ asOfTs: toFiniteNumber(cmcIndexes.asOfTs),
886
+ stale: typeof cmcIndexes.stale === "boolean" ? cmcIndexes.stale : null,
887
+ cmc100Value: toFiniteNumber(cmcIndexes.cmc100Value),
888
+ cmc100Change24hPct: toFiniteNumber(cmcIndexes.cmc100Change24hPct),
889
+ cmc100TopConstituentSymbol: typeof cmcIndexes.cmc100TopConstituentSymbol === "string" ? cmcIndexes.cmc100TopConstituentSymbol : null,
890
+ cmc100TopConstituentWeightPct: toFiniteNumber(
891
+ cmcIndexes.cmc100TopConstituentWeightPct
892
+ ),
893
+ cmc20Value: toFiniteNumber(cmcIndexes.cmc20Value),
894
+ cmc20Change24hPct: toFiniteNumber(cmcIndexes.cmc20Change24hPct),
895
+ cmc20TopConstituentSymbol: typeof cmcIndexes.cmc20TopConstituentSymbol === "string" ? cmcIndexes.cmc20TopConstituentSymbol : null,
896
+ cmc20TopConstituentWeightPct: toFiniteNumber(
897
+ cmcIndexes.cmc20TopConstituentWeightPct
898
+ ),
899
+ cmc20ToCmc100Ratio: toFiniteNumber(cmcIndexes.cmc20ToCmc100Ratio),
900
+ cmc20ToCmc100RatioChange24hPct: toFiniteNumber(
901
+ cmcIndexes.cmc20ToCmc100RatioChange24hPct
902
+ ),
903
+ indexRegime: typeof cmcIndexes.indexRegime === "string" ? cmcIndexes.indexRegime : null
904
+ };
905
+ };
906
+ var buildReferenceTradeFlowContextFromSignal = (signal) => {
907
+ const baseContext = toRecord(signal.additionalIndicators?.baseContext);
908
+ const relative = toRecord(baseContext?.relative);
909
+ const refs = toRecord(relative?.referenceTradeFlow);
910
+ const primaryReferenceSymbol = typeof refs?.primaryReferenceSymbol === "string" ? refs.primaryReferenceSymbol : null;
911
+ const tradeFlowBySymbol = toRecord(refs?.tradeFlowBySymbol);
912
+ const primaryTradeFlow = primaryReferenceSymbol != null ? toRecord(tradeFlowBySymbol?.[primaryReferenceSymbol]) : null;
913
+ if (!refs) {
914
+ return {
915
+ source: null,
916
+ available: false,
917
+ primaryReferenceSymbol: null,
918
+ referenceSymbols: [],
919
+ primaryTradeFlowBuyPressurePct: null,
920
+ primaryTradeFlowStale: null
921
+ };
943
922
  }
923
+ return {
924
+ source: String(refs.source ?? ""),
925
+ available: true,
926
+ primaryReferenceSymbol,
927
+ referenceSymbols: Array.isArray(refs.referenceSymbols) ? refs.referenceSymbols.map(String) : [],
928
+ primaryTradeFlowBuyPressurePct: toFiniteNumber(
929
+ primaryTradeFlow?.buyPressurePct
930
+ ),
931
+ primaryTradeFlowStale: typeof primaryTradeFlow?.stale === "boolean" ? primaryTradeFlow.stale : null
932
+ };
944
933
  };
934
+ var buildAiMarketContext = (signal) => ({
935
+ execution: {
936
+ binanceCoinbaseSpread: buildSpreadContextFromSignal(signal)
937
+ },
938
+ participation: {
939
+ trueDelta: buildTrueDeltaContextFromSignal(signal),
940
+ tradeFlow: buildTradeFlowContextFromSignal(signal)
941
+ },
942
+ relative: {
943
+ marketBreadth: buildMarketBreadthContextFromSignal(signal),
944
+ marketBreadths: buildMarketBreadthsContextFromSignal(signal),
945
+ targetVsBtc: buildTargetVsBtcContextFromSignal(signal),
946
+ targetVsEth: buildTargetVsEthContextFromSignal(signal),
947
+ btcAltRegime: buildBtcAltRegimeContextFromSignal(signal),
948
+ cmcGlobal: buildCmcGlobalContextFromSignal(signal),
949
+ cmcReferenceAssets: buildCmcReferenceAssetsContextFromSignal(signal),
950
+ cmcExchangeLiquidity: buildCmcExchangeLiquidityContextFromSignal(signal),
951
+ cmcFearGreed: buildCmcFearGreedContextFromSignal(signal),
952
+ cmcIndexes: buildCmcIndexesContextFromSignal(signal),
953
+ referenceTradeFlow: buildReferenceTradeFlowContextFromSignal(signal)
954
+ }
955
+ });
945
956
 
946
957
  // src/strategy/manifests.ts
958
+ var import_indicators = require("@tradejs/core/indicators");
947
959
  var SHARED_STRATEGY_REGISTRY_KEY = "__tradejsNodeSharedStrategyRegistryV1__";
948
960
  var sharedRegistryScope = globalThis;
949
961
  var sharedStrategyRegistry = sharedRegistryScope[SHARED_STRATEGY_REGISTRY_KEY] ?? (sharedRegistryScope[SHARED_STRATEGY_REGISTRY_KEY] = {
@@ -1211,12 +1223,61 @@ var buildBaseAiPayload = (signal) => {
1211
1223
  };
1212
1224
  };
1213
1225
  var defaultAiAdapter = {};
1226
+ var tickerProfileFeatureIds = new Set(
1227
+ import_aiFeatures.TICKER_PROFILE_AI_FEATURES.map(({ id }) => id)
1228
+ );
1214
1229
  var getStrategyAiAdapter = (strategy, profileId) => getStrategyProfileAiAdapter(getStrategyManifest(strategy), profileId) ?? defaultAiAdapter;
1215
1230
  var getSignalAiAdapter = (signal) => getStrategyAiAdapter(signal.strategy, signal.policyProfileId);
1216
1231
  var buildAiPayloadByStrategy = (signal) => {
1217
1232
  const basePayload = buildBaseAiPayload(signal);
1218
1233
  const adapter = getSignalAiAdapter(signal);
1219
- return adapter.buildPayload?.({ signal, basePayload }) ?? basePayload;
1234
+ const payload = adapter.buildPayload?.({ signal, basePayload }) ?? basePayload;
1235
+ const custom = getStrategyManifest(signal.strategy)?.aiFeatures ?? [];
1236
+ const frozen = signal.aiFeatureReplay?.snapshot;
1237
+ if (frozen) (0, import_aiFeatures.validateAiFeatureSnapshot)(frozen);
1238
+ const tickerProfile = signal.additionalIndicators?.tickerProfile ?? signal.additionalIndicators?.baseContext?.tickerProfile;
1239
+ const hasProfile = Boolean(
1240
+ tickerProfile || frozen?.definitions.some(({ id }) => tickerProfileFeatureIds.has(id))
1241
+ );
1242
+ const indicatorFeatures = (0, import_indicators2.getRegisteredIndicatorEntries)(
1243
+ getTradejsProjectCwd()
1244
+ ).flatMap((entry) => entry.indicator.enabled ? entry.aiFeatures ?? [] : []);
1245
+ const hasPortfolio = Boolean(
1246
+ signal.additionalIndicators?.portfolioContext || frozen?.definitions.some(
1247
+ ({ id }) => import_aiFeatures.PORTFOLIO_AI_FEATURES.some((d) => d.id === id)
1248
+ )
1249
+ );
1250
+ const definitions = [
1251
+ ...hasPortfolio ? import_aiFeatures.PORTFOLIO_AI_FEATURES : [],
1252
+ ...hasProfile ? import_aiFeatures.TICKER_PROFILE_AI_FEATURES : [],
1253
+ ...indicatorFeatures,
1254
+ ...custom
1255
+ ];
1256
+ if (!definitions.length && !signal.aiFeatureReplay?.snapshot) return payload;
1257
+ (0, import_aiFeatures.validateAiFeatureDefinitions)(definitions);
1258
+ if (signal.aiFeatureReplay && !frozen) {
1259
+ throw new Error(
1260
+ "Dataset has no frozen AI features; regenerate the decision-time export"
1261
+ );
1262
+ }
1263
+ if (frozen && JSON.stringify(frozen.definitions) !== JSON.stringify(definitions)) {
1264
+ throw new Error(
1265
+ "AI feature definition changed; regenerate the decision-time export"
1266
+ );
1267
+ }
1268
+ return {
1269
+ ...payload,
1270
+ featureSnapshot: frozen ?? (0, import_aiFeatures.projectAiFeatures)(definitions, {
1271
+ signal: tickerProfile ? {
1272
+ ...signal,
1273
+ additionalIndicators: {
1274
+ ...signal.additionalIndicators,
1275
+ tickerProfile
1276
+ }
1277
+ } : signal,
1278
+ payload
1279
+ })
1280
+ };
1220
1281
  };
1221
1282
  var buildAiSystemPromptAddonByStrategy = (signal) => getSignalAiAdapter(signal).buildSystemPromptAddon?.({ signal }) ?? "";
1222
1283
  var buildAiHumanPromptAddonByStrategy = (signal, payload) => getSignalAiAdapter(signal).buildHumanPromptAddon?.({