@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/strategies.js
CHANGED
|
@@ -1252,7 +1252,7 @@ var require_lodash = __commonJS({
|
|
|
1252
1252
|
if (typeof func != "function") {
|
|
1253
1253
|
throw new TypeError2(FUNC_ERROR_TEXT);
|
|
1254
1254
|
}
|
|
1255
|
-
return
|
|
1255
|
+
return setTimeout2(function() {
|
|
1256
1256
|
func.apply(undefined2, args);
|
|
1257
1257
|
}, wait);
|
|
1258
1258
|
}
|
|
@@ -2045,7 +2045,7 @@ var require_lodash = __commonJS({
|
|
|
2045
2045
|
end = end === undefined2 ? length : end;
|
|
2046
2046
|
return !start && end >= length ? array : baseSlice(array, start, end);
|
|
2047
2047
|
}
|
|
2048
|
-
var
|
|
2048
|
+
var clearTimeout2 = ctxClearTimeout || function(id) {
|
|
2049
2049
|
return root.clearTimeout(id);
|
|
2050
2050
|
};
|
|
2051
2051
|
function cloneBuffer(buffer, isDeep) {
|
|
@@ -3096,7 +3096,7 @@ var require_lodash = __commonJS({
|
|
|
3096
3096
|
return object[key];
|
|
3097
3097
|
}
|
|
3098
3098
|
var setData3 = shortOut(baseSetData);
|
|
3099
|
-
var
|
|
3099
|
+
var setTimeout2 = ctxSetTimeout || function(func, wait) {
|
|
3100
3100
|
return root.setTimeout(func, wait);
|
|
3101
3101
|
};
|
|
3102
3102
|
var setToString = shortOut(baseSetToString);
|
|
@@ -3888,7 +3888,7 @@ var require_lodash = __commonJS({
|
|
|
3888
3888
|
}
|
|
3889
3889
|
function leadingEdge(time) {
|
|
3890
3890
|
lastInvokeTime = time;
|
|
3891
|
-
timerId =
|
|
3891
|
+
timerId = setTimeout2(timerExpired, wait);
|
|
3892
3892
|
return leading ? invokeFunc(time) : result2;
|
|
3893
3893
|
}
|
|
3894
3894
|
function remainingWait(time) {
|
|
@@ -3904,7 +3904,7 @@ var require_lodash = __commonJS({
|
|
|
3904
3904
|
if (shouldInvoke(time)) {
|
|
3905
3905
|
return trailingEdge(time);
|
|
3906
3906
|
}
|
|
3907
|
-
timerId =
|
|
3907
|
+
timerId = setTimeout2(timerExpired, remainingWait(time));
|
|
3908
3908
|
}
|
|
3909
3909
|
function trailingEdge(time) {
|
|
3910
3910
|
timerId = undefined2;
|
|
@@ -3916,7 +3916,7 @@ var require_lodash = __commonJS({
|
|
|
3916
3916
|
}
|
|
3917
3917
|
function cancel() {
|
|
3918
3918
|
if (timerId !== undefined2) {
|
|
3919
|
-
|
|
3919
|
+
clearTimeout2(timerId);
|
|
3920
3920
|
}
|
|
3921
3921
|
lastInvokeTime = 0;
|
|
3922
3922
|
lastArgs = lastCallTime = lastThis = timerId = undefined2;
|
|
@@ -3934,13 +3934,13 @@ var require_lodash = __commonJS({
|
|
|
3934
3934
|
return leadingEdge(lastCallTime);
|
|
3935
3935
|
}
|
|
3936
3936
|
if (maxing) {
|
|
3937
|
-
|
|
3938
|
-
timerId =
|
|
3937
|
+
clearTimeout2(timerId);
|
|
3938
|
+
timerId = setTimeout2(timerExpired, wait);
|
|
3939
3939
|
return invokeFunc(lastCallTime);
|
|
3940
3940
|
}
|
|
3941
3941
|
}
|
|
3942
3942
|
if (timerId === undefined2) {
|
|
3943
|
-
timerId =
|
|
3943
|
+
timerId = setTimeout2(timerExpired, wait);
|
|
3944
3944
|
}
|
|
3945
3945
|
return result2;
|
|
3946
3946
|
}
|
|
@@ -6796,6 +6796,15 @@ var postProcessAiAnalysisByStrategy = (signal, analysis, payload = buildAiPayloa
|
|
|
6796
6796
|
payload,
|
|
6797
6797
|
analysis
|
|
6798
6798
|
}) ?? analysis;
|
|
6799
|
+
var postProcessLocalAiAnalysisByStrategy = (signal, analysis, payload = buildAiPayloadByStrategy(signal)) => {
|
|
6800
|
+
const adapter = getSignalAiAdapter(signal);
|
|
6801
|
+
const strategyAnalysis = adapter.postProcessAnalysis?.({ signal, payload, analysis }) ?? analysis;
|
|
6802
|
+
return adapter.postProcessLocalAnalysis?.({
|
|
6803
|
+
signal,
|
|
6804
|
+
payload,
|
|
6805
|
+
analysis: strategyAnalysis
|
|
6806
|
+
}) ?? strategyAnalysis;
|
|
6807
|
+
};
|
|
6799
6808
|
|
|
6800
6809
|
// src/ai.ts
|
|
6801
6810
|
var parseAIResponse = (input) => {
|
|
@@ -6944,7 +6953,7 @@ Input payload structure:
|
|
|
6944
6953
|
\u2022 \`baseContext.relative\`: BTC/ETH relative-strength, benchmark MA bias context, Binance alt-basket breadth, and CoinMarketCap historical global/exchange/index context when available.
|
|
6945
6954
|
\u2022 \`baseContext.derivatives\`: Coinalyze-aligned derivatives summary when available.
|
|
6946
6955
|
\u2022 \`baseContext.mtf\`: compact multi-timeframe summary plus only the latest few candles for each timeframe.
|
|
6947
|
-
\u2022 \`baseContext.gateFeatures\`: direction-aware
|
|
6956
|
+
\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.
|
|
6948
6957
|
Always inspect \`payload.additionalIndicators.marketContext\` when present:
|
|
6949
6958
|
\u2022 \`marketContext.execution.binanceCoinbaseSpread\`: AI-friendly BTC spread view projected from \`payload.additionalIndicators.baseContext.relative.execution.venueSpread\`; \`value=(Coinbase-Binance)/Binance\`, \`bps=value*10000\`.
|
|
6950
6959
|
\u2022 \`marketContext.participation.trueDelta\`: Binance taker buy/sell volume delta from kline payload when \`source=kline_taker_volume\`; otherwise absent/unavailable.
|
|
@@ -7226,7 +7235,7 @@ var runAiPromptLocal = async (signal, options = {}) => {
|
|
|
7226
7235
|
const signalDirection = getSignalDirection(signal);
|
|
7227
7236
|
const deterministicQuality = getDeterministicQuality(gateContext);
|
|
7228
7237
|
const approvalAllowedNow = typeof gateContext?.approvalAllowedNow === "boolean" ? gateContext.approvalAllowedNow : deterministicQuality >= 4;
|
|
7229
|
-
return
|
|
7238
|
+
return postProcessLocalAiAnalysisByStrategy(
|
|
7230
7239
|
signal,
|
|
7231
7240
|
{
|
|
7232
7241
|
direction: approvalAllowedNow ? signalDirection : null,
|
|
@@ -7263,10 +7272,10 @@ var import_node_path2 = __toESM(require("path"));
|
|
|
7263
7272
|
var import_constants4 = require("@tradejs/core/constants");
|
|
7264
7273
|
var import_data3 = require("@tradejs/core/data");
|
|
7265
7274
|
var import_strategies5 = require("@tradejs/core/strategies");
|
|
7266
|
-
var
|
|
7275
|
+
var import_logger10 = require("@tradejs/infra/logger");
|
|
7267
7276
|
|
|
7268
7277
|
// src/strategyHelpers/runtime.ts
|
|
7269
|
-
var
|
|
7278
|
+
var import_logger9 = require("@tradejs/infra/logger");
|
|
7270
7279
|
var import_constants3 = require("@tradejs/core/constants");
|
|
7271
7280
|
var import_ml2 = require("@tradejs/infra/ml");
|
|
7272
7281
|
|
|
@@ -7529,6 +7538,7 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
7529
7538
|
lastSyncedAt: now()
|
|
7530
7539
|
};
|
|
7531
7540
|
const dayKey = (0, import_time.getRuntimeStorageDayKey)(existing.entryTimestamp);
|
|
7541
|
+
const closeDayKey = (0, import_time.getRuntimeStorageDayKey)(next.exitTimestamp);
|
|
7532
7542
|
try {
|
|
7533
7543
|
await Promise.all([
|
|
7534
7544
|
(0, import_redis2.setData)(import_redis2.redisKeys.runtimeTrade(userName, orderId), next, {
|
|
@@ -7540,6 +7550,12 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
7540
7550
|
next,
|
|
7541
7551
|
{ expire: import_constants.TTL_1M }
|
|
7542
7552
|
),
|
|
7553
|
+
(0, import_redis2.setHashJsonField)(
|
|
7554
|
+
import_redis2.redisKeys.runtimeClosedTradeBucket(userName, closeDayKey),
|
|
7555
|
+
orderId,
|
|
7556
|
+
next,
|
|
7557
|
+
{ expire: import_constants.TTL_1M }
|
|
7558
|
+
),
|
|
7543
7559
|
(0, import_redis2.delKey)(
|
|
7544
7560
|
import_redis2.redisKeys.runtimeActiveTrade(
|
|
7545
7561
|
userName,
|
|
@@ -7558,251 +7574,13 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
7558
7574
|
return next;
|
|
7559
7575
|
};
|
|
7560
7576
|
|
|
7561
|
-
// src/strategyHelpers/
|
|
7562
|
-
var
|
|
7563
|
-
var import_data = require("@tradejs/core/data");
|
|
7564
|
-
var import_strategies = require("@tradejs/core/strategies");
|
|
7565
|
-
var import_constants2 = require("@tradejs/core/constants");
|
|
7566
|
-
var import_timescale = require("@tradejs/infra/timescale");
|
|
7567
|
-
var import_logger4 = require("@tradejs/infra/logger");
|
|
7568
|
-
var STORED_INTERVALS = ["15m", "1h"];
|
|
7569
|
-
var CONTEXT_INTERVALS = ["15m", "1h"];
|
|
7570
|
-
var DEFAULT_LOOKBACK_HOURS = 48;
|
|
7571
|
-
var PRIMARY_DERIVATIVES_REFERENCE_SYMBOL = import_constants2.DERIVATIVES_CONTEXT_BASE_REFERENCE_SYMBOLS[0];
|
|
7572
|
-
var SECONDARY_DERIVATIVES_REFERENCE_SYMBOL = import_constants2.DERIVATIVES_CONTEXT_BASE_REFERENCE_SYMBOLS[1];
|
|
7573
|
-
var derivativesContextUnavailable = false;
|
|
7574
|
-
var parseEnabledFlag = (value, env) => {
|
|
7575
|
-
const normalized = String(value ?? "").trim().toLowerCase();
|
|
7576
|
-
if (!normalized) return true;
|
|
7577
|
-
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
7578
|
-
if (normalized === "backtest") return env === "BACKTEST";
|
|
7579
|
-
if (normalized === "live") return env !== "BACKTEST";
|
|
7580
|
-
return false;
|
|
7581
|
-
};
|
|
7582
|
-
var parseBooleanFlag = (value, fallback = false) => {
|
|
7583
|
-
const normalized = String(value ?? "").trim().toLowerCase();
|
|
7584
|
-
if (!normalized) return fallback;
|
|
7585
|
-
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
7586
|
-
if (["0", "false", "no", "off"].includes(normalized)) return false;
|
|
7587
|
-
return fallback;
|
|
7588
|
-
};
|
|
7589
|
-
var parseLookbackMs = () => {
|
|
7590
|
-
const hours = Number(process.env.DERIVATIVES_CONTEXT_LOOKBACK_HOURS);
|
|
7591
|
-
const normalizedHours = Number.isFinite(hours) && hours > 0 ? hours : DEFAULT_LOOKBACK_HOURS;
|
|
7592
|
-
return normalizedHours * 60 * 60 * 1e3;
|
|
7593
|
-
};
|
|
7594
|
-
var withHourlyFallbackRows = (rowsByInterval) => ({
|
|
7595
|
-
"15m": rowsByInterval["15m"] ?? [],
|
|
7596
|
-
"1h": (0, import_indicators2.buildCoinalyzeHourlyRowsWithFallback)({
|
|
7597
|
-
rows15m: rowsByInterval["15m"],
|
|
7598
|
-
fallbackRows1h: rowsByInterval["1h"]
|
|
7599
|
-
})
|
|
7600
|
-
});
|
|
7601
|
-
var getDerivativesContextReferenceSymbols = () => [
|
|
7602
|
-
...(0, import_constants2.resolveDerivativesContextReferenceSymbols)(
|
|
7603
|
-
process.env.DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS
|
|
7604
|
-
)
|
|
7605
|
-
];
|
|
7606
|
-
var normalizeSymbol = (symbol) => String(symbol || "").trim().toUpperCase();
|
|
7607
|
-
var getSignalPriceChangePct1h = (signal) => {
|
|
7608
|
-
const baseContext = signal.additionalIndicators?.baseContext;
|
|
7609
|
-
if (!baseContext || typeof baseContext !== "object" || Array.isArray(baseContext)) {
|
|
7610
|
-
return null;
|
|
7611
|
-
}
|
|
7612
|
-
const raw = typeof baseContext.raw === "object" && baseContext.raw && !Array.isArray(baseContext.raw) ? baseContext.raw : null;
|
|
7613
|
-
const price = raw && typeof raw.price === "object" && raw.price && !Array.isArray(raw.price) ? raw.price : null;
|
|
7614
|
-
const value = price?.price1hPct;
|
|
7615
|
-
const numeric = typeof value === "number" ? value : Number(value);
|
|
7616
|
-
return Number.isFinite(numeric) ? numeric : null;
|
|
7617
|
-
};
|
|
7618
|
-
var resolvePrimaryReferenceSymbol = () => PRIMARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
7619
|
-
var resolveSecondaryReferenceSymbol = () => SECONDARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
7620
|
-
var getPrimaryIntervalContext = (context) => context?.intervals["15m"] ?? context?.intervals["1h"] ?? null;
|
|
7621
|
-
var hasDerivativesSymbolData = (context) => Object.keys(context.intervals).length > 0 && !context.summary.riskFlags.includes("missing_derivatives");
|
|
7622
|
-
var toFiniteNumberOrNull = (value) => {
|
|
7623
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
7624
|
-
if (typeof value === "string" && value.trim()) {
|
|
7625
|
-
const parsed = Number(value);
|
|
7626
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
7627
|
-
}
|
|
7628
|
-
return null;
|
|
7629
|
-
};
|
|
7630
|
-
var roundNullable = (value, digits = 4) => {
|
|
7631
|
-
if (value == null || !Number.isFinite(value)) return null;
|
|
7632
|
-
const multiplier = 10 ** digits;
|
|
7633
|
-
return Math.round(value * multiplier) / multiplier;
|
|
7634
|
-
};
|
|
7635
|
-
var deltaNullable = (targetValue, referenceValue) => {
|
|
7636
|
-
const target = toFiniteNumberOrNull(targetValue);
|
|
7637
|
-
const reference = toFiniteNumberOrNull(referenceValue);
|
|
7638
|
-
return target == null || reference == null ? null : roundNullable(target - reference);
|
|
7639
|
-
};
|
|
7640
|
-
var buildTargetDerivedContext = (params) => {
|
|
7641
|
-
const { targetContext, primaryReferenceContext } = params;
|
|
7642
|
-
const targetPrimary = getPrimaryIntervalContext(targetContext);
|
|
7643
|
-
const referencePrimary = getPrimaryIntervalContext(primaryReferenceContext);
|
|
7644
|
-
const targetDirectionAligned = targetContext.summary.directionAligned;
|
|
7645
|
-
const referenceDirectionAligned = primaryReferenceContext?.summary.directionAligned ?? null;
|
|
7646
|
-
return {
|
|
7647
|
-
available: hasDerivativesSymbolData(targetContext),
|
|
7648
|
-
stale: targetContext.summary.riskFlags.includes("stale_derivatives") || targetPrimary?.stale === true ? true : targetPrimary == null ? null : false,
|
|
7649
|
-
sourceSymbol: targetContext.symbol,
|
|
7650
|
-
referenceSymbol: primaryReferenceContext?.symbol ?? null,
|
|
7651
|
-
directionAligned: targetDirectionAligned,
|
|
7652
|
-
referenceDirectionAligned,
|
|
7653
|
-
pressure: targetContext.summary.pressure ?? null,
|
|
7654
|
-
referencePressure: primaryReferenceContext?.summary.pressure ?? null,
|
|
7655
|
-
riskFlags: targetContext.summary.riskFlags,
|
|
7656
|
-
oiChangePct1h: targetPrimary?.oiChangePct1h ?? null,
|
|
7657
|
-
oiAcceleration: targetContext.summary.oiAcceleration ?? null,
|
|
7658
|
-
fundingRate: targetPrimary?.fundingRate ?? null,
|
|
7659
|
-
fundingZScore: targetPrimary?.fundingZScore ?? null,
|
|
7660
|
-
fundingChange1h: targetContext.summary.fundingChange1h ?? null,
|
|
7661
|
-
liqSpikeRatio: targetPrimary?.liqSpikeRatio ?? null,
|
|
7662
|
-
liqImbalance: targetPrimary?.liqImbalance ?? null,
|
|
7663
|
-
targetVsPrimaryOiChangePct1hDelta: deltaNullable(
|
|
7664
|
-
targetPrimary?.oiChangePct1h,
|
|
7665
|
-
referencePrimary?.oiChangePct1h
|
|
7666
|
-
),
|
|
7667
|
-
targetVsPrimaryFundingZScoreDelta: deltaNullable(
|
|
7668
|
-
targetPrimary?.fundingZScore,
|
|
7669
|
-
referencePrimary?.fundingZScore
|
|
7670
|
-
),
|
|
7671
|
-
targetReferenceConflict: targetDirectionAligned == null || referenceDirectionAligned == null ? null : targetDirectionAligned !== referenceDirectionAligned
|
|
7672
|
-
};
|
|
7673
|
-
};
|
|
7674
|
-
var buildReferenceDerivativesContext = (params) => {
|
|
7675
|
-
const {
|
|
7676
|
-
targetSymbol,
|
|
7677
|
-
primaryReferenceSymbol,
|
|
7678
|
-
secondaryReferenceSymbol,
|
|
7679
|
-
referenceSymbols,
|
|
7680
|
-
referenceContexts,
|
|
7681
|
-
targetContext
|
|
7682
|
-
} = params;
|
|
7683
|
-
const primaryContext = referenceContexts[primaryReferenceSymbol] ?? referenceContexts[referenceSymbols[0]];
|
|
7684
|
-
if (!primaryContext) {
|
|
7685
|
-
throw new Error("No derivatives reference contexts built");
|
|
7686
|
-
}
|
|
7687
|
-
const referenceSymbolsMetadata = [
|
|
7688
|
-
.../* @__PURE__ */ new Set([
|
|
7689
|
-
primaryReferenceSymbol,
|
|
7690
|
-
secondaryReferenceSymbol,
|
|
7691
|
-
...referenceSymbols,
|
|
7692
|
-
...Object.keys(referenceContexts)
|
|
7693
|
-
])
|
|
7694
|
-
];
|
|
7695
|
-
const targetDerived = targetContext && hasDerivativesSymbolData(targetContext) ? buildTargetDerivedContext({
|
|
7696
|
-
targetContext,
|
|
7697
|
-
primaryReferenceContext: primaryContext
|
|
7698
|
-
}) : void 0;
|
|
7699
|
-
return {
|
|
7700
|
-
...primaryContext,
|
|
7701
|
-
targetSymbol,
|
|
7702
|
-
primaryReferenceSymbol: primaryContext.symbol,
|
|
7703
|
-
secondaryReferenceSymbol: referenceContexts[secondaryReferenceSymbol]?.symbol ?? secondaryReferenceSymbol,
|
|
7704
|
-
referenceSymbols: referenceSymbolsMetadata,
|
|
7705
|
-
referenceContexts,
|
|
7706
|
-
...targetContext && targetDerived ? {
|
|
7707
|
-
targetContext,
|
|
7708
|
-
targetDerived
|
|
7709
|
-
} : {}
|
|
7710
|
-
};
|
|
7711
|
-
};
|
|
7712
|
-
var isDerivativesContextEnabled = (env) => parseEnabledFlag(process.env.DERIVATIVES_CONTEXT_ENABLED, env);
|
|
7713
|
-
var isDerivativesTargetContextEnabled = () => parseBooleanFlag(process.env.DERIVATIVES_CONTEXT_TARGET_ENABLED, false);
|
|
7714
|
-
var enrichSignalWithDerivativesContext = async (params) => {
|
|
7715
|
-
const { signal, env, enabled = isDerivativesContextEnabled(env) } = params;
|
|
7716
|
-
if (signal.universe === "tradfi" || !enabled || derivativesContextUnavailable) {
|
|
7717
|
-
return false;
|
|
7718
|
-
}
|
|
7719
|
-
try {
|
|
7720
|
-
const referenceSymbols = getDerivativesContextReferenceSymbols();
|
|
7721
|
-
const targetSymbol = normalizeSymbol(signal.symbol);
|
|
7722
|
-
const lookbackMs = parseLookbackMs();
|
|
7723
|
-
const decisionTimeMs = signal.timestamp + (0, import_data.intervalToMs)(signal.interval);
|
|
7724
|
-
const derivativesEndMs = (0, import_indicators2.getLastClosedDerivativesBarStartMs)(
|
|
7725
|
-
decisionTimeMs,
|
|
7726
|
-
"15m"
|
|
7727
|
-
);
|
|
7728
|
-
const contexts = await Promise.all(
|
|
7729
|
-
referenceSymbols.map(async (symbol) => {
|
|
7730
|
-
const rowsByInterval = await (0, import_timescale.getDerivativesWindow)({
|
|
7731
|
-
symbol,
|
|
7732
|
-
intervals: STORED_INTERVALS,
|
|
7733
|
-
endMs: derivativesEndMs,
|
|
7734
|
-
lookbackMs
|
|
7735
|
-
});
|
|
7736
|
-
return [
|
|
7737
|
-
symbol,
|
|
7738
|
-
(0, import_indicators2.buildDerivativesContext)({
|
|
7739
|
-
symbol,
|
|
7740
|
-
direction: signal.direction,
|
|
7741
|
-
timestamp: derivativesEndMs,
|
|
7742
|
-
rowsByInterval: withHourlyFallbackRows(rowsByInterval),
|
|
7743
|
-
priceChangePct1h: getSignalPriceChangePct1h(signal),
|
|
7744
|
-
intervals: CONTEXT_INTERVALS
|
|
7745
|
-
})
|
|
7746
|
-
];
|
|
7747
|
-
})
|
|
7748
|
-
);
|
|
7749
|
-
const referenceContexts = Object.fromEntries(contexts);
|
|
7750
|
-
const primaryReferenceSymbol = resolvePrimaryReferenceSymbol();
|
|
7751
|
-
const secondaryReferenceSymbol = resolveSecondaryReferenceSymbol();
|
|
7752
|
-
const targetContextEnabled = isDerivativesTargetContextEnabled();
|
|
7753
|
-
const referenceTargetContext = targetContextEnabled && targetSymbol !== primaryReferenceSymbol ? referenceContexts[targetSymbol] : void 0;
|
|
7754
|
-
const shouldFetchTargetContext = targetContextEnabled && targetSymbol.length > 0 && !referenceSymbols.some(
|
|
7755
|
-
(referenceSymbol) => referenceSymbol === targetSymbol
|
|
7756
|
-
);
|
|
7757
|
-
const fetchedTargetContext = shouldFetchTargetContext ? await (async () => {
|
|
7758
|
-
const rowsByInterval = await (0, import_timescale.getDerivativesWindow)({
|
|
7759
|
-
symbol: targetSymbol,
|
|
7760
|
-
intervals: STORED_INTERVALS,
|
|
7761
|
-
endMs: derivativesEndMs,
|
|
7762
|
-
lookbackMs
|
|
7763
|
-
});
|
|
7764
|
-
const context = (0, import_indicators2.buildDerivativesContext)({
|
|
7765
|
-
symbol: targetSymbol,
|
|
7766
|
-
direction: signal.direction,
|
|
7767
|
-
timestamp: derivativesEndMs,
|
|
7768
|
-
rowsByInterval: withHourlyFallbackRows(rowsByInterval),
|
|
7769
|
-
priceChangePct1h: getSignalPriceChangePct1h(signal),
|
|
7770
|
-
intervals: CONTEXT_INTERVALS
|
|
7771
|
-
});
|
|
7772
|
-
return hasDerivativesSymbolData(context) ? context : void 0;
|
|
7773
|
-
})() : void 0;
|
|
7774
|
-
const targetContext = referenceTargetContext && hasDerivativesSymbolData(referenceTargetContext) ? referenceTargetContext : fetchedTargetContext;
|
|
7775
|
-
const derivativesContext = buildReferenceDerivativesContext({
|
|
7776
|
-
targetSymbol: targetSymbol || signal.symbol,
|
|
7777
|
-
primaryReferenceSymbol,
|
|
7778
|
-
secondaryReferenceSymbol,
|
|
7779
|
-
referenceSymbols,
|
|
7780
|
-
referenceContexts,
|
|
7781
|
-
targetContext
|
|
7782
|
-
});
|
|
7783
|
-
signal.additionalIndicators = {
|
|
7784
|
-
...signal.additionalIndicators ?? {},
|
|
7785
|
-
baseContext: signal.additionalIndicators?.baseContext && typeof signal.additionalIndicators.baseContext === "object" && !Array.isArray(signal.additionalIndicators.baseContext) ? {
|
|
7786
|
-
...signal.additionalIndicators.baseContext,
|
|
7787
|
-
derivatives: derivativesContext
|
|
7788
|
-
} : signal.additionalIndicators?.baseContext
|
|
7789
|
-
};
|
|
7790
|
-
(0, import_strategies.refreshSignalBaseContextGateFeatures)(signal);
|
|
7791
|
-
return true;
|
|
7792
|
-
} catch (error) {
|
|
7793
|
-
derivativesContextUnavailable = true;
|
|
7794
|
-
import_logger4.logger.warn(
|
|
7795
|
-
"Derivatives context disabled after Timescale read failure: %s",
|
|
7796
|
-
String(error)
|
|
7797
|
-
);
|
|
7798
|
-
return false;
|
|
7799
|
-
}
|
|
7800
|
-
};
|
|
7577
|
+
// src/strategyHelpers/marketContextStages.ts
|
|
7578
|
+
var import_logger8 = require("@tradejs/infra/logger");
|
|
7801
7579
|
|
|
7802
7580
|
// src/strategyHelpers/binanceMarketContext.ts
|
|
7803
|
-
var
|
|
7804
|
-
var
|
|
7805
|
-
var
|
|
7581
|
+
var import_timescale = require("@tradejs/infra/timescale");
|
|
7582
|
+
var import_logger4 = require("@tradejs/infra/logger");
|
|
7583
|
+
var import_strategies = require("@tradejs/core/strategies");
|
|
7806
7584
|
|
|
7807
7585
|
// src/binanceBreadthUniverses.ts
|
|
7808
7586
|
var import_node_crypto2 = require("crypto");
|
|
@@ -8109,6 +7887,13 @@ var getBinanceBreadthUniverses = () => BINANCE_BREADTH_UNIVERSE_KEYS.map((key) =
|
|
|
8109
7887
|
});
|
|
8110
7888
|
var getPrimaryBinanceBreadthUniverse = () => getBinanceBreadthUniverses().find(({ key }) => key === "top30");
|
|
8111
7889
|
|
|
7890
|
+
// src/strategyHelpers/marketContextErrors.ts
|
|
7891
|
+
var MARKET_CONTEXT_CANCELLATION_ERROR_NAMES = /* @__PURE__ */ new Set([
|
|
7892
|
+
"AbortError",
|
|
7893
|
+
"TimescaleQueryTimeoutError"
|
|
7894
|
+
]);
|
|
7895
|
+
var isMarketContextCancellationError = (error, abortSignal) => abortSignal?.aborted === true || error instanceof Error && MARKET_CONTEXT_CANCELLATION_ERROR_NAMES.has(error.name);
|
|
7896
|
+
|
|
8112
7897
|
// src/strategyHelpers/binanceMarketContext.ts
|
|
8113
7898
|
var DEFAULT_MAX_AGE_BY_INTERVAL = {
|
|
8114
7899
|
"1m": 3 * 6e4,
|
|
@@ -8119,7 +7904,7 @@ var DEFAULT_MAX_AGE_BY_INTERVAL = {
|
|
|
8119
7904
|
var binanceMarketContextUnavailable = false;
|
|
8120
7905
|
var referenceRowsCache = /* @__PURE__ */ new Map();
|
|
8121
7906
|
var breadthCache = /* @__PURE__ */ new Map();
|
|
8122
|
-
var
|
|
7907
|
+
var parseEnabledFlag = (value, env) => {
|
|
8123
7908
|
const normalized = String(value ?? "").trim().toLowerCase();
|
|
8124
7909
|
if (!normalized)
|
|
8125
7910
|
return env === "BACKTEST" || env === "CRON" || env === "PARITY";
|
|
@@ -8129,7 +7914,7 @@ var parseEnabledFlag2 = (value, env) => {
|
|
|
8129
7914
|
if (normalized === "live") return env !== "BACKTEST";
|
|
8130
7915
|
return false;
|
|
8131
7916
|
};
|
|
8132
|
-
var
|
|
7917
|
+
var toFiniteNumberOrNull = (value) => {
|
|
8133
7918
|
const numeric = typeof value === "number" ? value : Number(value);
|
|
8134
7919
|
return Number.isFinite(numeric) ? numeric : null;
|
|
8135
7920
|
};
|
|
@@ -8145,7 +7930,7 @@ var getReferenceSymbols = () => {
|
|
|
8145
7930
|
const symbols = (process.env.BINANCE_MARKET_CONTEXT_REFERENCE_SYMBOLS || "BTCUSDT,ETHUSDT").split(",").map((item) => item.trim().toUpperCase()).filter(Boolean);
|
|
8146
7931
|
return symbols.length ? [...new Set(symbols)] : ["BTCUSDT", "ETHUSDT"];
|
|
8147
7932
|
};
|
|
8148
|
-
var
|
|
7933
|
+
var resolvePrimaryReferenceSymbol = (signalSymbol) => {
|
|
8149
7934
|
const symbol = signalSymbol.trim().toUpperCase();
|
|
8150
7935
|
const referenceSymbols = getReferenceSymbols();
|
|
8151
7936
|
return referenceSymbols.includes(symbol) ? symbol : referenceSymbols[0];
|
|
@@ -8153,38 +7938,40 @@ var resolvePrimaryReferenceSymbol2 = (signalSymbol) => {
|
|
|
8153
7938
|
var hasBaseContext = (signal) => Boolean(
|
|
8154
7939
|
signal.additionalIndicators?.baseContext && typeof signal.additionalIndicators.baseContext === "object" && !Array.isArray(signal.additionalIndicators.baseContext)
|
|
8155
7940
|
);
|
|
8156
|
-
var isBinanceMarketContextEnabled = (env) =>
|
|
7941
|
+
var isBinanceMarketContextEnabled = (env) => parseEnabledFlag(process.env.BINANCE_MARKET_CONTEXT_ENABLED, env);
|
|
8157
7942
|
var toTradeFlowContext = (row, interval) => row ? {
|
|
8158
7943
|
source: "binance_agg_trades",
|
|
8159
7944
|
interval,
|
|
8160
7945
|
asOfTs: row.ts.getTime(),
|
|
8161
7946
|
ageMs: row.ageMs,
|
|
8162
7947
|
stale: row.stale,
|
|
8163
|
-
trades:
|
|
8164
|
-
buyPressurePct:
|
|
8165
|
-
buyBaseVolume:
|
|
8166
|
-
sellBaseVolume:
|
|
8167
|
-
buyQuoteVolume:
|
|
8168
|
-
sellQuoteVolume:
|
|
8169
|
-
netBaseDelta:
|
|
8170
|
-
netQuoteDelta:
|
|
7948
|
+
trades: toFiniteNumberOrNull(row.trades),
|
|
7949
|
+
buyPressurePct: toFiniteNumberOrNull(row.buyPressurePct),
|
|
7950
|
+
buyBaseVolume: toFiniteNumberOrNull(row.buyBaseVolume),
|
|
7951
|
+
sellBaseVolume: toFiniteNumberOrNull(row.sellBaseVolume),
|
|
7952
|
+
buyQuoteVolume: toFiniteNumberOrNull(row.buyQuoteVolume),
|
|
7953
|
+
sellQuoteVolume: toFiniteNumberOrNull(row.sellQuoteVolume),
|
|
7954
|
+
netBaseDelta: toFiniteNumberOrNull(row.netBaseDelta),
|
|
7955
|
+
netQuoteDelta: toFiniteNumberOrNull(row.netQuoteDelta)
|
|
8171
7956
|
} : null;
|
|
8172
7957
|
var getCachedReferenceRows = ({
|
|
8173
7958
|
referenceSymbols,
|
|
8174
7959
|
interval,
|
|
8175
7960
|
timestamp,
|
|
8176
|
-
maxAgeMs
|
|
7961
|
+
maxAgeMs,
|
|
7962
|
+
abortSignal
|
|
8177
7963
|
}) => {
|
|
8178
7964
|
const key = `${referenceSymbols.join(",")}:${interval}:${timestamp}:${maxAgeMs}`;
|
|
8179
7965
|
const cached = referenceRowsCache.get(key);
|
|
8180
7966
|
if (cached) return cached;
|
|
8181
7967
|
const promise = Promise.all(
|
|
8182
7968
|
referenceSymbols.map(async (symbol) => {
|
|
8183
|
-
const tradeFlow = await (0,
|
|
7969
|
+
const tradeFlow = await (0, import_timescale.getLatestMarketTradeFlow)({
|
|
8184
7970
|
symbol,
|
|
8185
7971
|
interval,
|
|
8186
7972
|
atMs: timestamp,
|
|
8187
|
-
maxAgeMs
|
|
7973
|
+
maxAgeMs,
|
|
7974
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
8188
7975
|
});
|
|
8189
7976
|
return {
|
|
8190
7977
|
symbol,
|
|
@@ -8193,24 +7980,28 @@ var getCachedReferenceRows = ({
|
|
|
8193
7980
|
})
|
|
8194
7981
|
);
|
|
8195
7982
|
referenceRowsCache.set(key, promise);
|
|
7983
|
+
void promise.catch(() => referenceRowsCache.delete(key));
|
|
8196
7984
|
return promise;
|
|
8197
7985
|
};
|
|
8198
7986
|
var getCachedBreadth = ({
|
|
8199
7987
|
breadthUniverse,
|
|
8200
7988
|
interval,
|
|
8201
7989
|
timestamp,
|
|
8202
|
-
maxAgeMs
|
|
7990
|
+
maxAgeMs,
|
|
7991
|
+
abortSignal
|
|
8203
7992
|
}) => {
|
|
8204
7993
|
const key = `${breadthUniverse}:${interval}:${timestamp}:${maxAgeMs}`;
|
|
8205
7994
|
const cached = breadthCache.get(key);
|
|
8206
7995
|
if (cached) return cached;
|
|
8207
|
-
const promise = (0,
|
|
7996
|
+
const promise = (0, import_timescale.getLatestMarketBreadth)({
|
|
8208
7997
|
universe: breadthUniverse,
|
|
8209
7998
|
interval,
|
|
8210
7999
|
atMs: timestamp,
|
|
8211
|
-
maxAgeMs
|
|
8000
|
+
maxAgeMs,
|
|
8001
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
8212
8002
|
});
|
|
8213
8003
|
breadthCache.set(key, promise);
|
|
8004
|
+
void promise.catch(() => breadthCache.delete(key));
|
|
8214
8005
|
return promise;
|
|
8215
8006
|
};
|
|
8216
8007
|
var toMarketBreadthContext = (breadth, interval) => ({
|
|
@@ -8220,16 +8011,16 @@ var toMarketBreadthContext = (breadth, interval) => ({
|
|
|
8220
8011
|
asOfTs: breadth.ts.getTime(),
|
|
8221
8012
|
ageMs: breadth.ageMs,
|
|
8222
8013
|
stale: breadth.stale,
|
|
8223
|
-
symbolsCount:
|
|
8224
|
-
advancers:
|
|
8225
|
-
decliners:
|
|
8226
|
-
unchanged:
|
|
8227
|
-
advanceDeclineRatio:
|
|
8228
|
-
pctAboveMa20:
|
|
8229
|
-
pctAboveMa50:
|
|
8230
|
-
equalWeightedReturn:
|
|
8231
|
-
volumeWeightedReturn:
|
|
8232
|
-
dispersion:
|
|
8014
|
+
symbolsCount: toFiniteNumberOrNull(breadth.symbolsCount),
|
|
8015
|
+
advancers: toFiniteNumberOrNull(breadth.advancers),
|
|
8016
|
+
decliners: toFiniteNumberOrNull(breadth.decliners),
|
|
8017
|
+
unchanged: toFiniteNumberOrNull(breadth.unchanged),
|
|
8018
|
+
advanceDeclineRatio: toFiniteNumberOrNull(breadth.advanceDeclineRatio),
|
|
8019
|
+
pctAboveMa20: toFiniteNumberOrNull(breadth.pctAboveMa20),
|
|
8020
|
+
pctAboveMa50: toFiniteNumberOrNull(breadth.pctAboveMa50),
|
|
8021
|
+
equalWeightedReturn: toFiniteNumberOrNull(breadth.equalWeightedReturn),
|
|
8022
|
+
volumeWeightedReturn: toFiniteNumberOrNull(breadth.volumeWeightedReturn),
|
|
8023
|
+
dispersion: toFiniteNumberOrNull(breadth.dispersion)
|
|
8233
8024
|
});
|
|
8234
8025
|
var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
8235
8026
|
const {
|
|
@@ -8244,7 +8035,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
8244
8035
|
}
|
|
8245
8036
|
try {
|
|
8246
8037
|
const referenceSymbols = getReferenceSymbols();
|
|
8247
|
-
const primaryReferenceSymbol =
|
|
8038
|
+
const primaryReferenceSymbol = resolvePrimaryReferenceSymbol(signal.symbol);
|
|
8248
8039
|
const breadthUniverses = params.breadthUniverse ? [
|
|
8249
8040
|
{
|
|
8250
8041
|
key: "top30",
|
|
@@ -8256,7 +8047,8 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
8256
8047
|
referenceSymbols,
|
|
8257
8048
|
interval,
|
|
8258
8049
|
timestamp: signal.timestamp,
|
|
8259
|
-
maxAgeMs
|
|
8050
|
+
maxAgeMs,
|
|
8051
|
+
abortSignal: params.abortSignal
|
|
8260
8052
|
}),
|
|
8261
8053
|
Promise.all(
|
|
8262
8054
|
breadthUniverses.map(async ({ key, universe }) => ({
|
|
@@ -8265,7 +8057,8 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
8265
8057
|
breadthUniverse: universe,
|
|
8266
8058
|
interval,
|
|
8267
8059
|
timestamp: signal.timestamp,
|
|
8268
|
-
maxAgeMs
|
|
8060
|
+
maxAgeMs,
|
|
8061
|
+
abortSignal: params.abortSignal
|
|
8269
8062
|
})
|
|
8270
8063
|
}))
|
|
8271
8064
|
)
|
|
@@ -8328,46 +8121,46 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
8328
8121
|
asOfTs: primaryBreadth.ts.getTime(),
|
|
8329
8122
|
ageMs: primaryBreadth.ageMs,
|
|
8330
8123
|
stale: primaryBreadth.stale,
|
|
8331
|
-
btcReturn1h:
|
|
8124
|
+
btcReturn1h: toFiniteNumberOrNull(
|
|
8332
8125
|
primaryBreadth.btcReturn1h
|
|
8333
8126
|
),
|
|
8334
|
-
btcReturn4h:
|
|
8127
|
+
btcReturn4h: toFiniteNumberOrNull(
|
|
8335
8128
|
primaryBreadth.btcReturn4h
|
|
8336
8129
|
),
|
|
8337
|
-
btcReturn24h:
|
|
8130
|
+
btcReturn24h: toFiniteNumberOrNull(
|
|
8338
8131
|
primaryBreadth.btcReturn24h
|
|
8339
8132
|
),
|
|
8340
|
-
altBasketReturn1h:
|
|
8133
|
+
altBasketReturn1h: toFiniteNumberOrNull(
|
|
8341
8134
|
primaryBreadth.altBasketReturn1h
|
|
8342
8135
|
),
|
|
8343
|
-
altBasketReturn4h:
|
|
8136
|
+
altBasketReturn4h: toFiniteNumberOrNull(
|
|
8344
8137
|
primaryBreadth.altBasketReturn4h
|
|
8345
8138
|
),
|
|
8346
|
-
altBasketReturn24h:
|
|
8139
|
+
altBasketReturn24h: toFiniteNumberOrNull(
|
|
8347
8140
|
primaryBreadth.altBasketReturn24h
|
|
8348
8141
|
),
|
|
8349
|
-
btcVsAltReturn1h:
|
|
8142
|
+
btcVsAltReturn1h: toFiniteNumberOrNull(
|
|
8350
8143
|
primaryBreadth.btcVsAltReturn1h
|
|
8351
8144
|
),
|
|
8352
|
-
btcVsAltReturn4h:
|
|
8145
|
+
btcVsAltReturn4h: toFiniteNumberOrNull(
|
|
8353
8146
|
primaryBreadth.btcVsAltReturn4h
|
|
8354
8147
|
),
|
|
8355
|
-
btcVsAltReturn24h:
|
|
8148
|
+
btcVsAltReturn24h: toFiniteNumberOrNull(
|
|
8356
8149
|
primaryBreadth.btcVsAltReturn24h
|
|
8357
8150
|
),
|
|
8358
|
-
btcTurnoverShare1h:
|
|
8151
|
+
btcTurnoverShare1h: toFiniteNumberOrNull(
|
|
8359
8152
|
primaryBreadth.btcTurnoverShare1h
|
|
8360
8153
|
),
|
|
8361
|
-
btcTurnoverShare24h:
|
|
8154
|
+
btcTurnoverShare24h: toFiniteNumberOrNull(
|
|
8362
8155
|
primaryBreadth.btcTurnoverShare24h
|
|
8363
8156
|
),
|
|
8364
|
-
btcTurnoverShareChange24h:
|
|
8157
|
+
btcTurnoverShareChange24h: toFiniteNumberOrNull(
|
|
8365
8158
|
primaryBreadth.btcTurnoverShareChange24h
|
|
8366
8159
|
),
|
|
8367
|
-
altVolToBtcVol24h:
|
|
8160
|
+
altVolToBtcVol24h: toFiniteNumberOrNull(
|
|
8368
8161
|
primaryBreadth.altVolToBtcVol24h
|
|
8369
8162
|
),
|
|
8370
|
-
altDispersion24h:
|
|
8163
|
+
altDispersion24h: toFiniteNumberOrNull(
|
|
8371
8164
|
primaryBreadth.altDispersion24h
|
|
8372
8165
|
),
|
|
8373
8166
|
regime: primaryBreadth.btcAltRegime ?? "unknown"
|
|
@@ -8377,11 +8170,14 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
8377
8170
|
}
|
|
8378
8171
|
}
|
|
8379
8172
|
};
|
|
8380
|
-
(0,
|
|
8173
|
+
(0, import_strategies.refreshSignalBaseContextGateFeatures)(signal);
|
|
8381
8174
|
return true;
|
|
8382
8175
|
} catch (error) {
|
|
8176
|
+
if (isMarketContextCancellationError(error, params.abortSignal)) {
|
|
8177
|
+
throw error;
|
|
8178
|
+
}
|
|
8383
8179
|
binanceMarketContextUnavailable = true;
|
|
8384
|
-
|
|
8180
|
+
import_logger4.logger.warn(
|
|
8385
8181
|
"Binance market context disabled after Timescale read failure: %s",
|
|
8386
8182
|
String(error)
|
|
8387
8183
|
);
|
|
@@ -8390,9 +8186,9 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
8390
8186
|
};
|
|
8391
8187
|
|
|
8392
8188
|
// src/strategyHelpers/coinMarketCapContext.ts
|
|
8393
|
-
var
|
|
8394
|
-
var
|
|
8395
|
-
var
|
|
8189
|
+
var import_strategies2 = require("@tradejs/core/strategies");
|
|
8190
|
+
var import_logger5 = require("@tradejs/infra/logger");
|
|
8191
|
+
var import_timescale2 = require("@tradejs/infra/timescale");
|
|
8396
8192
|
var DEFAULT_MAX_AGE_MS = 48 * 60 * 6e4;
|
|
8397
8193
|
var SOURCE_GLOBAL_DAILY = "coinmarketcap_global";
|
|
8398
8194
|
var SOURCE_REFERENCE = "coinmarketcap_reference_asset";
|
|
@@ -8406,7 +8202,7 @@ var referenceContextCache = /* @__PURE__ */ new Map();
|
|
|
8406
8202
|
var exchangeLiquidityContextCache = /* @__PURE__ */ new Map();
|
|
8407
8203
|
var fearGreedContextCache = /* @__PURE__ */ new Map();
|
|
8408
8204
|
var indexContextCache = /* @__PURE__ */ new Map();
|
|
8409
|
-
var
|
|
8205
|
+
var parseEnabledFlag2 = (value, env) => {
|
|
8410
8206
|
const normalized = String(value ?? "").trim().toLowerCase();
|
|
8411
8207
|
if (!normalized) {
|
|
8412
8208
|
return env === "BACKTEST" || env === "PARITY" || env === "CRON";
|
|
@@ -8421,7 +8217,7 @@ var asInt = (value, fallback) => {
|
|
|
8421
8217
|
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
8422
8218
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
8423
8219
|
};
|
|
8424
|
-
var
|
|
8220
|
+
var toFiniteNumberOrNull2 = (value) => {
|
|
8425
8221
|
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
|
|
8426
8222
|
return Number.isFinite(numeric) ? numeric : null;
|
|
8427
8223
|
};
|
|
@@ -8500,86 +8296,101 @@ var toIndexRegime = ({
|
|
|
8500
8296
|
};
|
|
8501
8297
|
var getCachedGlobalContext = ({
|
|
8502
8298
|
timestamp,
|
|
8503
|
-
maxAgeMs
|
|
8299
|
+
maxAgeMs,
|
|
8300
|
+
abortSignal
|
|
8504
8301
|
}) => {
|
|
8505
8302
|
const key = `${SOURCE_GLOBAL_DAILY}:${timestamp}:${maxAgeMs}`;
|
|
8506
8303
|
const cached = globalContextCache.get(key);
|
|
8507
8304
|
if (cached) return cached;
|
|
8508
|
-
const promise = (0,
|
|
8305
|
+
const promise = (0, import_timescale2.getLatestMarketGlobalContext)({
|
|
8509
8306
|
source: SOURCE_GLOBAL_DAILY,
|
|
8510
8307
|
atMs: timestamp,
|
|
8511
|
-
maxAgeMs
|
|
8308
|
+
maxAgeMs,
|
|
8309
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
8512
8310
|
});
|
|
8513
8311
|
globalContextCache.set(key, promise);
|
|
8312
|
+
void promise.catch(() => globalContextCache.delete(key));
|
|
8514
8313
|
return promise;
|
|
8515
8314
|
};
|
|
8516
8315
|
var getCachedReferenceContexts = ({
|
|
8517
8316
|
timestamp,
|
|
8518
|
-
maxAgeMs
|
|
8317
|
+
maxAgeMs,
|
|
8318
|
+
abortSignal
|
|
8519
8319
|
}) => {
|
|
8520
8320
|
const key = `${SOURCE_REFERENCE}:1d:${timestamp}:${maxAgeMs}`;
|
|
8521
8321
|
const cached = referenceContextCache.get(key);
|
|
8522
8322
|
if (cached) return cached;
|
|
8523
|
-
const promise = (0,
|
|
8323
|
+
const promise = (0, import_timescale2.getLatestMarketReferenceAssetContexts)({
|
|
8524
8324
|
source: SOURCE_REFERENCE,
|
|
8525
8325
|
symbols: ["BTCUSDT", "ETHUSDT"],
|
|
8526
8326
|
interval: "1d",
|
|
8527
8327
|
atMs: timestamp,
|
|
8528
|
-
maxAgeMs
|
|
8328
|
+
maxAgeMs,
|
|
8329
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
8529
8330
|
});
|
|
8530
8331
|
referenceContextCache.set(key, promise);
|
|
8332
|
+
void promise.catch(() => referenceContextCache.delete(key));
|
|
8531
8333
|
return promise;
|
|
8532
8334
|
};
|
|
8533
8335
|
var getCachedExchangeLiquidityContext = ({
|
|
8534
8336
|
timestamp,
|
|
8535
|
-
maxAgeMs
|
|
8337
|
+
maxAgeMs,
|
|
8338
|
+
abortSignal
|
|
8536
8339
|
}) => {
|
|
8537
8340
|
const key = `${SOURCE_EXCHANGE_LIQUIDITY}:1d:${timestamp}:${maxAgeMs}`;
|
|
8538
8341
|
const cached = exchangeLiquidityContextCache.get(key);
|
|
8539
8342
|
if (cached) return cached;
|
|
8540
|
-
const promise = (0,
|
|
8343
|
+
const promise = (0, import_timescale2.getLatestMarketCmcExchangeLiquidityContext)({
|
|
8541
8344
|
source: SOURCE_EXCHANGE_LIQUIDITY,
|
|
8542
8345
|
interval: "1d",
|
|
8543
8346
|
atMs: timestamp,
|
|
8544
|
-
maxAgeMs
|
|
8347
|
+
maxAgeMs,
|
|
8348
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
8545
8349
|
});
|
|
8546
8350
|
exchangeLiquidityContextCache.set(key, promise);
|
|
8351
|
+
void promise.catch(() => exchangeLiquidityContextCache.delete(key));
|
|
8547
8352
|
return promise;
|
|
8548
8353
|
};
|
|
8549
8354
|
var getCachedFearGreedContext = ({
|
|
8550
8355
|
timestamp,
|
|
8551
|
-
maxAgeMs
|
|
8356
|
+
maxAgeMs,
|
|
8357
|
+
abortSignal
|
|
8552
8358
|
}) => {
|
|
8553
8359
|
const key = `${SOURCE_FEAR_GREED}:1d:${timestamp}:${maxAgeMs}`;
|
|
8554
8360
|
const cached = fearGreedContextCache.get(key);
|
|
8555
8361
|
if (cached) return cached;
|
|
8556
|
-
const promise = (0,
|
|
8362
|
+
const promise = (0, import_timescale2.getLatestMarketCmcFearGreedContext)({
|
|
8557
8363
|
source: SOURCE_FEAR_GREED,
|
|
8558
8364
|
interval: "1d",
|
|
8559
8365
|
atMs: timestamp,
|
|
8560
|
-
maxAgeMs
|
|
8366
|
+
maxAgeMs,
|
|
8367
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
8561
8368
|
});
|
|
8562
8369
|
fearGreedContextCache.set(key, promise);
|
|
8370
|
+
void promise.catch(() => fearGreedContextCache.delete(key));
|
|
8563
8371
|
return promise;
|
|
8564
8372
|
};
|
|
8565
8373
|
var getCachedIndexContexts = ({
|
|
8566
8374
|
timestamp,
|
|
8567
|
-
maxAgeMs
|
|
8375
|
+
maxAgeMs,
|
|
8376
|
+
abortSignal
|
|
8568
8377
|
}) => {
|
|
8569
8378
|
const key = `${SOURCE_INDEX}:1d:${timestamp}:${maxAgeMs}`;
|
|
8570
8379
|
const cached = indexContextCache.get(key);
|
|
8571
8380
|
if (cached) return cached;
|
|
8572
|
-
const promise = (0,
|
|
8381
|
+
const promise = (0, import_timescale2.getLatestMarketCmcIndexContexts)({
|
|
8573
8382
|
source: SOURCE_INDEX,
|
|
8574
8383
|
indexSlugs: ["cmc100", "cmc20"],
|
|
8575
8384
|
interval: "1d",
|
|
8576
8385
|
atMs: timestamp,
|
|
8577
|
-
maxAgeMs
|
|
8386
|
+
maxAgeMs,
|
|
8387
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
8578
8388
|
});
|
|
8579
8389
|
indexContextCache.set(key, promise);
|
|
8390
|
+
void promise.catch(() => indexContextCache.delete(key));
|
|
8580
8391
|
return promise;
|
|
8581
8392
|
};
|
|
8582
|
-
var isCoinMarketCapContextEnabled = (env) =>
|
|
8393
|
+
var isCoinMarketCapContextEnabled = (env) => parseEnabledFlag2(process.env.COINMARKETCAP_CONTEXT_ENABLED, env);
|
|
8583
8394
|
var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
8584
8395
|
const {
|
|
8585
8396
|
signal,
|
|
@@ -8601,27 +8412,33 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
8601
8412
|
] = await Promise.all([
|
|
8602
8413
|
getCachedGlobalContext({
|
|
8603
8414
|
timestamp: signal.timestamp,
|
|
8604
|
-
maxAgeMs
|
|
8415
|
+
maxAgeMs,
|
|
8416
|
+
abortSignal: params.abortSignal
|
|
8605
8417
|
}),
|
|
8606
8418
|
getCachedReferenceContexts({
|
|
8607
8419
|
timestamp: signal.timestamp,
|
|
8608
|
-
maxAgeMs
|
|
8420
|
+
maxAgeMs,
|
|
8421
|
+
abortSignal: params.abortSignal
|
|
8609
8422
|
}),
|
|
8610
8423
|
getCachedReferenceContexts({
|
|
8611
8424
|
timestamp: signal.timestamp - DAY_MS,
|
|
8612
|
-
maxAgeMs: maxAgeMs + DAY_MS
|
|
8425
|
+
maxAgeMs: maxAgeMs + DAY_MS,
|
|
8426
|
+
abortSignal: params.abortSignal
|
|
8613
8427
|
}),
|
|
8614
8428
|
getCachedExchangeLiquidityContext({
|
|
8615
8429
|
timestamp: signal.timestamp,
|
|
8616
|
-
maxAgeMs
|
|
8430
|
+
maxAgeMs,
|
|
8431
|
+
abortSignal: params.abortSignal
|
|
8617
8432
|
}),
|
|
8618
8433
|
getCachedFearGreedContext({
|
|
8619
8434
|
timestamp: signal.timestamp,
|
|
8620
|
-
maxAgeMs
|
|
8435
|
+
maxAgeMs,
|
|
8436
|
+
abortSignal: params.abortSignal
|
|
8621
8437
|
}),
|
|
8622
8438
|
getCachedIndexContexts({
|
|
8623
8439
|
timestamp: signal.timestamp,
|
|
8624
|
-
maxAgeMs
|
|
8440
|
+
maxAgeMs,
|
|
8441
|
+
abortSignal: params.abortSignal
|
|
8625
8442
|
})
|
|
8626
8443
|
]);
|
|
8627
8444
|
const globalRow = globalDailyRow;
|
|
@@ -8634,12 +8451,12 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
8634
8451
|
const ethRow = references.get("ETHUSDT") ?? null;
|
|
8635
8452
|
const previousBtcRow = previousReferences.get("BTCUSDT") ?? null;
|
|
8636
8453
|
const previousEthRow = previousReferences.get("ETHUSDT") ?? null;
|
|
8637
|
-
const btcMarketCapUsd =
|
|
8638
|
-
const ethMarketCapUsd =
|
|
8639
|
-
const previousBtcMarketCapUsd =
|
|
8454
|
+
const btcMarketCapUsd = toFiniteNumberOrNull2(btcRow?.marketCapUsd);
|
|
8455
|
+
const ethMarketCapUsd = toFiniteNumberOrNull2(ethRow?.marketCapUsd);
|
|
8456
|
+
const previousBtcMarketCapUsd = toFiniteNumberOrNull2(
|
|
8640
8457
|
previousBtcRow?.marketCapUsd
|
|
8641
8458
|
);
|
|
8642
|
-
const previousEthMarketCapUsd =
|
|
8459
|
+
const previousEthMarketCapUsd = toFiniteNumberOrNull2(
|
|
8643
8460
|
previousEthRow?.marketCapUsd
|
|
8644
8461
|
);
|
|
8645
8462
|
const ethBtcMarketCapRatio = safeDivide(ethMarketCapUsd, btcMarketCapUsd);
|
|
@@ -8648,16 +8465,16 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
8648
8465
|
previousBtcMarketCapUsd
|
|
8649
8466
|
);
|
|
8650
8467
|
const ethBtcMarketCapRatioChange24hPct = ethBtcMarketCapRatio != null && previousEthBtcMarketCapRatio != null && previousEthBtcMarketCapRatio > 0 ? (ethBtcMarketCapRatio - previousEthBtcMarketCapRatio) / previousEthBtcMarketCapRatio : null;
|
|
8651
|
-
const btcVolumeUsd =
|
|
8652
|
-
const ethVolumeUsd =
|
|
8468
|
+
const btcVolumeUsd = toFiniteNumberOrNull2(btcRow?.volumeUsd);
|
|
8469
|
+
const ethVolumeUsd = toFiniteNumberOrNull2(ethRow?.volumeUsd);
|
|
8653
8470
|
const referenceStale = btcRow?.stale === true || ethRow?.stale === true || !btcRow || !ethRow;
|
|
8654
|
-
const btcDominanceChange24hPct =
|
|
8471
|
+
const btcDominanceChange24hPct = toFiniteNumberOrNull2(
|
|
8655
8472
|
globalRow?.btcDominanceChange24hPct
|
|
8656
8473
|
);
|
|
8657
|
-
const altMarketCapChange24hPct =
|
|
8474
|
+
const altMarketCapChange24hPct = toFiniteNumberOrNull2(
|
|
8658
8475
|
globalRow?.altMarketCapChange24hPct
|
|
8659
8476
|
);
|
|
8660
|
-
const altVolumeChange24hPct =
|
|
8477
|
+
const altVolumeChange24hPct = toFiniteNumberOrNull2(
|
|
8661
8478
|
globalRow?.altVolumeChange24hPct
|
|
8662
8479
|
);
|
|
8663
8480
|
const altLiquidityRegime = globalRow ? toAltLiquidityRegime({
|
|
@@ -8668,196 +8485,445 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
8668
8485
|
}) : "unknown";
|
|
8669
8486
|
const exchangeLiquidityRegime = exchangeLiquidityRow ? toExchangeLiquidityRegime({
|
|
8670
8487
|
stale: exchangeLiquidityRow.stale,
|
|
8671
|
-
totalVolumeChange24hPct:
|
|
8488
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull2(
|
|
8672
8489
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
8673
8490
|
),
|
|
8674
8491
|
fallback: exchangeLiquidityRow.liquidityRegime ?? "unknown"
|
|
8675
8492
|
}) : "unknown";
|
|
8676
8493
|
const cmc100Row = indexRows.get("cmc100") ?? null;
|
|
8677
8494
|
const cmc20Row = indexRows.get("cmc20") ?? null;
|
|
8678
|
-
const cmc100Value =
|
|
8679
|
-
const cmc20Value =
|
|
8680
|
-
const cmc100Change24hPct =
|
|
8495
|
+
const cmc100Value = toFiniteNumberOrNull2(cmc100Row?.value);
|
|
8496
|
+
const cmc20Value = toFiniteNumberOrNull2(cmc20Row?.value);
|
|
8497
|
+
const cmc100Change24hPct = toFiniteNumberOrNull2(
|
|
8681
8498
|
cmc100Row?.valueChange24hPct
|
|
8682
8499
|
);
|
|
8683
|
-
const cmc20Change24hPct =
|
|
8684
|
-
const cmc20ToCmc100Ratio = safeDivide(cmc20Value, cmc100Value);
|
|
8685
|
-
const cmc20ToCmc100RatioChange24hPct = cmc20Change24hPct != null && cmc100Change24hPct != null ? (1 + cmc20Change24hPct) / (1 + cmc100Change24hPct) - 1 : null;
|
|
8686
|
-
const indexStale = cmc100Row?.stale === true || cmc20Row?.stale === true || !cmc100Row || !cmc20Row;
|
|
8687
|
-
const indexRegime = toIndexRegime({
|
|
8688
|
-
stale: indexStale,
|
|
8689
|
-
cmc100Change24hPct,
|
|
8690
|
-
cmc20Change24hPct,
|
|
8691
|
-
cmc20ToCmc100RatioChange24hPct
|
|
8500
|
+
const cmc20Change24hPct = toFiniteNumberOrNull2(cmc20Row?.valueChange24hPct);
|
|
8501
|
+
const cmc20ToCmc100Ratio = safeDivide(cmc20Value, cmc100Value);
|
|
8502
|
+
const cmc20ToCmc100RatioChange24hPct = cmc20Change24hPct != null && cmc100Change24hPct != null ? (1 + cmc20Change24hPct) / (1 + cmc100Change24hPct) - 1 : null;
|
|
8503
|
+
const indexStale = cmc100Row?.stale === true || cmc20Row?.stale === true || !cmc100Row || !cmc20Row;
|
|
8504
|
+
const indexRegime = toIndexRegime({
|
|
8505
|
+
stale: indexStale,
|
|
8506
|
+
cmc100Change24hPct,
|
|
8507
|
+
cmc20Change24hPct,
|
|
8508
|
+
cmc20ToCmc100RatioChange24hPct
|
|
8509
|
+
});
|
|
8510
|
+
const baseContext = signal.additionalIndicators.baseContext;
|
|
8511
|
+
signal.additionalIndicators = {
|
|
8512
|
+
...signal.additionalIndicators,
|
|
8513
|
+
baseContext: {
|
|
8514
|
+
...baseContext,
|
|
8515
|
+
relative: {
|
|
8516
|
+
...baseContext.relative,
|
|
8517
|
+
...globalRow ? {
|
|
8518
|
+
cmcGlobal: {
|
|
8519
|
+
source: globalRow.source,
|
|
8520
|
+
interval: "1d",
|
|
8521
|
+
asOfTs: globalRow.ts.getTime(),
|
|
8522
|
+
ageMs: globalRow.ageMs,
|
|
8523
|
+
stale: globalRow.stale,
|
|
8524
|
+
totalMarketCapUsd: toFiniteNumberOrNull2(
|
|
8525
|
+
globalRow.totalMarketCapUsd
|
|
8526
|
+
),
|
|
8527
|
+
totalVolumeUsd: toFiniteNumberOrNull2(
|
|
8528
|
+
globalRow.totalVolumeUsd
|
|
8529
|
+
),
|
|
8530
|
+
totalVolumeReportedUsd: toFiniteNumberOrNull2(
|
|
8531
|
+
globalRow.totalVolumeReportedUsd
|
|
8532
|
+
),
|
|
8533
|
+
altMarketCapUsd: toFiniteNumberOrNull2(
|
|
8534
|
+
globalRow.altMarketCapUsd
|
|
8535
|
+
),
|
|
8536
|
+
altVolumeUsd: toFiniteNumberOrNull2(globalRow.altVolumeUsd),
|
|
8537
|
+
altVolumeReportedUsd: toFiniteNumberOrNull2(
|
|
8538
|
+
globalRow.altVolumeReportedUsd
|
|
8539
|
+
),
|
|
8540
|
+
btcDominancePct: toFiniteNumberOrNull2(
|
|
8541
|
+
globalRow.btcDominancePct
|
|
8542
|
+
),
|
|
8543
|
+
ethDominancePct: toFiniteNumberOrNull2(
|
|
8544
|
+
globalRow.ethDominancePct
|
|
8545
|
+
),
|
|
8546
|
+
btcDominanceChange24hPct,
|
|
8547
|
+
ethDominanceChange24hPct: toFiniteNumberOrNull2(
|
|
8548
|
+
globalRow.ethDominanceChange24hPct
|
|
8549
|
+
),
|
|
8550
|
+
altMarketCapChange24hPct,
|
|
8551
|
+
altVolumeChange24hPct,
|
|
8552
|
+
activeCryptocurrencies: toFiniteNumberOrNull2(
|
|
8553
|
+
globalRow.activeCryptocurrencies
|
|
8554
|
+
),
|
|
8555
|
+
activeExchanges: toFiniteNumberOrNull2(
|
|
8556
|
+
globalRow.activeExchanges
|
|
8557
|
+
),
|
|
8558
|
+
activeMarketPairs: toFiniteNumberOrNull2(
|
|
8559
|
+
globalRow.activeMarketPairs
|
|
8560
|
+
),
|
|
8561
|
+
altLiquidityRegime
|
|
8562
|
+
}
|
|
8563
|
+
} : {},
|
|
8564
|
+
...btcRow || ethRow ? {
|
|
8565
|
+
cmcReferenceAssets: {
|
|
8566
|
+
source: SOURCE_REFERENCE,
|
|
8567
|
+
interval: "1d",
|
|
8568
|
+
asOfTs: Math.max(
|
|
8569
|
+
btcRow?.ts.getTime() ?? 0,
|
|
8570
|
+
ethRow?.ts.getTime() ?? 0
|
|
8571
|
+
),
|
|
8572
|
+
ageMs: btcRow?.ageMs != null && ethRow?.ageMs != null ? Math.max(btcRow.ageMs, ethRow.ageMs) : btcRow?.ageMs ?? ethRow?.ageMs ?? null,
|
|
8573
|
+
stale: referenceStale,
|
|
8574
|
+
btcMarketCapUsd,
|
|
8575
|
+
ethMarketCapUsd,
|
|
8576
|
+
btcVolumeUsd,
|
|
8577
|
+
ethVolumeUsd,
|
|
8578
|
+
btcVolumeToMarketCap: safeDivide(
|
|
8579
|
+
btcVolumeUsd,
|
|
8580
|
+
btcMarketCapUsd
|
|
8581
|
+
),
|
|
8582
|
+
ethVolumeToMarketCap: safeDivide(
|
|
8583
|
+
ethVolumeUsd,
|
|
8584
|
+
ethMarketCapUsd
|
|
8585
|
+
),
|
|
8586
|
+
ethBtcMarketCapRatio,
|
|
8587
|
+
ethBtcMarketCapRatioChange24hPct,
|
|
8588
|
+
ethVsBtcVolumeRatio: safeDivide(ethVolumeUsd, btcVolumeUsd),
|
|
8589
|
+
referenceLiquidityRegime: toReferenceLiquidityRegime({
|
|
8590
|
+
stale: referenceStale,
|
|
8591
|
+
ethBtcMarketCapRatioChange24hPct,
|
|
8592
|
+
ethVsBtcVolumeRatio: safeDivide(ethVolumeUsd, btcVolumeUsd)
|
|
8593
|
+
})
|
|
8594
|
+
}
|
|
8595
|
+
} : {},
|
|
8596
|
+
...exchangeLiquidityRow ? {
|
|
8597
|
+
cmcExchangeLiquidity: {
|
|
8598
|
+
source: SOURCE_EXCHANGE_LIQUIDITY,
|
|
8599
|
+
interval: exchangeLiquidityRow.interval,
|
|
8600
|
+
asOfTs: exchangeLiquidityRow.ts.getTime(),
|
|
8601
|
+
ageMs: exchangeLiquidityRow.ageMs,
|
|
8602
|
+
stale: exchangeLiquidityRow.stale,
|
|
8603
|
+
exchangesCount: toFiniteNumberOrNull2(
|
|
8604
|
+
exchangeLiquidityRow.exchangesCount
|
|
8605
|
+
),
|
|
8606
|
+
totalVolumeUsd: toFiniteNumberOrNull2(
|
|
8607
|
+
exchangeLiquidityRow.totalVolumeUsd
|
|
8608
|
+
),
|
|
8609
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull2(
|
|
8610
|
+
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
8611
|
+
),
|
|
8612
|
+
binanceVolumeUsd: toFiniteNumberOrNull2(
|
|
8613
|
+
exchangeLiquidityRow.binanceVolumeUsd
|
|
8614
|
+
),
|
|
8615
|
+
binanceVolumeShare: toFiniteNumberOrNull2(
|
|
8616
|
+
exchangeLiquidityRow.binanceVolumeShare
|
|
8617
|
+
),
|
|
8618
|
+
topExchangeVolumeShare: toFiniteNumberOrNull2(
|
|
8619
|
+
exchangeLiquidityRow.topExchangeVolumeShare
|
|
8620
|
+
),
|
|
8621
|
+
liquidityRegime: exchangeLiquidityRegime
|
|
8622
|
+
}
|
|
8623
|
+
} : {},
|
|
8624
|
+
...fearGreedRow ? {
|
|
8625
|
+
cmcFearGreed: {
|
|
8626
|
+
source: SOURCE_FEAR_GREED,
|
|
8627
|
+
interval: "1d",
|
|
8628
|
+
asOfTs: fearGreedRow.ts.getTime(),
|
|
8629
|
+
ageMs: fearGreedRow.ageMs,
|
|
8630
|
+
stale: fearGreedRow.stale,
|
|
8631
|
+
value: toFiniteNumberOrNull2(fearGreedRow.value),
|
|
8632
|
+
valueChange24h: toFiniteNumberOrNull2(
|
|
8633
|
+
fearGreedRow.valueChange24h
|
|
8634
|
+
),
|
|
8635
|
+
valueChange7d: toFiniteNumberOrNull2(
|
|
8636
|
+
fearGreedRow.valueChange7d
|
|
8637
|
+
),
|
|
8638
|
+
classification: fearGreedRow.classification ?? "Unknown",
|
|
8639
|
+
sentimentRegime: fearGreedRow.sentimentRegime ?? "unknown"
|
|
8640
|
+
}
|
|
8641
|
+
} : {},
|
|
8642
|
+
...cmc100Row || cmc20Row ? {
|
|
8643
|
+
cmcIndexes: {
|
|
8644
|
+
source: SOURCE_INDEX,
|
|
8645
|
+
interval: "1d",
|
|
8646
|
+
asOfTs: Math.max(
|
|
8647
|
+
cmc100Row?.ts.getTime() ?? 0,
|
|
8648
|
+
cmc20Row?.ts.getTime() ?? 0
|
|
8649
|
+
),
|
|
8650
|
+
ageMs: cmc100Row?.ageMs != null && cmc20Row?.ageMs != null ? Math.max(cmc100Row.ageMs, cmc20Row.ageMs) : cmc100Row?.ageMs ?? cmc20Row?.ageMs ?? null,
|
|
8651
|
+
stale: indexStale,
|
|
8652
|
+
cmc100Value,
|
|
8653
|
+
cmc100Change24hPct,
|
|
8654
|
+
cmc100TopConstituentSymbol: cmc100Row?.topConstituentSymbol ?? null,
|
|
8655
|
+
cmc100TopConstituentWeightPct: toFiniteNumberOrNull2(
|
|
8656
|
+
cmc100Row?.topConstituentWeightPct
|
|
8657
|
+
),
|
|
8658
|
+
cmc20Value,
|
|
8659
|
+
cmc20Change24hPct,
|
|
8660
|
+
cmc20TopConstituentSymbol: cmc20Row?.topConstituentSymbol ?? null,
|
|
8661
|
+
cmc20TopConstituentWeightPct: toFiniteNumberOrNull2(
|
|
8662
|
+
cmc20Row?.topConstituentWeightPct
|
|
8663
|
+
),
|
|
8664
|
+
cmc20ToCmc100Ratio,
|
|
8665
|
+
cmc20ToCmc100RatioChange24hPct,
|
|
8666
|
+
indexRegime
|
|
8667
|
+
}
|
|
8668
|
+
} : {}
|
|
8669
|
+
}
|
|
8670
|
+
}
|
|
8671
|
+
};
|
|
8672
|
+
(0, import_strategies2.refreshSignalBaseContextGateFeatures)(signal);
|
|
8673
|
+
return true;
|
|
8674
|
+
} catch (error) {
|
|
8675
|
+
if (isMarketContextCancellationError(error, params.abortSignal)) {
|
|
8676
|
+
throw error;
|
|
8677
|
+
}
|
|
8678
|
+
coinMarketCapContextUnavailable = true;
|
|
8679
|
+
import_logger5.logger.warn(
|
|
8680
|
+
"CoinMarketCap context disabled after Timescale read failure: %s",
|
|
8681
|
+
String(error)
|
|
8682
|
+
);
|
|
8683
|
+
return false;
|
|
8684
|
+
}
|
|
8685
|
+
};
|
|
8686
|
+
|
|
8687
|
+
// src/strategyHelpers/derivativesContext.ts
|
|
8688
|
+
var import_indicators2 = require("@tradejs/core/indicators");
|
|
8689
|
+
var import_data = require("@tradejs/core/data");
|
|
8690
|
+
var import_strategies3 = require("@tradejs/core/strategies");
|
|
8691
|
+
var import_constants2 = require("@tradejs/core/constants");
|
|
8692
|
+
var import_timescale3 = require("@tradejs/infra/timescale");
|
|
8693
|
+
var import_logger6 = require("@tradejs/infra/logger");
|
|
8694
|
+
var STORED_INTERVALS = ["15m", "1h"];
|
|
8695
|
+
var CONTEXT_INTERVALS = ["15m", "1h"];
|
|
8696
|
+
var DEFAULT_LOOKBACK_HOURS = 48;
|
|
8697
|
+
var PRIMARY_DERIVATIVES_REFERENCE_SYMBOL = import_constants2.DERIVATIVES_CONTEXT_BASE_REFERENCE_SYMBOLS[0];
|
|
8698
|
+
var SECONDARY_DERIVATIVES_REFERENCE_SYMBOL = import_constants2.DERIVATIVES_CONTEXT_BASE_REFERENCE_SYMBOLS[1];
|
|
8699
|
+
var derivativesContextUnavailable = false;
|
|
8700
|
+
var parseEnabledFlag3 = (value, env) => {
|
|
8701
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
8702
|
+
if (!normalized) return true;
|
|
8703
|
+
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
8704
|
+
if (normalized === "backtest") return env === "BACKTEST";
|
|
8705
|
+
if (normalized === "live") return env !== "BACKTEST";
|
|
8706
|
+
return false;
|
|
8707
|
+
};
|
|
8708
|
+
var parseBooleanFlag = (value, fallback = false) => {
|
|
8709
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
8710
|
+
if (!normalized) return fallback;
|
|
8711
|
+
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
8712
|
+
if (["0", "false", "no", "off"].includes(normalized)) return false;
|
|
8713
|
+
return fallback;
|
|
8714
|
+
};
|
|
8715
|
+
var parseLookbackMs = () => {
|
|
8716
|
+
const hours = Number(process.env.DERIVATIVES_CONTEXT_LOOKBACK_HOURS);
|
|
8717
|
+
const normalizedHours = Number.isFinite(hours) && hours > 0 ? hours : DEFAULT_LOOKBACK_HOURS;
|
|
8718
|
+
return normalizedHours * 60 * 60 * 1e3;
|
|
8719
|
+
};
|
|
8720
|
+
var withHourlyFallbackRows = (rowsByInterval) => ({
|
|
8721
|
+
"15m": rowsByInterval["15m"] ?? [],
|
|
8722
|
+
"1h": (0, import_indicators2.buildCoinalyzeHourlyRowsWithFallback)({
|
|
8723
|
+
rows15m: rowsByInterval["15m"],
|
|
8724
|
+
fallbackRows1h: rowsByInterval["1h"]
|
|
8725
|
+
})
|
|
8726
|
+
});
|
|
8727
|
+
var getDerivativesContextReferenceSymbols = () => [
|
|
8728
|
+
...(0, import_constants2.resolveDerivativesContextReferenceSymbols)(
|
|
8729
|
+
process.env.DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS
|
|
8730
|
+
)
|
|
8731
|
+
];
|
|
8732
|
+
var normalizeSymbol = (symbol) => String(symbol || "").trim().toUpperCase();
|
|
8733
|
+
var getSignalPriceChangePct1h = (signal) => {
|
|
8734
|
+
const baseContext = signal.additionalIndicators?.baseContext;
|
|
8735
|
+
if (!baseContext || typeof baseContext !== "object" || Array.isArray(baseContext)) {
|
|
8736
|
+
return null;
|
|
8737
|
+
}
|
|
8738
|
+
const raw = typeof baseContext.raw === "object" && baseContext.raw && !Array.isArray(baseContext.raw) ? baseContext.raw : null;
|
|
8739
|
+
const price = raw && typeof raw.price === "object" && raw.price && !Array.isArray(raw.price) ? raw.price : null;
|
|
8740
|
+
const value = price?.price1hPct;
|
|
8741
|
+
const numeric = typeof value === "number" ? value : Number(value);
|
|
8742
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
8743
|
+
};
|
|
8744
|
+
var resolvePrimaryReferenceSymbol2 = () => PRIMARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
8745
|
+
var resolveSecondaryReferenceSymbol = () => SECONDARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
8746
|
+
var getPrimaryIntervalContext = (context) => context?.intervals["15m"] ?? context?.intervals["1h"] ?? null;
|
|
8747
|
+
var hasDerivativesSymbolData = (context) => Object.keys(context.intervals).length > 0 && !context.summary.riskFlags.includes("missing_derivatives");
|
|
8748
|
+
var toFiniteNumberOrNull3 = (value) => {
|
|
8749
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
8750
|
+
if (typeof value === "string" && value.trim()) {
|
|
8751
|
+
const parsed = Number(value);
|
|
8752
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
8753
|
+
}
|
|
8754
|
+
return null;
|
|
8755
|
+
};
|
|
8756
|
+
var roundNullable = (value, digits = 4) => {
|
|
8757
|
+
if (value == null || !Number.isFinite(value)) return null;
|
|
8758
|
+
const multiplier = 10 ** digits;
|
|
8759
|
+
return Math.round(value * multiplier) / multiplier;
|
|
8760
|
+
};
|
|
8761
|
+
var deltaNullable = (targetValue, referenceValue) => {
|
|
8762
|
+
const target = toFiniteNumberOrNull3(targetValue);
|
|
8763
|
+
const reference = toFiniteNumberOrNull3(referenceValue);
|
|
8764
|
+
return target == null || reference == null ? null : roundNullable(target - reference);
|
|
8765
|
+
};
|
|
8766
|
+
var buildTargetDerivedContext = (params) => {
|
|
8767
|
+
const { targetContext, primaryReferenceContext } = params;
|
|
8768
|
+
const targetPrimary = getPrimaryIntervalContext(targetContext);
|
|
8769
|
+
const referencePrimary = getPrimaryIntervalContext(primaryReferenceContext);
|
|
8770
|
+
const targetDirectionAligned = targetContext.summary.directionAligned;
|
|
8771
|
+
const referenceDirectionAligned = primaryReferenceContext?.summary.directionAligned ?? null;
|
|
8772
|
+
return {
|
|
8773
|
+
available: hasDerivativesSymbolData(targetContext),
|
|
8774
|
+
stale: targetContext.summary.riskFlags.includes("stale_derivatives") || targetPrimary?.stale === true ? true : targetPrimary == null ? null : false,
|
|
8775
|
+
sourceSymbol: targetContext.symbol,
|
|
8776
|
+
referenceSymbol: primaryReferenceContext?.symbol ?? null,
|
|
8777
|
+
directionAligned: targetDirectionAligned,
|
|
8778
|
+
referenceDirectionAligned,
|
|
8779
|
+
pressure: targetContext.summary.pressure ?? null,
|
|
8780
|
+
referencePressure: primaryReferenceContext?.summary.pressure ?? null,
|
|
8781
|
+
riskFlags: targetContext.summary.riskFlags,
|
|
8782
|
+
oiChangePct1h: targetPrimary?.oiChangePct1h ?? null,
|
|
8783
|
+
oiAcceleration: targetContext.summary.oiAcceleration ?? null,
|
|
8784
|
+
fundingRate: targetPrimary?.fundingRate ?? null,
|
|
8785
|
+
fundingZScore: targetPrimary?.fundingZScore ?? null,
|
|
8786
|
+
fundingChange1h: targetContext.summary.fundingChange1h ?? null,
|
|
8787
|
+
liqSpikeRatio: targetPrimary?.liqSpikeRatio ?? null,
|
|
8788
|
+
liqImbalance: targetPrimary?.liqImbalance ?? null,
|
|
8789
|
+
targetVsPrimaryOiChangePct1hDelta: deltaNullable(
|
|
8790
|
+
targetPrimary?.oiChangePct1h,
|
|
8791
|
+
referencePrimary?.oiChangePct1h
|
|
8792
|
+
),
|
|
8793
|
+
targetVsPrimaryFundingZScoreDelta: deltaNullable(
|
|
8794
|
+
targetPrimary?.fundingZScore,
|
|
8795
|
+
referencePrimary?.fundingZScore
|
|
8796
|
+
),
|
|
8797
|
+
targetReferenceConflict: targetDirectionAligned == null || referenceDirectionAligned == null ? null : targetDirectionAligned !== referenceDirectionAligned
|
|
8798
|
+
};
|
|
8799
|
+
};
|
|
8800
|
+
var buildReferenceDerivativesContext = (params) => {
|
|
8801
|
+
const {
|
|
8802
|
+
targetSymbol,
|
|
8803
|
+
primaryReferenceSymbol,
|
|
8804
|
+
secondaryReferenceSymbol,
|
|
8805
|
+
referenceSymbols,
|
|
8806
|
+
referenceContexts,
|
|
8807
|
+
targetContext
|
|
8808
|
+
} = params;
|
|
8809
|
+
const primaryContext = referenceContexts[primaryReferenceSymbol] ?? referenceContexts[referenceSymbols[0]];
|
|
8810
|
+
if (!primaryContext) {
|
|
8811
|
+
throw new Error("No derivatives reference contexts built");
|
|
8812
|
+
}
|
|
8813
|
+
const referenceSymbolsMetadata = [
|
|
8814
|
+
.../* @__PURE__ */ new Set([
|
|
8815
|
+
primaryReferenceSymbol,
|
|
8816
|
+
secondaryReferenceSymbol,
|
|
8817
|
+
...referenceSymbols,
|
|
8818
|
+
...Object.keys(referenceContexts)
|
|
8819
|
+
])
|
|
8820
|
+
];
|
|
8821
|
+
const targetDerived = targetContext && hasDerivativesSymbolData(targetContext) ? buildTargetDerivedContext({
|
|
8822
|
+
targetContext,
|
|
8823
|
+
primaryReferenceContext: primaryContext
|
|
8824
|
+
}) : void 0;
|
|
8825
|
+
return {
|
|
8826
|
+
...primaryContext,
|
|
8827
|
+
targetSymbol,
|
|
8828
|
+
primaryReferenceSymbol: primaryContext.symbol,
|
|
8829
|
+
secondaryReferenceSymbol: referenceContexts[secondaryReferenceSymbol]?.symbol ?? secondaryReferenceSymbol,
|
|
8830
|
+
referenceSymbols: referenceSymbolsMetadata,
|
|
8831
|
+
referenceContexts,
|
|
8832
|
+
...targetContext && targetDerived ? {
|
|
8833
|
+
targetContext,
|
|
8834
|
+
targetDerived
|
|
8835
|
+
} : {}
|
|
8836
|
+
};
|
|
8837
|
+
};
|
|
8838
|
+
var isDerivativesContextEnabled = (env) => parseEnabledFlag3(process.env.DERIVATIVES_CONTEXT_ENABLED, env);
|
|
8839
|
+
var isDerivativesTargetContextEnabled = () => parseBooleanFlag(process.env.DERIVATIVES_CONTEXT_TARGET_ENABLED, false);
|
|
8840
|
+
var enrichSignalWithDerivativesContext = async (params) => {
|
|
8841
|
+
const { signal, env, enabled = isDerivativesContextEnabled(env) } = params;
|
|
8842
|
+
if (signal.universe === "tradfi" || !enabled || derivativesContextUnavailable) {
|
|
8843
|
+
return false;
|
|
8844
|
+
}
|
|
8845
|
+
try {
|
|
8846
|
+
const referenceSymbols = getDerivativesContextReferenceSymbols();
|
|
8847
|
+
const targetSymbol = normalizeSymbol(signal.symbol);
|
|
8848
|
+
const lookbackMs = parseLookbackMs();
|
|
8849
|
+
const decisionTimeMs = signal.timestamp + (0, import_data.intervalToMs)(signal.interval);
|
|
8850
|
+
const derivativesEndMs = (0, import_indicators2.getLastClosedDerivativesBarStartMs)(
|
|
8851
|
+
decisionTimeMs,
|
|
8852
|
+
"15m"
|
|
8853
|
+
);
|
|
8854
|
+
const contexts = await Promise.all(
|
|
8855
|
+
referenceSymbols.map(async (symbol) => {
|
|
8856
|
+
const rowsByInterval = await (0, import_timescale3.getDerivativesWindow)({
|
|
8857
|
+
symbol,
|
|
8858
|
+
intervals: STORED_INTERVALS,
|
|
8859
|
+
endMs: derivativesEndMs,
|
|
8860
|
+
lookbackMs,
|
|
8861
|
+
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
8862
|
+
});
|
|
8863
|
+
return [
|
|
8864
|
+
symbol,
|
|
8865
|
+
(0, import_indicators2.buildDerivativesContext)({
|
|
8866
|
+
symbol,
|
|
8867
|
+
direction: signal.direction,
|
|
8868
|
+
timestamp: derivativesEndMs,
|
|
8869
|
+
rowsByInterval: withHourlyFallbackRows(rowsByInterval),
|
|
8870
|
+
priceChangePct1h: getSignalPriceChangePct1h(signal),
|
|
8871
|
+
intervals: CONTEXT_INTERVALS
|
|
8872
|
+
})
|
|
8873
|
+
];
|
|
8874
|
+
})
|
|
8875
|
+
);
|
|
8876
|
+
const referenceContexts = Object.fromEntries(contexts);
|
|
8877
|
+
const primaryReferenceSymbol = resolvePrimaryReferenceSymbol2();
|
|
8878
|
+
const secondaryReferenceSymbol = resolveSecondaryReferenceSymbol();
|
|
8879
|
+
const targetContextEnabled = isDerivativesTargetContextEnabled();
|
|
8880
|
+
const referenceTargetContext = targetContextEnabled && targetSymbol !== primaryReferenceSymbol ? referenceContexts[targetSymbol] : void 0;
|
|
8881
|
+
const shouldFetchTargetContext = targetContextEnabled && targetSymbol.length > 0 && !referenceSymbols.some(
|
|
8882
|
+
(referenceSymbol) => referenceSymbol === targetSymbol
|
|
8883
|
+
);
|
|
8884
|
+
const fetchedTargetContext = shouldFetchTargetContext ? await (async () => {
|
|
8885
|
+
const rowsByInterval = await (0, import_timescale3.getDerivativesWindow)({
|
|
8886
|
+
symbol: targetSymbol,
|
|
8887
|
+
intervals: STORED_INTERVALS,
|
|
8888
|
+
endMs: derivativesEndMs,
|
|
8889
|
+
lookbackMs,
|
|
8890
|
+
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
8891
|
+
});
|
|
8892
|
+
const context = (0, import_indicators2.buildDerivativesContext)({
|
|
8893
|
+
symbol: targetSymbol,
|
|
8894
|
+
direction: signal.direction,
|
|
8895
|
+
timestamp: derivativesEndMs,
|
|
8896
|
+
rowsByInterval: withHourlyFallbackRows(rowsByInterval),
|
|
8897
|
+
priceChangePct1h: getSignalPriceChangePct1h(signal),
|
|
8898
|
+
intervals: CONTEXT_INTERVALS
|
|
8899
|
+
});
|
|
8900
|
+
return hasDerivativesSymbolData(context) ? context : void 0;
|
|
8901
|
+
})() : void 0;
|
|
8902
|
+
const targetContext = referenceTargetContext && hasDerivativesSymbolData(referenceTargetContext) ? referenceTargetContext : fetchedTargetContext;
|
|
8903
|
+
const derivativesContext = buildReferenceDerivativesContext({
|
|
8904
|
+
targetSymbol: targetSymbol || signal.symbol,
|
|
8905
|
+
primaryReferenceSymbol,
|
|
8906
|
+
secondaryReferenceSymbol,
|
|
8907
|
+
referenceSymbols,
|
|
8908
|
+
referenceContexts,
|
|
8909
|
+
targetContext
|
|
8692
8910
|
});
|
|
8693
|
-
const baseContext = signal.additionalIndicators.baseContext;
|
|
8694
8911
|
signal.additionalIndicators = {
|
|
8695
|
-
...signal.additionalIndicators,
|
|
8696
|
-
baseContext: {
|
|
8697
|
-
...baseContext,
|
|
8698
|
-
|
|
8699
|
-
|
|
8700
|
-
...globalRow ? {
|
|
8701
|
-
cmcGlobal: {
|
|
8702
|
-
source: globalRow.source,
|
|
8703
|
-
interval: "1d",
|
|
8704
|
-
asOfTs: globalRow.ts.getTime(),
|
|
8705
|
-
ageMs: globalRow.ageMs,
|
|
8706
|
-
stale: globalRow.stale,
|
|
8707
|
-
totalMarketCapUsd: toFiniteNumberOrNull3(
|
|
8708
|
-
globalRow.totalMarketCapUsd
|
|
8709
|
-
),
|
|
8710
|
-
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
8711
|
-
globalRow.totalVolumeUsd
|
|
8712
|
-
),
|
|
8713
|
-
totalVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
8714
|
-
globalRow.totalVolumeReportedUsd
|
|
8715
|
-
),
|
|
8716
|
-
altMarketCapUsd: toFiniteNumberOrNull3(
|
|
8717
|
-
globalRow.altMarketCapUsd
|
|
8718
|
-
),
|
|
8719
|
-
altVolumeUsd: toFiniteNumberOrNull3(globalRow.altVolumeUsd),
|
|
8720
|
-
altVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
8721
|
-
globalRow.altVolumeReportedUsd
|
|
8722
|
-
),
|
|
8723
|
-
btcDominancePct: toFiniteNumberOrNull3(
|
|
8724
|
-
globalRow.btcDominancePct
|
|
8725
|
-
),
|
|
8726
|
-
ethDominancePct: toFiniteNumberOrNull3(
|
|
8727
|
-
globalRow.ethDominancePct
|
|
8728
|
-
),
|
|
8729
|
-
btcDominanceChange24hPct,
|
|
8730
|
-
ethDominanceChange24hPct: toFiniteNumberOrNull3(
|
|
8731
|
-
globalRow.ethDominanceChange24hPct
|
|
8732
|
-
),
|
|
8733
|
-
altMarketCapChange24hPct,
|
|
8734
|
-
altVolumeChange24hPct,
|
|
8735
|
-
activeCryptocurrencies: toFiniteNumberOrNull3(
|
|
8736
|
-
globalRow.activeCryptocurrencies
|
|
8737
|
-
),
|
|
8738
|
-
activeExchanges: toFiniteNumberOrNull3(
|
|
8739
|
-
globalRow.activeExchanges
|
|
8740
|
-
),
|
|
8741
|
-
activeMarketPairs: toFiniteNumberOrNull3(
|
|
8742
|
-
globalRow.activeMarketPairs
|
|
8743
|
-
),
|
|
8744
|
-
altLiquidityRegime
|
|
8745
|
-
}
|
|
8746
|
-
} : {},
|
|
8747
|
-
...btcRow || ethRow ? {
|
|
8748
|
-
cmcReferenceAssets: {
|
|
8749
|
-
source: SOURCE_REFERENCE,
|
|
8750
|
-
interval: "1d",
|
|
8751
|
-
asOfTs: Math.max(
|
|
8752
|
-
btcRow?.ts.getTime() ?? 0,
|
|
8753
|
-
ethRow?.ts.getTime() ?? 0
|
|
8754
|
-
),
|
|
8755
|
-
ageMs: btcRow?.ageMs != null && ethRow?.ageMs != null ? Math.max(btcRow.ageMs, ethRow.ageMs) : btcRow?.ageMs ?? ethRow?.ageMs ?? null,
|
|
8756
|
-
stale: referenceStale,
|
|
8757
|
-
btcMarketCapUsd,
|
|
8758
|
-
ethMarketCapUsd,
|
|
8759
|
-
btcVolumeUsd,
|
|
8760
|
-
ethVolumeUsd,
|
|
8761
|
-
btcVolumeToMarketCap: safeDivide(
|
|
8762
|
-
btcVolumeUsd,
|
|
8763
|
-
btcMarketCapUsd
|
|
8764
|
-
),
|
|
8765
|
-
ethVolumeToMarketCap: safeDivide(
|
|
8766
|
-
ethVolumeUsd,
|
|
8767
|
-
ethMarketCapUsd
|
|
8768
|
-
),
|
|
8769
|
-
ethBtcMarketCapRatio,
|
|
8770
|
-
ethBtcMarketCapRatioChange24hPct,
|
|
8771
|
-
ethVsBtcVolumeRatio: safeDivide(ethVolumeUsd, btcVolumeUsd),
|
|
8772
|
-
referenceLiquidityRegime: toReferenceLiquidityRegime({
|
|
8773
|
-
stale: referenceStale,
|
|
8774
|
-
ethBtcMarketCapRatioChange24hPct,
|
|
8775
|
-
ethVsBtcVolumeRatio: safeDivide(ethVolumeUsd, btcVolumeUsd)
|
|
8776
|
-
})
|
|
8777
|
-
}
|
|
8778
|
-
} : {},
|
|
8779
|
-
...exchangeLiquidityRow ? {
|
|
8780
|
-
cmcExchangeLiquidity: {
|
|
8781
|
-
source: SOURCE_EXCHANGE_LIQUIDITY,
|
|
8782
|
-
interval: exchangeLiquidityRow.interval,
|
|
8783
|
-
asOfTs: exchangeLiquidityRow.ts.getTime(),
|
|
8784
|
-
ageMs: exchangeLiquidityRow.ageMs,
|
|
8785
|
-
stale: exchangeLiquidityRow.stale,
|
|
8786
|
-
exchangesCount: toFiniteNumberOrNull3(
|
|
8787
|
-
exchangeLiquidityRow.exchangesCount
|
|
8788
|
-
),
|
|
8789
|
-
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
8790
|
-
exchangeLiquidityRow.totalVolumeUsd
|
|
8791
|
-
),
|
|
8792
|
-
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
8793
|
-
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
8794
|
-
),
|
|
8795
|
-
binanceVolumeUsd: toFiniteNumberOrNull3(
|
|
8796
|
-
exchangeLiquidityRow.binanceVolumeUsd
|
|
8797
|
-
),
|
|
8798
|
-
binanceVolumeShare: toFiniteNumberOrNull3(
|
|
8799
|
-
exchangeLiquidityRow.binanceVolumeShare
|
|
8800
|
-
),
|
|
8801
|
-
topExchangeVolumeShare: toFiniteNumberOrNull3(
|
|
8802
|
-
exchangeLiquidityRow.topExchangeVolumeShare
|
|
8803
|
-
),
|
|
8804
|
-
liquidityRegime: exchangeLiquidityRegime
|
|
8805
|
-
}
|
|
8806
|
-
} : {},
|
|
8807
|
-
...fearGreedRow ? {
|
|
8808
|
-
cmcFearGreed: {
|
|
8809
|
-
source: SOURCE_FEAR_GREED,
|
|
8810
|
-
interval: "1d",
|
|
8811
|
-
asOfTs: fearGreedRow.ts.getTime(),
|
|
8812
|
-
ageMs: fearGreedRow.ageMs,
|
|
8813
|
-
stale: fearGreedRow.stale,
|
|
8814
|
-
value: toFiniteNumberOrNull3(fearGreedRow.value),
|
|
8815
|
-
valueChange24h: toFiniteNumberOrNull3(
|
|
8816
|
-
fearGreedRow.valueChange24h
|
|
8817
|
-
),
|
|
8818
|
-
valueChange7d: toFiniteNumberOrNull3(
|
|
8819
|
-
fearGreedRow.valueChange7d
|
|
8820
|
-
),
|
|
8821
|
-
classification: fearGreedRow.classification ?? "Unknown",
|
|
8822
|
-
sentimentRegime: fearGreedRow.sentimentRegime ?? "unknown"
|
|
8823
|
-
}
|
|
8824
|
-
} : {},
|
|
8825
|
-
...cmc100Row || cmc20Row ? {
|
|
8826
|
-
cmcIndexes: {
|
|
8827
|
-
source: SOURCE_INDEX,
|
|
8828
|
-
interval: "1d",
|
|
8829
|
-
asOfTs: Math.max(
|
|
8830
|
-
cmc100Row?.ts.getTime() ?? 0,
|
|
8831
|
-
cmc20Row?.ts.getTime() ?? 0
|
|
8832
|
-
),
|
|
8833
|
-
ageMs: cmc100Row?.ageMs != null && cmc20Row?.ageMs != null ? Math.max(cmc100Row.ageMs, cmc20Row.ageMs) : cmc100Row?.ageMs ?? cmc20Row?.ageMs ?? null,
|
|
8834
|
-
stale: indexStale,
|
|
8835
|
-
cmc100Value,
|
|
8836
|
-
cmc100Change24hPct,
|
|
8837
|
-
cmc100TopConstituentSymbol: cmc100Row?.topConstituentSymbol ?? null,
|
|
8838
|
-
cmc100TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
8839
|
-
cmc100Row?.topConstituentWeightPct
|
|
8840
|
-
),
|
|
8841
|
-
cmc20Value,
|
|
8842
|
-
cmc20Change24hPct,
|
|
8843
|
-
cmc20TopConstituentSymbol: cmc20Row?.topConstituentSymbol ?? null,
|
|
8844
|
-
cmc20TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
8845
|
-
cmc20Row?.topConstituentWeightPct
|
|
8846
|
-
),
|
|
8847
|
-
cmc20ToCmc100Ratio,
|
|
8848
|
-
cmc20ToCmc100RatioChange24hPct,
|
|
8849
|
-
indexRegime
|
|
8850
|
-
}
|
|
8851
|
-
} : {}
|
|
8852
|
-
}
|
|
8853
|
-
}
|
|
8912
|
+
...signal.additionalIndicators ?? {},
|
|
8913
|
+
baseContext: signal.additionalIndicators?.baseContext && typeof signal.additionalIndicators.baseContext === "object" && !Array.isArray(signal.additionalIndicators.baseContext) ? {
|
|
8914
|
+
...signal.additionalIndicators.baseContext,
|
|
8915
|
+
derivatives: derivativesContext
|
|
8916
|
+
} : signal.additionalIndicators?.baseContext
|
|
8854
8917
|
};
|
|
8855
8918
|
(0, import_strategies3.refreshSignalBaseContextGateFeatures)(signal);
|
|
8856
8919
|
return true;
|
|
8857
8920
|
} catch (error) {
|
|
8858
|
-
|
|
8921
|
+
if (isMarketContextCancellationError(error, params.abortSignal)) {
|
|
8922
|
+
throw error;
|
|
8923
|
+
}
|
|
8924
|
+
derivativesContextUnavailable = true;
|
|
8859
8925
|
import_logger6.logger.warn(
|
|
8860
|
-
"
|
|
8926
|
+
"Derivatives context disabled after Timescale read failure: %s",
|
|
8861
8927
|
String(error)
|
|
8862
8928
|
);
|
|
8863
8929
|
return false;
|
|
@@ -9092,7 +9158,7 @@ var resolveHyperliquidPerpFromSignalSymbol = (symbol) => {
|
|
|
9092
9158
|
var MAX_CACHE_ENTRIES = 2048;
|
|
9093
9159
|
var MAX_COVERAGE_SERIES_CACHE_ENTRIES = 20;
|
|
9094
9160
|
var MAX_FLOW_SERIES_CACHE_ENTRIES = 64;
|
|
9095
|
-
var SERIES_CHUNK_MS =
|
|
9161
|
+
var SERIES_CHUNK_MS = 90 * 24 * 60 * 6e4;
|
|
9096
9162
|
var MAX_SERIES_LOOKBACK_MS = 60 * 6e4;
|
|
9097
9163
|
var DEFAULT_MIN_COVERAGE_PCT = 0.8;
|
|
9098
9164
|
var INTERVAL_MS = {
|
|
@@ -9140,6 +9206,7 @@ var setBoundedCache = (key, value) => {
|
|
|
9140
9206
|
if (oldestKey != null) contextCache.delete(oldestKey);
|
|
9141
9207
|
}
|
|
9142
9208
|
contextCache.set(key, value);
|
|
9209
|
+
void value.catch(() => contextCache.delete(key));
|
|
9143
9210
|
};
|
|
9144
9211
|
var setBoundedSeriesCache = (cache, maxEntries, key, value) => {
|
|
9145
9212
|
if (cache.size >= maxEntries) {
|
|
@@ -9147,6 +9214,7 @@ var setBoundedSeriesCache = (cache, maxEntries, key, value) => {
|
|
|
9147
9214
|
if (oldestKey != null) cache.delete(oldestKey);
|
|
9148
9215
|
}
|
|
9149
9216
|
cache.set(key, value);
|
|
9217
|
+
void value.catch(() => cache.delete(key));
|
|
9150
9218
|
};
|
|
9151
9219
|
var uniqueAddressCount = (rows, field) => {
|
|
9152
9220
|
const addresses = /* @__PURE__ */ new Set();
|
|
@@ -9261,7 +9329,8 @@ var loadHyperliquidWhaleFlowAggregateFromSeries = async (params) => {
|
|
|
9261
9329
|
fromMs,
|
|
9262
9330
|
toMs,
|
|
9263
9331
|
universeFingerprint: params.universeFingerprint,
|
|
9264
|
-
whaleRegistryFingerprint: params.whaleRegistryFingerprint
|
|
9332
|
+
whaleRegistryFingerprint: params.whaleRegistryFingerprint,
|
|
9333
|
+
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
9265
9334
|
});
|
|
9266
9335
|
setBoundedSeriesCache(
|
|
9267
9336
|
coverageSeriesCache,
|
|
@@ -9278,7 +9347,8 @@ var loadHyperliquidWhaleFlowAggregateFromSeries = async (params) => {
|
|
|
9278
9347
|
fromMs,
|
|
9279
9348
|
toMs,
|
|
9280
9349
|
universeFingerprint: params.universeFingerprint,
|
|
9281
|
-
whaleRegistryFingerprint: params.whaleRegistryFingerprint
|
|
9350
|
+
whaleRegistryFingerprint: params.whaleRegistryFingerprint,
|
|
9351
|
+
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
9282
9352
|
});
|
|
9283
9353
|
setBoundedSeriesCache(
|
|
9284
9354
|
flowSeriesCache,
|
|
@@ -9362,7 +9432,8 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
9362
9432
|
decisionTimeMs,
|
|
9363
9433
|
maxAgeMs,
|
|
9364
9434
|
universeFingerprint: universe.fingerprint,
|
|
9365
|
-
whaleRegistryFingerprint: whales.fingerprint
|
|
9435
|
+
whaleRegistryFingerprint: whales.fingerprint,
|
|
9436
|
+
abortSignal: params.abortSignal
|
|
9366
9437
|
});
|
|
9367
9438
|
} else {
|
|
9368
9439
|
let pending = contextCache.get(cacheKey);
|
|
@@ -9373,7 +9444,8 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
9373
9444
|
decisionTimeMs,
|
|
9374
9445
|
maxAgeMs,
|
|
9375
9446
|
universeFingerprint: universe.fingerprint,
|
|
9376
|
-
whaleRegistryFingerprint: whales.fingerprint
|
|
9447
|
+
whaleRegistryFingerprint: whales.fingerprint,
|
|
9448
|
+
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
9377
9449
|
});
|
|
9378
9450
|
setBoundedCache(cacheKey, pending);
|
|
9379
9451
|
}
|
|
@@ -9382,6 +9454,9 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
9382
9454
|
if (!row) return null;
|
|
9383
9455
|
return toBaseHyperliquidWhaleFlowContext(row, minimumCoveragePct);
|
|
9384
9456
|
} catch (error) {
|
|
9457
|
+
if (isMarketContextCancellationError(error, params.abortSignal)) {
|
|
9458
|
+
throw error;
|
|
9459
|
+
}
|
|
9385
9460
|
hyperliquidWhaleContextUnavailable = true;
|
|
9386
9461
|
import_logger7.logger.warn(
|
|
9387
9462
|
"Hyperliquid whale context disabled after Timescale read failure: %s",
|
|
@@ -9402,7 +9477,8 @@ var enrichSignalWithHyperliquidWhaleContext = async (params) => {
|
|
|
9402
9477
|
env,
|
|
9403
9478
|
enabled: params.enabled,
|
|
9404
9479
|
marketInterval: params.interval,
|
|
9405
|
-
maxAgeMs: params.maxAgeMs
|
|
9480
|
+
maxAgeMs: params.maxAgeMs,
|
|
9481
|
+
abortSignal: params.abortSignal
|
|
9406
9482
|
});
|
|
9407
9483
|
if (!hyperliquidWhales) return false;
|
|
9408
9484
|
const baseContext = signal.additionalIndicators.baseContext;
|
|
@@ -9420,6 +9496,125 @@ var enrichSignalWithHyperliquidWhaleContext = async (params) => {
|
|
|
9420
9496
|
return true;
|
|
9421
9497
|
};
|
|
9422
9498
|
|
|
9499
|
+
// src/strategyHelpers/marketContextStages.ts
|
|
9500
|
+
var STAGE_ENV_KEYS = {
|
|
9501
|
+
binance: "BINANCE_MARKET_CONTEXT_STAGE_TIMEOUT_MS",
|
|
9502
|
+
coinmarketcap: "COINMARKETCAP_CONTEXT_STAGE_TIMEOUT_MS",
|
|
9503
|
+
derivatives: "DERIVATIVES_CONTEXT_STAGE_TIMEOUT_MS",
|
|
9504
|
+
hyperliquidWhales: "HYPERLIQUID_WHALE_CONTEXT_STAGE_TIMEOUT_MS"
|
|
9505
|
+
};
|
|
9506
|
+
var parsePositiveInt = (value) => {
|
|
9507
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
9508
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
9509
|
+
};
|
|
9510
|
+
var resolveMarketContextStageTimeoutMs = (stage) => parsePositiveInt(process.env[STAGE_ENV_KEYS[stage]]) ?? parsePositiveInt(process.env.MARKET_CONTEXT_STAGE_TIMEOUT_MS) ?? 35e3;
|
|
9511
|
+
var runMarketContextStage = async ({
|
|
9512
|
+
stage,
|
|
9513
|
+
parentSignal,
|
|
9514
|
+
operation,
|
|
9515
|
+
onStart,
|
|
9516
|
+
onComplete
|
|
9517
|
+
}) => {
|
|
9518
|
+
const controller = new AbortController();
|
|
9519
|
+
const timeoutMs = resolveMarketContextStageTimeoutMs(stage);
|
|
9520
|
+
const startedAt = Date.now();
|
|
9521
|
+
const onParentAbort = () => controller.abort(parentSignal?.reason);
|
|
9522
|
+
parentSignal?.addEventListener("abort", onParentAbort, { once: true });
|
|
9523
|
+
if (parentSignal?.aborted) onParentAbort();
|
|
9524
|
+
onStart?.(stage);
|
|
9525
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
9526
|
+
timer.unref?.();
|
|
9527
|
+
try {
|
|
9528
|
+
let available = false;
|
|
9529
|
+
try {
|
|
9530
|
+
available = await operation(controller.signal);
|
|
9531
|
+
} catch (error) {
|
|
9532
|
+
if (isMarketContextCancellationError(error)) {
|
|
9533
|
+
controller.abort(error);
|
|
9534
|
+
}
|
|
9535
|
+
if (!controller.signal.aborted) throw error;
|
|
9536
|
+
}
|
|
9537
|
+
const status = controller.signal.aborted ? "timed_out" : available ? "available" : "absent";
|
|
9538
|
+
const result = {
|
|
9539
|
+
stage,
|
|
9540
|
+
status,
|
|
9541
|
+
elapsedMs: Date.now() - startedAt
|
|
9542
|
+
};
|
|
9543
|
+
if (status === "timed_out") {
|
|
9544
|
+
import_logger8.logger.warn(
|
|
9545
|
+
"Market context stage timed out: %s after %sms",
|
|
9546
|
+
stage,
|
|
9547
|
+
result.elapsedMs
|
|
9548
|
+
);
|
|
9549
|
+
}
|
|
9550
|
+
onComplete?.(result);
|
|
9551
|
+
return result;
|
|
9552
|
+
} finally {
|
|
9553
|
+
clearTimeout(timer);
|
|
9554
|
+
parentSignal?.removeEventListener("abort", onParentAbort);
|
|
9555
|
+
}
|
|
9556
|
+
};
|
|
9557
|
+
var enrichSignalWithMarketContextStages = async ({
|
|
9558
|
+
signal,
|
|
9559
|
+
env,
|
|
9560
|
+
coinMarketCapEnabled,
|
|
9561
|
+
includeHyperliquidWhales = true,
|
|
9562
|
+
abortSignal,
|
|
9563
|
+
onStageStart,
|
|
9564
|
+
onStageComplete
|
|
9565
|
+
}) => {
|
|
9566
|
+
const stages = [
|
|
9567
|
+
{
|
|
9568
|
+
stage: "binance",
|
|
9569
|
+
operation: (stageSignal) => enrichSignalWithBinanceMarketContext({
|
|
9570
|
+
signal,
|
|
9571
|
+
env,
|
|
9572
|
+
abortSignal: stageSignal
|
|
9573
|
+
})
|
|
9574
|
+
},
|
|
9575
|
+
{
|
|
9576
|
+
stage: "coinmarketcap",
|
|
9577
|
+
operation: (stageSignal) => enrichSignalWithCoinMarketCapContext({
|
|
9578
|
+
signal,
|
|
9579
|
+
env,
|
|
9580
|
+
enabled: coinMarketCapEnabled,
|
|
9581
|
+
abortSignal: stageSignal
|
|
9582
|
+
})
|
|
9583
|
+
},
|
|
9584
|
+
{
|
|
9585
|
+
stage: "derivatives",
|
|
9586
|
+
operation: (stageSignal) => enrichSignalWithDerivativesContext({
|
|
9587
|
+
signal,
|
|
9588
|
+
env,
|
|
9589
|
+
abortSignal: stageSignal
|
|
9590
|
+
})
|
|
9591
|
+
}
|
|
9592
|
+
];
|
|
9593
|
+
if (includeHyperliquidWhales) {
|
|
9594
|
+
stages.push({
|
|
9595
|
+
stage: "hyperliquidWhales",
|
|
9596
|
+
operation: (stageSignal) => enrichSignalWithHyperliquidWhaleContext({
|
|
9597
|
+
signal,
|
|
9598
|
+
env,
|
|
9599
|
+
abortSignal: stageSignal
|
|
9600
|
+
})
|
|
9601
|
+
});
|
|
9602
|
+
}
|
|
9603
|
+
const results = [];
|
|
9604
|
+
for (const stage of stages) {
|
|
9605
|
+
if (abortSignal?.aborted) break;
|
|
9606
|
+
results.push(
|
|
9607
|
+
await runMarketContextStage({
|
|
9608
|
+
...stage,
|
|
9609
|
+
parentSignal: abortSignal,
|
|
9610
|
+
onStart: onStageStart,
|
|
9611
|
+
onComplete: onStageComplete
|
|
9612
|
+
})
|
|
9613
|
+
);
|
|
9614
|
+
}
|
|
9615
|
+
return results;
|
|
9616
|
+
};
|
|
9617
|
+
|
|
9423
9618
|
// src/strategyHelpers/runtime.ts
|
|
9424
9619
|
var formatAiError = (err) => {
|
|
9425
9620
|
const error = err;
|
|
@@ -9536,7 +9731,7 @@ var enrichSignalWithAi = async ({
|
|
|
9536
9731
|
signal.aiAnalysis = analysis;
|
|
9537
9732
|
return resolveAiQuality(analysis, direction);
|
|
9538
9733
|
} catch (err) {
|
|
9539
|
-
|
|
9734
|
+
import_logger9.logger.error("AI analysis error: %s %s", symbol, formatAiError(err));
|
|
9540
9735
|
}
|
|
9541
9736
|
return void 0;
|
|
9542
9737
|
};
|
|
@@ -9549,10 +9744,7 @@ var enrichSignalWithMlAi = async ({
|
|
|
9549
9744
|
ml,
|
|
9550
9745
|
ai
|
|
9551
9746
|
}) => {
|
|
9552
|
-
await
|
|
9553
|
-
await enrichSignalWithCoinMarketCapContext({ signal, env });
|
|
9554
|
-
await enrichSignalWithDerivativesContext({ signal, env });
|
|
9555
|
-
await enrichSignalWithHyperliquidWhaleContext({ signal, env });
|
|
9747
|
+
await enrichSignalWithMarketContextStages({ signal, env });
|
|
9556
9748
|
await enrichSignalWithMl({ signal, env, ml });
|
|
9557
9749
|
return enrichSignalWithAi({ signal, userName, symbol, direction, env, ai });
|
|
9558
9750
|
};
|
|
@@ -9587,7 +9779,7 @@ var getOrderArrivalSnapshot = async ({
|
|
|
9587
9779
|
spreadBps
|
|
9588
9780
|
};
|
|
9589
9781
|
} catch (error) {
|
|
9590
|
-
|
|
9782
|
+
import_logger9.logger.warn(
|
|
9591
9783
|
"runtime order arrival snapshot failed: %s %s",
|
|
9592
9784
|
symbol,
|
|
9593
9785
|
error?.message || String(error)
|
|
@@ -9816,6 +10008,7 @@ var executeEntryOrder = async ({
|
|
|
9816
10008
|
deploymentId: signal.deploymentId,
|
|
9817
10009
|
policyProfileId: signal.policyProfileId,
|
|
9818
10010
|
runtimeConfigId: signal.runtimeConfigId,
|
|
10011
|
+
runtimeLineage: signal.runtimeLineage,
|
|
9819
10012
|
...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {}
|
|
9820
10013
|
});
|
|
9821
10014
|
}
|
|
@@ -9934,6 +10127,10 @@ var resolveStrategyConfig = async ({
|
|
|
9934
10127
|
|
|
9935
10128
|
// src/strategyRuntime.ts
|
|
9936
10129
|
var import_types = require("@tradejs/types");
|
|
10130
|
+
var cloneWithPropertyDescriptors = (value) => Object.create(
|
|
10131
|
+
Object.getPrototypeOf(value),
|
|
10132
|
+
Object.getOwnPropertyDescriptors(value)
|
|
10133
|
+
);
|
|
9937
10134
|
var buildExitOrderSignal = ({
|
|
9938
10135
|
strategyName,
|
|
9939
10136
|
symbol,
|
|
@@ -10453,7 +10650,7 @@ var handleExitDecision = async ({
|
|
|
10453
10650
|
deploymentId: connector.deploymentId
|
|
10454
10651
|
});
|
|
10455
10652
|
if (!activeTrade) {
|
|
10456
|
-
|
|
10653
|
+
import_logger10.logger.warn(
|
|
10457
10654
|
"[%s] blocked closePosition for untracked runtime position: %s",
|
|
10458
10655
|
strategyName ?? "unknown",
|
|
10459
10656
|
symbol
|
|
@@ -10461,7 +10658,7 @@ var handleExitDecision = async ({
|
|
|
10461
10658
|
return "CLOSE_BLOCKED_BY_UNTRACKED_POSITION";
|
|
10462
10659
|
}
|
|
10463
10660
|
if (!strategyName || activeTrade.strategy !== strategyName) {
|
|
10464
|
-
|
|
10661
|
+
import_logger10.logger.warn(
|
|
10465
10662
|
"[%s] blocked closePosition for foreign runtime position: %s ownedBy=%s",
|
|
10466
10663
|
strategyName ?? "unknown",
|
|
10467
10664
|
symbol,
|
|
@@ -10513,7 +10710,7 @@ var handleExitDecision = async ({
|
|
|
10513
10710
|
exitType: closedTrade?.exitType ?? "exit"
|
|
10514
10711
|
});
|
|
10515
10712
|
} catch (notificationError) {
|
|
10516
|
-
|
|
10713
|
+
import_logger10.logger.error(
|
|
10517
10714
|
"runtime close notification error: %s %s",
|
|
10518
10715
|
symbol,
|
|
10519
10716
|
notificationError
|
|
@@ -10527,7 +10724,7 @@ var handleExitDecision = async ({
|
|
|
10527
10724
|
decision,
|
|
10528
10725
|
market
|
|
10529
10726
|
});
|
|
10530
|
-
|
|
10727
|
+
import_logger10.logger.error("close order error: %s %s", symbol, err);
|
|
10531
10728
|
return "ORDER_ERROR";
|
|
10532
10729
|
}
|
|
10533
10730
|
return decision.code;
|
|
@@ -10554,7 +10751,7 @@ var handleProtectDecision = async ({
|
|
|
10554
10751
|
decision,
|
|
10555
10752
|
market
|
|
10556
10753
|
});
|
|
10557
|
-
|
|
10754
|
+
import_logger10.logger.error("protect position error: %s %s", symbol, err);
|
|
10558
10755
|
return "ORDER_ERROR";
|
|
10559
10756
|
}
|
|
10560
10757
|
return decision.code;
|
|
@@ -10717,9 +10914,9 @@ var executeEntryDecision = async ({
|
|
|
10717
10914
|
market
|
|
10718
10915
|
});
|
|
10719
10916
|
if (err?.message === import_types.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY) {
|
|
10720
|
-
|
|
10917
|
+
import_logger10.logger.warn("order warning: %s %s", symbol, err);
|
|
10721
10918
|
} else {
|
|
10722
|
-
|
|
10919
|
+
import_logger10.logger.error("order error: %s %s", symbol, err);
|
|
10723
10920
|
}
|
|
10724
10921
|
return signal ?? "ORDER_ERROR";
|
|
10725
10922
|
}
|
|
@@ -10874,7 +11071,7 @@ var createStrategyRuntime = ({
|
|
|
10874
11071
|
try {
|
|
10875
11072
|
await projectHook(errorParams);
|
|
10876
11073
|
} catch (hookError) {
|
|
10877
|
-
|
|
11074
|
+
import_logger10.logger.error(
|
|
10878
11075
|
"project hook onRuntimeError failed: %s %s",
|
|
10879
11076
|
strategyName,
|
|
10880
11077
|
hookError
|
|
@@ -10888,7 +11085,7 @@ var createStrategyRuntime = ({
|
|
|
10888
11085
|
try {
|
|
10889
11086
|
await onRuntimeError(errorParams);
|
|
10890
11087
|
} catch (hookError) {
|
|
10891
|
-
|
|
11088
|
+
import_logger10.logger.error(
|
|
10892
11089
|
"runtime hook onRuntimeError failed: %s %s",
|
|
10893
11090
|
strategyName,
|
|
10894
11091
|
hookError
|
|
@@ -10902,7 +11099,7 @@ var createStrategyRuntime = ({
|
|
|
10902
11099
|
try {
|
|
10903
11100
|
return await hook(params);
|
|
10904
11101
|
} catch (error) {
|
|
10905
|
-
|
|
11102
|
+
import_logger10.logger.error(
|
|
10906
11103
|
'strategy hook "%s" failed for %s: %s',
|
|
10907
11104
|
stage,
|
|
10908
11105
|
strategyName,
|
|
@@ -11050,6 +11247,9 @@ var createStrategyRuntime = ({
|
|
|
11050
11247
|
sharedReplayKey: indicatorSharedReplayKey,
|
|
11051
11248
|
useBtcReference: universe === "crypto"
|
|
11052
11249
|
});
|
|
11250
|
+
const coreContextRequirements = new Set(
|
|
11251
|
+
strategyManifest?.contextRequirements?.core ?? []
|
|
11252
|
+
);
|
|
11053
11253
|
const strategyApi = (0, import_strategies5.createStrategyAPI)({
|
|
11054
11254
|
strategy: strategyName,
|
|
11055
11255
|
symbol,
|
|
@@ -11068,6 +11268,9 @@ var createStrategyRuntime = ({
|
|
|
11068
11268
|
interval: decisionInterval
|
|
11069
11269
|
}) => {
|
|
11070
11270
|
if (!baseContext) return void 0;
|
|
11271
|
+
if (!coreContextRequirements.has("hyperliquidWhales")) {
|
|
11272
|
+
return baseContext;
|
|
11273
|
+
}
|
|
11071
11274
|
const hyperliquidWhales = await loadHyperliquidWhaleFlowContext({
|
|
11072
11275
|
symbol: decisionSymbol,
|
|
11073
11276
|
interval: decisionInterval,
|
|
@@ -11076,13 +11279,23 @@ var createStrategyRuntime = ({
|
|
|
11076
11279
|
useSeriesCache: env === "BACKTEST" || env === "PARITY"
|
|
11077
11280
|
});
|
|
11078
11281
|
if (!hyperliquidWhales) return baseContext;
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
|
|
11085
|
-
|
|
11282
|
+
const participation = cloneWithPropertyDescriptors(
|
|
11283
|
+
baseContext.participation
|
|
11284
|
+
);
|
|
11285
|
+
Object.defineProperty(participation, "hyperliquidWhales", {
|
|
11286
|
+
configurable: true,
|
|
11287
|
+
enumerable: true,
|
|
11288
|
+
value: hyperliquidWhales,
|
|
11289
|
+
writable: true
|
|
11290
|
+
});
|
|
11291
|
+
const enrichedBaseContext = cloneWithPropertyDescriptors(baseContext);
|
|
11292
|
+
Object.defineProperty(enrichedBaseContext, "participation", {
|
|
11293
|
+
configurable: true,
|
|
11294
|
+
enumerable: true,
|
|
11295
|
+
value: participation,
|
|
11296
|
+
writable: true
|
|
11297
|
+
});
|
|
11298
|
+
return enrichedBaseContext;
|
|
11086
11299
|
}
|
|
11087
11300
|
});
|
|
11088
11301
|
const core = await createCore({
|
|
@@ -11473,17 +11686,10 @@ var createStrategyRuntime = ({
|
|
|
11473
11686
|
let ai;
|
|
11474
11687
|
if (signal) {
|
|
11475
11688
|
try {
|
|
11476
|
-
await
|
|
11689
|
+
await enrichSignalWithMarketContextStages({
|
|
11477
11690
|
signal,
|
|
11478
|
-
env
|
|
11479
|
-
|
|
11480
|
-
await enrichSignalWithCoinMarketCapContext({
|
|
11481
|
-
signal,
|
|
11482
|
-
env
|
|
11483
|
-
});
|
|
11484
|
-
await enrichSignalWithDerivativesContext({
|
|
11485
|
-
signal,
|
|
11486
|
-
env
|
|
11691
|
+
env,
|
|
11692
|
+
includeHyperliquidWhales: false
|
|
11487
11693
|
});
|
|
11488
11694
|
quality = await enrichSignalWithAi({
|
|
11489
11695
|
signal,
|
|
@@ -11644,7 +11850,7 @@ var createStrategyRuntime = ({
|
|
|
11644
11850
|
|
|
11645
11851
|
// src/strategyHooks/closeOppositePositionsBeforeOpen.ts
|
|
11646
11852
|
var import_lodash2 = __toESM(require_lodash());
|
|
11647
|
-
var
|
|
11853
|
+
var import_logger11 = require("@tradejs/infra/logger");
|
|
11648
11854
|
var closeOppositePositionsBeforeOpen = async ({
|
|
11649
11855
|
connector,
|
|
11650
11856
|
entryContext
|
|
@@ -11658,7 +11864,7 @@ var closeOppositePositionsBeforeOpen = async ({
|
|
|
11658
11864
|
} = entryContext;
|
|
11659
11865
|
const price = prices.currentPrice;
|
|
11660
11866
|
try {
|
|
11661
|
-
|
|
11867
|
+
import_logger11.logger.log(
|
|
11662
11868
|
"info",
|
|
11663
11869
|
"[%s] checking open positions before open: %s %s",
|
|
11664
11870
|
strategyName,
|
|
@@ -11669,7 +11875,7 @@ var closeOppositePositionsBeforeOpen = async ({
|
|
|
11669
11875
|
const openPositions = (positions || []).filter(
|
|
11670
11876
|
(item) => item && Number(item.qty) > 0
|
|
11671
11877
|
);
|
|
11672
|
-
|
|
11878
|
+
import_logger11.logger.log(
|
|
11673
11879
|
"info",
|
|
11674
11880
|
"[%s] open positions found: %s",
|
|
11675
11881
|
strategyName,
|
|
@@ -11679,7 +11885,7 @@ var closeOppositePositionsBeforeOpen = async ({
|
|
|
11679
11885
|
(item) => item.symbol !== currentSymbol && item.direction !== currentDirection
|
|
11680
11886
|
);
|
|
11681
11887
|
if (import_lodash2.default.isEmpty(oppositePositions)) {
|
|
11682
|
-
|
|
11888
|
+
import_logger11.logger.log(
|
|
11683
11889
|
"info",
|
|
11684
11890
|
"[%s] no opposite positions to close before open: %s",
|
|
11685
11891
|
strategyName,
|
|
@@ -11688,7 +11894,7 @@ var closeOppositePositionsBeforeOpen = async ({
|
|
|
11688
11894
|
return;
|
|
11689
11895
|
}
|
|
11690
11896
|
for (const position of oppositePositions) {
|
|
11691
|
-
|
|
11897
|
+
import_logger11.logger.log(
|
|
11692
11898
|
"info",
|
|
11693
11899
|
"[%s] closing opposite position: %s %s qty=%s",
|
|
11694
11900
|
strategyName,
|
|
@@ -11703,14 +11909,14 @@ var closeOppositePositionsBeforeOpen = async ({
|
|
|
11703
11909
|
timestamp,
|
|
11704
11910
|
direction: position.direction
|
|
11705
11911
|
});
|
|
11706
|
-
|
|
11912
|
+
import_logger11.logger.log(
|
|
11707
11913
|
"info",
|
|
11708
11914
|
"[%s] opposite position closed: %s",
|
|
11709
11915
|
strategyName,
|
|
11710
11916
|
position.symbol
|
|
11711
11917
|
);
|
|
11712
11918
|
} catch (err) {
|
|
11713
|
-
|
|
11919
|
+
import_logger11.logger.log(
|
|
11714
11920
|
"error",
|
|
11715
11921
|
"[%s] failed to close opposite position: %s %s",
|
|
11716
11922
|
strategyName,
|
|
@@ -11720,7 +11926,7 @@ var closeOppositePositionsBeforeOpen = async ({
|
|
|
11720
11926
|
}
|
|
11721
11927
|
}
|
|
11722
11928
|
} catch (err) {
|
|
11723
|
-
|
|
11929
|
+
import_logger11.logger.log(
|
|
11724
11930
|
"error",
|
|
11725
11931
|
"[%s] failed to load open positions before open: %s %s",
|
|
11726
11932
|
strategyName,
|
|
@@ -11932,7 +12138,7 @@ var createMoveStopToBreakEvenOnBarHook = ({
|
|
|
11932
12138
|
var createMoveStopToBreakEvenAfterCoreDecisionHook = createMoveStopToBreakEvenOnBarHook;
|
|
11933
12139
|
|
|
11934
12140
|
// src/signalsHooks/closeAllPositionsOnGlobalProfitBeforeSignals.ts
|
|
11935
|
-
var
|
|
12141
|
+
var import_logger12 = require("@tradejs/infra/logger");
|
|
11936
12142
|
var createCloseAllOnGlobalProfitBeforeSignalsHook = ({
|
|
11937
12143
|
getStrategyDefaultConfig = () => void 0,
|
|
11938
12144
|
profitRiskMultiplier = DEFAULT_GLOBAL_UNREALIZED_PNL_TRIGGER_RISK_MULTIPLIER
|
|
@@ -11971,7 +12177,7 @@ var createCloseAllOnGlobalProfitBeforeSignalsHook = ({
|
|
|
11971
12177
|
if (!Number.isFinite(unrealizedPnlThreshold) || unrealizedPnlThreshold <= 0 || totalUnrealizedPnl < unrealizedPnlThreshold) {
|
|
11972
12178
|
return;
|
|
11973
12179
|
}
|
|
11974
|
-
|
|
12180
|
+
import_logger12.logger.info(
|
|
11975
12181
|
"closing all positions before signals by global unrealized pnl threshold: totalPnl=%s threshold=%s positions=%s",
|
|
11976
12182
|
totalUnrealizedPnl,
|
|
11977
12183
|
unrealizedPnlThreshold,
|
|
@@ -11997,7 +12203,7 @@ var createCloseAllOnGlobalProfitBeforeSignalsHook = ({
|
|
|
11997
12203
|
];
|
|
11998
12204
|
});
|
|
11999
12205
|
if (failedClosures.length) {
|
|
12000
|
-
|
|
12206
|
+
import_logger12.logger.warn(
|
|
12001
12207
|
"close-all before signals hook could not confirm closures for %s",
|
|
12002
12208
|
failedClosures.join(", ")
|
|
12003
12209
|
);
|