@tradejs/node 3.1.29-beta.288 → 3.1.29-beta.290
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/backtest.js +442 -234
- package/dist/backtest.mjs +10 -5
- package/dist/{chunk-F7CLF3PT.mjs → chunk-PB2KHBGH.mjs} +415 -212
- package/dist/cli.js +3 -2
- package/dist/cli.mjs +3 -2
- package/dist/diagnostics.js +416 -213
- package/dist/diagnostics.mjs +1 -1
- package/dist/mcp.d.mts +3 -1
- package/dist/mcp.d.ts +3 -1
- package/dist/mcp.js +455 -230
- package/dist/mcp.mjs +24 -3
- package/dist/registry.js +416 -213
- package/dist/registry.mjs +1 -1
- package/dist/runtimeDashboard.js +418 -215
- package/dist/runtimeDashboard.mjs +1 -1
- package/dist/runtimeStrategies.js +416 -213
- package/dist/runtimeStrategies.mjs +1 -1
- package/dist/strategies.d.mts +32 -22
- package/dist/strategies.d.ts +32 -22
- package/dist/strategies.js +424 -221
- package/dist/strategies.mjs +1 -1
- package/package.json +5 -5
package/dist/mcp.js
CHANGED
|
@@ -781,6 +781,7 @@ __export(mcp_exports, {
|
|
|
781
781
|
McpCursorError: () => McpCursorError,
|
|
782
782
|
cancelMcpJob: () => cancelMcpJob,
|
|
783
783
|
enqueueMcpJob: () => enqueueMcpJob,
|
|
784
|
+
formatMcpArtifactLogLine: () => formatMcpArtifactLogLine,
|
|
784
785
|
isMcpRecord: () => isMcpRecord,
|
|
785
786
|
mcpId: () => mcpId,
|
|
786
787
|
mcpJobDirectory: () => mcpJobDirectory,
|
|
@@ -1066,13 +1067,210 @@ var assessSignalWithJev = async ({
|
|
|
1066
1067
|
// src/strategyRuntime.ts
|
|
1067
1068
|
var import_constants5 = require("@tradejs/core/constants");
|
|
1068
1069
|
var import_strategies6 = require("@tradejs/core/strategies");
|
|
1069
|
-
var
|
|
1070
|
+
var import_logger11 = require("@tradejs/infra/logger");
|
|
1070
1071
|
|
|
1071
1072
|
// src/strategyHelpers/runtime.ts
|
|
1072
|
-
var
|
|
1073
|
+
var import_logger9 = require("@tradejs/infra/logger");
|
|
1073
1074
|
var import_constants3 = require("@tradejs/core/constants");
|
|
1074
1075
|
var import_ml2 = require("@tradejs/infra/ml");
|
|
1075
1076
|
|
|
1077
|
+
// src/strategy/entrySubmissionValidation.ts
|
|
1078
|
+
var import_logger = require("@tradejs/infra/logger");
|
|
1079
|
+
|
|
1080
|
+
// src/strategy/entryExecutionValidation.ts
|
|
1081
|
+
var EntrySubmissionValidationError = class extends Error {
|
|
1082
|
+
constructor() {
|
|
1083
|
+
super(...arguments);
|
|
1084
|
+
this.name = "EntrySubmissionValidationError";
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
var resolveEntryExecutionLimits = (config, direction) => {
|
|
1088
|
+
const side = config[direction];
|
|
1089
|
+
const minimum = side && typeof side === "object" && "minRiskRatio" in side ? side.minRiskRatio : void 0;
|
|
1090
|
+
const maximum = config.MAX_LOSS_VALUE;
|
|
1091
|
+
if (minimum == null && maximum == null) return void 0;
|
|
1092
|
+
const minimumNetRiskRatio = minimum == null ? void 0 : Number(minimum);
|
|
1093
|
+
const maxLossValue = maximum == null ? void 0 : Number(maximum);
|
|
1094
|
+
const feeRate = Number(config.RISK_FEE_RATE ?? 0);
|
|
1095
|
+
const slippage = Number(config.RISK_SLIPPAGE_BPS ?? 0);
|
|
1096
|
+
const impact = Number(config.RISK_MARKET_IMPACT_BPS ?? 0);
|
|
1097
|
+
const slippageBps = slippage + impact;
|
|
1098
|
+
if (minimumNetRiskRatio != null && (!Number.isFinite(minimumNetRiskRatio) || minimumNetRiskRatio < 0) || maxLossValue != null && (!Number.isFinite(maxLossValue) || maxLossValue <= 0) || !Number.isFinite(feeRate) || feeRate < 0 || feeRate >= 1 || !Number.isFinite(slippage) || slippage < 0 || !Number.isFinite(impact) || impact < 0 || !Number.isFinite(slippageBps) || slippageBps < 0 || slippageBps >= 1e4)
|
|
1099
|
+
throw new Error("INVALID_ENTRY_EXECUTION_LIMITS");
|
|
1100
|
+
return { minimumNetRiskRatio, maxLossValue, feeRate, slippageBps };
|
|
1101
|
+
};
|
|
1102
|
+
var validateEntryExecution = ({
|
|
1103
|
+
direction,
|
|
1104
|
+
price,
|
|
1105
|
+
qty,
|
|
1106
|
+
stopLossPrice,
|
|
1107
|
+
takeProfitPrice,
|
|
1108
|
+
limits
|
|
1109
|
+
}) => {
|
|
1110
|
+
if (!Number.isFinite(price) || price <= 0 || !Number.isFinite(qty) || qty <= 0 || stopLossPrice == null || !Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || !Number.isFinite(takeProfitPrice) || takeProfitPrice <= 0)
|
|
1111
|
+
throw new EntrySubmissionValidationError(
|
|
1112
|
+
"ENTRY_ECONOMICS_INPUTS_UNAVAILABLE"
|
|
1113
|
+
);
|
|
1114
|
+
const side = direction === "LONG" ? 1 : -1;
|
|
1115
|
+
if (side * (price - stopLossPrice) <= 0 || side * (takeProfitPrice - price) <= 0)
|
|
1116
|
+
throw new EntrySubmissionValidationError(
|
|
1117
|
+
"INVALID_ENTRY_PROTECTION_AT_SUBMISSION"
|
|
1118
|
+
);
|
|
1119
|
+
const slippage = limits.slippageBps / 1e4;
|
|
1120
|
+
const cost = (value, sign) => value * slippage + value * (1 + sign * slippage) * limits.feeRate;
|
|
1121
|
+
const entryCost = cost(price, side);
|
|
1122
|
+
const lossPerUnit = Math.abs(price - stopLossPrice) + entryCost + cost(stopLossPrice, -side);
|
|
1123
|
+
const rewardPerUnit = Math.max(
|
|
1124
|
+
0,
|
|
1125
|
+
Math.abs(takeProfitPrice - price) - entryCost - cost(takeProfitPrice, -side)
|
|
1126
|
+
);
|
|
1127
|
+
const netRiskRatio = rewardPerUnit / lossPerUnit;
|
|
1128
|
+
const plannedLossValue = qty * lossPerUnit;
|
|
1129
|
+
if (!Number.isFinite(netRiskRatio) || !Number.isFinite(plannedLossValue))
|
|
1130
|
+
throw new EntrySubmissionValidationError(
|
|
1131
|
+
"ENTRY_ECONOMICS_INPUTS_UNAVAILABLE"
|
|
1132
|
+
);
|
|
1133
|
+
if (limits.minimumNetRiskRatio != null && netRiskRatio <= limits.minimumNetRiskRatio)
|
|
1134
|
+
throw new EntrySubmissionValidationError(
|
|
1135
|
+
"ENTRY_RR_BELOW_MIN_AT_SUBMISSION"
|
|
1136
|
+
);
|
|
1137
|
+
const riskExceeded = limits.maxLossValue != null && plannedLossValue > limits.maxLossValue * (1 + 1e-9);
|
|
1138
|
+
return { netRiskRatio, plannedLossValue, riskExceeded };
|
|
1139
|
+
};
|
|
1140
|
+
|
|
1141
|
+
// src/strategy/entrySubmissionValidation.ts
|
|
1142
|
+
var MAX_ENTRY_QUOTE_AGE_MS = 5e3;
|
|
1143
|
+
var toFiniteNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1144
|
+
var getOrderArrivalSnapshot = async ({
|
|
1145
|
+
connector,
|
|
1146
|
+
symbol,
|
|
1147
|
+
requireTimestamp = false
|
|
1148
|
+
}) => {
|
|
1149
|
+
if (typeof connector.getTopOfBookTicker !== "function") {
|
|
1150
|
+
return {
|
|
1151
|
+
arrivalSnapshotTime: Date.now(),
|
|
1152
|
+
arrivalSource: "unavailable",
|
|
1153
|
+
bid: null,
|
|
1154
|
+
ask: null,
|
|
1155
|
+
arrivalMid: null,
|
|
1156
|
+
spreadBps: null
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
try {
|
|
1160
|
+
const ticker = await connector.getTopOfBookTicker(symbol);
|
|
1161
|
+
const arrivalSnapshotTime = toFiniteNumberOrNull(ticker?.timestamp);
|
|
1162
|
+
const timestampUnavailable = requireTimestamp && arrivalSnapshotTime == null;
|
|
1163
|
+
const bid = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.bidPrice);
|
|
1164
|
+
const ask = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.askPrice);
|
|
1165
|
+
const arrivalMid = bid != null && ask != null ? (bid + ask) / 2 : null;
|
|
1166
|
+
const spreadBps = bid != null && ask != null && arrivalMid != null && arrivalMid > 0 ? (ask - bid) / arrivalMid * 1e4 : null;
|
|
1167
|
+
return {
|
|
1168
|
+
arrivalSnapshotTime: arrivalSnapshotTime ?? Date.now(),
|
|
1169
|
+
arrivalSource: "top_of_book",
|
|
1170
|
+
bid,
|
|
1171
|
+
ask,
|
|
1172
|
+
arrivalMid,
|
|
1173
|
+
spreadBps
|
|
1174
|
+
};
|
|
1175
|
+
} catch (error) {
|
|
1176
|
+
import_logger.logger.warn(
|
|
1177
|
+
"runtime order arrival snapshot failed: %s %s",
|
|
1178
|
+
symbol,
|
|
1179
|
+
error?.message || String(error)
|
|
1180
|
+
);
|
|
1181
|
+
return {
|
|
1182
|
+
arrivalSnapshotTime: Date.now(),
|
|
1183
|
+
arrivalSource: "top_of_book_error",
|
|
1184
|
+
bid: null,
|
|
1185
|
+
ask: null,
|
|
1186
|
+
arrivalMid: null,
|
|
1187
|
+
spreadBps: null
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
};
|
|
1191
|
+
var validateEntryProtectionAtArrival = ({
|
|
1192
|
+
direction,
|
|
1193
|
+
signalPrice,
|
|
1194
|
+
bid,
|
|
1195
|
+
ask,
|
|
1196
|
+
arrivalMid,
|
|
1197
|
+
takeProfits,
|
|
1198
|
+
stopLossPrice
|
|
1199
|
+
}) => {
|
|
1200
|
+
const entryReference = direction === "LONG" ? ask ?? arrivalMid ?? signalPrice : bid ?? arrivalMid ?? signalPrice;
|
|
1201
|
+
const stopReference = direction === "LONG" ? bid ?? arrivalMid ?? signalPrice : ask ?? arrivalMid ?? signalPrice;
|
|
1202
|
+
if (!Number.isFinite(entryReference) || entryReference <= 0 || !Number.isFinite(stopReference) || stopReference <= 0) {
|
|
1203
|
+
throw new Error("INVALID_ENTRY_REFERENCE_AT_ARRIVAL");
|
|
1204
|
+
}
|
|
1205
|
+
for (const takeProfit of takeProfits) {
|
|
1206
|
+
if (!Number.isFinite(takeProfit.price) || takeProfit.price <= 0 || (direction === "LONG" ? takeProfit.price <= entryReference : takeProfit.price >= entryReference)) {
|
|
1207
|
+
throw new Error("TAKE_PROFIT_CROSSED_BEFORE_ENTRY");
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
if (stopLossPrice != null && (!Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || (direction === "LONG" ? stopLossPrice >= stopReference : stopLossPrice <= stopReference))) {
|
|
1211
|
+
throw new Error("STOP_LOSS_CROSSED_BEFORE_ENTRY");
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
var createEntrySubmissionValidation = ({
|
|
1215
|
+
connector,
|
|
1216
|
+
symbol,
|
|
1217
|
+
direction,
|
|
1218
|
+
takeProfits,
|
|
1219
|
+
stopLossPrice,
|
|
1220
|
+
limits,
|
|
1221
|
+
onValidated
|
|
1222
|
+
}) => {
|
|
1223
|
+
if (!limits) return void 0;
|
|
1224
|
+
if (!connector.capabilities?.orderSubmissionValidation)
|
|
1225
|
+
throw new EntrySubmissionValidationError(
|
|
1226
|
+
"ENTRY_SUBMISSION_VALIDATION_UNSUPPORTED"
|
|
1227
|
+
);
|
|
1228
|
+
return async ({ qty, executionPrice }) => {
|
|
1229
|
+
const simulated = Reflect.get(connector, "__tradejsTestConnector") === true;
|
|
1230
|
+
const snapshot2 = simulated && executionPrice != null ? {
|
|
1231
|
+
arrivalSnapshotTime: Date.now(),
|
|
1232
|
+
arrivalSource: "simulation",
|
|
1233
|
+
bid: executionPrice,
|
|
1234
|
+
ask: executionPrice,
|
|
1235
|
+
arrivalMid: executionPrice,
|
|
1236
|
+
spreadBps: 0
|
|
1237
|
+
} : await getOrderArrivalSnapshot({
|
|
1238
|
+
connector,
|
|
1239
|
+
symbol,
|
|
1240
|
+
requireTimestamp: true
|
|
1241
|
+
});
|
|
1242
|
+
const age = Date.now() - snapshot2.arrivalSnapshotTime;
|
|
1243
|
+
if (snapshot2.bid == null || snapshot2.ask == null || !Number.isFinite(snapshot2.bid) || !Number.isFinite(snapshot2.ask) || snapshot2.bid <= 0 || snapshot2.ask < snapshot2.bid)
|
|
1244
|
+
throw new EntrySubmissionValidationError(
|
|
1245
|
+
"ENTRY_QUOTE_UNAVAILABLE_AT_SUBMISSION"
|
|
1246
|
+
);
|
|
1247
|
+
if (!Number.isFinite(age) || age < -MAX_ENTRY_QUOTE_AGE_MS || age > MAX_ENTRY_QUOTE_AGE_MS)
|
|
1248
|
+
throw new EntrySubmissionValidationError(
|
|
1249
|
+
"ENTRY_QUOTE_STALE_AT_SUBMISSION"
|
|
1250
|
+
);
|
|
1251
|
+
const price = direction === "LONG" ? snapshot2.ask : snapshot2.bid;
|
|
1252
|
+
validateEntryProtectionAtArrival({
|
|
1253
|
+
direction,
|
|
1254
|
+
signalPrice: price,
|
|
1255
|
+
bid: snapshot2.bid,
|
|
1256
|
+
ask: snapshot2.ask,
|
|
1257
|
+
arrivalMid: snapshot2.arrivalMid,
|
|
1258
|
+
takeProfits,
|
|
1259
|
+
stopLossPrice
|
|
1260
|
+
});
|
|
1261
|
+
const prices = takeProfits.map((tp) => tp.price);
|
|
1262
|
+
const economics2 = validateEntryExecution({
|
|
1263
|
+
direction,
|
|
1264
|
+
price,
|
|
1265
|
+
qty,
|
|
1266
|
+
stopLossPrice,
|
|
1267
|
+
takeProfitPrice: direction === "LONG" ? Math.max(...prices) : Math.min(...prices),
|
|
1268
|
+
limits
|
|
1269
|
+
});
|
|
1270
|
+
onValidated?.(snapshot2, economics2);
|
|
1271
|
+
};
|
|
1272
|
+
};
|
|
1273
|
+
|
|
1076
1274
|
// src/ai.ts
|
|
1077
1275
|
var import_aiLanguages2 = require("@tradejs/core/aiLanguages");
|
|
1078
1276
|
var import_redis = require("@tradejs/infra/redis");
|
|
@@ -1657,7 +1855,7 @@ var import_fs = __toESM(require("fs"));
|
|
|
1657
1855
|
var import_path = __toESM(require("path"));
|
|
1658
1856
|
var import_url = require("url");
|
|
1659
1857
|
var import_config = require("@tradejs/core/config");
|
|
1660
|
-
var
|
|
1858
|
+
var import_logger2 = require("@tradejs/infra/logger");
|
|
1661
1859
|
var CONFIG_FILE_NAMES = [
|
|
1662
1860
|
"tradejs.config.ts",
|
|
1663
1861
|
"tradejs.config.mts",
|
|
@@ -1924,11 +2122,11 @@ var loadTradejsConfig = async (cwd = getTradejsProjectCwd()) => {
|
|
|
1924
2122
|
cachedByCwd.set(cwd, config);
|
|
1925
2123
|
if (!announcedConfigFile.has(configFilePath)) {
|
|
1926
2124
|
announcedConfigFile.add(configFilePath);
|
|
1927
|
-
|
|
2125
|
+
import_logger2.logger.log("debug", "Loaded TradeJS config: %s", configFilePath);
|
|
1928
2126
|
}
|
|
1929
2127
|
return config;
|
|
1930
2128
|
} catch (error) {
|
|
1931
|
-
|
|
2129
|
+
import_logger2.logger.log(
|
|
1932
2130
|
"warn",
|
|
1933
2131
|
"Failed to load TradeJS config from %s: %s",
|
|
1934
2132
|
configFilePath,
|
|
@@ -2895,7 +3093,7 @@ var import_node_crypto = require("crypto");
|
|
|
2895
3093
|
var import_constants = require("@tradejs/core/constants");
|
|
2896
3094
|
var import_time = require("@tradejs/core/time");
|
|
2897
3095
|
var import_trade = require("@tradejs/core/trade");
|
|
2898
|
-
var
|
|
3096
|
+
var import_logger3 = require("@tradejs/infra/logger");
|
|
2899
3097
|
var import_redis2 = require("@tradejs/infra/redis");
|
|
2900
3098
|
var now = () => Date.now();
|
|
2901
3099
|
var toRandomOrderSuffix = () => (0, import_node_crypto.randomUUID)().replace(/-/g, "").slice(0, 12).toLowerCase();
|
|
@@ -2954,7 +3152,7 @@ var recordRuntimeTradeOpen = async (params) => {
|
|
|
2954
3152
|
)
|
|
2955
3153
|
]);
|
|
2956
3154
|
} catch (error) {
|
|
2957
|
-
|
|
3155
|
+
import_logger3.logger.error(
|
|
2958
3156
|
"runtime trade open journal failed: %s %s",
|
|
2959
3157
|
record.symbol,
|
|
2960
3158
|
error?.message || String(error)
|
|
@@ -3022,7 +3220,7 @@ var recordRuntimeTradeIncrease = async (params) => {
|
|
|
3022
3220
|
)
|
|
3023
3221
|
]);
|
|
3024
3222
|
} catch (error) {
|
|
3025
|
-
|
|
3223
|
+
import_logger3.logger.error(
|
|
3026
3224
|
"runtime trade increase journal failed: %s %s",
|
|
3027
3225
|
symbol,
|
|
3028
3226
|
error?.message || String(error)
|
|
@@ -3129,7 +3327,7 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
3129
3327
|
)
|
|
3130
3328
|
]);
|
|
3131
3329
|
} catch (error) {
|
|
3132
|
-
|
|
3330
|
+
import_logger3.logger.error(
|
|
3133
3331
|
"runtime trade close journal failed: %s %s",
|
|
3134
3332
|
symbol,
|
|
3135
3333
|
error?.message || String(error)
|
|
@@ -3139,11 +3337,11 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
3139
3337
|
};
|
|
3140
3338
|
|
|
3141
3339
|
// src/strategyHelpers/marketContextStages.ts
|
|
3142
|
-
var
|
|
3340
|
+
var import_logger8 = require("@tradejs/infra/logger");
|
|
3143
3341
|
|
|
3144
3342
|
// src/strategyHelpers/binanceMarketContext.ts
|
|
3145
3343
|
var import_marketContext = require("@tradejs/infra/timescale/marketContext");
|
|
3146
|
-
var
|
|
3344
|
+
var import_logger4 = require("@tradejs/infra/logger");
|
|
3147
3345
|
var import_strategies = require("@tradejs/core/strategies");
|
|
3148
3346
|
|
|
3149
3347
|
// src/binanceBreadthUniverses.ts
|
|
@@ -3507,7 +3705,7 @@ var parseEnabledFlag = (value, env) => {
|
|
|
3507
3705
|
if (normalized === "live") return env !== "BACKTEST";
|
|
3508
3706
|
return false;
|
|
3509
3707
|
};
|
|
3510
|
-
var
|
|
3708
|
+
var toFiniteNumberOrNull2 = (value) => {
|
|
3511
3709
|
const numeric = typeof value === "number" ? value : Number(value);
|
|
3512
3710
|
return Number.isFinite(numeric) ? numeric : null;
|
|
3513
3711
|
};
|
|
@@ -3632,14 +3830,14 @@ var toTradeFlowContext = (row, interval) => row ? {
|
|
|
3632
3830
|
asOfTs: row.ts.getTime(),
|
|
3633
3831
|
ageMs: row.ageMs,
|
|
3634
3832
|
stale: row.stale,
|
|
3635
|
-
trades:
|
|
3636
|
-
buyPressurePct:
|
|
3637
|
-
buyBaseVolume:
|
|
3638
|
-
sellBaseVolume:
|
|
3639
|
-
buyQuoteVolume:
|
|
3640
|
-
sellQuoteVolume:
|
|
3641
|
-
netBaseDelta:
|
|
3642
|
-
netQuoteDelta:
|
|
3833
|
+
trades: toFiniteNumberOrNull2(row.trades),
|
|
3834
|
+
buyPressurePct: toFiniteNumberOrNull2(row.buyPressurePct),
|
|
3835
|
+
buyBaseVolume: toFiniteNumberOrNull2(row.buyBaseVolume),
|
|
3836
|
+
sellBaseVolume: toFiniteNumberOrNull2(row.sellBaseVolume),
|
|
3837
|
+
buyQuoteVolume: toFiniteNumberOrNull2(row.buyQuoteVolume),
|
|
3838
|
+
sellQuoteVolume: toFiniteNumberOrNull2(row.sellQuoteVolume),
|
|
3839
|
+
netBaseDelta: toFiniteNumberOrNull2(row.netBaseDelta),
|
|
3840
|
+
netQuoteDelta: toFiniteNumberOrNull2(row.netQuoteDelta)
|
|
3643
3841
|
} : null;
|
|
3644
3842
|
var getCachedReferenceRows = ({
|
|
3645
3843
|
referenceSymbols,
|
|
@@ -3728,16 +3926,16 @@ var toMarketBreadthContext = (breadth, interval) => ({
|
|
|
3728
3926
|
asOfTs: breadth.ts.getTime(),
|
|
3729
3927
|
ageMs: breadth.ageMs,
|
|
3730
3928
|
stale: breadth.stale,
|
|
3731
|
-
symbolsCount:
|
|
3732
|
-
advancers:
|
|
3733
|
-
decliners:
|
|
3734
|
-
unchanged:
|
|
3735
|
-
advanceDeclineRatio:
|
|
3736
|
-
pctAboveMa20:
|
|
3737
|
-
pctAboveMa50:
|
|
3738
|
-
equalWeightedReturn:
|
|
3739
|
-
volumeWeightedReturn:
|
|
3740
|
-
dispersion:
|
|
3929
|
+
symbolsCount: toFiniteNumberOrNull2(breadth.symbolsCount),
|
|
3930
|
+
advancers: toFiniteNumberOrNull2(breadth.advancers),
|
|
3931
|
+
decliners: toFiniteNumberOrNull2(breadth.decliners),
|
|
3932
|
+
unchanged: toFiniteNumberOrNull2(breadth.unchanged),
|
|
3933
|
+
advanceDeclineRatio: toFiniteNumberOrNull2(breadth.advanceDeclineRatio),
|
|
3934
|
+
pctAboveMa20: toFiniteNumberOrNull2(breadth.pctAboveMa20),
|
|
3935
|
+
pctAboveMa50: toFiniteNumberOrNull2(breadth.pctAboveMa50),
|
|
3936
|
+
equalWeightedReturn: toFiniteNumberOrNull2(breadth.equalWeightedReturn),
|
|
3937
|
+
volumeWeightedReturn: toFiniteNumberOrNull2(breadth.volumeWeightedReturn),
|
|
3938
|
+
dispersion: toFiniteNumberOrNull2(breadth.dispersion)
|
|
3741
3939
|
});
|
|
3742
3940
|
var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
3743
3941
|
const {
|
|
@@ -3838,46 +4036,46 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
3838
4036
|
asOfTs: primaryBreadth.ts.getTime(),
|
|
3839
4037
|
ageMs: primaryBreadth.ageMs,
|
|
3840
4038
|
stale: primaryBreadth.stale,
|
|
3841
|
-
btcReturn1h:
|
|
4039
|
+
btcReturn1h: toFiniteNumberOrNull2(
|
|
3842
4040
|
primaryBreadth.btcReturn1h
|
|
3843
4041
|
),
|
|
3844
|
-
btcReturn4h:
|
|
4042
|
+
btcReturn4h: toFiniteNumberOrNull2(
|
|
3845
4043
|
primaryBreadth.btcReturn4h
|
|
3846
4044
|
),
|
|
3847
|
-
btcReturn24h:
|
|
4045
|
+
btcReturn24h: toFiniteNumberOrNull2(
|
|
3848
4046
|
primaryBreadth.btcReturn24h
|
|
3849
4047
|
),
|
|
3850
|
-
altBasketReturn1h:
|
|
4048
|
+
altBasketReturn1h: toFiniteNumberOrNull2(
|
|
3851
4049
|
primaryBreadth.altBasketReturn1h
|
|
3852
4050
|
),
|
|
3853
|
-
altBasketReturn4h:
|
|
4051
|
+
altBasketReturn4h: toFiniteNumberOrNull2(
|
|
3854
4052
|
primaryBreadth.altBasketReturn4h
|
|
3855
4053
|
),
|
|
3856
|
-
altBasketReturn24h:
|
|
4054
|
+
altBasketReturn24h: toFiniteNumberOrNull2(
|
|
3857
4055
|
primaryBreadth.altBasketReturn24h
|
|
3858
4056
|
),
|
|
3859
|
-
btcVsAltReturn1h:
|
|
4057
|
+
btcVsAltReturn1h: toFiniteNumberOrNull2(
|
|
3860
4058
|
primaryBreadth.btcVsAltReturn1h
|
|
3861
4059
|
),
|
|
3862
|
-
btcVsAltReturn4h:
|
|
4060
|
+
btcVsAltReturn4h: toFiniteNumberOrNull2(
|
|
3863
4061
|
primaryBreadth.btcVsAltReturn4h
|
|
3864
4062
|
),
|
|
3865
|
-
btcVsAltReturn24h:
|
|
4063
|
+
btcVsAltReturn24h: toFiniteNumberOrNull2(
|
|
3866
4064
|
primaryBreadth.btcVsAltReturn24h
|
|
3867
4065
|
),
|
|
3868
|
-
btcTurnoverShare1h:
|
|
4066
|
+
btcTurnoverShare1h: toFiniteNumberOrNull2(
|
|
3869
4067
|
primaryBreadth.btcTurnoverShare1h
|
|
3870
4068
|
),
|
|
3871
|
-
btcTurnoverShare24h:
|
|
4069
|
+
btcTurnoverShare24h: toFiniteNumberOrNull2(
|
|
3872
4070
|
primaryBreadth.btcTurnoverShare24h
|
|
3873
4071
|
),
|
|
3874
|
-
btcTurnoverShareChange24h:
|
|
4072
|
+
btcTurnoverShareChange24h: toFiniteNumberOrNull2(
|
|
3875
4073
|
primaryBreadth.btcTurnoverShareChange24h
|
|
3876
4074
|
),
|
|
3877
|
-
altVolToBtcVol24h:
|
|
4075
|
+
altVolToBtcVol24h: toFiniteNumberOrNull2(
|
|
3878
4076
|
primaryBreadth.altVolToBtcVol24h
|
|
3879
4077
|
),
|
|
3880
|
-
altDispersion24h:
|
|
4078
|
+
altDispersion24h: toFiniteNumberOrNull2(
|
|
3881
4079
|
primaryBreadth.altDispersion24h
|
|
3882
4080
|
),
|
|
3883
4081
|
regime: primaryBreadth.btcAltRegime ?? "unknown"
|
|
@@ -3894,7 +4092,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
3894
4092
|
throw error;
|
|
3895
4093
|
}
|
|
3896
4094
|
binanceMarketContextUnavailable = true;
|
|
3897
|
-
|
|
4095
|
+
import_logger4.logger.warn(
|
|
3898
4096
|
"Binance market context disabled after Timescale read failure: %s",
|
|
3899
4097
|
String(error)
|
|
3900
4098
|
);
|
|
@@ -3904,7 +4102,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
3904
4102
|
|
|
3905
4103
|
// src/strategyHelpers/coinMarketCapContext.ts
|
|
3906
4104
|
var import_strategies2 = require("@tradejs/core/strategies");
|
|
3907
|
-
var
|
|
4105
|
+
var import_logger5 = require("@tradejs/infra/logger");
|
|
3908
4106
|
var import_marketContext2 = require("@tradejs/infra/timescale/marketContext");
|
|
3909
4107
|
var DEFAULT_MAX_AGE_MS = 48 * 60 * 6e4;
|
|
3910
4108
|
var SOURCE_GLOBAL_DAILY = "coinmarketcap_global";
|
|
@@ -3942,7 +4140,7 @@ var asInt = (value, fallback) => {
|
|
|
3942
4140
|
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
3943
4141
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
3944
4142
|
};
|
|
3945
|
-
var
|
|
4143
|
+
var toFiniteNumberOrNull3 = (value) => {
|
|
3946
4144
|
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
|
|
3947
4145
|
return Number.isFinite(numeric) ? numeric : null;
|
|
3948
4146
|
};
|
|
@@ -4176,12 +4374,12 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4176
4374
|
const ethRow = references.get("ETHUSDT") ?? null;
|
|
4177
4375
|
const previousBtcRow = previousReferences.get("BTCUSDT") ?? null;
|
|
4178
4376
|
const previousEthRow = previousReferences.get("ETHUSDT") ?? null;
|
|
4179
|
-
const btcMarketCapUsd =
|
|
4180
|
-
const ethMarketCapUsd =
|
|
4181
|
-
const previousBtcMarketCapUsd =
|
|
4377
|
+
const btcMarketCapUsd = toFiniteNumberOrNull3(btcRow?.marketCapUsd);
|
|
4378
|
+
const ethMarketCapUsd = toFiniteNumberOrNull3(ethRow?.marketCapUsd);
|
|
4379
|
+
const previousBtcMarketCapUsd = toFiniteNumberOrNull3(
|
|
4182
4380
|
previousBtcRow?.marketCapUsd
|
|
4183
4381
|
);
|
|
4184
|
-
const previousEthMarketCapUsd =
|
|
4382
|
+
const previousEthMarketCapUsd = toFiniteNumberOrNull3(
|
|
4185
4383
|
previousEthRow?.marketCapUsd
|
|
4186
4384
|
);
|
|
4187
4385
|
const ethBtcMarketCapRatio = safeDivide(ethMarketCapUsd, btcMarketCapUsd);
|
|
@@ -4190,16 +4388,16 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4190
4388
|
previousBtcMarketCapUsd
|
|
4191
4389
|
);
|
|
4192
4390
|
const ethBtcMarketCapRatioChange24hPct = ethBtcMarketCapRatio != null && previousEthBtcMarketCapRatio != null && previousEthBtcMarketCapRatio > 0 ? (ethBtcMarketCapRatio - previousEthBtcMarketCapRatio) / previousEthBtcMarketCapRatio : null;
|
|
4193
|
-
const btcVolumeUsd =
|
|
4194
|
-
const ethVolumeUsd =
|
|
4391
|
+
const btcVolumeUsd = toFiniteNumberOrNull3(btcRow?.volumeUsd);
|
|
4392
|
+
const ethVolumeUsd = toFiniteNumberOrNull3(ethRow?.volumeUsd);
|
|
4195
4393
|
const referenceStale = btcRow?.stale === true || ethRow?.stale === true || !btcRow || !ethRow;
|
|
4196
|
-
const btcDominanceChange24hPct =
|
|
4394
|
+
const btcDominanceChange24hPct = toFiniteNumberOrNull3(
|
|
4197
4395
|
globalRow?.btcDominanceChange24hPct
|
|
4198
4396
|
);
|
|
4199
|
-
const altMarketCapChange24hPct =
|
|
4397
|
+
const altMarketCapChange24hPct = toFiniteNumberOrNull3(
|
|
4200
4398
|
globalRow?.altMarketCapChange24hPct
|
|
4201
4399
|
);
|
|
4202
|
-
const altVolumeChange24hPct =
|
|
4400
|
+
const altVolumeChange24hPct = toFiniteNumberOrNull3(
|
|
4203
4401
|
globalRow?.altVolumeChange24hPct
|
|
4204
4402
|
);
|
|
4205
4403
|
const altLiquidityRegime = globalRow ? toAltLiquidityRegime({
|
|
@@ -4210,19 +4408,19 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4210
4408
|
}) : "unknown";
|
|
4211
4409
|
const exchangeLiquidityRegime = exchangeLiquidityRow ? toExchangeLiquidityRegime({
|
|
4212
4410
|
stale: exchangeLiquidityRow.stale,
|
|
4213
|
-
totalVolumeChange24hPct:
|
|
4411
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
4214
4412
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
4215
4413
|
),
|
|
4216
4414
|
fallback: exchangeLiquidityRow.liquidityRegime ?? "unknown"
|
|
4217
4415
|
}) : "unknown";
|
|
4218
4416
|
const cmc100Row = indexRows.get("cmc100") ?? null;
|
|
4219
4417
|
const cmc20Row = indexRows.get("cmc20") ?? null;
|
|
4220
|
-
const cmc100Value =
|
|
4221
|
-
const cmc20Value =
|
|
4222
|
-
const cmc100Change24hPct =
|
|
4418
|
+
const cmc100Value = toFiniteNumberOrNull3(cmc100Row?.value);
|
|
4419
|
+
const cmc20Value = toFiniteNumberOrNull3(cmc20Row?.value);
|
|
4420
|
+
const cmc100Change24hPct = toFiniteNumberOrNull3(
|
|
4223
4421
|
cmc100Row?.valueChange24hPct
|
|
4224
4422
|
);
|
|
4225
|
-
const cmc20Change24hPct =
|
|
4423
|
+
const cmc20Change24hPct = toFiniteNumberOrNull3(cmc20Row?.valueChange24hPct);
|
|
4226
4424
|
const cmc20ToCmc100Ratio = safeDivide(cmc20Value, cmc100Value);
|
|
4227
4425
|
const cmc20ToCmc100RatioChange24hPct = cmc20Change24hPct != null && cmc100Change24hPct != null ? (1 + cmc20Change24hPct) / (1 + cmc100Change24hPct) - 1 : null;
|
|
4228
4426
|
const indexStale = cmc100Row?.stale === true || cmc20Row?.stale === true || !cmc100Row || !cmc20Row;
|
|
@@ -4246,41 +4444,41 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4246
4444
|
asOfTs: globalRow.ts.getTime(),
|
|
4247
4445
|
ageMs: globalRow.ageMs,
|
|
4248
4446
|
stale: globalRow.stale,
|
|
4249
|
-
totalMarketCapUsd:
|
|
4447
|
+
totalMarketCapUsd: toFiniteNumberOrNull3(
|
|
4250
4448
|
globalRow.totalMarketCapUsd
|
|
4251
4449
|
),
|
|
4252
|
-
totalVolumeUsd:
|
|
4450
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
4253
4451
|
globalRow.totalVolumeUsd
|
|
4254
4452
|
),
|
|
4255
|
-
totalVolumeReportedUsd:
|
|
4453
|
+
totalVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
4256
4454
|
globalRow.totalVolumeReportedUsd
|
|
4257
4455
|
),
|
|
4258
|
-
altMarketCapUsd:
|
|
4456
|
+
altMarketCapUsd: toFiniteNumberOrNull3(
|
|
4259
4457
|
globalRow.altMarketCapUsd
|
|
4260
4458
|
),
|
|
4261
|
-
altVolumeUsd:
|
|
4262
|
-
altVolumeReportedUsd:
|
|
4459
|
+
altVolumeUsd: toFiniteNumberOrNull3(globalRow.altVolumeUsd),
|
|
4460
|
+
altVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
4263
4461
|
globalRow.altVolumeReportedUsd
|
|
4264
4462
|
),
|
|
4265
|
-
btcDominancePct:
|
|
4463
|
+
btcDominancePct: toFiniteNumberOrNull3(
|
|
4266
4464
|
globalRow.btcDominancePct
|
|
4267
4465
|
),
|
|
4268
|
-
ethDominancePct:
|
|
4466
|
+
ethDominancePct: toFiniteNumberOrNull3(
|
|
4269
4467
|
globalRow.ethDominancePct
|
|
4270
4468
|
),
|
|
4271
4469
|
btcDominanceChange24hPct,
|
|
4272
|
-
ethDominanceChange24hPct:
|
|
4470
|
+
ethDominanceChange24hPct: toFiniteNumberOrNull3(
|
|
4273
4471
|
globalRow.ethDominanceChange24hPct
|
|
4274
4472
|
),
|
|
4275
4473
|
altMarketCapChange24hPct,
|
|
4276
4474
|
altVolumeChange24hPct,
|
|
4277
|
-
activeCryptocurrencies:
|
|
4475
|
+
activeCryptocurrencies: toFiniteNumberOrNull3(
|
|
4278
4476
|
globalRow.activeCryptocurrencies
|
|
4279
4477
|
),
|
|
4280
|
-
activeExchanges:
|
|
4478
|
+
activeExchanges: toFiniteNumberOrNull3(
|
|
4281
4479
|
globalRow.activeExchanges
|
|
4282
4480
|
),
|
|
4283
|
-
activeMarketPairs:
|
|
4481
|
+
activeMarketPairs: toFiniteNumberOrNull3(
|
|
4284
4482
|
globalRow.activeMarketPairs
|
|
4285
4483
|
),
|
|
4286
4484
|
altLiquidityRegime
|
|
@@ -4325,22 +4523,22 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4325
4523
|
asOfTs: exchangeLiquidityRow.ts.getTime(),
|
|
4326
4524
|
ageMs: exchangeLiquidityRow.ageMs,
|
|
4327
4525
|
stale: exchangeLiquidityRow.stale,
|
|
4328
|
-
exchangesCount:
|
|
4526
|
+
exchangesCount: toFiniteNumberOrNull3(
|
|
4329
4527
|
exchangeLiquidityRow.exchangesCount
|
|
4330
4528
|
),
|
|
4331
|
-
totalVolumeUsd:
|
|
4529
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
4332
4530
|
exchangeLiquidityRow.totalVolumeUsd
|
|
4333
4531
|
),
|
|
4334
|
-
totalVolumeChange24hPct:
|
|
4532
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
4335
4533
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
4336
4534
|
),
|
|
4337
|
-
binanceVolumeUsd:
|
|
4535
|
+
binanceVolumeUsd: toFiniteNumberOrNull3(
|
|
4338
4536
|
exchangeLiquidityRow.binanceVolumeUsd
|
|
4339
4537
|
),
|
|
4340
|
-
binanceVolumeShare:
|
|
4538
|
+
binanceVolumeShare: toFiniteNumberOrNull3(
|
|
4341
4539
|
exchangeLiquidityRow.binanceVolumeShare
|
|
4342
4540
|
),
|
|
4343
|
-
topExchangeVolumeShare:
|
|
4541
|
+
topExchangeVolumeShare: toFiniteNumberOrNull3(
|
|
4344
4542
|
exchangeLiquidityRow.topExchangeVolumeShare
|
|
4345
4543
|
),
|
|
4346
4544
|
liquidityRegime: exchangeLiquidityRegime
|
|
@@ -4353,11 +4551,11 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4353
4551
|
asOfTs: fearGreedRow.ts.getTime(),
|
|
4354
4552
|
ageMs: fearGreedRow.ageMs,
|
|
4355
4553
|
stale: fearGreedRow.stale,
|
|
4356
|
-
value:
|
|
4357
|
-
valueChange24h:
|
|
4554
|
+
value: toFiniteNumberOrNull3(fearGreedRow.value),
|
|
4555
|
+
valueChange24h: toFiniteNumberOrNull3(
|
|
4358
4556
|
fearGreedRow.valueChange24h
|
|
4359
4557
|
),
|
|
4360
|
-
valueChange7d:
|
|
4558
|
+
valueChange7d: toFiniteNumberOrNull3(
|
|
4361
4559
|
fearGreedRow.valueChange7d
|
|
4362
4560
|
),
|
|
4363
4561
|
classification: fearGreedRow.classification ?? "Unknown",
|
|
@@ -4377,13 +4575,13 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4377
4575
|
cmc100Value,
|
|
4378
4576
|
cmc100Change24hPct,
|
|
4379
4577
|
cmc100TopConstituentSymbol: cmc100Row?.topConstituentSymbol ?? null,
|
|
4380
|
-
cmc100TopConstituentWeightPct:
|
|
4578
|
+
cmc100TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
4381
4579
|
cmc100Row?.topConstituentWeightPct
|
|
4382
4580
|
),
|
|
4383
4581
|
cmc20Value,
|
|
4384
4582
|
cmc20Change24hPct,
|
|
4385
4583
|
cmc20TopConstituentSymbol: cmc20Row?.topConstituentSymbol ?? null,
|
|
4386
|
-
cmc20TopConstituentWeightPct:
|
|
4584
|
+
cmc20TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
4387
4585
|
cmc20Row?.topConstituentWeightPct
|
|
4388
4586
|
),
|
|
4389
4587
|
cmc20ToCmc100Ratio,
|
|
@@ -4401,7 +4599,7 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4401
4599
|
throw error;
|
|
4402
4600
|
}
|
|
4403
4601
|
coinMarketCapContextUnavailable = true;
|
|
4404
|
-
|
|
4602
|
+
import_logger5.logger.warn(
|
|
4405
4603
|
"CoinMarketCap context disabled after Timescale read failure: %s",
|
|
4406
4604
|
String(error)
|
|
4407
4605
|
);
|
|
@@ -4415,7 +4613,7 @@ var import_data2 = require("@tradejs/core/data");
|
|
|
4415
4613
|
var import_strategies3 = require("@tradejs/core/strategies");
|
|
4416
4614
|
var import_constants2 = require("@tradejs/core/constants");
|
|
4417
4615
|
var import_derivatives = require("@tradejs/infra/timescale/derivatives");
|
|
4418
|
-
var
|
|
4616
|
+
var import_logger6 = require("@tradejs/infra/logger");
|
|
4419
4617
|
var STORED_INTERVALS = ["15m", "1h"];
|
|
4420
4618
|
var CONTEXT_INTERVALS = ["15m", "1h"];
|
|
4421
4619
|
var DEFAULT_LOOKBACK_HOURS = 48;
|
|
@@ -4470,7 +4668,7 @@ var resolvePrimaryReferenceSymbol2 = () => PRIMARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
|
4470
4668
|
var resolveSecondaryReferenceSymbol = () => SECONDARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
4471
4669
|
var getPrimaryIntervalContext = (context) => context?.intervals["15m"] ?? context?.intervals["1h"] ?? null;
|
|
4472
4670
|
var hasDerivativesSymbolData = (context) => Object.keys(context.intervals).length > 0 && !context.summary.riskFlags.includes("missing_derivatives");
|
|
4473
|
-
var
|
|
4671
|
+
var toFiniteNumberOrNull4 = (value) => {
|
|
4474
4672
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
4475
4673
|
if (typeof value === "string" && value.trim()) {
|
|
4476
4674
|
const parsed = Number(value);
|
|
@@ -4484,8 +4682,8 @@ var roundNullable = (value, digits = 4) => {
|
|
|
4484
4682
|
return Math.round(value * multiplier) / multiplier;
|
|
4485
4683
|
};
|
|
4486
4684
|
var deltaNullable = (targetValue, referenceValue) => {
|
|
4487
|
-
const target =
|
|
4488
|
-
const reference =
|
|
4685
|
+
const target = toFiniteNumberOrNull4(targetValue);
|
|
4686
|
+
const reference = toFiniteNumberOrNull4(referenceValue);
|
|
4489
4687
|
return target == null || reference == null ? null : roundNullable(target - reference);
|
|
4490
4688
|
};
|
|
4491
4689
|
var buildTargetDerivedContext = (params) => {
|
|
@@ -4647,7 +4845,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
4647
4845
|
throw error;
|
|
4648
4846
|
}
|
|
4649
4847
|
derivativesContextUnavailable = true;
|
|
4650
|
-
|
|
4848
|
+
import_logger6.logger.warn(
|
|
4651
4849
|
"Derivatives context disabled after Timescale read failure: %s",
|
|
4652
4850
|
String(error)
|
|
4653
4851
|
);
|
|
@@ -4659,7 +4857,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
4659
4857
|
var import_data3 = require("@tradejs/core/data");
|
|
4660
4858
|
var import_strategies4 = require("@tradejs/core/strategies");
|
|
4661
4859
|
var import_hyperliquidWhales2 = require("@tradejs/infra/timescale/hyperliquidWhales");
|
|
4662
|
-
var
|
|
4860
|
+
var import_logger7 = require("@tradejs/infra/logger");
|
|
4663
4861
|
|
|
4664
4862
|
// src/hyperliquidWhaleUniverse.ts
|
|
4665
4863
|
var import_node_crypto3 = require("crypto");
|
|
@@ -5179,7 +5377,7 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
5179
5377
|
throw error;
|
|
5180
5378
|
}
|
|
5181
5379
|
hyperliquidWhaleContextUnavailable = true;
|
|
5182
|
-
|
|
5380
|
+
import_logger7.logger.warn(
|
|
5183
5381
|
"Hyperliquid whale context disabled after Timescale read failure: %s",
|
|
5184
5382
|
String(error)
|
|
5185
5383
|
);
|
|
@@ -5262,7 +5460,7 @@ var runMarketContextStage = async ({
|
|
|
5262
5460
|
elapsedMs: Date.now() - startedAt
|
|
5263
5461
|
};
|
|
5264
5462
|
if (status === "timed_out") {
|
|
5265
|
-
|
|
5463
|
+
import_logger8.logger.warn(
|
|
5266
5464
|
"Market context stage timed out: %s after %sms",
|
|
5267
5465
|
stage,
|
|
5268
5466
|
result.elapsedMs
|
|
@@ -5452,79 +5650,10 @@ var enrichSignalWithAi = async ({
|
|
|
5452
5650
|
signal.aiAnalysis = analysis;
|
|
5453
5651
|
return resolveAiQuality(analysis, direction);
|
|
5454
5652
|
} catch (err) {
|
|
5455
|
-
|
|
5653
|
+
import_logger9.logger.error("AI analysis error: %s %s", symbol, formatAiError(err));
|
|
5456
5654
|
}
|
|
5457
5655
|
return void 0;
|
|
5458
5656
|
};
|
|
5459
|
-
var toFiniteNumberOrNull4 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
5460
|
-
var getOrderArrivalSnapshot = async ({
|
|
5461
|
-
connector,
|
|
5462
|
-
symbol
|
|
5463
|
-
}) => {
|
|
5464
|
-
if (typeof connector.getTopOfBookTicker !== "function") {
|
|
5465
|
-
return {
|
|
5466
|
-
arrivalSnapshotTime: Date.now(),
|
|
5467
|
-
arrivalSource: "unavailable",
|
|
5468
|
-
bid: null,
|
|
5469
|
-
ask: null,
|
|
5470
|
-
arrivalMid: null,
|
|
5471
|
-
spreadBps: null
|
|
5472
|
-
};
|
|
5473
|
-
}
|
|
5474
|
-
try {
|
|
5475
|
-
const ticker = await connector.getTopOfBookTicker(symbol);
|
|
5476
|
-
const arrivalSnapshotTime = toFiniteNumberOrNull4(ticker?.timestamp);
|
|
5477
|
-
const bid = toFiniteNumberOrNull4(ticker?.bidPrice);
|
|
5478
|
-
const ask = toFiniteNumberOrNull4(ticker?.askPrice);
|
|
5479
|
-
const arrivalMid = bid != null && ask != null ? (bid + ask) / 2 : null;
|
|
5480
|
-
const spreadBps = bid != null && ask != null && arrivalMid != null && arrivalMid > 0 ? (ask - bid) / arrivalMid * 1e4 : null;
|
|
5481
|
-
return {
|
|
5482
|
-
arrivalSnapshotTime: arrivalSnapshotTime ?? Date.now(),
|
|
5483
|
-
arrivalSource: "top_of_book",
|
|
5484
|
-
bid,
|
|
5485
|
-
ask,
|
|
5486
|
-
arrivalMid,
|
|
5487
|
-
spreadBps
|
|
5488
|
-
};
|
|
5489
|
-
} catch (error) {
|
|
5490
|
-
import_logger8.logger.warn(
|
|
5491
|
-
"runtime order arrival snapshot failed: %s %s",
|
|
5492
|
-
symbol,
|
|
5493
|
-
error?.message || String(error)
|
|
5494
|
-
);
|
|
5495
|
-
return {
|
|
5496
|
-
arrivalSnapshotTime: Date.now(),
|
|
5497
|
-
arrivalSource: "top_of_book_error",
|
|
5498
|
-
bid: null,
|
|
5499
|
-
ask: null,
|
|
5500
|
-
arrivalMid: null,
|
|
5501
|
-
spreadBps: null
|
|
5502
|
-
};
|
|
5503
|
-
}
|
|
5504
|
-
};
|
|
5505
|
-
var validateEntryProtectionAtArrival = ({
|
|
5506
|
-
direction,
|
|
5507
|
-
signalPrice,
|
|
5508
|
-
bid,
|
|
5509
|
-
ask,
|
|
5510
|
-
arrivalMid,
|
|
5511
|
-
takeProfits,
|
|
5512
|
-
stopLossPrice
|
|
5513
|
-
}) => {
|
|
5514
|
-
const entryReference = direction === "LONG" ? ask ?? arrivalMid ?? signalPrice : bid ?? arrivalMid ?? signalPrice;
|
|
5515
|
-
const stopReference = direction === "LONG" ? bid ?? arrivalMid ?? signalPrice : ask ?? arrivalMid ?? signalPrice;
|
|
5516
|
-
if (!Number.isFinite(entryReference) || entryReference <= 0 || !Number.isFinite(stopReference) || stopReference <= 0) {
|
|
5517
|
-
throw new Error("INVALID_ENTRY_REFERENCE_AT_ARRIVAL");
|
|
5518
|
-
}
|
|
5519
|
-
for (const takeProfit of takeProfits) {
|
|
5520
|
-
if (!Number.isFinite(takeProfit.price) || takeProfit.price <= 0 || (direction === "LONG" ? takeProfit.price <= entryReference : takeProfit.price >= entryReference)) {
|
|
5521
|
-
throw new Error("TAKE_PROFIT_CROSSED_BEFORE_ENTRY");
|
|
5522
|
-
}
|
|
5523
|
-
}
|
|
5524
|
-
if (stopLossPrice != null && (!Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || (direction === "LONG" ? stopLossPrice >= stopReference : stopLossPrice <= stopReference))) {
|
|
5525
|
-
throw new Error("STOP_LOSS_CROSSED_BEFORE_ENTRY");
|
|
5526
|
-
}
|
|
5527
|
-
};
|
|
5528
5657
|
var resolveRuntimeTelemetryQuality = ({
|
|
5529
5658
|
signalClosePrice,
|
|
5530
5659
|
arrivalMid,
|
|
@@ -5601,7 +5730,8 @@ var executeEntryOrder = async ({
|
|
|
5601
5730
|
signal,
|
|
5602
5731
|
beforePlaceOrder,
|
|
5603
5732
|
recordRuntimeTrade = true,
|
|
5604
|
-
leverage
|
|
5733
|
+
leverage,
|
|
5734
|
+
executionLimits
|
|
5605
5735
|
}) => {
|
|
5606
5736
|
await beforePlaceOrder?.();
|
|
5607
5737
|
const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
|
|
@@ -5612,23 +5742,57 @@ var executeEntryOrder = async ({
|
|
|
5612
5742
|
signal.orderQty = qty;
|
|
5613
5743
|
signal.orderValue = qty * currentPrice;
|
|
5614
5744
|
signal.orderFailureReason = void 0;
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5745
|
+
let arrivalSnapshot = {
|
|
5746
|
+
arrivalSnapshotTime: Date.now(),
|
|
5747
|
+
arrivalSource: "unavailable",
|
|
5748
|
+
bid: null,
|
|
5749
|
+
ask: null,
|
|
5750
|
+
arrivalMid: null,
|
|
5751
|
+
spreadBps: null
|
|
5752
|
+
};
|
|
5753
|
+
let orderSubmitTime = Date.now();
|
|
5754
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
5755
|
+
connector,
|
|
5756
|
+
symbol,
|
|
5757
|
+
direction,
|
|
5758
|
+
takeProfits,
|
|
5759
|
+
stopLossPrice,
|
|
5760
|
+
limits: executionLimits,
|
|
5761
|
+
onValidated: (snapshot2, economics2) => {
|
|
5762
|
+
arrivalSnapshot = snapshot2;
|
|
5763
|
+
orderSubmitTime = Date.now();
|
|
5764
|
+
if (executionLimits?.maxLossValue != null) {
|
|
5765
|
+
signal.riskDecision = {
|
|
5766
|
+
stage: "order_plan",
|
|
5767
|
+
status: economics2.riskExceeded ? "warning" : "approved",
|
|
5768
|
+
reason: economics2.riskExceeded ? "ENTRY_RISK_EXCEEDS_MAX_LOSS_AT_SUBMISSION" : "PLANNED_LOSS_WITHIN_MAX_LOSS_VALUE",
|
|
5769
|
+
plannedLossValue: economics2.plannedLossValue,
|
|
5770
|
+
maxLossValue: executionLimits.maxLossValue,
|
|
5771
|
+
limitUtilization: economics2.plannedLossValue / executionLimits.maxLossValue,
|
|
5772
|
+
enforced: false
|
|
5773
|
+
};
|
|
5774
|
+
}
|
|
5775
|
+
}
|
|
5776
|
+
});
|
|
5777
|
+
if (!beforeSubmit) {
|
|
5778
|
+
arrivalSnapshot = await getOrderArrivalSnapshot({ connector, symbol });
|
|
5779
|
+
try {
|
|
5780
|
+
validateEntryProtectionAtArrival({
|
|
5781
|
+
direction,
|
|
5782
|
+
signalPrice: currentPrice,
|
|
5783
|
+
bid: arrivalSnapshot.bid,
|
|
5784
|
+
ask: arrivalSnapshot.ask,
|
|
5785
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
5786
|
+
takeProfits,
|
|
5787
|
+
stopLossPrice
|
|
5788
|
+
});
|
|
5789
|
+
} catch (error) {
|
|
5790
|
+
signal.orderStatus = "failed";
|
|
5791
|
+
signal.orderFailureReason = error.message;
|
|
5792
|
+
throw error;
|
|
5793
|
+
}
|
|
5794
|
+
orderSubmitTime = Date.now();
|
|
5630
5795
|
}
|
|
5631
|
-
const orderSubmitTime = Date.now();
|
|
5632
5796
|
const orderPlaced = await connector.placeOrder({
|
|
5633
5797
|
symbol,
|
|
5634
5798
|
qty,
|
|
@@ -5639,7 +5803,8 @@ var executeEntryOrder = async ({
|
|
|
5639
5803
|
direction,
|
|
5640
5804
|
...typeof leverage === "number" && Number.isFinite(leverage) ? { leverage } : {},
|
|
5641
5805
|
orderId,
|
|
5642
|
-
signal
|
|
5806
|
+
signal,
|
|
5807
|
+
...beforeSubmit ? { beforeSubmit } : {}
|
|
5643
5808
|
});
|
|
5644
5809
|
const orderAckTime = Date.now();
|
|
5645
5810
|
const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
|
|
@@ -6468,7 +6633,7 @@ var canUseSharedReplayState = ({
|
|
|
6468
6633
|
}) => (env === "BACKTEST" || env === "PARITY") && Boolean(sharedReplayKey);
|
|
6469
6634
|
|
|
6470
6635
|
// src/strategy/runtimeExecution.ts
|
|
6471
|
-
var
|
|
6636
|
+
var import_logger10 = require("@tradejs/infra/logger");
|
|
6472
6637
|
var import_types = require("@tradejs/types");
|
|
6473
6638
|
var buildExitOrderSignal = ({
|
|
6474
6639
|
strategyName,
|
|
@@ -6520,7 +6685,7 @@ var handleExitDecision = async ({
|
|
|
6520
6685
|
deploymentId: connector.deploymentId
|
|
6521
6686
|
});
|
|
6522
6687
|
if (!activeTrade) {
|
|
6523
|
-
|
|
6688
|
+
import_logger10.logger.warn(
|
|
6524
6689
|
"[%s] blocked closePosition for untracked runtime position: %s",
|
|
6525
6690
|
strategyName ?? "unknown",
|
|
6526
6691
|
symbol
|
|
@@ -6528,7 +6693,7 @@ var handleExitDecision = async ({
|
|
|
6528
6693
|
return "CLOSE_BLOCKED_BY_UNTRACKED_POSITION";
|
|
6529
6694
|
}
|
|
6530
6695
|
if (!strategyName || activeTrade.strategy !== strategyName) {
|
|
6531
|
-
|
|
6696
|
+
import_logger10.logger.warn(
|
|
6532
6697
|
"[%s] blocked closePosition for foreign runtime position: %s ownedBy=%s",
|
|
6533
6698
|
strategyName ?? "unknown",
|
|
6534
6699
|
symbol,
|
|
@@ -6581,7 +6746,7 @@ var handleExitDecision = async ({
|
|
|
6581
6746
|
exitType: closedTrade?.exitType ?? "exit"
|
|
6582
6747
|
});
|
|
6583
6748
|
} catch (notificationError) {
|
|
6584
|
-
|
|
6749
|
+
import_logger10.logger.error(
|
|
6585
6750
|
"runtime close notification error: %s %s",
|
|
6586
6751
|
symbol,
|
|
6587
6752
|
notificationError
|
|
@@ -6595,7 +6760,7 @@ var handleExitDecision = async ({
|
|
|
6595
6760
|
decision,
|
|
6596
6761
|
market
|
|
6597
6762
|
});
|
|
6598
|
-
|
|
6763
|
+
import_logger10.logger.error("close order error: %s %s", symbol, err);
|
|
6599
6764
|
return "ORDER_ERROR";
|
|
6600
6765
|
}
|
|
6601
6766
|
return decision.code;
|
|
@@ -6622,7 +6787,7 @@ var handleProtectDecision = async ({
|
|
|
6622
6787
|
decision,
|
|
6623
6788
|
market
|
|
6624
6789
|
});
|
|
6625
|
-
|
|
6790
|
+
import_logger10.logger.error("protect position error: %s %s", symbol, err);
|
|
6626
6791
|
return "ORDER_ERROR";
|
|
6627
6792
|
}
|
|
6628
6793
|
return decision.code;
|
|
@@ -6688,7 +6853,11 @@ var executeEntryDecision = async ({
|
|
|
6688
6853
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {},
|
|
6689
6854
|
signal,
|
|
6690
6855
|
beforePlaceOrder,
|
|
6691
|
-
recordRuntimeTrade: recordRuntimeJournal
|
|
6856
|
+
recordRuntimeTrade: recordRuntimeJournal,
|
|
6857
|
+
executionLimits: resolveEntryExecutionLimits(
|
|
6858
|
+
hookCtx.strategyConfig,
|
|
6859
|
+
decision.entryContext.direction
|
|
6860
|
+
)
|
|
6692
6861
|
});
|
|
6693
6862
|
await invokeStageHooks(
|
|
6694
6863
|
"afterPlaceOrder",
|
|
@@ -6710,19 +6879,32 @@ var executeEntryDecision = async ({
|
|
|
6710
6879
|
return signal;
|
|
6711
6880
|
}
|
|
6712
6881
|
await beforePlaceOrder();
|
|
6713
|
-
const
|
|
6882
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
6714
6883
|
connector,
|
|
6715
|
-
symbol
|
|
6716
|
-
});
|
|
6717
|
-
validateEntryProtectionAtArrival({
|
|
6884
|
+
symbol,
|
|
6718
6885
|
direction: decision.entryContext.direction,
|
|
6719
|
-
signalPrice: decision.entryContext.prices.currentPrice,
|
|
6720
|
-
bid: arrivalSnapshot.bid,
|
|
6721
|
-
ask: arrivalSnapshot.ask,
|
|
6722
|
-
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
6723
6886
|
takeProfits: decision.orderPlan.takeProfits,
|
|
6724
|
-
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
6887
|
+
stopLossPrice: decision.orderPlan.stopLossPrice,
|
|
6888
|
+
limits: resolveEntryExecutionLimits(
|
|
6889
|
+
hookCtx.strategyConfig,
|
|
6890
|
+
decision.entryContext.direction
|
|
6891
|
+
)
|
|
6725
6892
|
});
|
|
6893
|
+
if (!beforeSubmit) {
|
|
6894
|
+
const arrivalSnapshot = await getOrderArrivalSnapshot({
|
|
6895
|
+
connector,
|
|
6896
|
+
symbol
|
|
6897
|
+
});
|
|
6898
|
+
validateEntryProtectionAtArrival({
|
|
6899
|
+
direction: decision.entryContext.direction,
|
|
6900
|
+
signalPrice: decision.entryContext.prices.currentPrice,
|
|
6901
|
+
bid: arrivalSnapshot.bid,
|
|
6902
|
+
ask: arrivalSnapshot.ask,
|
|
6903
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
6904
|
+
takeProfits: decision.orderPlan.takeProfits,
|
|
6905
|
+
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
6906
|
+
});
|
|
6907
|
+
}
|
|
6726
6908
|
const orderPlaced = await connector.placeOrder({
|
|
6727
6909
|
symbol,
|
|
6728
6910
|
qty: decision.orderPlan.qty,
|
|
@@ -6730,6 +6912,7 @@ var executeEntryDecision = async ({
|
|
|
6730
6912
|
timestamp: decision.entryContext.timestamp,
|
|
6731
6913
|
direction: decision.entryContext.direction,
|
|
6732
6914
|
positionIntent: decision.orderPlan.positionIntent,
|
|
6915
|
+
...beforeSubmit ? { beforeSubmit } : {},
|
|
6733
6916
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {}
|
|
6734
6917
|
});
|
|
6735
6918
|
if (!orderPlaced) {
|
|
@@ -6771,6 +6954,23 @@ var executeEntryDecision = async ({
|
|
|
6771
6954
|
{ decision, entry, market }
|
|
6772
6955
|
);
|
|
6773
6956
|
} catch (err) {
|
|
6957
|
+
if (err instanceof EntrySubmissionValidationError) {
|
|
6958
|
+
if (signal) {
|
|
6959
|
+
signal.orderStatus = "skipped";
|
|
6960
|
+
signal.orderSkipReason = err.message;
|
|
6961
|
+
signal.allocatorDecision = {
|
|
6962
|
+
stage: "runtime_admission",
|
|
6963
|
+
status: "rejected",
|
|
6964
|
+
reason: err.message
|
|
6965
|
+
};
|
|
6966
|
+
}
|
|
6967
|
+
import_logger10.logger.warn(
|
|
6968
|
+
"entry rejected before submission: %s %s",
|
|
6969
|
+
symbol,
|
|
6970
|
+
err.message
|
|
6971
|
+
);
|
|
6972
|
+
return signal ?? err.message;
|
|
6973
|
+
}
|
|
6774
6974
|
if (signal) {
|
|
6775
6975
|
signal.orderStatus = "failed";
|
|
6776
6976
|
if (typeof signal.orderFailureReason !== "string" || !signal.orderFailureReason.trim()) {
|
|
@@ -6785,9 +6985,9 @@ var executeEntryDecision = async ({
|
|
|
6785
6985
|
market
|
|
6786
6986
|
});
|
|
6787
6987
|
if (err?.message === import_types.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY) {
|
|
6788
|
-
|
|
6988
|
+
import_logger10.logger.warn("order warning: %s %s", symbol, err);
|
|
6789
6989
|
} else {
|
|
6790
|
-
|
|
6990
|
+
import_logger10.logger.error("order error: %s %s", symbol, err);
|
|
6791
6991
|
}
|
|
6792
6992
|
return signal ?? "ORDER_ERROR";
|
|
6793
6993
|
}
|
|
@@ -6895,6 +7095,9 @@ var createStrategyRuntime = ({
|
|
|
6895
7095
|
});
|
|
6896
7096
|
const strategyPolicyProfile = getPolicyProfile();
|
|
6897
7097
|
const indicatorPeriods = (0, import_strategies6.buildDefaultIndicatorPeriods)(config);
|
|
7098
|
+
const strategyConfig = { ...config };
|
|
7099
|
+
delete strategyConfig.RECORD_RUNTIME_TRADES;
|
|
7100
|
+
delete strategyConfig.SIMULATE_ORDERS;
|
|
6898
7101
|
const hookBase = {
|
|
6899
7102
|
connector,
|
|
6900
7103
|
strategyName,
|
|
@@ -6905,7 +7108,7 @@ var createStrategyRuntime = ({
|
|
|
6905
7108
|
accountId,
|
|
6906
7109
|
deploymentId,
|
|
6907
7110
|
policyProfileId: strategyPolicyProfile?.id ?? requestedPolicyProfileId,
|
|
6908
|
-
strategyConfig
|
|
7111
|
+
strategyConfig,
|
|
6909
7112
|
env,
|
|
6910
7113
|
isConfigFromBacktest
|
|
6911
7114
|
};
|
|
@@ -6952,7 +7155,7 @@ var createStrategyRuntime = ({
|
|
|
6952
7155
|
try {
|
|
6953
7156
|
await projectHook(errorParams);
|
|
6954
7157
|
} catch (hookError) {
|
|
6955
|
-
|
|
7158
|
+
import_logger11.logger.error(
|
|
6956
7159
|
"project hook onRuntimeError failed: %s %s",
|
|
6957
7160
|
strategyName,
|
|
6958
7161
|
hookError
|
|
@@ -6966,21 +7169,21 @@ var createStrategyRuntime = ({
|
|
|
6966
7169
|
try {
|
|
6967
7170
|
await onRuntimeError(errorParams);
|
|
6968
7171
|
} catch (hookError) {
|
|
6969
|
-
|
|
7172
|
+
import_logger11.logger.error(
|
|
6970
7173
|
"runtime hook onRuntimeError failed: %s %s",
|
|
6971
7174
|
strategyName,
|
|
6972
7175
|
hookError
|
|
6973
7176
|
);
|
|
6974
7177
|
}
|
|
6975
7178
|
};
|
|
6976
|
-
const invokeHook = async (stage, hook, params, errorContext = {}) => {
|
|
7179
|
+
const invokeHook = async (stage, hook, params, errorContext = {}, failureResult) => {
|
|
6977
7180
|
if (!hook) {
|
|
6978
7181
|
return void 0;
|
|
6979
7182
|
}
|
|
6980
7183
|
try {
|
|
6981
7184
|
return await hook(params);
|
|
6982
7185
|
} catch (error) {
|
|
6983
|
-
|
|
7186
|
+
import_logger11.logger.error(
|
|
6984
7187
|
'strategy hook "%s" failed for %s: %s',
|
|
6985
7188
|
stage,
|
|
6986
7189
|
strategyName,
|
|
@@ -6993,17 +7196,18 @@ var createStrategyRuntime = ({
|
|
|
6993
7196
|
entry: errorContext.entry,
|
|
6994
7197
|
market: errorContext.market
|
|
6995
7198
|
});
|
|
6996
|
-
return
|
|
7199
|
+
return failureResult;
|
|
6997
7200
|
}
|
|
6998
7201
|
};
|
|
6999
|
-
const invokeProjectHooks = async (stage, params, errorContext = {}) => {
|
|
7202
|
+
const invokeProjectHooks = async (stage, params, errorContext = {}, failureResult) => {
|
|
7000
7203
|
const results = [];
|
|
7001
7204
|
for (const hook of getProjectHookList(stage)) {
|
|
7002
7205
|
const result = await invokeHook(
|
|
7003
7206
|
stage,
|
|
7004
7207
|
hook,
|
|
7005
7208
|
params,
|
|
7006
|
-
errorContext
|
|
7209
|
+
errorContext,
|
|
7210
|
+
failureResult
|
|
7007
7211
|
);
|
|
7008
7212
|
if (result !== void 0) {
|
|
7009
7213
|
results.push(result);
|
|
@@ -7018,10 +7222,12 @@ var createStrategyRuntime = ({
|
|
|
7018
7222
|
return invokeHook(stage, hook, params, errorContext);
|
|
7019
7223
|
};
|
|
7020
7224
|
const invokeGateHooks = async (stage, hook, params, errorContext = {}) => {
|
|
7225
|
+
const failureResult = stage === "beforeEntryGate" ? { allow: false, reason: "ERROR" } : void 0;
|
|
7021
7226
|
const projectResults = await invokeProjectHooks(
|
|
7022
7227
|
stage,
|
|
7023
7228
|
params,
|
|
7024
|
-
errorContext
|
|
7229
|
+
errorContext,
|
|
7230
|
+
failureResult
|
|
7025
7231
|
);
|
|
7026
7232
|
const projectBlock = projectResults.find(
|
|
7027
7233
|
(result) => result?.allow === false
|
|
@@ -7033,7 +7239,8 @@ var createStrategyRuntime = ({
|
|
|
7033
7239
|
stage,
|
|
7034
7240
|
hook,
|
|
7035
7241
|
params,
|
|
7036
|
-
errorContext
|
|
7242
|
+
errorContext,
|
|
7243
|
+
failureResult
|
|
7037
7244
|
);
|
|
7038
7245
|
};
|
|
7039
7246
|
const applyProjectAfterCoreDecisionHooks = async ({
|
|
@@ -7179,11 +7386,8 @@ var createStrategyRuntime = ({
|
|
|
7179
7386
|
return enrichedBaseContext;
|
|
7180
7387
|
}
|
|
7181
7388
|
});
|
|
7182
|
-
const coreConfig = { ...config };
|
|
7183
|
-
delete coreConfig.RECORD_RUNTIME_TRADES;
|
|
7184
|
-
delete coreConfig.SIMULATE_ORDERS;
|
|
7185
7389
|
const core = await createCore({
|
|
7186
|
-
config:
|
|
7390
|
+
config: strategyConfig,
|
|
7187
7391
|
data,
|
|
7188
7392
|
strategyApi,
|
|
7189
7393
|
indicatorsState
|
|
@@ -7802,10 +8006,10 @@ var createStrategyRuntime = ({
|
|
|
7802
8006
|
setStrategyRuntimeFactory(createStrategyRuntime);
|
|
7803
8007
|
|
|
7804
8008
|
// src/connectorsRegistry.ts
|
|
7805
|
-
var
|
|
8009
|
+
var import_logger14 = require("@tradejs/infra/logger");
|
|
7806
8010
|
|
|
7807
8011
|
// src/connectorRuntime.ts
|
|
7808
|
-
var
|
|
8012
|
+
var import_logger13 = require("@tradejs/infra/logger");
|
|
7809
8013
|
var import_tradingAccounts = require("@tradejs/infra/tradingAccounts");
|
|
7810
8014
|
|
|
7811
8015
|
// src/timescaleCachedKline.ts
|
|
@@ -7813,7 +8017,7 @@ var import_constants6 = require("@tradejs/core/constants");
|
|
|
7813
8017
|
var import_data5 = require("@tradejs/core/data");
|
|
7814
8018
|
var import_time2 = require("@tradejs/core/time");
|
|
7815
8019
|
var import_async = require("@tradejs/core/async");
|
|
7816
|
-
var
|
|
8020
|
+
var import_logger12 = require("@tradejs/infra/logger");
|
|
7817
8021
|
var import_candles = require("@tradejs/infra/timescale/candles");
|
|
7818
8022
|
var DEFAULT_LIMIT = 1e3;
|
|
7819
8023
|
var DEFAULT_CACHE_FALLBACK_WINDOW = 1e3;
|
|
@@ -7923,7 +8127,7 @@ var createTimescaleCachedKline = ({
|
|
|
7923
8127
|
}) => {
|
|
7924
8128
|
const intMinutes = intervalToMinutes(interval);
|
|
7925
8129
|
if (!intMinutes) {
|
|
7926
|
-
|
|
8130
|
+
import_logger12.logger.log("error", "refreshTail: invalid interval %s", interval);
|
|
7927
8131
|
return;
|
|
7928
8132
|
}
|
|
7929
8133
|
const intervalMs = intervalMsOf(intMinutes);
|
|
@@ -7954,7 +8158,7 @@ var createTimescaleCachedKline = ({
|
|
|
7954
8158
|
}) => {
|
|
7955
8159
|
const intMinutes = intervalToMinutes(interval);
|
|
7956
8160
|
if (!intMinutes) {
|
|
7957
|
-
|
|
8161
|
+
import_logger12.logger.log("error", "kline: invalid interval %s", interval);
|
|
7958
8162
|
return [];
|
|
7959
8163
|
}
|
|
7960
8164
|
if (defaultStart !== void 0 && defaultEnd !== void 0 && defaultEnd <= defaultStart) {
|
|
@@ -8063,7 +8267,7 @@ var createTimescaleCachedKline = ({
|
|
|
8063
8267
|
if (warmOnly) {
|
|
8064
8268
|
if (isTimescaleFallbackMode) {
|
|
8065
8269
|
isTimescaleFallbackMode = false;
|
|
8066
|
-
|
|
8270
|
+
import_logger12.logger.log("info", "TimescaleDB connection restored for kline cache");
|
|
8067
8271
|
}
|
|
8068
8272
|
return [];
|
|
8069
8273
|
}
|
|
@@ -8081,13 +8285,13 @@ var createTimescaleCachedKline = ({
|
|
|
8081
8285
|
);
|
|
8082
8286
|
if (isTimescaleFallbackMode) {
|
|
8083
8287
|
isTimescaleFallbackMode = false;
|
|
8084
|
-
|
|
8288
|
+
import_logger12.logger.log("info", "TimescaleDB connection restored for kline cache");
|
|
8085
8289
|
}
|
|
8086
8290
|
return dbData;
|
|
8087
8291
|
} catch (error) {
|
|
8088
8292
|
if (!isTimescaleFallbackMode) {
|
|
8089
8293
|
isTimescaleFallbackMode = true;
|
|
8090
|
-
|
|
8294
|
+
import_logger12.logger.log(
|
|
8091
8295
|
"warn",
|
|
8092
8296
|
"TimescaleDB unavailable for %s %s: %s. Falling back to exchange API.",
|
|
8093
8297
|
symbol,
|
|
@@ -8111,7 +8315,7 @@ var createTimescaleCachedKline = ({
|
|
|
8111
8315
|
|
|
8112
8316
|
// src/connectorRuntime.ts
|
|
8113
8317
|
var connectorRuntime = {
|
|
8114
|
-
logger:
|
|
8318
|
+
logger: import_logger13.logger,
|
|
8115
8319
|
resolveTradingAccount: import_tradingAccounts.resolveTradingAccount,
|
|
8116
8320
|
createCachedKline: createTimescaleCachedKline
|
|
8117
8321
|
};
|
|
@@ -8172,7 +8376,7 @@ var normalizeProviders = (providers, connectorName) => {
|
|
|
8172
8376
|
var registerProvider = (provider, connectorName, source, providerToConnectorName) => {
|
|
8173
8377
|
const existing = providerToConnectorName.get(provider);
|
|
8174
8378
|
if (existing && existing !== connectorName) {
|
|
8175
|
-
|
|
8379
|
+
import_logger14.logger.warn(
|
|
8176
8380
|
'Skip duplicate connector provider "%s" from %s: already mapped to %s',
|
|
8177
8381
|
provider,
|
|
8178
8382
|
source,
|
|
@@ -8185,11 +8389,11 @@ var registerProvider = (provider, connectorName, source, providerToConnectorName
|
|
|
8185
8389
|
var registerEntry = (entry, source, state) => {
|
|
8186
8390
|
const connectorName = String(entry?.name ?? "").trim();
|
|
8187
8391
|
if (!connectorName) {
|
|
8188
|
-
|
|
8392
|
+
import_logger14.logger.warn("Skip connector entry without name from %s", source);
|
|
8189
8393
|
return;
|
|
8190
8394
|
}
|
|
8191
8395
|
if (typeof entry.creator !== "function") {
|
|
8192
|
-
|
|
8396
|
+
import_logger14.logger.warn(
|
|
8193
8397
|
'Skip connector entry "%s" from %s: creator must be a function',
|
|
8194
8398
|
connectorName,
|
|
8195
8399
|
source
|
|
@@ -8201,7 +8405,7 @@ var registerEntry = (entry, source, state) => {
|
|
|
8201
8405
|
state.connectorCreators
|
|
8202
8406
|
);
|
|
8203
8407
|
if (existingByName) {
|
|
8204
|
-
|
|
8408
|
+
import_logger14.logger.warn(
|
|
8205
8409
|
'Skip duplicate connector "%s" from %s: already registered as %s',
|
|
8206
8410
|
connectorName,
|
|
8207
8411
|
source,
|
|
@@ -8276,7 +8480,7 @@ var ensureConnectorPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
|
|
|
8276
8480
|
);
|
|
8277
8481
|
const pluginDefinition = extractConnectorPluginDefinition(moduleExport);
|
|
8278
8482
|
if (!pluginDefinition) {
|
|
8279
|
-
|
|
8483
|
+
import_logger14.logger.warn(
|
|
8280
8484
|
'Skip connector plugin "%s": export { connectorEntries } is missing',
|
|
8281
8485
|
moduleName
|
|
8282
8486
|
);
|
|
@@ -8284,7 +8488,7 @@ var ensureConnectorPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
|
|
|
8284
8488
|
}
|
|
8285
8489
|
registerEntries2(pluginDefinition.connectorEntries, moduleName, state);
|
|
8286
8490
|
} catch (error) {
|
|
8287
|
-
|
|
8491
|
+
import_logger14.logger.warn(
|
|
8288
8492
|
'Failed to load connector plugin "%s": %s',
|
|
8289
8493
|
moduleName,
|
|
8290
8494
|
String(error)
|
|
@@ -8850,6 +9054,7 @@ var loadResolvedRuntimeStrategies = async ({
|
|
|
8850
9054
|
var import_node_crypto5 = require("crypto");
|
|
8851
9055
|
var import_promises3 = require("fs/promises");
|
|
8852
9056
|
var import_node_path3 = __toESM(require("path"));
|
|
9057
|
+
var import_node_util = require("util");
|
|
8853
9058
|
var import_redis3 = require("@tradejs/infra/redis");
|
|
8854
9059
|
var MCP_JOB_TTL = 7 * 86400;
|
|
8855
9060
|
var hash = (value) => (0, import_node_crypto5.createHash)("sha256").update(value).digest("hex");
|
|
@@ -9015,10 +9220,29 @@ var readAuthorizedMcpJob = async (principal, id) => {
|
|
|
9015
9220
|
throw new Error("Insufficient scope");
|
|
9016
9221
|
return job;
|
|
9017
9222
|
};
|
|
9018
|
-
var
|
|
9223
|
+
var redactMcpLogLine = (line) => line.replace(/\u001b\[[0-9;]*m/g, "").replace(/\b(Bearer|Basic)\s+\S+/gi, "$1 [redacted]").replace(/([a-z][a-z0-9+.-]*:\/\/)[^\s/@]*:[^\s/@]+@/gi, "$1[redacted]@").replace(/((?:authorization|cookie)\s*:\s*)[^\r\n]+/gi, "$1[redacted]").replace(
|
|
9019
9224
|
/((?:[\w.-]*(?:secret|password|token|api[_-]?key|private[_-]?key|authorization|cookie|credential)[\w.-]*)["']?\s*[:=]\s*)(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s,;&}]+)/gi,
|
|
9020
9225
|
"$1[redacted]"
|
|
9021
|
-
)
|
|
9226
|
+
);
|
|
9227
|
+
var MCP_LOG_LINE_LIMIT = 1e3;
|
|
9228
|
+
var redactMcpJobLog = (line) => redactMcpLogLine(line).slice(0, MCP_LOG_LINE_LIMIT);
|
|
9229
|
+
var formatMcpArtifactLogLine = (line) => {
|
|
9230
|
+
const text = redactMcpLogLine(
|
|
9231
|
+
(0, import_node_util.stripVTControlCharacters)(line).replace(/\r/g, "")
|
|
9232
|
+
);
|
|
9233
|
+
const fragments = [];
|
|
9234
|
+
let start = 0;
|
|
9235
|
+
while (start < text.length) {
|
|
9236
|
+
let end = Math.min(start + MCP_LOG_LINE_LIMIT, text.length);
|
|
9237
|
+
const lastCodeUnit = text.charCodeAt(end - 1);
|
|
9238
|
+
if (end < text.length && lastCodeUnit >= 55296 && lastCodeUnit <= 56319) {
|
|
9239
|
+
end -= 1;
|
|
9240
|
+
}
|
|
9241
|
+
fragments.push(redactMcpJobLog(text.slice(start, end)));
|
|
9242
|
+
start = end;
|
|
9243
|
+
}
|
|
9244
|
+
return fragments.join("\n") + "\n";
|
|
9245
|
+
};
|
|
9022
9246
|
var readMcpJobLogs = async (principal, id, offset = 0, limit = 50) => {
|
|
9023
9247
|
if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(limit) || limit < 1 || limit > 100)
|
|
9024
9248
|
throw new Error("Select a non-negative log offset and a limit of 1\u2013100");
|
|
@@ -9481,6 +9705,7 @@ var readMcpBacktestResult = async (userName, id, offset = 0, limit = 20) => {
|
|
|
9481
9705
|
McpCursorError,
|
|
9482
9706
|
cancelMcpJob,
|
|
9483
9707
|
enqueueMcpJob,
|
|
9708
|
+
formatMcpArtifactLogLine,
|
|
9484
9709
|
isMcpRecord,
|
|
9485
9710
|
mcpId,
|
|
9486
9711
|
mcpJobDirectory,
|