@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.
- package/dist/ai.js +1 -0
- package/dist/ai.mjs +1 -1
- package/dist/backtest.d.mts +3 -1
- package/dist/backtest.d.ts +3 -1
- package/dist/backtest.js +287 -39
- package/dist/backtest.mjs +30 -16
- package/dist/{chunk-NQO2MFP6.mjs → chunk-K2YQ3MQC.mjs} +58 -10
- package/dist/{chunk-FB5NUEOQ.mjs → chunk-OFVUEYUR.mjs} +4 -1
- package/dist/{chunk-IKRSNRP6.mjs → chunk-UITD6LG6.mjs} +83 -4
- package/dist/{chunk-XHNJ757K.mjs → chunk-VQSGH7YA.mjs} +341 -22
- package/dist/cli.js +2 -1
- package/dist/cli.mjs +1 -1
- package/dist/{registry-DHTLjQcr.d.mts → registry-3TsO1Ff1.d.mts} +2 -2
- package/dist/{registry-DHTLjQcr.d.ts → registry-3TsO1Ff1.d.ts} +2 -2
- package/dist/registry.d.mts +1 -1
- package/dist/registry.d.ts +1 -1
- package/dist/registry.js +260 -23
- package/dist/registry.mjs +2 -2
- package/dist/runtimeDashboard.js +378 -37
- package/dist/runtimeDashboard.mjs +54 -8
- package/dist/runtimeStrategies.d.mts +4 -1
- package/dist/runtimeStrategies.d.ts +4 -1
- package/dist/runtimeStrategies.js +327 -34
- package/dist/runtimeStrategies.mjs +3 -3
- package/dist/runtimeTrades.d.mts +2 -1
- package/dist/runtimeTrades.d.ts +2 -1
- package/dist/runtimeTrades.js +4 -1
- package/dist/runtimeTrades.mjs +1 -1
- package/dist/strategies.d.mts +14 -2
- package/dist/strategies.d.ts +14 -2
- package/dist/strategies.js +417 -24
- package/dist/strategies.mjs +4 -2
- package/package.json +5 -5
package/dist/ai.js
CHANGED
|
@@ -652,6 +652,7 @@ var createStrategyRegistryState = () => ({
|
|
|
652
652
|
strategyManifestsMap: /* @__PURE__ */ new Map(),
|
|
653
653
|
strategyEntriesMap: /* @__PURE__ */ new Map(),
|
|
654
654
|
strategySourcesMap: /* @__PURE__ */ new Map(),
|
|
655
|
+
strategyModuleResolutions: /* @__PURE__ */ new Map(),
|
|
655
656
|
pluginsLoadPromise: null
|
|
656
657
|
});
|
|
657
658
|
var registryStateByProjectRoot = sharedStrategyRegistry.registryStateByProjectRoot;
|
package/dist/ai.mjs
CHANGED
package/dist/backtest.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from '@tradejs/core/backtest';
|
|
2
|
-
import { Test, TestingBox, TestingBoxResult, TestConnectorCreator, Connector, StrategyConfig, InstrumentDescriptor, ExecutionCostModel, FundingRatePoint } from '@tradejs/types';
|
|
2
|
+
import { Test, TestingBox, TestingBoxResult, TestConnectorCreator, Connector, StrategyConfig, BacktestExecutionCosts, InstrumentDescriptor, ExecutionCostModel, FundingRatePoint } from '@tradejs/types';
|
|
3
3
|
|
|
4
4
|
type TestingGroupResult = {
|
|
5
5
|
test: Test;
|
|
@@ -22,6 +22,8 @@ declare const resolveExecutionCosts: (params: {
|
|
|
22
22
|
connector: Connector;
|
|
23
23
|
symbol: string;
|
|
24
24
|
config: StrategyConfig;
|
|
25
|
+
executionCosts?: BacktestExecutionCosts;
|
|
26
|
+
cacheOnly?: boolean;
|
|
25
27
|
startTime: number;
|
|
26
28
|
endTime: number;
|
|
27
29
|
instrument?: InstrumentDescriptor;
|
package/dist/backtest.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from '@tradejs/core/backtest';
|
|
2
|
-
import { Test, TestingBox, TestingBoxResult, TestConnectorCreator, Connector, StrategyConfig, InstrumentDescriptor, ExecutionCostModel, FundingRatePoint } from '@tradejs/types';
|
|
2
|
+
import { Test, TestingBox, TestingBoxResult, TestConnectorCreator, Connector, StrategyConfig, BacktestExecutionCosts, InstrumentDescriptor, ExecutionCostModel, FundingRatePoint } from '@tradejs/types';
|
|
3
3
|
|
|
4
4
|
type TestingGroupResult = {
|
|
5
5
|
test: Test;
|
|
@@ -22,6 +22,8 @@ declare const resolveExecutionCosts: (params: {
|
|
|
22
22
|
connector: Connector;
|
|
23
23
|
symbol: string;
|
|
24
24
|
config: StrategyConfig;
|
|
25
|
+
executionCosts?: BacktestExecutionCosts;
|
|
26
|
+
cacheOnly?: boolean;
|
|
25
27
|
startTime: number;
|
|
26
28
|
endTime: number;
|
|
27
29
|
instrument?: InstrumentDescriptor;
|
package/dist/backtest.js
CHANGED
|
@@ -936,6 +936,7 @@ var createStrategyRegistryState = () => ({
|
|
|
936
936
|
strategyManifestsMap: /* @__PURE__ */ new Map(),
|
|
937
937
|
strategyEntriesMap: /* @__PURE__ */ new Map(),
|
|
938
938
|
strategySourcesMap: /* @__PURE__ */ new Map(),
|
|
939
|
+
strategyModuleResolutions: /* @__PURE__ */ new Map(),
|
|
939
940
|
pluginsLoadPromise: null
|
|
940
941
|
});
|
|
941
942
|
var registryStateByProjectRoot = sharedStrategyRegistry.registryStateByProjectRoot;
|
|
@@ -997,7 +998,7 @@ var validateStrategyEntries = (moduleName, entries, state) => {
|
|
|
997
998
|
const strategyName = entry?.manifest?.name;
|
|
998
999
|
if (typeof strategyName !== "string" || !strategyName.trim()) {
|
|
999
1000
|
issues.push(`${entryPath}: manifest.name is required`);
|
|
1000
|
-
} else if (names.has(strategyName) || state
|
|
1001
|
+
} else if (names.has(strategyName) || state?.strategyEntriesMap.has(strategyName)) {
|
|
1001
1002
|
issues.push(`${entryPath}: duplicate strategy ${strategyName}`);
|
|
1002
1003
|
} else {
|
|
1003
1004
|
names.add(strategyName);
|
|
@@ -1059,6 +1060,72 @@ var importStrategyPluginModule = async (moduleName, cwd = getTradejsProjectCwd()
|
|
|
1059
1060
|
moduleName
|
|
1060
1061
|
);
|
|
1061
1062
|
};
|
|
1063
|
+
var resolveStrategyModuleEntry = async ({
|
|
1064
|
+
strategyName,
|
|
1065
|
+
moduleName,
|
|
1066
|
+
cwd
|
|
1067
|
+
}) => {
|
|
1068
|
+
const { projectRoot, state } = getStrategyRegistryState(cwd);
|
|
1069
|
+
const source = moduleName.trim();
|
|
1070
|
+
const resolvedModuleName = resolvePluginModuleSpecifier(source, projectRoot);
|
|
1071
|
+
const cacheKey = `${resolvedModuleName}\0${strategyName}`;
|
|
1072
|
+
const cached = state.strategyModuleResolutions.get(cacheKey);
|
|
1073
|
+
if (cached) return cached;
|
|
1074
|
+
const resolution = (async () => {
|
|
1075
|
+
const moduleExport = await importStrategyPluginModule(
|
|
1076
|
+
resolvedModuleName,
|
|
1077
|
+
projectRoot
|
|
1078
|
+
);
|
|
1079
|
+
const pluginDefinition = extractStrategyPluginDefinition(moduleExport);
|
|
1080
|
+
if (!pluginDefinition) {
|
|
1081
|
+
throw new Error(`${source}: export { strategyEntries } is missing`);
|
|
1082
|
+
}
|
|
1083
|
+
const issues = validateStrategyEntries(
|
|
1084
|
+
source,
|
|
1085
|
+
pluginDefinition.strategyEntries
|
|
1086
|
+
);
|
|
1087
|
+
if (issues.length > 0) {
|
|
1088
|
+
throw new Error(
|
|
1089
|
+
["Invalid TradeJS strategy module:", ...issues].join("\n")
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
const entry = pluginDefinition.strategyEntries.find(
|
|
1093
|
+
(candidate) => candidate.manifest.name === strategyName
|
|
1094
|
+
);
|
|
1095
|
+
if (!entry) {
|
|
1096
|
+
throw new Error(`${source}: strategy ${strategyName} is missing`);
|
|
1097
|
+
}
|
|
1098
|
+
const factory = sharedStrategyRegistry.strategyRuntimeFactory;
|
|
1099
|
+
if (!factory) {
|
|
1100
|
+
throw new Error("Strategy runtime factory is not configured");
|
|
1101
|
+
}
|
|
1102
|
+
const manifestsByName = new Map(
|
|
1103
|
+
pluginDefinition.strategyEntries.map(
|
|
1104
|
+
(candidate) => [candidate.manifest.name, candidate.manifest]
|
|
1105
|
+
)
|
|
1106
|
+
);
|
|
1107
|
+
return {
|
|
1108
|
+
entry,
|
|
1109
|
+
creator: factory({
|
|
1110
|
+
strategyName,
|
|
1111
|
+
defaults: entry.defaults,
|
|
1112
|
+
createCore: entry.createCore,
|
|
1113
|
+
manifest: entry.manifest,
|
|
1114
|
+
detectorKey: entry.detectorKey,
|
|
1115
|
+
detectorNoSignalSkipReason: entry.detectorNoSignalSkipReason,
|
|
1116
|
+
resolveRegisteredManifest: (name) => manifestsByName.get(name) ?? state.strategyManifestsMap.get(name)
|
|
1117
|
+
}),
|
|
1118
|
+
source
|
|
1119
|
+
};
|
|
1120
|
+
})();
|
|
1121
|
+
state.strategyModuleResolutions.set(cacheKey, resolution);
|
|
1122
|
+
try {
|
|
1123
|
+
return await resolution;
|
|
1124
|
+
} catch (error) {
|
|
1125
|
+
state.strategyModuleResolutions.delete(cacheKey);
|
|
1126
|
+
throw error;
|
|
1127
|
+
}
|
|
1128
|
+
};
|
|
1062
1129
|
var ensureStrategyPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
|
|
1063
1130
|
const { projectRoot, state } = getStrategyRegistryState(cwd);
|
|
1064
1131
|
if (!state.pluginsLoadPromise) {
|
|
@@ -1136,7 +1203,11 @@ var ensureStrategyPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
|
|
|
1136
1203
|
}
|
|
1137
1204
|
await state.pluginsLoadPromise;
|
|
1138
1205
|
};
|
|
1139
|
-
var getStrategyCreator = async (name, cwd = getTradejsProjectCwd()) => {
|
|
1206
|
+
var getStrategyCreator = async (name, cwd = getTradejsProjectCwd(), moduleName) => {
|
|
1207
|
+
if (moduleName?.trim()) {
|
|
1208
|
+
await ensureStrategyPluginsLoaded(cwd);
|
|
1209
|
+
return (await resolveStrategyModuleEntry({ strategyName: name, moduleName, cwd })).creator;
|
|
1210
|
+
}
|
|
1140
1211
|
await ensureStrategyPluginsLoaded(cwd);
|
|
1141
1212
|
const { state } = getStrategyRegistryState(cwd);
|
|
1142
1213
|
return state.strategyCreators.get(name);
|
|
@@ -2355,9 +2426,40 @@ var DEFAULT_MAX_AGE_BY_INTERVAL = {
|
|
|
2355
2426
|
"15m": 30 * 6e4,
|
|
2356
2427
|
"1h": 2 * 60 * 6e4
|
|
2357
2428
|
};
|
|
2429
|
+
var MARKET_CONTEXT_PRELOAD_CHUNK_MS = 7 * 24 * 60 * 6e4;
|
|
2358
2430
|
var binanceMarketContextUnavailable = false;
|
|
2359
2431
|
var referenceRowsCache = /* @__PURE__ */ new Map();
|
|
2360
2432
|
var breadthCache = /* @__PURE__ */ new Map();
|
|
2433
|
+
var BREADTH_COMMON_NUMERIC_FIELDS = [
|
|
2434
|
+
"symbolsCount",
|
|
2435
|
+
"advancers",
|
|
2436
|
+
"decliners",
|
|
2437
|
+
"unchanged",
|
|
2438
|
+
"advanceDeclineRatio",
|
|
2439
|
+
"pctAboveMa20",
|
|
2440
|
+
"pctAboveMa50",
|
|
2441
|
+
"equalWeightedReturn",
|
|
2442
|
+
"volumeWeightedReturn",
|
|
2443
|
+
"dispersion"
|
|
2444
|
+
];
|
|
2445
|
+
var BREADTH_REGIME_NUMERIC_FIELDS = [
|
|
2446
|
+
"btcReturn1h",
|
|
2447
|
+
"btcReturn4h",
|
|
2448
|
+
"btcReturn24h",
|
|
2449
|
+
"altBasketReturn1h",
|
|
2450
|
+
"altBasketReturn4h",
|
|
2451
|
+
"altBasketReturn24h",
|
|
2452
|
+
"btcVsAltReturn1h",
|
|
2453
|
+
"btcVsAltReturn4h",
|
|
2454
|
+
"btcVsAltReturn24h",
|
|
2455
|
+
"btcTurnoverShare1h",
|
|
2456
|
+
"btcTurnoverShare24h",
|
|
2457
|
+
"btcTurnoverShareChange24h",
|
|
2458
|
+
"altVolToBtcVol24h",
|
|
2459
|
+
"altDispersion24h"
|
|
2460
|
+
];
|
|
2461
|
+
var preloadedTradeFlowBySymbol = null;
|
|
2462
|
+
var preloadedBreadthByUniverse = null;
|
|
2361
2463
|
var parseEnabledFlag = (value, env) => {
|
|
2362
2464
|
const normalized = String(value ?? "").trim().toLowerCase();
|
|
2363
2465
|
if (!normalized)
|
|
@@ -2393,6 +2495,100 @@ var hasBaseContext = (signal) => Boolean(
|
|
|
2393
2495
|
signal.additionalIndicators?.baseContext && typeof signal.additionalIndicators.baseContext === "object" && !Array.isArray(signal.additionalIndicators.baseContext)
|
|
2394
2496
|
);
|
|
2395
2497
|
var isBinanceMarketContextEnabled = (env) => parseEnabledFlag(process.env.BINANCE_MARKET_CONTEXT_ENABLED, env);
|
|
2498
|
+
var latestIndexAtOrBefore = (timestamps, timestamp) => {
|
|
2499
|
+
if (!timestamps?.length) return -1;
|
|
2500
|
+
let low = 0;
|
|
2501
|
+
let high = timestamps.length - 1;
|
|
2502
|
+
let match = -1;
|
|
2503
|
+
while (low <= high) {
|
|
2504
|
+
const middle = Math.floor((low + high) / 2);
|
|
2505
|
+
const rowTimestamp = timestamps[middle];
|
|
2506
|
+
if (rowTimestamp == null) break;
|
|
2507
|
+
if (rowTimestamp <= timestamp) {
|
|
2508
|
+
match = middle;
|
|
2509
|
+
low = middle + 1;
|
|
2510
|
+
} else {
|
|
2511
|
+
high = middle - 1;
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
return match;
|
|
2515
|
+
};
|
|
2516
|
+
var latestChunkAtOrBefore = (chunks, timestamp) => {
|
|
2517
|
+
if (!chunks?.length) return null;
|
|
2518
|
+
let low = 0;
|
|
2519
|
+
let high = chunks.length - 1;
|
|
2520
|
+
let match = null;
|
|
2521
|
+
while (low <= high) {
|
|
2522
|
+
const middle = Math.floor((low + high) / 2);
|
|
2523
|
+
const chunk = chunks[middle];
|
|
2524
|
+
const firstTimestamp = chunk?.timestamps[0];
|
|
2525
|
+
if (!chunk || firstTimestamp == null) break;
|
|
2526
|
+
if (firstTimestamp <= timestamp) {
|
|
2527
|
+
match = chunk;
|
|
2528
|
+
low = middle + 1;
|
|
2529
|
+
} else {
|
|
2530
|
+
high = middle - 1;
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
return match;
|
|
2534
|
+
};
|
|
2535
|
+
var unpackedNumber = (value) => value == null || Number.isNaN(value) ? null : value;
|
|
2536
|
+
var unpackTradeFlowRow = (chunks, timestamp) => {
|
|
2537
|
+
const rows = latestChunkAtOrBefore(chunks, timestamp);
|
|
2538
|
+
const index = latestIndexAtOrBefore(rows?.timestamps, timestamp);
|
|
2539
|
+
if (!rows || index < 0) return null;
|
|
2540
|
+
return {
|
|
2541
|
+
symbol: rows.symbol,
|
|
2542
|
+
interval: rows.interval,
|
|
2543
|
+
ts: new Date(rows.timestamps[index]),
|
|
2544
|
+
trades: unpackedNumber(rows.columns.trades[index]) ?? 0,
|
|
2545
|
+
buyBaseVolume: unpackedNumber(rows.columns.buyBaseVolume[index]),
|
|
2546
|
+
sellBaseVolume: unpackedNumber(rows.columns.sellBaseVolume[index]),
|
|
2547
|
+
buyQuoteVolume: unpackedNumber(rows.columns.buyQuoteVolume[index]),
|
|
2548
|
+
sellQuoteVolume: unpackedNumber(rows.columns.sellQuoteVolume[index]),
|
|
2549
|
+
netBaseDelta: unpackedNumber(rows.columns.netBaseDelta[index]),
|
|
2550
|
+
netQuoteDelta: unpackedNumber(rows.columns.netQuoteDelta[index]),
|
|
2551
|
+
buyPressurePct: unpackedNumber(rows.columns.buyPressurePct[index])
|
|
2552
|
+
};
|
|
2553
|
+
};
|
|
2554
|
+
var unpackBreadthRow = (chunks, timestamp) => {
|
|
2555
|
+
const rows = latestChunkAtOrBefore(chunks, timestamp);
|
|
2556
|
+
const index = latestIndexAtOrBefore(rows?.timestamps, timestamp);
|
|
2557
|
+
if (!rows || index < 0) return null;
|
|
2558
|
+
const commonNumeric = Object.fromEntries(
|
|
2559
|
+
BREADTH_COMMON_NUMERIC_FIELDS.map((field) => [
|
|
2560
|
+
field,
|
|
2561
|
+
unpackedNumber(rows.commonColumns[field][index])
|
|
2562
|
+
])
|
|
2563
|
+
);
|
|
2564
|
+
const regimeNumeric = Object.fromEntries(
|
|
2565
|
+
BREADTH_REGIME_NUMERIC_FIELDS.map((field) => [
|
|
2566
|
+
field,
|
|
2567
|
+
unpackedNumber(rows.regimeColumns?.[field][index])
|
|
2568
|
+
])
|
|
2569
|
+
);
|
|
2570
|
+
return {
|
|
2571
|
+
universe: rows.universe,
|
|
2572
|
+
interval: rows.interval,
|
|
2573
|
+
ts: new Date(rows.timestamps[index]),
|
|
2574
|
+
...commonNumeric,
|
|
2575
|
+
...regimeNumeric,
|
|
2576
|
+
symbolsCount: commonNumeric.symbolsCount ?? 0,
|
|
2577
|
+
advancers: commonNumeric.advancers ?? 0,
|
|
2578
|
+
decliners: commonNumeric.decliners ?? 0,
|
|
2579
|
+
unchanged: commonNumeric.unchanged ?? 0,
|
|
2580
|
+
btcAltRegime: rows.regimes?.[index] ?? null
|
|
2581
|
+
};
|
|
2582
|
+
};
|
|
2583
|
+
var toAsOfRow = (row, timestamp, maxAgeMs) => {
|
|
2584
|
+
if (!row) return null;
|
|
2585
|
+
const ageMs = timestamp - row.ts.getTime();
|
|
2586
|
+
return {
|
|
2587
|
+
...row,
|
|
2588
|
+
ageMs: Number.isFinite(ageMs) ? ageMs : null,
|
|
2589
|
+
stale: !Number.isFinite(ageMs) || ageMs < 0 || ageMs > maxAgeMs
|
|
2590
|
+
};
|
|
2591
|
+
};
|
|
2396
2592
|
var toTradeFlowContext = (row, interval) => row ? {
|
|
2397
2593
|
source: "binance_agg_trades",
|
|
2398
2594
|
interval,
|
|
@@ -2415,6 +2611,24 @@ var getCachedReferenceRows = ({
|
|
|
2415
2611
|
maxAgeMs,
|
|
2416
2612
|
abortSignal
|
|
2417
2613
|
}) => {
|
|
2614
|
+
if (preloadedTradeFlowBySymbol) {
|
|
2615
|
+
return Promise.resolve(
|
|
2616
|
+
referenceSymbols.map((symbol) => ({
|
|
2617
|
+
symbol,
|
|
2618
|
+
tradeFlow: toTradeFlowContext(
|
|
2619
|
+
toAsOfRow(
|
|
2620
|
+
unpackTradeFlowRow(
|
|
2621
|
+
preloadedTradeFlowBySymbol?.get(symbol),
|
|
2622
|
+
timestamp
|
|
2623
|
+
),
|
|
2624
|
+
timestamp,
|
|
2625
|
+
maxAgeMs
|
|
2626
|
+
),
|
|
2627
|
+
interval
|
|
2628
|
+
)
|
|
2629
|
+
}))
|
|
2630
|
+
);
|
|
2631
|
+
}
|
|
2418
2632
|
const key = `${referenceSymbols.join(",")}:${interval}:${timestamp}:${maxAgeMs}`;
|
|
2419
2633
|
const cached = referenceRowsCache.get(key);
|
|
2420
2634
|
if (cached) return cached;
|
|
@@ -2444,6 +2658,18 @@ var getCachedBreadth = ({
|
|
|
2444
2658
|
maxAgeMs,
|
|
2445
2659
|
abortSignal
|
|
2446
2660
|
}) => {
|
|
2661
|
+
if (preloadedBreadthByUniverse) {
|
|
2662
|
+
return Promise.resolve(
|
|
2663
|
+
toAsOfRow(
|
|
2664
|
+
unpackBreadthRow(
|
|
2665
|
+
preloadedBreadthByUniverse.get(breadthUniverse),
|
|
2666
|
+
timestamp
|
|
2667
|
+
),
|
|
2668
|
+
timestamp,
|
|
2669
|
+
maxAgeMs
|
|
2670
|
+
)
|
|
2671
|
+
);
|
|
2672
|
+
}
|
|
2447
2673
|
const key = `${breadthUniverse}:${interval}:${timestamp}:${maxAgeMs}`;
|
|
2448
2674
|
const cached = breadthCache.get(key);
|
|
2449
2675
|
if (cached) return cached;
|
|
@@ -2650,12 +2876,20 @@ var SOURCE_EXCHANGE_LIQUIDITY = "coinmarketcap_exchange_liquidity";
|
|
|
2650
2876
|
var SOURCE_FEAR_GREED = "coinmarketcap_fear_greed";
|
|
2651
2877
|
var SOURCE_INDEX = "coinmarketcap_index";
|
|
2652
2878
|
var DAY_MS = 864e5;
|
|
2879
|
+
var MAX_TIMESTAMP_CACHE_ENTRIES = 512;
|
|
2653
2880
|
var coinMarketCapContextUnavailable = false;
|
|
2654
2881
|
var globalContextCache = /* @__PURE__ */ new Map();
|
|
2655
2882
|
var referenceContextCache = /* @__PURE__ */ new Map();
|
|
2656
2883
|
var exchangeLiquidityContextCache = /* @__PURE__ */ new Map();
|
|
2657
2884
|
var fearGreedContextCache = /* @__PURE__ */ new Map();
|
|
2658
2885
|
var indexContextCache = /* @__PURE__ */ new Map();
|
|
2886
|
+
var setBoundedCache = (cache, key, value) => {
|
|
2887
|
+
if (cache.size >= MAX_TIMESTAMP_CACHE_ENTRIES) {
|
|
2888
|
+
const oldestKey = cache.keys().next().value;
|
|
2889
|
+
if (oldestKey != null) cache.delete(oldestKey);
|
|
2890
|
+
}
|
|
2891
|
+
cache.set(key, value);
|
|
2892
|
+
};
|
|
2659
2893
|
var parseEnabledFlag2 = (value, env) => {
|
|
2660
2894
|
const normalized = String(value ?? "").trim().toLowerCase();
|
|
2661
2895
|
if (!normalized) {
|
|
@@ -2762,7 +2996,7 @@ var getCachedGlobalContext = ({
|
|
|
2762
2996
|
maxAgeMs,
|
|
2763
2997
|
...abortSignal ? { signal: abortSignal } : {}
|
|
2764
2998
|
});
|
|
2765
|
-
globalContextCache
|
|
2999
|
+
setBoundedCache(globalContextCache, key, promise);
|
|
2766
3000
|
void promise.catch(() => globalContextCache.delete(key));
|
|
2767
3001
|
return promise;
|
|
2768
3002
|
};
|
|
@@ -2782,7 +3016,7 @@ var getCachedReferenceContexts = ({
|
|
|
2782
3016
|
maxAgeMs,
|
|
2783
3017
|
...abortSignal ? { signal: abortSignal } : {}
|
|
2784
3018
|
});
|
|
2785
|
-
referenceContextCache
|
|
3019
|
+
setBoundedCache(referenceContextCache, key, promise);
|
|
2786
3020
|
void promise.catch(() => referenceContextCache.delete(key));
|
|
2787
3021
|
return promise;
|
|
2788
3022
|
};
|
|
@@ -2801,7 +3035,7 @@ var getCachedExchangeLiquidityContext = ({
|
|
|
2801
3035
|
maxAgeMs,
|
|
2802
3036
|
...abortSignal ? { signal: abortSignal } : {}
|
|
2803
3037
|
});
|
|
2804
|
-
exchangeLiquidityContextCache
|
|
3038
|
+
setBoundedCache(exchangeLiquidityContextCache, key, promise);
|
|
2805
3039
|
void promise.catch(() => exchangeLiquidityContextCache.delete(key));
|
|
2806
3040
|
return promise;
|
|
2807
3041
|
};
|
|
@@ -2820,7 +3054,7 @@ var getCachedFearGreedContext = ({
|
|
|
2820
3054
|
maxAgeMs,
|
|
2821
3055
|
...abortSignal ? { signal: abortSignal } : {}
|
|
2822
3056
|
});
|
|
2823
|
-
fearGreedContextCache
|
|
3057
|
+
setBoundedCache(fearGreedContextCache, key, promise);
|
|
2824
3058
|
void promise.catch(() => fearGreedContextCache.delete(key));
|
|
2825
3059
|
return promise;
|
|
2826
3060
|
};
|
|
@@ -2840,7 +3074,7 @@ var getCachedIndexContexts = ({
|
|
|
2840
3074
|
maxAgeMs,
|
|
2841
3075
|
...abortSignal ? { signal: abortSignal } : {}
|
|
2842
3076
|
});
|
|
2843
|
-
indexContextCache
|
|
3077
|
+
setBoundedCache(indexContextCache, key, promise);
|
|
2844
3078
|
void promise.catch(() => indexContextCache.delete(key));
|
|
2845
3079
|
return promise;
|
|
2846
3080
|
};
|
|
@@ -3650,7 +3884,7 @@ var signalIntervalToMarketInterval2 = (value) => {
|
|
|
3650
3884
|
var hasBaseContext3 = (signal) => Boolean(
|
|
3651
3885
|
signal.additionalIndicators?.baseContext && typeof signal.additionalIndicators.baseContext === "object" && !Array.isArray(signal.additionalIndicators.baseContext)
|
|
3652
3886
|
);
|
|
3653
|
-
var
|
|
3887
|
+
var setBoundedCache2 = (key, value) => {
|
|
3654
3888
|
if (contextCache.size >= MAX_CACHE_ENTRIES) {
|
|
3655
3889
|
const oldestKey = contextCache.keys().next().value;
|
|
3656
3890
|
if (oldestKey != null) contextCache.delete(oldestKey);
|
|
@@ -3897,7 +4131,7 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
3897
4131
|
whaleRegistryFingerprint: whales.fingerprint,
|
|
3898
4132
|
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
3899
4133
|
});
|
|
3900
|
-
|
|
4134
|
+
setBoundedCache2(cacheKey, pending);
|
|
3901
4135
|
}
|
|
3902
4136
|
row = await pending;
|
|
3903
4137
|
}
|
|
@@ -5032,20 +5266,23 @@ var buildHookCtx = ({
|
|
|
5032
5266
|
strategyConfig,
|
|
5033
5267
|
env,
|
|
5034
5268
|
isConfigFromBacktest
|
|
5035
|
-
}) =>
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5269
|
+
}) => {
|
|
5270
|
+
const context = {
|
|
5271
|
+
connector,
|
|
5272
|
+
strategyName,
|
|
5273
|
+
userName,
|
|
5274
|
+
symbol
|
|
5275
|
+
};
|
|
5276
|
+
if (universe) context.universe = universe;
|
|
5277
|
+
if (assetClass) context.assetClass = assetClass;
|
|
5278
|
+
if (accountId) context.accountId = accountId;
|
|
5279
|
+
if (deploymentId) context.deploymentId = deploymentId;
|
|
5280
|
+
if (policyProfileId) context.policyProfileId = policyProfileId;
|
|
5281
|
+
context.strategyConfig = strategyConfig;
|
|
5282
|
+
context.env = env;
|
|
5283
|
+
context.isConfigFromBacktest = isConfigFromBacktest;
|
|
5284
|
+
return context;
|
|
5285
|
+
};
|
|
5049
5286
|
var shouldRecordRuntimeJournal = ({
|
|
5050
5287
|
env,
|
|
5051
5288
|
config
|
|
@@ -6989,6 +7226,7 @@ var import_ml3 = require("@tradejs/infra/ml");
|
|
|
6989
7226
|
|
|
6990
7227
|
// src/executionCosts.ts
|
|
6991
7228
|
var import_constants7 = require("@tradejs/core/constants");
|
|
7229
|
+
var import_backtest = require("@tradejs/core/backtest");
|
|
6992
7230
|
var finiteOr = (value, fallback) => {
|
|
6993
7231
|
const parsed = Number(value);
|
|
6994
7232
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
@@ -7025,20 +7263,27 @@ var loadFundingRates = (connector, symbol, startTime, endTime) => {
|
|
|
7025
7263
|
};
|
|
7026
7264
|
var resolveExecutionCosts = async (params) => {
|
|
7027
7265
|
const { connector, symbol, config, startTime, endTime, instrument } = params;
|
|
7028
|
-
|
|
7029
|
-
const
|
|
7266
|
+
(0, import_backtest.assertStrategyExecutionIsolation)(config);
|
|
7267
|
+
const costs = params.executionCosts == null ? void 0 : (0, import_backtest.parseBacktestExecutionCosts)(params.executionCosts);
|
|
7268
|
+
const cacheOnly = params.cacheOnly === true;
|
|
7269
|
+
const hasConfiguredFees = costs != null;
|
|
7030
7270
|
const exchangeFees = !cacheOnly && !hasConfiguredFees && connector.getTradingFeeRate ? await loadTradingFee(connector, symbol).catch(() => null) : null;
|
|
7031
|
-
const makerRate = hasConfiguredFees ?
|
|
7032
|
-
const takerRate = hasConfiguredFees ?
|
|
7033
|
-
const fundingEnabled =
|
|
7271
|
+
const makerRate = hasConfiguredFees ? costs.fees.makerRate : exchangeFees?.makerRate ?? import_constants7.FEE_PERCENT;
|
|
7272
|
+
const takerRate = hasConfiguredFees ? costs.fees.takerRate : exchangeFees?.takerRate ?? import_constants7.FEE_PERCENT;
|
|
7273
|
+
const fundingEnabled = costs?.funding.enabled !== false && !cacheOnly && typeof connector.getFundingRateHistory === "function";
|
|
7034
7274
|
const fundingRates = fundingEnabled ? await loadFundingRates(connector, symbol, startTime, endTime) : [];
|
|
7035
7275
|
const requestedLeverage = Math.max(1, finiteOr(config.LEVERAGE, 10));
|
|
7036
7276
|
const venueMaxLeverage = Number(instrument?.venueMetadata?.maxLeverage);
|
|
7037
7277
|
const maxAllowed = Number.isFinite(venueMaxLeverage) ? venueMaxLeverage : null;
|
|
7038
7278
|
const effectiveLeverage = maxAllowed == null ? requestedLeverage : Math.min(requestedLeverage, maxAllowed);
|
|
7039
7279
|
const feeSource = hasConfiguredFees ? "config" : exchangeFees?.source ?? "fallback";
|
|
7040
|
-
const fundingSource = !fundingEnabled ? cacheOnly ? "fallback" : "disabled" : fundingRates.length ? "historical" : "unavailable";
|
|
7041
|
-
const usesFallback = feeSource === "fallback" || fundingEnabled && fundingSource === "unavailable" ||
|
|
7280
|
+
const fundingSource = !fundingEnabled ? costs?.funding.enabled === false ? "disabled" : cacheOnly ? "fallback" : "disabled" : fundingRates.length ? "historical" : "unavailable";
|
|
7281
|
+
const usesFallback = feeSource === "fallback" || fundingEnabled && fundingSource === "unavailable" || !costs;
|
|
7282
|
+
if (costs?.funding.enabled && (!fundingEnabled || fundingSource !== "historical")) {
|
|
7283
|
+
throw new Error(
|
|
7284
|
+
"Requested funding history is unavailable; explicitly disable funding or provide historical data"
|
|
7285
|
+
);
|
|
7286
|
+
}
|
|
7042
7287
|
return {
|
|
7043
7288
|
model: {
|
|
7044
7289
|
fees: { makerRate, takerRate, source: feeSource },
|
|
@@ -7050,20 +7295,20 @@ var resolveExecutionCosts = async (params) => {
|
|
|
7050
7295
|
toTimestamp: fundingRates.at(-1)?.timestamp ?? null
|
|
7051
7296
|
},
|
|
7052
7297
|
slippage: {
|
|
7053
|
-
baseBps:
|
|
7298
|
+
baseBps: costs?.slippage.baseBps ?? import_constants7.BACKTEST_BASE_SLIPPAGE_BPS,
|
|
7054
7299
|
spreadMultiplier: finiteOr(
|
|
7055
|
-
|
|
7300
|
+
costs?.slippage.spreadMultiplier,
|
|
7056
7301
|
import_constants7.BACKTEST_SPREAD_SLIPPAGE_MULTIPLIER
|
|
7057
7302
|
),
|
|
7058
7303
|
marketImpactBps: finiteOr(
|
|
7059
|
-
|
|
7304
|
+
costs?.slippage.marketImpactBps,
|
|
7060
7305
|
import_constants7.BACKTEST_MARKET_IMPACT_BPS
|
|
7061
7306
|
),
|
|
7062
7307
|
delayRiskMultiplier: finiteOr(
|
|
7063
|
-
|
|
7308
|
+
costs?.slippage.delayRiskMultiplier,
|
|
7064
7309
|
import_constants7.BACKTEST_DELAY_RISK_MULTIPLIER
|
|
7065
7310
|
),
|
|
7066
|
-
source:
|
|
7311
|
+
source: costs != null ? "config" : "fallback"
|
|
7067
7312
|
},
|
|
7068
7313
|
leverage: {
|
|
7069
7314
|
requested: requestedLeverage,
|
|
@@ -7080,7 +7325,7 @@ var resolveExecutionCosts = async (params) => {
|
|
|
7080
7325
|
// src/testConnector.ts
|
|
7081
7326
|
var import_node_crypto4 = require("crypto");
|
|
7082
7327
|
var import_constants8 = require("@tradejs/core/constants");
|
|
7083
|
-
var
|
|
7328
|
+
var import_backtest2 = require("@tradejs/core/backtest");
|
|
7084
7329
|
var import_trade2 = require("@tradejs/core/trade");
|
|
7085
7330
|
var import_math = require("@tradejs/core/math");
|
|
7086
7331
|
var PRICE_PRECISION = 8;
|
|
@@ -7379,7 +7624,7 @@ var createTestConnector = (connector, context) => {
|
|
|
7379
7624
|
allocatedQty += legExitQty;
|
|
7380
7625
|
const legResult = currentEntryLegResults[leg.index];
|
|
7381
7626
|
const legGrossProfit = currentPosition.direction === "LONG" ? (executionPrice - legResult.entryPrice) * legExitQty : (legResult.entryPrice - executionPrice) * legExitQty;
|
|
7382
|
-
const legFee =
|
|
7627
|
+
const legFee = fee * (legExitQty / qty);
|
|
7383
7628
|
currentEntryLegResults[leg.index] = appendExitToTradeResult({
|
|
7384
7629
|
tradeResult: legResult,
|
|
7385
7630
|
direction: currentPosition.direction,
|
|
@@ -7557,7 +7802,7 @@ var createTestConnector = (connector, context) => {
|
|
|
7557
7802
|
kline: async (options) => connector.kline(options),
|
|
7558
7803
|
getResult: async () => {
|
|
7559
7804
|
const orderLogId = (0, import_node_crypto4.randomUUID)().slice(-12);
|
|
7560
|
-
const fullStat = fastMode ? (0,
|
|
7805
|
+
const fullStat = fastMode ? (0, import_backtest2.calculateStatsFull)(positionLog) : null;
|
|
7561
7806
|
return {
|
|
7562
7807
|
stat: fullStat ? {
|
|
7563
7808
|
...fullStat,
|
|
@@ -8063,7 +8308,8 @@ var createTestConnector = (connector, context) => {
|
|
|
8063
8308
|
const { fee, profit } = getNetProfit({
|
|
8064
8309
|
grossProfit,
|
|
8065
8310
|
price: executionPrice,
|
|
8066
|
-
qty: currentPosition.qty
|
|
8311
|
+
qty: currentPosition.qty,
|
|
8312
|
+
feeRate: order.isLimit ? makerFeeRate : takerFeeRate
|
|
8067
8313
|
});
|
|
8068
8314
|
recordExitResult({
|
|
8069
8315
|
timestamp: order.timestamp,
|
|
@@ -8208,6 +8454,8 @@ var createBacktestSession = async ({
|
|
|
8208
8454
|
connector,
|
|
8209
8455
|
symbol: test.symbol,
|
|
8210
8456
|
config: test.strategyConfig,
|
|
8457
|
+
executionCosts: test.executionCosts,
|
|
8458
|
+
cacheOnly: test.executionCostsCacheOnly,
|
|
8211
8459
|
startTime: start,
|
|
8212
8460
|
endTime: test.options.end,
|
|
8213
8461
|
instrument
|