@tradejs/node 2.0.14 → 2.0.16
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 +11 -2
- package/dist/ai.mjs +1 -1
- package/dist/backtest.js +236 -97
- package/dist/backtest.mjs +21 -64
- package/dist/{chunk-FEQR4XGR.mjs → chunk-3BP3ETAD.mjs} +213 -32
- package/dist/{chunk-DPHKRDD5.mjs → chunk-WK5EUCX5.mjs} +11 -2
- package/dist/cli.js +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/strategies.d.mts +3 -0
- package/dist/strategies.d.ts +3 -0
- package/dist/strategies.js +770 -564
- package/dist/strategies.mjs +42 -24
- package/package.json +4 -4
package/dist/backtest.mjs
CHANGED
|
@@ -4,14 +4,11 @@ import {
|
|
|
4
4
|
} from "./chunk-V3YMKE4I.mjs";
|
|
5
5
|
import {
|
|
6
6
|
buildMlPayload,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
enrichSignalWithDerivativesContext,
|
|
10
|
-
enrichSignalWithHyperliquidWhaleContext
|
|
11
|
-
} from "./chunk-FEQR4XGR.mjs";
|
|
7
|
+
enrichSignalWithMarketContextStages
|
|
8
|
+
} from "./chunk-3BP3ETAD.mjs";
|
|
12
9
|
import {
|
|
13
10
|
buildAiPayload
|
|
14
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-WK5EUCX5.mjs";
|
|
15
12
|
import {
|
|
16
13
|
getStrategyCreator
|
|
17
14
|
} from "./chunk-QVSMINLG.mjs";
|
|
@@ -2065,35 +2062,15 @@ var testing = async ({
|
|
|
2065
2062
|
}
|
|
2066
2063
|
const shouldCapturePayload = signal && typeof signal !== "string" && signal.signalId && (ml || ai);
|
|
2067
2064
|
if (shouldCapturePayload) {
|
|
2068
|
-
await
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
enrichSignalWithCoinMarketCapContext({
|
|
2078
|
-
signal,
|
|
2079
|
-
env: "BACKTEST",
|
|
2080
|
-
enabled: Boolean(ml || ai)
|
|
2081
|
-
})
|
|
2082
|
-
);
|
|
2083
|
-
await withTimeout(
|
|
2084
|
-
"derivatives context",
|
|
2085
|
-
enrichSignalWithDerivativesContext({
|
|
2086
|
-
signal,
|
|
2087
|
-
env: "BACKTEST"
|
|
2088
|
-
})
|
|
2089
|
-
);
|
|
2090
|
-
await withTimeout(
|
|
2091
|
-
"hyperliquid whale context",
|
|
2092
|
-
enrichSignalWithHyperliquidWhaleContext({
|
|
2093
|
-
signal,
|
|
2094
|
-
env: "BACKTEST"
|
|
2095
|
-
})
|
|
2096
|
-
);
|
|
2065
|
+
await enrichSignalWithMarketContextStages({
|
|
2066
|
+
signal,
|
|
2067
|
+
env: "BACKTEST",
|
|
2068
|
+
coinMarketCapEnabled: true,
|
|
2069
|
+
onStageStart: (stage) => {
|
|
2070
|
+
activeStageStartedAt = Date.now();
|
|
2071
|
+
emitProgress(`${stage} context`, { force: true });
|
|
2072
|
+
}
|
|
2073
|
+
});
|
|
2097
2074
|
}
|
|
2098
2075
|
if (ml && signal && typeof signal !== "string" && signal.signalId) {
|
|
2099
2076
|
const payload = buildMlPayload({
|
|
@@ -2497,35 +2474,15 @@ var testingGroupInSharedCandleLoop = async (tests) => {
|
|
|
2497
2474
|
}
|
|
2498
2475
|
const shouldCapturePayload = signal && typeof signal !== "string" && signal.signalId && (test.ml || test.ai);
|
|
2499
2476
|
if (shouldCapturePayload) {
|
|
2500
|
-
await
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
enrichSignalWithCoinMarketCapContext({
|
|
2510
|
-
signal,
|
|
2511
|
-
env: "BACKTEST",
|
|
2512
|
-
enabled: Boolean(test.ml || test.ai)
|
|
2513
|
-
})
|
|
2514
|
-
);
|
|
2515
|
-
await withTimeout(
|
|
2516
|
-
"derivatives context",
|
|
2517
|
-
enrichSignalWithDerivativesContext({
|
|
2518
|
-
signal,
|
|
2519
|
-
env: "BACKTEST"
|
|
2520
|
-
})
|
|
2521
|
-
);
|
|
2522
|
-
await withTimeout(
|
|
2523
|
-
"hyperliquid whale context",
|
|
2524
|
-
enrichSignalWithHyperliquidWhaleContext({
|
|
2525
|
-
signal,
|
|
2526
|
-
env: "BACKTEST"
|
|
2527
|
-
})
|
|
2528
|
-
);
|
|
2477
|
+
await enrichSignalWithMarketContextStages({
|
|
2478
|
+
signal,
|
|
2479
|
+
env: "BACKTEST",
|
|
2480
|
+
coinMarketCapEnabled: true,
|
|
2481
|
+
onStageStart: (stage) => {
|
|
2482
|
+
activeStageStartedAt = Date.now();
|
|
2483
|
+
emitProgress(`${stage} context`, { force: true });
|
|
2484
|
+
}
|
|
2485
|
+
});
|
|
2529
2486
|
}
|
|
2530
2487
|
if (test.ml && signal && typeof signal !== "string" && signal.signalId) {
|
|
2531
2488
|
const payload = buildMlPayload({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getStrategyProfileMlAdapter
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-WK5EUCX5.mjs";
|
|
4
4
|
import {
|
|
5
5
|
getStrategyManifest
|
|
6
6
|
} from "./chunk-QVSMINLG.mjs";
|
|
@@ -317,6 +317,15 @@ import {
|
|
|
317
317
|
} from "@tradejs/infra/timescale";
|
|
318
318
|
import { logger } from "@tradejs/infra/logger";
|
|
319
319
|
import { refreshSignalBaseContextGateFeatures } from "@tradejs/core/strategies";
|
|
320
|
+
|
|
321
|
+
// src/strategyHelpers/marketContextErrors.ts
|
|
322
|
+
var MARKET_CONTEXT_CANCELLATION_ERROR_NAMES = /* @__PURE__ */ new Set([
|
|
323
|
+
"AbortError",
|
|
324
|
+
"TimescaleQueryTimeoutError"
|
|
325
|
+
]);
|
|
326
|
+
var isMarketContextCancellationError = (error, abortSignal) => abortSignal?.aborted === true || error instanceof Error && MARKET_CONTEXT_CANCELLATION_ERROR_NAMES.has(error.name);
|
|
327
|
+
|
|
328
|
+
// src/strategyHelpers/binanceMarketContext.ts
|
|
320
329
|
var DEFAULT_MAX_AGE_BY_INTERVAL = {
|
|
321
330
|
"1m": 3 * 6e4,
|
|
322
331
|
"5m": 10 * 6e4,
|
|
@@ -380,7 +389,8 @@ var getCachedReferenceRows = ({
|
|
|
380
389
|
referenceSymbols,
|
|
381
390
|
interval,
|
|
382
391
|
timestamp,
|
|
383
|
-
maxAgeMs
|
|
392
|
+
maxAgeMs,
|
|
393
|
+
abortSignal
|
|
384
394
|
}) => {
|
|
385
395
|
const key = `${referenceSymbols.join(",")}:${interval}:${timestamp}:${maxAgeMs}`;
|
|
386
396
|
const cached = referenceRowsCache.get(key);
|
|
@@ -391,7 +401,8 @@ var getCachedReferenceRows = ({
|
|
|
391
401
|
symbol,
|
|
392
402
|
interval,
|
|
393
403
|
atMs: timestamp,
|
|
394
|
-
maxAgeMs
|
|
404
|
+
maxAgeMs,
|
|
405
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
395
406
|
});
|
|
396
407
|
return {
|
|
397
408
|
symbol,
|
|
@@ -400,13 +411,15 @@ var getCachedReferenceRows = ({
|
|
|
400
411
|
})
|
|
401
412
|
);
|
|
402
413
|
referenceRowsCache.set(key, promise);
|
|
414
|
+
void promise.catch(() => referenceRowsCache.delete(key));
|
|
403
415
|
return promise;
|
|
404
416
|
};
|
|
405
417
|
var getCachedBreadth = ({
|
|
406
418
|
breadthUniverse,
|
|
407
419
|
interval,
|
|
408
420
|
timestamp,
|
|
409
|
-
maxAgeMs
|
|
421
|
+
maxAgeMs,
|
|
422
|
+
abortSignal
|
|
410
423
|
}) => {
|
|
411
424
|
const key = `${breadthUniverse}:${interval}:${timestamp}:${maxAgeMs}`;
|
|
412
425
|
const cached = breadthCache.get(key);
|
|
@@ -415,9 +428,11 @@ var getCachedBreadth = ({
|
|
|
415
428
|
universe: breadthUniverse,
|
|
416
429
|
interval,
|
|
417
430
|
atMs: timestamp,
|
|
418
|
-
maxAgeMs
|
|
431
|
+
maxAgeMs,
|
|
432
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
419
433
|
});
|
|
420
434
|
breadthCache.set(key, promise);
|
|
435
|
+
void promise.catch(() => breadthCache.delete(key));
|
|
421
436
|
return promise;
|
|
422
437
|
};
|
|
423
438
|
var toMarketBreadthContext = (breadth, interval) => ({
|
|
@@ -463,7 +478,8 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
463
478
|
referenceSymbols,
|
|
464
479
|
interval,
|
|
465
480
|
timestamp: signal.timestamp,
|
|
466
|
-
maxAgeMs
|
|
481
|
+
maxAgeMs,
|
|
482
|
+
abortSignal: params.abortSignal
|
|
467
483
|
}),
|
|
468
484
|
Promise.all(
|
|
469
485
|
breadthUniverses.map(async ({ key, universe }) => ({
|
|
@@ -472,7 +488,8 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
472
488
|
breadthUniverse: universe,
|
|
473
489
|
interval,
|
|
474
490
|
timestamp: signal.timestamp,
|
|
475
|
-
maxAgeMs
|
|
491
|
+
maxAgeMs,
|
|
492
|
+
abortSignal: params.abortSignal
|
|
476
493
|
})
|
|
477
494
|
}))
|
|
478
495
|
)
|
|
@@ -587,6 +604,9 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
587
604
|
refreshSignalBaseContextGateFeatures(signal);
|
|
588
605
|
return true;
|
|
589
606
|
} catch (error) {
|
|
607
|
+
if (isMarketContextCancellationError(error, params.abortSignal)) {
|
|
608
|
+
throw error;
|
|
609
|
+
}
|
|
590
610
|
binanceMarketContextUnavailable = true;
|
|
591
611
|
logger.warn(
|
|
592
612
|
"Binance market context disabled after Timescale read failure: %s",
|
|
@@ -713,7 +733,8 @@ var toIndexRegime = ({
|
|
|
713
733
|
};
|
|
714
734
|
var getCachedGlobalContext = ({
|
|
715
735
|
timestamp,
|
|
716
|
-
maxAgeMs
|
|
736
|
+
maxAgeMs,
|
|
737
|
+
abortSignal
|
|
717
738
|
}) => {
|
|
718
739
|
const key = `${SOURCE_GLOBAL_DAILY}:${timestamp}:${maxAgeMs}`;
|
|
719
740
|
const cached = globalContextCache.get(key);
|
|
@@ -721,14 +742,17 @@ var getCachedGlobalContext = ({
|
|
|
721
742
|
const promise = getLatestMarketGlobalContext({
|
|
722
743
|
source: SOURCE_GLOBAL_DAILY,
|
|
723
744
|
atMs: timestamp,
|
|
724
|
-
maxAgeMs
|
|
745
|
+
maxAgeMs,
|
|
746
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
725
747
|
});
|
|
726
748
|
globalContextCache.set(key, promise);
|
|
749
|
+
void promise.catch(() => globalContextCache.delete(key));
|
|
727
750
|
return promise;
|
|
728
751
|
};
|
|
729
752
|
var getCachedReferenceContexts = ({
|
|
730
753
|
timestamp,
|
|
731
|
-
maxAgeMs
|
|
754
|
+
maxAgeMs,
|
|
755
|
+
abortSignal
|
|
732
756
|
}) => {
|
|
733
757
|
const key = `${SOURCE_REFERENCE}:1d:${timestamp}:${maxAgeMs}`;
|
|
734
758
|
const cached = referenceContextCache.get(key);
|
|
@@ -738,14 +762,17 @@ var getCachedReferenceContexts = ({
|
|
|
738
762
|
symbols: ["BTCUSDT", "ETHUSDT"],
|
|
739
763
|
interval: "1d",
|
|
740
764
|
atMs: timestamp,
|
|
741
|
-
maxAgeMs
|
|
765
|
+
maxAgeMs,
|
|
766
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
742
767
|
});
|
|
743
768
|
referenceContextCache.set(key, promise);
|
|
769
|
+
void promise.catch(() => referenceContextCache.delete(key));
|
|
744
770
|
return promise;
|
|
745
771
|
};
|
|
746
772
|
var getCachedExchangeLiquidityContext = ({
|
|
747
773
|
timestamp,
|
|
748
|
-
maxAgeMs
|
|
774
|
+
maxAgeMs,
|
|
775
|
+
abortSignal
|
|
749
776
|
}) => {
|
|
750
777
|
const key = `${SOURCE_EXCHANGE_LIQUIDITY}:1d:${timestamp}:${maxAgeMs}`;
|
|
751
778
|
const cached = exchangeLiquidityContextCache.get(key);
|
|
@@ -754,14 +781,17 @@ var getCachedExchangeLiquidityContext = ({
|
|
|
754
781
|
source: SOURCE_EXCHANGE_LIQUIDITY,
|
|
755
782
|
interval: "1d",
|
|
756
783
|
atMs: timestamp,
|
|
757
|
-
maxAgeMs
|
|
784
|
+
maxAgeMs,
|
|
785
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
758
786
|
});
|
|
759
787
|
exchangeLiquidityContextCache.set(key, promise);
|
|
788
|
+
void promise.catch(() => exchangeLiquidityContextCache.delete(key));
|
|
760
789
|
return promise;
|
|
761
790
|
};
|
|
762
791
|
var getCachedFearGreedContext = ({
|
|
763
792
|
timestamp,
|
|
764
|
-
maxAgeMs
|
|
793
|
+
maxAgeMs,
|
|
794
|
+
abortSignal
|
|
765
795
|
}) => {
|
|
766
796
|
const key = `${SOURCE_FEAR_GREED}:1d:${timestamp}:${maxAgeMs}`;
|
|
767
797
|
const cached = fearGreedContextCache.get(key);
|
|
@@ -770,14 +800,17 @@ var getCachedFearGreedContext = ({
|
|
|
770
800
|
source: SOURCE_FEAR_GREED,
|
|
771
801
|
interval: "1d",
|
|
772
802
|
atMs: timestamp,
|
|
773
|
-
maxAgeMs
|
|
803
|
+
maxAgeMs,
|
|
804
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
774
805
|
});
|
|
775
806
|
fearGreedContextCache.set(key, promise);
|
|
807
|
+
void promise.catch(() => fearGreedContextCache.delete(key));
|
|
776
808
|
return promise;
|
|
777
809
|
};
|
|
778
810
|
var getCachedIndexContexts = ({
|
|
779
811
|
timestamp,
|
|
780
|
-
maxAgeMs
|
|
812
|
+
maxAgeMs,
|
|
813
|
+
abortSignal
|
|
781
814
|
}) => {
|
|
782
815
|
const key = `${SOURCE_INDEX}:1d:${timestamp}:${maxAgeMs}`;
|
|
783
816
|
const cached = indexContextCache.get(key);
|
|
@@ -787,9 +820,11 @@ var getCachedIndexContexts = ({
|
|
|
787
820
|
indexSlugs: ["cmc100", "cmc20"],
|
|
788
821
|
interval: "1d",
|
|
789
822
|
atMs: timestamp,
|
|
790
|
-
maxAgeMs
|
|
823
|
+
maxAgeMs,
|
|
824
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
791
825
|
});
|
|
792
826
|
indexContextCache.set(key, promise);
|
|
827
|
+
void promise.catch(() => indexContextCache.delete(key));
|
|
793
828
|
return promise;
|
|
794
829
|
};
|
|
795
830
|
var isCoinMarketCapContextEnabled = (env) => parseEnabledFlag2(process.env.COINMARKETCAP_CONTEXT_ENABLED, env);
|
|
@@ -814,27 +849,33 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
814
849
|
] = await Promise.all([
|
|
815
850
|
getCachedGlobalContext({
|
|
816
851
|
timestamp: signal.timestamp,
|
|
817
|
-
maxAgeMs
|
|
852
|
+
maxAgeMs,
|
|
853
|
+
abortSignal: params.abortSignal
|
|
818
854
|
}),
|
|
819
855
|
getCachedReferenceContexts({
|
|
820
856
|
timestamp: signal.timestamp,
|
|
821
|
-
maxAgeMs
|
|
857
|
+
maxAgeMs,
|
|
858
|
+
abortSignal: params.abortSignal
|
|
822
859
|
}),
|
|
823
860
|
getCachedReferenceContexts({
|
|
824
861
|
timestamp: signal.timestamp - DAY_MS,
|
|
825
|
-
maxAgeMs: maxAgeMs + DAY_MS
|
|
862
|
+
maxAgeMs: maxAgeMs + DAY_MS,
|
|
863
|
+
abortSignal: params.abortSignal
|
|
826
864
|
}),
|
|
827
865
|
getCachedExchangeLiquidityContext({
|
|
828
866
|
timestamp: signal.timestamp,
|
|
829
|
-
maxAgeMs
|
|
867
|
+
maxAgeMs,
|
|
868
|
+
abortSignal: params.abortSignal
|
|
830
869
|
}),
|
|
831
870
|
getCachedFearGreedContext({
|
|
832
871
|
timestamp: signal.timestamp,
|
|
833
|
-
maxAgeMs
|
|
872
|
+
maxAgeMs,
|
|
873
|
+
abortSignal: params.abortSignal
|
|
834
874
|
}),
|
|
835
875
|
getCachedIndexContexts({
|
|
836
876
|
timestamp: signal.timestamp,
|
|
837
|
-
maxAgeMs
|
|
877
|
+
maxAgeMs,
|
|
878
|
+
abortSignal: params.abortSignal
|
|
838
879
|
})
|
|
839
880
|
]);
|
|
840
881
|
const globalRow = globalDailyRow;
|
|
@@ -1068,6 +1109,9 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
1068
1109
|
refreshSignalBaseContextGateFeatures2(signal);
|
|
1069
1110
|
return true;
|
|
1070
1111
|
} catch (error) {
|
|
1112
|
+
if (isMarketContextCancellationError(error, params.abortSignal)) {
|
|
1113
|
+
throw error;
|
|
1114
|
+
}
|
|
1071
1115
|
coinMarketCapContextUnavailable = true;
|
|
1072
1116
|
logger2.warn(
|
|
1073
1117
|
"CoinMarketCap context disabled after Timescale read failure: %s",
|
|
@@ -1307,7 +1351,7 @@ import { logger as logger3 } from "@tradejs/infra/logger";
|
|
|
1307
1351
|
var MAX_CACHE_ENTRIES = 2048;
|
|
1308
1352
|
var MAX_COVERAGE_SERIES_CACHE_ENTRIES = 20;
|
|
1309
1353
|
var MAX_FLOW_SERIES_CACHE_ENTRIES = 64;
|
|
1310
|
-
var SERIES_CHUNK_MS =
|
|
1354
|
+
var SERIES_CHUNK_MS = 90 * 24 * 60 * 6e4;
|
|
1311
1355
|
var MAX_SERIES_LOOKBACK_MS = 60 * 6e4;
|
|
1312
1356
|
var DEFAULT_MIN_COVERAGE_PCT = 0.8;
|
|
1313
1357
|
var INTERVAL_MS = {
|
|
@@ -1355,6 +1399,7 @@ var setBoundedCache = (key, value) => {
|
|
|
1355
1399
|
if (oldestKey != null) contextCache.delete(oldestKey);
|
|
1356
1400
|
}
|
|
1357
1401
|
contextCache.set(key, value);
|
|
1402
|
+
void value.catch(() => contextCache.delete(key));
|
|
1358
1403
|
};
|
|
1359
1404
|
var setBoundedSeriesCache = (cache, maxEntries, key, value) => {
|
|
1360
1405
|
if (cache.size >= maxEntries) {
|
|
@@ -1362,6 +1407,7 @@ var setBoundedSeriesCache = (cache, maxEntries, key, value) => {
|
|
|
1362
1407
|
if (oldestKey != null) cache.delete(oldestKey);
|
|
1363
1408
|
}
|
|
1364
1409
|
cache.set(key, value);
|
|
1410
|
+
void value.catch(() => cache.delete(key));
|
|
1365
1411
|
};
|
|
1366
1412
|
var uniqueAddressCount = (rows, field) => {
|
|
1367
1413
|
const addresses = /* @__PURE__ */ new Set();
|
|
@@ -1476,7 +1522,8 @@ var loadHyperliquidWhaleFlowAggregateFromSeries = async (params) => {
|
|
|
1476
1522
|
fromMs,
|
|
1477
1523
|
toMs,
|
|
1478
1524
|
universeFingerprint: params.universeFingerprint,
|
|
1479
|
-
whaleRegistryFingerprint: params.whaleRegistryFingerprint
|
|
1525
|
+
whaleRegistryFingerprint: params.whaleRegistryFingerprint,
|
|
1526
|
+
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
1480
1527
|
});
|
|
1481
1528
|
setBoundedSeriesCache(
|
|
1482
1529
|
coverageSeriesCache,
|
|
@@ -1493,7 +1540,8 @@ var loadHyperliquidWhaleFlowAggregateFromSeries = async (params) => {
|
|
|
1493
1540
|
fromMs,
|
|
1494
1541
|
toMs,
|
|
1495
1542
|
universeFingerprint: params.universeFingerprint,
|
|
1496
|
-
whaleRegistryFingerprint: params.whaleRegistryFingerprint
|
|
1543
|
+
whaleRegistryFingerprint: params.whaleRegistryFingerprint,
|
|
1544
|
+
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
1497
1545
|
});
|
|
1498
1546
|
setBoundedSeriesCache(
|
|
1499
1547
|
flowSeriesCache,
|
|
@@ -1577,7 +1625,8 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
1577
1625
|
decisionTimeMs,
|
|
1578
1626
|
maxAgeMs,
|
|
1579
1627
|
universeFingerprint: universe.fingerprint,
|
|
1580
|
-
whaleRegistryFingerprint: whales.fingerprint
|
|
1628
|
+
whaleRegistryFingerprint: whales.fingerprint,
|
|
1629
|
+
abortSignal: params.abortSignal
|
|
1581
1630
|
});
|
|
1582
1631
|
} else {
|
|
1583
1632
|
let pending = contextCache.get(cacheKey);
|
|
@@ -1588,7 +1637,8 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
1588
1637
|
decisionTimeMs,
|
|
1589
1638
|
maxAgeMs,
|
|
1590
1639
|
universeFingerprint: universe.fingerprint,
|
|
1591
|
-
whaleRegistryFingerprint: whales.fingerprint
|
|
1640
|
+
whaleRegistryFingerprint: whales.fingerprint,
|
|
1641
|
+
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
1592
1642
|
});
|
|
1593
1643
|
setBoundedCache(cacheKey, pending);
|
|
1594
1644
|
}
|
|
@@ -1597,6 +1647,9 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
1597
1647
|
if (!row) return null;
|
|
1598
1648
|
return toBaseHyperliquidWhaleFlowContext(row, minimumCoveragePct);
|
|
1599
1649
|
} catch (error) {
|
|
1650
|
+
if (isMarketContextCancellationError(error, params.abortSignal)) {
|
|
1651
|
+
throw error;
|
|
1652
|
+
}
|
|
1600
1653
|
hyperliquidWhaleContextUnavailable = true;
|
|
1601
1654
|
logger3.warn(
|
|
1602
1655
|
"Hyperliquid whale context disabled after Timescale read failure: %s",
|
|
@@ -1617,7 +1670,8 @@ var enrichSignalWithHyperliquidWhaleContext = async (params) => {
|
|
|
1617
1670
|
env,
|
|
1618
1671
|
enabled: params.enabled,
|
|
1619
1672
|
marketInterval: params.interval,
|
|
1620
|
-
maxAgeMs: params.maxAgeMs
|
|
1673
|
+
maxAgeMs: params.maxAgeMs,
|
|
1674
|
+
abortSignal: params.abortSignal
|
|
1621
1675
|
});
|
|
1622
1676
|
if (!hyperliquidWhales) return false;
|
|
1623
1677
|
const baseContext = signal.additionalIndicators.baseContext;
|
|
@@ -1635,6 +1689,9 @@ var enrichSignalWithHyperliquidWhaleContext = async (params) => {
|
|
|
1635
1689
|
return true;
|
|
1636
1690
|
};
|
|
1637
1691
|
|
|
1692
|
+
// src/strategyHelpers/marketContextStages.ts
|
|
1693
|
+
import { logger as logger5 } from "@tradejs/infra/logger";
|
|
1694
|
+
|
|
1638
1695
|
// src/strategyHelpers/derivativesContext.ts
|
|
1639
1696
|
import {
|
|
1640
1697
|
buildDerivativesContext,
|
|
@@ -1815,7 +1872,8 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
1815
1872
|
symbol,
|
|
1816
1873
|
intervals: STORED_INTERVALS,
|
|
1817
1874
|
endMs: derivativesEndMs,
|
|
1818
|
-
lookbackMs
|
|
1875
|
+
lookbackMs,
|
|
1876
|
+
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
1819
1877
|
});
|
|
1820
1878
|
return [
|
|
1821
1879
|
symbol,
|
|
@@ -1843,7 +1901,8 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
1843
1901
|
symbol: targetSymbol,
|
|
1844
1902
|
intervals: STORED_INTERVALS,
|
|
1845
1903
|
endMs: derivativesEndMs,
|
|
1846
|
-
lookbackMs
|
|
1904
|
+
lookbackMs,
|
|
1905
|
+
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
1847
1906
|
});
|
|
1848
1907
|
const context = buildDerivativesContext({
|
|
1849
1908
|
symbol: targetSymbol,
|
|
@@ -1874,6 +1933,9 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
1874
1933
|
refreshSignalBaseContextGateFeatures4(signal);
|
|
1875
1934
|
return true;
|
|
1876
1935
|
} catch (error) {
|
|
1936
|
+
if (isMarketContextCancellationError(error, params.abortSignal)) {
|
|
1937
|
+
throw error;
|
|
1938
|
+
}
|
|
1877
1939
|
derivativesContextUnavailable = true;
|
|
1878
1940
|
logger4.warn(
|
|
1879
1941
|
"Derivatives context disabled after Timescale read failure: %s",
|
|
@@ -1883,6 +1945,125 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
1883
1945
|
}
|
|
1884
1946
|
};
|
|
1885
1947
|
|
|
1948
|
+
// src/strategyHelpers/marketContextStages.ts
|
|
1949
|
+
var STAGE_ENV_KEYS = {
|
|
1950
|
+
binance: "BINANCE_MARKET_CONTEXT_STAGE_TIMEOUT_MS",
|
|
1951
|
+
coinmarketcap: "COINMARKETCAP_CONTEXT_STAGE_TIMEOUT_MS",
|
|
1952
|
+
derivatives: "DERIVATIVES_CONTEXT_STAGE_TIMEOUT_MS",
|
|
1953
|
+
hyperliquidWhales: "HYPERLIQUID_WHALE_CONTEXT_STAGE_TIMEOUT_MS"
|
|
1954
|
+
};
|
|
1955
|
+
var parsePositiveInt = (value) => {
|
|
1956
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
1957
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
1958
|
+
};
|
|
1959
|
+
var resolveMarketContextStageTimeoutMs = (stage) => parsePositiveInt(process.env[STAGE_ENV_KEYS[stage]]) ?? parsePositiveInt(process.env.MARKET_CONTEXT_STAGE_TIMEOUT_MS) ?? 35e3;
|
|
1960
|
+
var runMarketContextStage = async ({
|
|
1961
|
+
stage,
|
|
1962
|
+
parentSignal,
|
|
1963
|
+
operation,
|
|
1964
|
+
onStart,
|
|
1965
|
+
onComplete
|
|
1966
|
+
}) => {
|
|
1967
|
+
const controller = new AbortController();
|
|
1968
|
+
const timeoutMs = resolveMarketContextStageTimeoutMs(stage);
|
|
1969
|
+
const startedAt = Date.now();
|
|
1970
|
+
const onParentAbort = () => controller.abort(parentSignal?.reason);
|
|
1971
|
+
parentSignal?.addEventListener("abort", onParentAbort, { once: true });
|
|
1972
|
+
if (parentSignal?.aborted) onParentAbort();
|
|
1973
|
+
onStart?.(stage);
|
|
1974
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
1975
|
+
timer.unref?.();
|
|
1976
|
+
try {
|
|
1977
|
+
let available = false;
|
|
1978
|
+
try {
|
|
1979
|
+
available = await operation(controller.signal);
|
|
1980
|
+
} catch (error) {
|
|
1981
|
+
if (isMarketContextCancellationError(error)) {
|
|
1982
|
+
controller.abort(error);
|
|
1983
|
+
}
|
|
1984
|
+
if (!controller.signal.aborted) throw error;
|
|
1985
|
+
}
|
|
1986
|
+
const status = controller.signal.aborted ? "timed_out" : available ? "available" : "absent";
|
|
1987
|
+
const result = {
|
|
1988
|
+
stage,
|
|
1989
|
+
status,
|
|
1990
|
+
elapsedMs: Date.now() - startedAt
|
|
1991
|
+
};
|
|
1992
|
+
if (status === "timed_out") {
|
|
1993
|
+
logger5.warn(
|
|
1994
|
+
"Market context stage timed out: %s after %sms",
|
|
1995
|
+
stage,
|
|
1996
|
+
result.elapsedMs
|
|
1997
|
+
);
|
|
1998
|
+
}
|
|
1999
|
+
onComplete?.(result);
|
|
2000
|
+
return result;
|
|
2001
|
+
} finally {
|
|
2002
|
+
clearTimeout(timer);
|
|
2003
|
+
parentSignal?.removeEventListener("abort", onParentAbort);
|
|
2004
|
+
}
|
|
2005
|
+
};
|
|
2006
|
+
var enrichSignalWithMarketContextStages = async ({
|
|
2007
|
+
signal,
|
|
2008
|
+
env,
|
|
2009
|
+
coinMarketCapEnabled,
|
|
2010
|
+
includeHyperliquidWhales = true,
|
|
2011
|
+
abortSignal,
|
|
2012
|
+
onStageStart,
|
|
2013
|
+
onStageComplete
|
|
2014
|
+
}) => {
|
|
2015
|
+
const stages = [
|
|
2016
|
+
{
|
|
2017
|
+
stage: "binance",
|
|
2018
|
+
operation: (stageSignal) => enrichSignalWithBinanceMarketContext({
|
|
2019
|
+
signal,
|
|
2020
|
+
env,
|
|
2021
|
+
abortSignal: stageSignal
|
|
2022
|
+
})
|
|
2023
|
+
},
|
|
2024
|
+
{
|
|
2025
|
+
stage: "coinmarketcap",
|
|
2026
|
+
operation: (stageSignal) => enrichSignalWithCoinMarketCapContext({
|
|
2027
|
+
signal,
|
|
2028
|
+
env,
|
|
2029
|
+
enabled: coinMarketCapEnabled,
|
|
2030
|
+
abortSignal: stageSignal
|
|
2031
|
+
})
|
|
2032
|
+
},
|
|
2033
|
+
{
|
|
2034
|
+
stage: "derivatives",
|
|
2035
|
+
operation: (stageSignal) => enrichSignalWithDerivativesContext({
|
|
2036
|
+
signal,
|
|
2037
|
+
env,
|
|
2038
|
+
abortSignal: stageSignal
|
|
2039
|
+
})
|
|
2040
|
+
}
|
|
2041
|
+
];
|
|
2042
|
+
if (includeHyperliquidWhales) {
|
|
2043
|
+
stages.push({
|
|
2044
|
+
stage: "hyperliquidWhales",
|
|
2045
|
+
operation: (stageSignal) => enrichSignalWithHyperliquidWhaleContext({
|
|
2046
|
+
signal,
|
|
2047
|
+
env,
|
|
2048
|
+
abortSignal: stageSignal
|
|
2049
|
+
})
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
const results = [];
|
|
2053
|
+
for (const stage of stages) {
|
|
2054
|
+
if (abortSignal?.aborted) break;
|
|
2055
|
+
results.push(
|
|
2056
|
+
await runMarketContextStage({
|
|
2057
|
+
...stage,
|
|
2058
|
+
parentSignal: abortSignal,
|
|
2059
|
+
onStart: onStageStart,
|
|
2060
|
+
onComplete: onStageComplete
|
|
2061
|
+
})
|
|
2062
|
+
);
|
|
2063
|
+
}
|
|
2064
|
+
return results;
|
|
2065
|
+
};
|
|
2066
|
+
|
|
1886
2067
|
// src/strategyAdapters/ml.ts
|
|
1887
2068
|
var defaultMlAdapter = {
|
|
1888
2069
|
normalizeStrategyConfig: (strategyConfig) => strategyConfig
|
|
@@ -1939,7 +2120,6 @@ export {
|
|
|
1939
2120
|
getPrimaryBinanceBreadthUniverse,
|
|
1940
2121
|
enrichSignalWithBinanceMarketContext,
|
|
1941
2122
|
enrichSignalWithCoinMarketCapContext,
|
|
1942
|
-
enrichSignalWithDerivativesContext,
|
|
1943
2123
|
getHyperliquidPerpUniverseSnapshot,
|
|
1944
2124
|
getHyperliquidWhaleRegistrySnapshot,
|
|
1945
2125
|
getHyperliquidPerpSymbols,
|
|
@@ -1949,5 +2129,6 @@ export {
|
|
|
1949
2129
|
resolveHyperliquidPerpFromSignalSymbol,
|
|
1950
2130
|
loadHyperliquidWhaleFlowContext,
|
|
1951
2131
|
enrichSignalWithHyperliquidWhaleContext,
|
|
2132
|
+
enrichSignalWithMarketContextStages,
|
|
1952
2133
|
buildMlPayload
|
|
1953
2134
|
};
|
|
@@ -699,6 +699,15 @@ var postProcessAiAnalysisByStrategy = (signal, analysis, payload = buildAiPayloa
|
|
|
699
699
|
payload,
|
|
700
700
|
analysis
|
|
701
701
|
}) ?? analysis;
|
|
702
|
+
var postProcessLocalAiAnalysisByStrategy = (signal, analysis, payload = buildAiPayloadByStrategy(signal)) => {
|
|
703
|
+
const adapter = getSignalAiAdapter(signal);
|
|
704
|
+
const strategyAnalysis = adapter.postProcessAnalysis?.({ signal, payload, analysis }) ?? analysis;
|
|
705
|
+
return adapter.postProcessLocalAnalysis?.({
|
|
706
|
+
signal,
|
|
707
|
+
payload,
|
|
708
|
+
analysis: strategyAnalysis
|
|
709
|
+
}) ?? strategyAnalysis;
|
|
710
|
+
};
|
|
702
711
|
|
|
703
712
|
// src/ai.ts
|
|
704
713
|
var parseAIResponse = (input) => {
|
|
@@ -847,7 +856,7 @@ Input payload structure:
|
|
|
847
856
|
\u2022 \`baseContext.relative\`: BTC/ETH relative-strength, benchmark MA bias context, Binance alt-basket breadth, and CoinMarketCap historical global/exchange/index context when available.
|
|
848
857
|
\u2022 \`baseContext.derivatives\`: Coinalyze-aligned derivatives summary when available.
|
|
849
858
|
\u2022 \`baseContext.mtf\`: compact multi-timeframe summary plus only the latest few candles for each timeframe.
|
|
850
|
-
\u2022 \`baseContext.gateFeatures\`: direction-aware
|
|
859
|
+
\u2022 \`baseContext.gateFeatures\`: compact direction-aware fields derived from baseContext; prefer \`setup\`, \`scores\`, \`conflicts\`, \`risk\`, \`decisionHints\`, \`mtf\`, \`volatility\`, \`participation\`, and \`relative\` for quick gate checks before inspecting raw nested context.
|
|
851
860
|
Always inspect \`payload.additionalIndicators.marketContext\` when present:
|
|
852
861
|
\u2022 \`marketContext.execution.binanceCoinbaseSpread\`: AI-friendly BTC spread view projected from \`payload.additionalIndicators.baseContext.relative.execution.venueSpread\`; \`value=(Coinbase-Binance)/Binance\`, \`bps=value*10000\`.
|
|
853
862
|
\u2022 \`marketContext.participation.trueDelta\`: Binance taker buy/sell volume delta from kline payload when \`source=kline_taker_volume\`; otherwise absent/unavailable.
|
|
@@ -1129,7 +1138,7 @@ var runAiPromptLocal = async (signal, options = {}) => {
|
|
|
1129
1138
|
const signalDirection = getSignalDirection(signal);
|
|
1130
1139
|
const deterministicQuality = getDeterministicQuality(gateContext);
|
|
1131
1140
|
const approvalAllowedNow = typeof gateContext?.approvalAllowedNow === "boolean" ? gateContext.approvalAllowedNow : deterministicQuality >= 4;
|
|
1132
|
-
return
|
|
1141
|
+
return postProcessLocalAiAnalysisByStrategy(
|
|
1133
1142
|
signal,
|
|
1134
1143
|
{
|
|
1135
1144
|
direction: approvalAllowedNow ? signalDirection : null,
|
package/dist/cli.js
CHANGED
|
@@ -6814,7 +6814,7 @@ Input payload structure:
|
|
|
6814
6814
|
\u2022 \`baseContext.relative\`: BTC/ETH relative-strength, benchmark MA bias context, Binance alt-basket breadth, and CoinMarketCap historical global/exchange/index context when available.
|
|
6815
6815
|
\u2022 \`baseContext.derivatives\`: Coinalyze-aligned derivatives summary when available.
|
|
6816
6816
|
\u2022 \`baseContext.mtf\`: compact multi-timeframe summary plus only the latest few candles for each timeframe.
|
|
6817
|
-
\u2022 \`baseContext.gateFeatures\`: direction-aware
|
|
6817
|
+
\u2022 \`baseContext.gateFeatures\`: compact direction-aware fields derived from baseContext; prefer \`setup\`, \`scores\`, \`conflicts\`, \`risk\`, \`decisionHints\`, \`mtf\`, \`volatility\`, \`participation\`, and \`relative\` for quick gate checks before inspecting raw nested context.
|
|
6818
6818
|
Always inspect \`payload.additionalIndicators.marketContext\` when present:
|
|
6819
6819
|
\u2022 \`marketContext.execution.binanceCoinbaseSpread\`: AI-friendly BTC spread view projected from \`payload.additionalIndicators.baseContext.relative.execution.venueSpread\`; \`value=(Coinbase-Binance)/Binance\`, \`bps=value*10000\`.
|
|
6820
6820
|
\u2022 \`marketContext.participation.trueDelta\`: Binance taker buy/sell volume delta from kline payload when \`source=kline_taker_volume\`; otherwise absent/unavailable.
|