@tradejs/node 3.1.29-beta.288 → 3.1.29-beta.289
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 +430 -223
- package/dist/backtest.mjs +10 -5
- package/dist/{chunk-F7CLF3PT.mjs → chunk-KYGJBMKE.mjs} +403 -201
- package/dist/diagnostics.js +404 -202
- package/dist/diagnostics.mjs +1 -1
- package/dist/mcp.js +419 -217
- package/dist/mcp.mjs +1 -1
- package/dist/registry.js +404 -202
- package/dist/registry.mjs +1 -1
- package/dist/runtimeDashboard.js +406 -204
- package/dist/runtimeDashboard.mjs +1 -1
- package/dist/runtimeStrategies.js +404 -202
- package/dist/runtimeStrategies.mjs +1 -1
- package/dist/strategies.d.mts +32 -22
- package/dist/strategies.d.ts +32 -22
- package/dist/strategies.js +412 -210
- package/dist/strategies.mjs +1 -1
- package/package.json +5 -5
package/dist/registry.js
CHANGED
|
@@ -1045,13 +1045,213 @@ var assessSignalWithJev = async ({
|
|
|
1045
1045
|
// src/strategyRuntime.ts
|
|
1046
1046
|
var import_constants5 = require("@tradejs/core/constants");
|
|
1047
1047
|
var import_strategies6 = require("@tradejs/core/strategies");
|
|
1048
|
-
var
|
|
1048
|
+
var import_logger11 = require("@tradejs/infra/logger");
|
|
1049
1049
|
|
|
1050
1050
|
// src/strategyHelpers/runtime.ts
|
|
1051
|
-
var
|
|
1051
|
+
var import_logger9 = require("@tradejs/infra/logger");
|
|
1052
1052
|
var import_constants3 = require("@tradejs/core/constants");
|
|
1053
1053
|
var import_ml2 = require("@tradejs/infra/ml");
|
|
1054
1054
|
|
|
1055
|
+
// src/strategy/entrySubmissionValidation.ts
|
|
1056
|
+
var import_logger = require("@tradejs/infra/logger");
|
|
1057
|
+
|
|
1058
|
+
// src/strategy/entryExecutionValidation.ts
|
|
1059
|
+
var EntrySubmissionValidationError = class extends Error {
|
|
1060
|
+
constructor() {
|
|
1061
|
+
super(...arguments);
|
|
1062
|
+
this.name = "EntrySubmissionValidationError";
|
|
1063
|
+
}
|
|
1064
|
+
};
|
|
1065
|
+
var resolveEntryExecutionLimits = (config, direction) => {
|
|
1066
|
+
const side = config[direction];
|
|
1067
|
+
const minimum = side && typeof side === "object" && "minRiskRatio" in side ? side.minRiskRatio : void 0;
|
|
1068
|
+
const maximum = config.MAX_LOSS_VALUE;
|
|
1069
|
+
if (minimum == null && maximum == null) return void 0;
|
|
1070
|
+
const minimumNetRiskRatio = minimum == null ? void 0 : Number(minimum);
|
|
1071
|
+
const maxLossValue = maximum == null ? void 0 : Number(maximum);
|
|
1072
|
+
const feeRate = Number(config.RISK_FEE_RATE ?? 0);
|
|
1073
|
+
const slippage = Number(config.RISK_SLIPPAGE_BPS ?? 0);
|
|
1074
|
+
const impact = Number(config.RISK_MARKET_IMPACT_BPS ?? 0);
|
|
1075
|
+
const slippageBps = slippage + impact;
|
|
1076
|
+
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)
|
|
1077
|
+
throw new Error("INVALID_ENTRY_EXECUTION_LIMITS");
|
|
1078
|
+
return { minimumNetRiskRatio, maxLossValue, feeRate, slippageBps };
|
|
1079
|
+
};
|
|
1080
|
+
var validateEntryExecution = ({
|
|
1081
|
+
direction,
|
|
1082
|
+
price,
|
|
1083
|
+
qty,
|
|
1084
|
+
stopLossPrice,
|
|
1085
|
+
takeProfitPrice,
|
|
1086
|
+
limits
|
|
1087
|
+
}) => {
|
|
1088
|
+
if (!Number.isFinite(price) || price <= 0 || !Number.isFinite(qty) || qty <= 0 || stopLossPrice == null || !Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || !Number.isFinite(takeProfitPrice) || takeProfitPrice <= 0)
|
|
1089
|
+
throw new EntrySubmissionValidationError(
|
|
1090
|
+
"ENTRY_ECONOMICS_INPUTS_UNAVAILABLE"
|
|
1091
|
+
);
|
|
1092
|
+
const side = direction === "LONG" ? 1 : -1;
|
|
1093
|
+
if (side * (price - stopLossPrice) <= 0 || side * (takeProfitPrice - price) <= 0)
|
|
1094
|
+
throw new EntrySubmissionValidationError(
|
|
1095
|
+
"INVALID_ENTRY_PROTECTION_AT_SUBMISSION"
|
|
1096
|
+
);
|
|
1097
|
+
const slippage = limits.slippageBps / 1e4;
|
|
1098
|
+
const cost = (value, sign) => value * slippage + value * (1 + sign * slippage) * limits.feeRate;
|
|
1099
|
+
const entryCost = cost(price, side);
|
|
1100
|
+
const lossPerUnit = Math.abs(price - stopLossPrice) + entryCost + cost(stopLossPrice, -side);
|
|
1101
|
+
const rewardPerUnit = Math.max(
|
|
1102
|
+
0,
|
|
1103
|
+
Math.abs(takeProfitPrice - price) - entryCost - cost(takeProfitPrice, -side)
|
|
1104
|
+
);
|
|
1105
|
+
const netRiskRatio = rewardPerUnit / lossPerUnit;
|
|
1106
|
+
const plannedLossValue = qty * lossPerUnit;
|
|
1107
|
+
if (!Number.isFinite(netRiskRatio) || !Number.isFinite(plannedLossValue))
|
|
1108
|
+
throw new EntrySubmissionValidationError(
|
|
1109
|
+
"ENTRY_ECONOMICS_INPUTS_UNAVAILABLE"
|
|
1110
|
+
);
|
|
1111
|
+
if (limits.minimumNetRiskRatio != null && netRiskRatio <= limits.minimumNetRiskRatio)
|
|
1112
|
+
throw new EntrySubmissionValidationError(
|
|
1113
|
+
"ENTRY_RR_BELOW_MIN_AT_SUBMISSION"
|
|
1114
|
+
);
|
|
1115
|
+
if (limits.maxLossValue != null && plannedLossValue > limits.maxLossValue * (1 + 1e-9))
|
|
1116
|
+
throw new EntrySubmissionValidationError(
|
|
1117
|
+
"ENTRY_RISK_EXCEEDS_MAX_LOSS_AT_SUBMISSION"
|
|
1118
|
+
);
|
|
1119
|
+
return { netRiskRatio, plannedLossValue };
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
// src/strategy/entrySubmissionValidation.ts
|
|
1123
|
+
var MAX_ENTRY_QUOTE_AGE_MS = 5e3;
|
|
1124
|
+
var toFiniteNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1125
|
+
var getOrderArrivalSnapshot = async ({
|
|
1126
|
+
connector,
|
|
1127
|
+
symbol,
|
|
1128
|
+
requireTimestamp = false
|
|
1129
|
+
}) => {
|
|
1130
|
+
if (typeof connector.getTopOfBookTicker !== "function") {
|
|
1131
|
+
return {
|
|
1132
|
+
arrivalSnapshotTime: Date.now(),
|
|
1133
|
+
arrivalSource: "unavailable",
|
|
1134
|
+
bid: null,
|
|
1135
|
+
ask: null,
|
|
1136
|
+
arrivalMid: null,
|
|
1137
|
+
spreadBps: null
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
try {
|
|
1141
|
+
const ticker = await connector.getTopOfBookTicker(symbol);
|
|
1142
|
+
const arrivalSnapshotTime = toFiniteNumberOrNull(ticker?.timestamp);
|
|
1143
|
+
const timestampUnavailable = requireTimestamp && arrivalSnapshotTime == null;
|
|
1144
|
+
const bid = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.bidPrice);
|
|
1145
|
+
const ask = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.askPrice);
|
|
1146
|
+
const arrivalMid = bid != null && ask != null ? (bid + ask) / 2 : null;
|
|
1147
|
+
const spreadBps = bid != null && ask != null && arrivalMid != null && arrivalMid > 0 ? (ask - bid) / arrivalMid * 1e4 : null;
|
|
1148
|
+
return {
|
|
1149
|
+
arrivalSnapshotTime: arrivalSnapshotTime ?? Date.now(),
|
|
1150
|
+
arrivalSource: "top_of_book",
|
|
1151
|
+
bid,
|
|
1152
|
+
ask,
|
|
1153
|
+
arrivalMid,
|
|
1154
|
+
spreadBps
|
|
1155
|
+
};
|
|
1156
|
+
} catch (error) {
|
|
1157
|
+
import_logger.logger.warn(
|
|
1158
|
+
"runtime order arrival snapshot failed: %s %s",
|
|
1159
|
+
symbol,
|
|
1160
|
+
error?.message || String(error)
|
|
1161
|
+
);
|
|
1162
|
+
return {
|
|
1163
|
+
arrivalSnapshotTime: Date.now(),
|
|
1164
|
+
arrivalSource: "top_of_book_error",
|
|
1165
|
+
bid: null,
|
|
1166
|
+
ask: null,
|
|
1167
|
+
arrivalMid: null,
|
|
1168
|
+
spreadBps: null
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
};
|
|
1172
|
+
var validateEntryProtectionAtArrival = ({
|
|
1173
|
+
direction,
|
|
1174
|
+
signalPrice,
|
|
1175
|
+
bid,
|
|
1176
|
+
ask,
|
|
1177
|
+
arrivalMid,
|
|
1178
|
+
takeProfits,
|
|
1179
|
+
stopLossPrice
|
|
1180
|
+
}) => {
|
|
1181
|
+
const entryReference = direction === "LONG" ? ask ?? arrivalMid ?? signalPrice : bid ?? arrivalMid ?? signalPrice;
|
|
1182
|
+
const stopReference = direction === "LONG" ? bid ?? arrivalMid ?? signalPrice : ask ?? arrivalMid ?? signalPrice;
|
|
1183
|
+
if (!Number.isFinite(entryReference) || entryReference <= 0 || !Number.isFinite(stopReference) || stopReference <= 0) {
|
|
1184
|
+
throw new Error("INVALID_ENTRY_REFERENCE_AT_ARRIVAL");
|
|
1185
|
+
}
|
|
1186
|
+
for (const takeProfit of takeProfits) {
|
|
1187
|
+
if (!Number.isFinite(takeProfit.price) || takeProfit.price <= 0 || (direction === "LONG" ? takeProfit.price <= entryReference : takeProfit.price >= entryReference)) {
|
|
1188
|
+
throw new Error("TAKE_PROFIT_CROSSED_BEFORE_ENTRY");
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
if (stopLossPrice != null && (!Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || (direction === "LONG" ? stopLossPrice >= stopReference : stopLossPrice <= stopReference))) {
|
|
1192
|
+
throw new Error("STOP_LOSS_CROSSED_BEFORE_ENTRY");
|
|
1193
|
+
}
|
|
1194
|
+
};
|
|
1195
|
+
var createEntrySubmissionValidation = ({
|
|
1196
|
+
connector,
|
|
1197
|
+
symbol,
|
|
1198
|
+
direction,
|
|
1199
|
+
takeProfits,
|
|
1200
|
+
stopLossPrice,
|
|
1201
|
+
limits,
|
|
1202
|
+
onValidated
|
|
1203
|
+
}) => {
|
|
1204
|
+
if (!limits) return void 0;
|
|
1205
|
+
if (!connector.capabilities?.orderSubmissionValidation)
|
|
1206
|
+
throw new EntrySubmissionValidationError(
|
|
1207
|
+
"ENTRY_SUBMISSION_VALIDATION_UNSUPPORTED"
|
|
1208
|
+
);
|
|
1209
|
+
return async ({ qty, executionPrice }) => {
|
|
1210
|
+
const simulated = Reflect.get(connector, "__tradejsTestConnector") === true;
|
|
1211
|
+
const snapshot2 = simulated && executionPrice != null ? {
|
|
1212
|
+
arrivalSnapshotTime: Date.now(),
|
|
1213
|
+
arrivalSource: "simulation",
|
|
1214
|
+
bid: executionPrice,
|
|
1215
|
+
ask: executionPrice,
|
|
1216
|
+
arrivalMid: executionPrice,
|
|
1217
|
+
spreadBps: 0
|
|
1218
|
+
} : await getOrderArrivalSnapshot({
|
|
1219
|
+
connector,
|
|
1220
|
+
symbol,
|
|
1221
|
+
requireTimestamp: true
|
|
1222
|
+
});
|
|
1223
|
+
const age = Date.now() - snapshot2.arrivalSnapshotTime;
|
|
1224
|
+
if (snapshot2.bid == null || snapshot2.ask == null || !Number.isFinite(snapshot2.bid) || !Number.isFinite(snapshot2.ask) || snapshot2.bid <= 0 || snapshot2.ask < snapshot2.bid)
|
|
1225
|
+
throw new EntrySubmissionValidationError(
|
|
1226
|
+
"ENTRY_QUOTE_UNAVAILABLE_AT_SUBMISSION"
|
|
1227
|
+
);
|
|
1228
|
+
if (!Number.isFinite(age) || age < -MAX_ENTRY_QUOTE_AGE_MS || age > MAX_ENTRY_QUOTE_AGE_MS)
|
|
1229
|
+
throw new EntrySubmissionValidationError(
|
|
1230
|
+
"ENTRY_QUOTE_STALE_AT_SUBMISSION"
|
|
1231
|
+
);
|
|
1232
|
+
const price = direction === "LONG" ? snapshot2.ask : snapshot2.bid;
|
|
1233
|
+
validateEntryProtectionAtArrival({
|
|
1234
|
+
direction,
|
|
1235
|
+
signalPrice: price,
|
|
1236
|
+
bid: snapshot2.bid,
|
|
1237
|
+
ask: snapshot2.ask,
|
|
1238
|
+
arrivalMid: snapshot2.arrivalMid,
|
|
1239
|
+
takeProfits,
|
|
1240
|
+
stopLossPrice
|
|
1241
|
+
});
|
|
1242
|
+
const prices = takeProfits.map((tp) => tp.price);
|
|
1243
|
+
const economics2 = validateEntryExecution({
|
|
1244
|
+
direction,
|
|
1245
|
+
price,
|
|
1246
|
+
qty,
|
|
1247
|
+
stopLossPrice,
|
|
1248
|
+
takeProfitPrice: direction === "LONG" ? Math.max(...prices) : Math.min(...prices),
|
|
1249
|
+
limits
|
|
1250
|
+
});
|
|
1251
|
+
onValidated?.(snapshot2, economics2);
|
|
1252
|
+
};
|
|
1253
|
+
};
|
|
1254
|
+
|
|
1055
1255
|
// src/ai.ts
|
|
1056
1256
|
var import_aiLanguages2 = require("@tradejs/core/aiLanguages");
|
|
1057
1257
|
var import_redis = require("@tradejs/infra/redis");
|
|
@@ -1636,7 +1836,7 @@ var import_fs = __toESM(require("fs"));
|
|
|
1636
1836
|
var import_path = __toESM(require("path"));
|
|
1637
1837
|
var import_url = require("url");
|
|
1638
1838
|
var import_config = require("@tradejs/core/config");
|
|
1639
|
-
var
|
|
1839
|
+
var import_logger2 = require("@tradejs/infra/logger");
|
|
1640
1840
|
var CONFIG_FILE_NAMES = [
|
|
1641
1841
|
"tradejs.config.ts",
|
|
1642
1842
|
"tradejs.config.mts",
|
|
@@ -1903,11 +2103,11 @@ var loadTradejsConfig = async (cwd = getTradejsProjectCwd()) => {
|
|
|
1903
2103
|
cachedByCwd.set(cwd, config);
|
|
1904
2104
|
if (!announcedConfigFile.has(configFilePath)) {
|
|
1905
2105
|
announcedConfigFile.add(configFilePath);
|
|
1906
|
-
|
|
2106
|
+
import_logger2.logger.log("debug", "Loaded TradeJS config: %s", configFilePath);
|
|
1907
2107
|
}
|
|
1908
2108
|
return config;
|
|
1909
2109
|
} catch (error) {
|
|
1910
|
-
|
|
2110
|
+
import_logger2.logger.log(
|
|
1911
2111
|
"warn",
|
|
1912
2112
|
"Failed to load TradeJS config from %s: %s",
|
|
1913
2113
|
configFilePath,
|
|
@@ -2897,7 +3097,7 @@ var import_node_crypto = require("crypto");
|
|
|
2897
3097
|
var import_constants = require("@tradejs/core/constants");
|
|
2898
3098
|
var import_time = require("@tradejs/core/time");
|
|
2899
3099
|
var import_trade = require("@tradejs/core/trade");
|
|
2900
|
-
var
|
|
3100
|
+
var import_logger3 = require("@tradejs/infra/logger");
|
|
2901
3101
|
var import_redis2 = require("@tradejs/infra/redis");
|
|
2902
3102
|
var now = () => Date.now();
|
|
2903
3103
|
var toRandomOrderSuffix = () => (0, import_node_crypto.randomUUID)().replace(/-/g, "").slice(0, 12).toLowerCase();
|
|
@@ -2956,7 +3156,7 @@ var recordRuntimeTradeOpen = async (params) => {
|
|
|
2956
3156
|
)
|
|
2957
3157
|
]);
|
|
2958
3158
|
} catch (error) {
|
|
2959
|
-
|
|
3159
|
+
import_logger3.logger.error(
|
|
2960
3160
|
"runtime trade open journal failed: %s %s",
|
|
2961
3161
|
record.symbol,
|
|
2962
3162
|
error?.message || String(error)
|
|
@@ -3024,7 +3224,7 @@ var recordRuntimeTradeIncrease = async (params) => {
|
|
|
3024
3224
|
)
|
|
3025
3225
|
]);
|
|
3026
3226
|
} catch (error) {
|
|
3027
|
-
|
|
3227
|
+
import_logger3.logger.error(
|
|
3028
3228
|
"runtime trade increase journal failed: %s %s",
|
|
3029
3229
|
symbol,
|
|
3030
3230
|
error?.message || String(error)
|
|
@@ -3131,7 +3331,7 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
3131
3331
|
)
|
|
3132
3332
|
]);
|
|
3133
3333
|
} catch (error) {
|
|
3134
|
-
|
|
3334
|
+
import_logger3.logger.error(
|
|
3135
3335
|
"runtime trade close journal failed: %s %s",
|
|
3136
3336
|
symbol,
|
|
3137
3337
|
error?.message || String(error)
|
|
@@ -3141,11 +3341,11 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
3141
3341
|
};
|
|
3142
3342
|
|
|
3143
3343
|
// src/strategyHelpers/marketContextStages.ts
|
|
3144
|
-
var
|
|
3344
|
+
var import_logger8 = require("@tradejs/infra/logger");
|
|
3145
3345
|
|
|
3146
3346
|
// src/strategyHelpers/binanceMarketContext.ts
|
|
3147
3347
|
var import_marketContext = require("@tradejs/infra/timescale/marketContext");
|
|
3148
|
-
var
|
|
3348
|
+
var import_logger4 = require("@tradejs/infra/logger");
|
|
3149
3349
|
var import_strategies = require("@tradejs/core/strategies");
|
|
3150
3350
|
|
|
3151
3351
|
// src/binanceBreadthUniverses.ts
|
|
@@ -3509,7 +3709,7 @@ var parseEnabledFlag = (value, env) => {
|
|
|
3509
3709
|
if (normalized === "live") return env !== "BACKTEST";
|
|
3510
3710
|
return false;
|
|
3511
3711
|
};
|
|
3512
|
-
var
|
|
3712
|
+
var toFiniteNumberOrNull2 = (value) => {
|
|
3513
3713
|
const numeric = typeof value === "number" ? value : Number(value);
|
|
3514
3714
|
return Number.isFinite(numeric) ? numeric : null;
|
|
3515
3715
|
};
|
|
@@ -3634,14 +3834,14 @@ var toTradeFlowContext = (row, interval) => row ? {
|
|
|
3634
3834
|
asOfTs: row.ts.getTime(),
|
|
3635
3835
|
ageMs: row.ageMs,
|
|
3636
3836
|
stale: row.stale,
|
|
3637
|
-
trades:
|
|
3638
|
-
buyPressurePct:
|
|
3639
|
-
buyBaseVolume:
|
|
3640
|
-
sellBaseVolume:
|
|
3641
|
-
buyQuoteVolume:
|
|
3642
|
-
sellQuoteVolume:
|
|
3643
|
-
netBaseDelta:
|
|
3644
|
-
netQuoteDelta:
|
|
3837
|
+
trades: toFiniteNumberOrNull2(row.trades),
|
|
3838
|
+
buyPressurePct: toFiniteNumberOrNull2(row.buyPressurePct),
|
|
3839
|
+
buyBaseVolume: toFiniteNumberOrNull2(row.buyBaseVolume),
|
|
3840
|
+
sellBaseVolume: toFiniteNumberOrNull2(row.sellBaseVolume),
|
|
3841
|
+
buyQuoteVolume: toFiniteNumberOrNull2(row.buyQuoteVolume),
|
|
3842
|
+
sellQuoteVolume: toFiniteNumberOrNull2(row.sellQuoteVolume),
|
|
3843
|
+
netBaseDelta: toFiniteNumberOrNull2(row.netBaseDelta),
|
|
3844
|
+
netQuoteDelta: toFiniteNumberOrNull2(row.netQuoteDelta)
|
|
3645
3845
|
} : null;
|
|
3646
3846
|
var getCachedReferenceRows = ({
|
|
3647
3847
|
referenceSymbols,
|
|
@@ -3730,16 +3930,16 @@ var toMarketBreadthContext = (breadth, interval) => ({
|
|
|
3730
3930
|
asOfTs: breadth.ts.getTime(),
|
|
3731
3931
|
ageMs: breadth.ageMs,
|
|
3732
3932
|
stale: breadth.stale,
|
|
3733
|
-
symbolsCount:
|
|
3734
|
-
advancers:
|
|
3735
|
-
decliners:
|
|
3736
|
-
unchanged:
|
|
3737
|
-
advanceDeclineRatio:
|
|
3738
|
-
pctAboveMa20:
|
|
3739
|
-
pctAboveMa50:
|
|
3740
|
-
equalWeightedReturn:
|
|
3741
|
-
volumeWeightedReturn:
|
|
3742
|
-
dispersion:
|
|
3933
|
+
symbolsCount: toFiniteNumberOrNull2(breadth.symbolsCount),
|
|
3934
|
+
advancers: toFiniteNumberOrNull2(breadth.advancers),
|
|
3935
|
+
decliners: toFiniteNumberOrNull2(breadth.decliners),
|
|
3936
|
+
unchanged: toFiniteNumberOrNull2(breadth.unchanged),
|
|
3937
|
+
advanceDeclineRatio: toFiniteNumberOrNull2(breadth.advanceDeclineRatio),
|
|
3938
|
+
pctAboveMa20: toFiniteNumberOrNull2(breadth.pctAboveMa20),
|
|
3939
|
+
pctAboveMa50: toFiniteNumberOrNull2(breadth.pctAboveMa50),
|
|
3940
|
+
equalWeightedReturn: toFiniteNumberOrNull2(breadth.equalWeightedReturn),
|
|
3941
|
+
volumeWeightedReturn: toFiniteNumberOrNull2(breadth.volumeWeightedReturn),
|
|
3942
|
+
dispersion: toFiniteNumberOrNull2(breadth.dispersion)
|
|
3743
3943
|
});
|
|
3744
3944
|
var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
3745
3945
|
const {
|
|
@@ -3840,46 +4040,46 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
3840
4040
|
asOfTs: primaryBreadth.ts.getTime(),
|
|
3841
4041
|
ageMs: primaryBreadth.ageMs,
|
|
3842
4042
|
stale: primaryBreadth.stale,
|
|
3843
|
-
btcReturn1h:
|
|
4043
|
+
btcReturn1h: toFiniteNumberOrNull2(
|
|
3844
4044
|
primaryBreadth.btcReturn1h
|
|
3845
4045
|
),
|
|
3846
|
-
btcReturn4h:
|
|
4046
|
+
btcReturn4h: toFiniteNumberOrNull2(
|
|
3847
4047
|
primaryBreadth.btcReturn4h
|
|
3848
4048
|
),
|
|
3849
|
-
btcReturn24h:
|
|
4049
|
+
btcReturn24h: toFiniteNumberOrNull2(
|
|
3850
4050
|
primaryBreadth.btcReturn24h
|
|
3851
4051
|
),
|
|
3852
|
-
altBasketReturn1h:
|
|
4052
|
+
altBasketReturn1h: toFiniteNumberOrNull2(
|
|
3853
4053
|
primaryBreadth.altBasketReturn1h
|
|
3854
4054
|
),
|
|
3855
|
-
altBasketReturn4h:
|
|
4055
|
+
altBasketReturn4h: toFiniteNumberOrNull2(
|
|
3856
4056
|
primaryBreadth.altBasketReturn4h
|
|
3857
4057
|
),
|
|
3858
|
-
altBasketReturn24h:
|
|
4058
|
+
altBasketReturn24h: toFiniteNumberOrNull2(
|
|
3859
4059
|
primaryBreadth.altBasketReturn24h
|
|
3860
4060
|
),
|
|
3861
|
-
btcVsAltReturn1h:
|
|
4061
|
+
btcVsAltReturn1h: toFiniteNumberOrNull2(
|
|
3862
4062
|
primaryBreadth.btcVsAltReturn1h
|
|
3863
4063
|
),
|
|
3864
|
-
btcVsAltReturn4h:
|
|
4064
|
+
btcVsAltReturn4h: toFiniteNumberOrNull2(
|
|
3865
4065
|
primaryBreadth.btcVsAltReturn4h
|
|
3866
4066
|
),
|
|
3867
|
-
btcVsAltReturn24h:
|
|
4067
|
+
btcVsAltReturn24h: toFiniteNumberOrNull2(
|
|
3868
4068
|
primaryBreadth.btcVsAltReturn24h
|
|
3869
4069
|
),
|
|
3870
|
-
btcTurnoverShare1h:
|
|
4070
|
+
btcTurnoverShare1h: toFiniteNumberOrNull2(
|
|
3871
4071
|
primaryBreadth.btcTurnoverShare1h
|
|
3872
4072
|
),
|
|
3873
|
-
btcTurnoverShare24h:
|
|
4073
|
+
btcTurnoverShare24h: toFiniteNumberOrNull2(
|
|
3874
4074
|
primaryBreadth.btcTurnoverShare24h
|
|
3875
4075
|
),
|
|
3876
|
-
btcTurnoverShareChange24h:
|
|
4076
|
+
btcTurnoverShareChange24h: toFiniteNumberOrNull2(
|
|
3877
4077
|
primaryBreadth.btcTurnoverShareChange24h
|
|
3878
4078
|
),
|
|
3879
|
-
altVolToBtcVol24h:
|
|
4079
|
+
altVolToBtcVol24h: toFiniteNumberOrNull2(
|
|
3880
4080
|
primaryBreadth.altVolToBtcVol24h
|
|
3881
4081
|
),
|
|
3882
|
-
altDispersion24h:
|
|
4082
|
+
altDispersion24h: toFiniteNumberOrNull2(
|
|
3883
4083
|
primaryBreadth.altDispersion24h
|
|
3884
4084
|
),
|
|
3885
4085
|
regime: primaryBreadth.btcAltRegime ?? "unknown"
|
|
@@ -3896,7 +4096,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
3896
4096
|
throw error;
|
|
3897
4097
|
}
|
|
3898
4098
|
binanceMarketContextUnavailable = true;
|
|
3899
|
-
|
|
4099
|
+
import_logger4.logger.warn(
|
|
3900
4100
|
"Binance market context disabled after Timescale read failure: %s",
|
|
3901
4101
|
String(error)
|
|
3902
4102
|
);
|
|
@@ -3906,7 +4106,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
3906
4106
|
|
|
3907
4107
|
// src/strategyHelpers/coinMarketCapContext.ts
|
|
3908
4108
|
var import_strategies2 = require("@tradejs/core/strategies");
|
|
3909
|
-
var
|
|
4109
|
+
var import_logger5 = require("@tradejs/infra/logger");
|
|
3910
4110
|
var import_marketContext2 = require("@tradejs/infra/timescale/marketContext");
|
|
3911
4111
|
var DEFAULT_MAX_AGE_MS = 48 * 60 * 6e4;
|
|
3912
4112
|
var SOURCE_GLOBAL_DAILY = "coinmarketcap_global";
|
|
@@ -3944,7 +4144,7 @@ var asInt = (value, fallback) => {
|
|
|
3944
4144
|
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
3945
4145
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
3946
4146
|
};
|
|
3947
|
-
var
|
|
4147
|
+
var toFiniteNumberOrNull3 = (value) => {
|
|
3948
4148
|
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
|
|
3949
4149
|
return Number.isFinite(numeric) ? numeric : null;
|
|
3950
4150
|
};
|
|
@@ -4178,12 +4378,12 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4178
4378
|
const ethRow = references.get("ETHUSDT") ?? null;
|
|
4179
4379
|
const previousBtcRow = previousReferences.get("BTCUSDT") ?? null;
|
|
4180
4380
|
const previousEthRow = previousReferences.get("ETHUSDT") ?? null;
|
|
4181
|
-
const btcMarketCapUsd =
|
|
4182
|
-
const ethMarketCapUsd =
|
|
4183
|
-
const previousBtcMarketCapUsd =
|
|
4381
|
+
const btcMarketCapUsd = toFiniteNumberOrNull3(btcRow?.marketCapUsd);
|
|
4382
|
+
const ethMarketCapUsd = toFiniteNumberOrNull3(ethRow?.marketCapUsd);
|
|
4383
|
+
const previousBtcMarketCapUsd = toFiniteNumberOrNull3(
|
|
4184
4384
|
previousBtcRow?.marketCapUsd
|
|
4185
4385
|
);
|
|
4186
|
-
const previousEthMarketCapUsd =
|
|
4386
|
+
const previousEthMarketCapUsd = toFiniteNumberOrNull3(
|
|
4187
4387
|
previousEthRow?.marketCapUsd
|
|
4188
4388
|
);
|
|
4189
4389
|
const ethBtcMarketCapRatio = safeDivide(ethMarketCapUsd, btcMarketCapUsd);
|
|
@@ -4192,16 +4392,16 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4192
4392
|
previousBtcMarketCapUsd
|
|
4193
4393
|
);
|
|
4194
4394
|
const ethBtcMarketCapRatioChange24hPct = ethBtcMarketCapRatio != null && previousEthBtcMarketCapRatio != null && previousEthBtcMarketCapRatio > 0 ? (ethBtcMarketCapRatio - previousEthBtcMarketCapRatio) / previousEthBtcMarketCapRatio : null;
|
|
4195
|
-
const btcVolumeUsd =
|
|
4196
|
-
const ethVolumeUsd =
|
|
4395
|
+
const btcVolumeUsd = toFiniteNumberOrNull3(btcRow?.volumeUsd);
|
|
4396
|
+
const ethVolumeUsd = toFiniteNumberOrNull3(ethRow?.volumeUsd);
|
|
4197
4397
|
const referenceStale = btcRow?.stale === true || ethRow?.stale === true || !btcRow || !ethRow;
|
|
4198
|
-
const btcDominanceChange24hPct =
|
|
4398
|
+
const btcDominanceChange24hPct = toFiniteNumberOrNull3(
|
|
4199
4399
|
globalRow?.btcDominanceChange24hPct
|
|
4200
4400
|
);
|
|
4201
|
-
const altMarketCapChange24hPct =
|
|
4401
|
+
const altMarketCapChange24hPct = toFiniteNumberOrNull3(
|
|
4202
4402
|
globalRow?.altMarketCapChange24hPct
|
|
4203
4403
|
);
|
|
4204
|
-
const altVolumeChange24hPct =
|
|
4404
|
+
const altVolumeChange24hPct = toFiniteNumberOrNull3(
|
|
4205
4405
|
globalRow?.altVolumeChange24hPct
|
|
4206
4406
|
);
|
|
4207
4407
|
const altLiquidityRegime = globalRow ? toAltLiquidityRegime({
|
|
@@ -4212,19 +4412,19 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4212
4412
|
}) : "unknown";
|
|
4213
4413
|
const exchangeLiquidityRegime = exchangeLiquidityRow ? toExchangeLiquidityRegime({
|
|
4214
4414
|
stale: exchangeLiquidityRow.stale,
|
|
4215
|
-
totalVolumeChange24hPct:
|
|
4415
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
4216
4416
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
4217
4417
|
),
|
|
4218
4418
|
fallback: exchangeLiquidityRow.liquidityRegime ?? "unknown"
|
|
4219
4419
|
}) : "unknown";
|
|
4220
4420
|
const cmc100Row = indexRows.get("cmc100") ?? null;
|
|
4221
4421
|
const cmc20Row = indexRows.get("cmc20") ?? null;
|
|
4222
|
-
const cmc100Value =
|
|
4223
|
-
const cmc20Value =
|
|
4224
|
-
const cmc100Change24hPct =
|
|
4422
|
+
const cmc100Value = toFiniteNumberOrNull3(cmc100Row?.value);
|
|
4423
|
+
const cmc20Value = toFiniteNumberOrNull3(cmc20Row?.value);
|
|
4424
|
+
const cmc100Change24hPct = toFiniteNumberOrNull3(
|
|
4225
4425
|
cmc100Row?.valueChange24hPct
|
|
4226
4426
|
);
|
|
4227
|
-
const cmc20Change24hPct =
|
|
4427
|
+
const cmc20Change24hPct = toFiniteNumberOrNull3(cmc20Row?.valueChange24hPct);
|
|
4228
4428
|
const cmc20ToCmc100Ratio = safeDivide(cmc20Value, cmc100Value);
|
|
4229
4429
|
const cmc20ToCmc100RatioChange24hPct = cmc20Change24hPct != null && cmc100Change24hPct != null ? (1 + cmc20Change24hPct) / (1 + cmc100Change24hPct) - 1 : null;
|
|
4230
4430
|
const indexStale = cmc100Row?.stale === true || cmc20Row?.stale === true || !cmc100Row || !cmc20Row;
|
|
@@ -4248,41 +4448,41 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4248
4448
|
asOfTs: globalRow.ts.getTime(),
|
|
4249
4449
|
ageMs: globalRow.ageMs,
|
|
4250
4450
|
stale: globalRow.stale,
|
|
4251
|
-
totalMarketCapUsd:
|
|
4451
|
+
totalMarketCapUsd: toFiniteNumberOrNull3(
|
|
4252
4452
|
globalRow.totalMarketCapUsd
|
|
4253
4453
|
),
|
|
4254
|
-
totalVolumeUsd:
|
|
4454
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
4255
4455
|
globalRow.totalVolumeUsd
|
|
4256
4456
|
),
|
|
4257
|
-
totalVolumeReportedUsd:
|
|
4457
|
+
totalVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
4258
4458
|
globalRow.totalVolumeReportedUsd
|
|
4259
4459
|
),
|
|
4260
|
-
altMarketCapUsd:
|
|
4460
|
+
altMarketCapUsd: toFiniteNumberOrNull3(
|
|
4261
4461
|
globalRow.altMarketCapUsd
|
|
4262
4462
|
),
|
|
4263
|
-
altVolumeUsd:
|
|
4264
|
-
altVolumeReportedUsd:
|
|
4463
|
+
altVolumeUsd: toFiniteNumberOrNull3(globalRow.altVolumeUsd),
|
|
4464
|
+
altVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
4265
4465
|
globalRow.altVolumeReportedUsd
|
|
4266
4466
|
),
|
|
4267
|
-
btcDominancePct:
|
|
4467
|
+
btcDominancePct: toFiniteNumberOrNull3(
|
|
4268
4468
|
globalRow.btcDominancePct
|
|
4269
4469
|
),
|
|
4270
|
-
ethDominancePct:
|
|
4470
|
+
ethDominancePct: toFiniteNumberOrNull3(
|
|
4271
4471
|
globalRow.ethDominancePct
|
|
4272
4472
|
),
|
|
4273
4473
|
btcDominanceChange24hPct,
|
|
4274
|
-
ethDominanceChange24hPct:
|
|
4474
|
+
ethDominanceChange24hPct: toFiniteNumberOrNull3(
|
|
4275
4475
|
globalRow.ethDominanceChange24hPct
|
|
4276
4476
|
),
|
|
4277
4477
|
altMarketCapChange24hPct,
|
|
4278
4478
|
altVolumeChange24hPct,
|
|
4279
|
-
activeCryptocurrencies:
|
|
4479
|
+
activeCryptocurrencies: toFiniteNumberOrNull3(
|
|
4280
4480
|
globalRow.activeCryptocurrencies
|
|
4281
4481
|
),
|
|
4282
|
-
activeExchanges:
|
|
4482
|
+
activeExchanges: toFiniteNumberOrNull3(
|
|
4283
4483
|
globalRow.activeExchanges
|
|
4284
4484
|
),
|
|
4285
|
-
activeMarketPairs:
|
|
4485
|
+
activeMarketPairs: toFiniteNumberOrNull3(
|
|
4286
4486
|
globalRow.activeMarketPairs
|
|
4287
4487
|
),
|
|
4288
4488
|
altLiquidityRegime
|
|
@@ -4327,22 +4527,22 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4327
4527
|
asOfTs: exchangeLiquidityRow.ts.getTime(),
|
|
4328
4528
|
ageMs: exchangeLiquidityRow.ageMs,
|
|
4329
4529
|
stale: exchangeLiquidityRow.stale,
|
|
4330
|
-
exchangesCount:
|
|
4530
|
+
exchangesCount: toFiniteNumberOrNull3(
|
|
4331
4531
|
exchangeLiquidityRow.exchangesCount
|
|
4332
4532
|
),
|
|
4333
|
-
totalVolumeUsd:
|
|
4533
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
4334
4534
|
exchangeLiquidityRow.totalVolumeUsd
|
|
4335
4535
|
),
|
|
4336
|
-
totalVolumeChange24hPct:
|
|
4536
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
4337
4537
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
4338
4538
|
),
|
|
4339
|
-
binanceVolumeUsd:
|
|
4539
|
+
binanceVolumeUsd: toFiniteNumberOrNull3(
|
|
4340
4540
|
exchangeLiquidityRow.binanceVolumeUsd
|
|
4341
4541
|
),
|
|
4342
|
-
binanceVolumeShare:
|
|
4542
|
+
binanceVolumeShare: toFiniteNumberOrNull3(
|
|
4343
4543
|
exchangeLiquidityRow.binanceVolumeShare
|
|
4344
4544
|
),
|
|
4345
|
-
topExchangeVolumeShare:
|
|
4545
|
+
topExchangeVolumeShare: toFiniteNumberOrNull3(
|
|
4346
4546
|
exchangeLiquidityRow.topExchangeVolumeShare
|
|
4347
4547
|
),
|
|
4348
4548
|
liquidityRegime: exchangeLiquidityRegime
|
|
@@ -4355,11 +4555,11 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4355
4555
|
asOfTs: fearGreedRow.ts.getTime(),
|
|
4356
4556
|
ageMs: fearGreedRow.ageMs,
|
|
4357
4557
|
stale: fearGreedRow.stale,
|
|
4358
|
-
value:
|
|
4359
|
-
valueChange24h:
|
|
4558
|
+
value: toFiniteNumberOrNull3(fearGreedRow.value),
|
|
4559
|
+
valueChange24h: toFiniteNumberOrNull3(
|
|
4360
4560
|
fearGreedRow.valueChange24h
|
|
4361
4561
|
),
|
|
4362
|
-
valueChange7d:
|
|
4562
|
+
valueChange7d: toFiniteNumberOrNull3(
|
|
4363
4563
|
fearGreedRow.valueChange7d
|
|
4364
4564
|
),
|
|
4365
4565
|
classification: fearGreedRow.classification ?? "Unknown",
|
|
@@ -4379,13 +4579,13 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4379
4579
|
cmc100Value,
|
|
4380
4580
|
cmc100Change24hPct,
|
|
4381
4581
|
cmc100TopConstituentSymbol: cmc100Row?.topConstituentSymbol ?? null,
|
|
4382
|
-
cmc100TopConstituentWeightPct:
|
|
4582
|
+
cmc100TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
4383
4583
|
cmc100Row?.topConstituentWeightPct
|
|
4384
4584
|
),
|
|
4385
4585
|
cmc20Value,
|
|
4386
4586
|
cmc20Change24hPct,
|
|
4387
4587
|
cmc20TopConstituentSymbol: cmc20Row?.topConstituentSymbol ?? null,
|
|
4388
|
-
cmc20TopConstituentWeightPct:
|
|
4588
|
+
cmc20TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
4389
4589
|
cmc20Row?.topConstituentWeightPct
|
|
4390
4590
|
),
|
|
4391
4591
|
cmc20ToCmc100Ratio,
|
|
@@ -4403,7 +4603,7 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4403
4603
|
throw error;
|
|
4404
4604
|
}
|
|
4405
4605
|
coinMarketCapContextUnavailable = true;
|
|
4406
|
-
|
|
4606
|
+
import_logger5.logger.warn(
|
|
4407
4607
|
"CoinMarketCap context disabled after Timescale read failure: %s",
|
|
4408
4608
|
String(error)
|
|
4409
4609
|
);
|
|
@@ -4417,7 +4617,7 @@ var import_data2 = require("@tradejs/core/data");
|
|
|
4417
4617
|
var import_strategies3 = require("@tradejs/core/strategies");
|
|
4418
4618
|
var import_constants2 = require("@tradejs/core/constants");
|
|
4419
4619
|
var import_derivatives = require("@tradejs/infra/timescale/derivatives");
|
|
4420
|
-
var
|
|
4620
|
+
var import_logger6 = require("@tradejs/infra/logger");
|
|
4421
4621
|
var STORED_INTERVALS = ["15m", "1h"];
|
|
4422
4622
|
var CONTEXT_INTERVALS = ["15m", "1h"];
|
|
4423
4623
|
var DEFAULT_LOOKBACK_HOURS = 48;
|
|
@@ -4472,7 +4672,7 @@ var resolvePrimaryReferenceSymbol2 = () => PRIMARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
|
4472
4672
|
var resolveSecondaryReferenceSymbol = () => SECONDARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
4473
4673
|
var getPrimaryIntervalContext = (context) => context?.intervals["15m"] ?? context?.intervals["1h"] ?? null;
|
|
4474
4674
|
var hasDerivativesSymbolData = (context) => Object.keys(context.intervals).length > 0 && !context.summary.riskFlags.includes("missing_derivatives");
|
|
4475
|
-
var
|
|
4675
|
+
var toFiniteNumberOrNull4 = (value) => {
|
|
4476
4676
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
4477
4677
|
if (typeof value === "string" && value.trim()) {
|
|
4478
4678
|
const parsed = Number(value);
|
|
@@ -4486,8 +4686,8 @@ var roundNullable = (value, digits = 4) => {
|
|
|
4486
4686
|
return Math.round(value * multiplier) / multiplier;
|
|
4487
4687
|
};
|
|
4488
4688
|
var deltaNullable = (targetValue, referenceValue) => {
|
|
4489
|
-
const target =
|
|
4490
|
-
const reference =
|
|
4689
|
+
const target = toFiniteNumberOrNull4(targetValue);
|
|
4690
|
+
const reference = toFiniteNumberOrNull4(referenceValue);
|
|
4491
4691
|
return target == null || reference == null ? null : roundNullable(target - reference);
|
|
4492
4692
|
};
|
|
4493
4693
|
var buildTargetDerivedContext = (params) => {
|
|
@@ -4649,7 +4849,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
4649
4849
|
throw error;
|
|
4650
4850
|
}
|
|
4651
4851
|
derivativesContextUnavailable = true;
|
|
4652
|
-
|
|
4852
|
+
import_logger6.logger.warn(
|
|
4653
4853
|
"Derivatives context disabled after Timescale read failure: %s",
|
|
4654
4854
|
String(error)
|
|
4655
4855
|
);
|
|
@@ -4661,7 +4861,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
4661
4861
|
var import_data3 = require("@tradejs/core/data");
|
|
4662
4862
|
var import_strategies4 = require("@tradejs/core/strategies");
|
|
4663
4863
|
var import_hyperliquidWhales2 = require("@tradejs/infra/timescale/hyperliquidWhales");
|
|
4664
|
-
var
|
|
4864
|
+
var import_logger7 = require("@tradejs/infra/logger");
|
|
4665
4865
|
|
|
4666
4866
|
// src/hyperliquidWhaleUniverse.ts
|
|
4667
4867
|
var import_node_crypto3 = require("crypto");
|
|
@@ -5181,7 +5381,7 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
5181
5381
|
throw error;
|
|
5182
5382
|
}
|
|
5183
5383
|
hyperliquidWhaleContextUnavailable = true;
|
|
5184
|
-
|
|
5384
|
+
import_logger7.logger.warn(
|
|
5185
5385
|
"Hyperliquid whale context disabled after Timescale read failure: %s",
|
|
5186
5386
|
String(error)
|
|
5187
5387
|
);
|
|
@@ -5264,7 +5464,7 @@ var runMarketContextStage = async ({
|
|
|
5264
5464
|
elapsedMs: Date.now() - startedAt
|
|
5265
5465
|
};
|
|
5266
5466
|
if (status === "timed_out") {
|
|
5267
|
-
|
|
5467
|
+
import_logger8.logger.warn(
|
|
5268
5468
|
"Market context stage timed out: %s after %sms",
|
|
5269
5469
|
stage,
|
|
5270
5470
|
result.elapsedMs
|
|
@@ -5454,79 +5654,10 @@ var enrichSignalWithAi = async ({
|
|
|
5454
5654
|
signal.aiAnalysis = analysis;
|
|
5455
5655
|
return resolveAiQuality(analysis, direction);
|
|
5456
5656
|
} catch (err) {
|
|
5457
|
-
|
|
5657
|
+
import_logger9.logger.error("AI analysis error: %s %s", symbol, formatAiError(err));
|
|
5458
5658
|
}
|
|
5459
5659
|
return void 0;
|
|
5460
5660
|
};
|
|
5461
|
-
var toFiniteNumberOrNull4 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
5462
|
-
var getOrderArrivalSnapshot = async ({
|
|
5463
|
-
connector,
|
|
5464
|
-
symbol
|
|
5465
|
-
}) => {
|
|
5466
|
-
if (typeof connector.getTopOfBookTicker !== "function") {
|
|
5467
|
-
return {
|
|
5468
|
-
arrivalSnapshotTime: Date.now(),
|
|
5469
|
-
arrivalSource: "unavailable",
|
|
5470
|
-
bid: null,
|
|
5471
|
-
ask: null,
|
|
5472
|
-
arrivalMid: null,
|
|
5473
|
-
spreadBps: null
|
|
5474
|
-
};
|
|
5475
|
-
}
|
|
5476
|
-
try {
|
|
5477
|
-
const ticker = await connector.getTopOfBookTicker(symbol);
|
|
5478
|
-
const arrivalSnapshotTime = toFiniteNumberOrNull4(ticker?.timestamp);
|
|
5479
|
-
const bid = toFiniteNumberOrNull4(ticker?.bidPrice);
|
|
5480
|
-
const ask = toFiniteNumberOrNull4(ticker?.askPrice);
|
|
5481
|
-
const arrivalMid = bid != null && ask != null ? (bid + ask) / 2 : null;
|
|
5482
|
-
const spreadBps = bid != null && ask != null && arrivalMid != null && arrivalMid > 0 ? (ask - bid) / arrivalMid * 1e4 : null;
|
|
5483
|
-
return {
|
|
5484
|
-
arrivalSnapshotTime: arrivalSnapshotTime ?? Date.now(),
|
|
5485
|
-
arrivalSource: "top_of_book",
|
|
5486
|
-
bid,
|
|
5487
|
-
ask,
|
|
5488
|
-
arrivalMid,
|
|
5489
|
-
spreadBps
|
|
5490
|
-
};
|
|
5491
|
-
} catch (error) {
|
|
5492
|
-
import_logger8.logger.warn(
|
|
5493
|
-
"runtime order arrival snapshot failed: %s %s",
|
|
5494
|
-
symbol,
|
|
5495
|
-
error?.message || String(error)
|
|
5496
|
-
);
|
|
5497
|
-
return {
|
|
5498
|
-
arrivalSnapshotTime: Date.now(),
|
|
5499
|
-
arrivalSource: "top_of_book_error",
|
|
5500
|
-
bid: null,
|
|
5501
|
-
ask: null,
|
|
5502
|
-
arrivalMid: null,
|
|
5503
|
-
spreadBps: null
|
|
5504
|
-
};
|
|
5505
|
-
}
|
|
5506
|
-
};
|
|
5507
|
-
var validateEntryProtectionAtArrival = ({
|
|
5508
|
-
direction,
|
|
5509
|
-
signalPrice,
|
|
5510
|
-
bid,
|
|
5511
|
-
ask,
|
|
5512
|
-
arrivalMid,
|
|
5513
|
-
takeProfits,
|
|
5514
|
-
stopLossPrice
|
|
5515
|
-
}) => {
|
|
5516
|
-
const entryReference = direction === "LONG" ? ask ?? arrivalMid ?? signalPrice : bid ?? arrivalMid ?? signalPrice;
|
|
5517
|
-
const stopReference = direction === "LONG" ? bid ?? arrivalMid ?? signalPrice : ask ?? arrivalMid ?? signalPrice;
|
|
5518
|
-
if (!Number.isFinite(entryReference) || entryReference <= 0 || !Number.isFinite(stopReference) || stopReference <= 0) {
|
|
5519
|
-
throw new Error("INVALID_ENTRY_REFERENCE_AT_ARRIVAL");
|
|
5520
|
-
}
|
|
5521
|
-
for (const takeProfit of takeProfits) {
|
|
5522
|
-
if (!Number.isFinite(takeProfit.price) || takeProfit.price <= 0 || (direction === "LONG" ? takeProfit.price <= entryReference : takeProfit.price >= entryReference)) {
|
|
5523
|
-
throw new Error("TAKE_PROFIT_CROSSED_BEFORE_ENTRY");
|
|
5524
|
-
}
|
|
5525
|
-
}
|
|
5526
|
-
if (stopLossPrice != null && (!Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || (direction === "LONG" ? stopLossPrice >= stopReference : stopLossPrice <= stopReference))) {
|
|
5527
|
-
throw new Error("STOP_LOSS_CROSSED_BEFORE_ENTRY");
|
|
5528
|
-
}
|
|
5529
|
-
};
|
|
5530
5661
|
var resolveRuntimeTelemetryQuality = ({
|
|
5531
5662
|
signalClosePrice,
|
|
5532
5663
|
arrivalMid,
|
|
@@ -5603,7 +5734,8 @@ var executeEntryOrder = async ({
|
|
|
5603
5734
|
signal,
|
|
5604
5735
|
beforePlaceOrder,
|
|
5605
5736
|
recordRuntimeTrade = true,
|
|
5606
|
-
leverage
|
|
5737
|
+
leverage,
|
|
5738
|
+
executionLimits
|
|
5607
5739
|
}) => {
|
|
5608
5740
|
await beforePlaceOrder?.();
|
|
5609
5741
|
const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
|
|
@@ -5614,23 +5746,57 @@ var executeEntryOrder = async ({
|
|
|
5614
5746
|
signal.orderQty = qty;
|
|
5615
5747
|
signal.orderValue = qty * currentPrice;
|
|
5616
5748
|
signal.orderFailureReason = void 0;
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5749
|
+
let arrivalSnapshot = {
|
|
5750
|
+
arrivalSnapshotTime: Date.now(),
|
|
5751
|
+
arrivalSource: "unavailable",
|
|
5752
|
+
bid: null,
|
|
5753
|
+
ask: null,
|
|
5754
|
+
arrivalMid: null,
|
|
5755
|
+
spreadBps: null
|
|
5756
|
+
};
|
|
5757
|
+
let orderSubmitTime = Date.now();
|
|
5758
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
5759
|
+
connector,
|
|
5760
|
+
symbol,
|
|
5761
|
+
direction,
|
|
5762
|
+
takeProfits,
|
|
5763
|
+
stopLossPrice,
|
|
5764
|
+
limits: executionLimits,
|
|
5765
|
+
onValidated: (snapshot2, economics2) => {
|
|
5766
|
+
arrivalSnapshot = snapshot2;
|
|
5767
|
+
orderSubmitTime = Date.now();
|
|
5768
|
+
if (executionLimits?.maxLossValue != null) {
|
|
5769
|
+
signal.riskDecision = {
|
|
5770
|
+
stage: "order_plan",
|
|
5771
|
+
status: "approved",
|
|
5772
|
+
reason: "PLANNED_LOSS_WITHIN_MAX_LOSS_VALUE",
|
|
5773
|
+
plannedLossValue: economics2.plannedLossValue,
|
|
5774
|
+
maxLossValue: executionLimits.maxLossValue,
|
|
5775
|
+
limitUtilization: economics2.plannedLossValue / executionLimits.maxLossValue,
|
|
5776
|
+
enforced: true
|
|
5777
|
+
};
|
|
5778
|
+
}
|
|
5779
|
+
}
|
|
5780
|
+
});
|
|
5781
|
+
if (!beforeSubmit) {
|
|
5782
|
+
arrivalSnapshot = await getOrderArrivalSnapshot({ connector, symbol });
|
|
5783
|
+
try {
|
|
5784
|
+
validateEntryProtectionAtArrival({
|
|
5785
|
+
direction,
|
|
5786
|
+
signalPrice: currentPrice,
|
|
5787
|
+
bid: arrivalSnapshot.bid,
|
|
5788
|
+
ask: arrivalSnapshot.ask,
|
|
5789
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
5790
|
+
takeProfits,
|
|
5791
|
+
stopLossPrice
|
|
5792
|
+
});
|
|
5793
|
+
} catch (error) {
|
|
5794
|
+
signal.orderStatus = "failed";
|
|
5795
|
+
signal.orderFailureReason = error.message;
|
|
5796
|
+
throw error;
|
|
5797
|
+
}
|
|
5798
|
+
orderSubmitTime = Date.now();
|
|
5632
5799
|
}
|
|
5633
|
-
const orderSubmitTime = Date.now();
|
|
5634
5800
|
const orderPlaced = await connector.placeOrder({
|
|
5635
5801
|
symbol,
|
|
5636
5802
|
qty,
|
|
@@ -5641,7 +5807,8 @@ var executeEntryOrder = async ({
|
|
|
5641
5807
|
direction,
|
|
5642
5808
|
...typeof leverage === "number" && Number.isFinite(leverage) ? { leverage } : {},
|
|
5643
5809
|
orderId,
|
|
5644
|
-
signal
|
|
5810
|
+
signal,
|
|
5811
|
+
...beforeSubmit ? { beforeSubmit } : {}
|
|
5645
5812
|
});
|
|
5646
5813
|
const orderAckTime = Date.now();
|
|
5647
5814
|
const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
|
|
@@ -6470,7 +6637,7 @@ var canUseSharedReplayState = ({
|
|
|
6470
6637
|
}) => (env === "BACKTEST" || env === "PARITY") && Boolean(sharedReplayKey);
|
|
6471
6638
|
|
|
6472
6639
|
// src/strategy/runtimeExecution.ts
|
|
6473
|
-
var
|
|
6640
|
+
var import_logger10 = require("@tradejs/infra/logger");
|
|
6474
6641
|
var import_types = require("@tradejs/types");
|
|
6475
6642
|
var buildExitOrderSignal = ({
|
|
6476
6643
|
strategyName,
|
|
@@ -6522,7 +6689,7 @@ var handleExitDecision = async ({
|
|
|
6522
6689
|
deploymentId: connector.deploymentId
|
|
6523
6690
|
});
|
|
6524
6691
|
if (!activeTrade) {
|
|
6525
|
-
|
|
6692
|
+
import_logger10.logger.warn(
|
|
6526
6693
|
"[%s] blocked closePosition for untracked runtime position: %s",
|
|
6527
6694
|
strategyName ?? "unknown",
|
|
6528
6695
|
symbol
|
|
@@ -6530,7 +6697,7 @@ var handleExitDecision = async ({
|
|
|
6530
6697
|
return "CLOSE_BLOCKED_BY_UNTRACKED_POSITION";
|
|
6531
6698
|
}
|
|
6532
6699
|
if (!strategyName || activeTrade.strategy !== strategyName) {
|
|
6533
|
-
|
|
6700
|
+
import_logger10.logger.warn(
|
|
6534
6701
|
"[%s] blocked closePosition for foreign runtime position: %s ownedBy=%s",
|
|
6535
6702
|
strategyName ?? "unknown",
|
|
6536
6703
|
symbol,
|
|
@@ -6583,7 +6750,7 @@ var handleExitDecision = async ({
|
|
|
6583
6750
|
exitType: closedTrade?.exitType ?? "exit"
|
|
6584
6751
|
});
|
|
6585
6752
|
} catch (notificationError) {
|
|
6586
|
-
|
|
6753
|
+
import_logger10.logger.error(
|
|
6587
6754
|
"runtime close notification error: %s %s",
|
|
6588
6755
|
symbol,
|
|
6589
6756
|
notificationError
|
|
@@ -6597,7 +6764,7 @@ var handleExitDecision = async ({
|
|
|
6597
6764
|
decision,
|
|
6598
6765
|
market
|
|
6599
6766
|
});
|
|
6600
|
-
|
|
6767
|
+
import_logger10.logger.error("close order error: %s %s", symbol, err);
|
|
6601
6768
|
return "ORDER_ERROR";
|
|
6602
6769
|
}
|
|
6603
6770
|
return decision.code;
|
|
@@ -6624,7 +6791,7 @@ var handleProtectDecision = async ({
|
|
|
6624
6791
|
decision,
|
|
6625
6792
|
market
|
|
6626
6793
|
});
|
|
6627
|
-
|
|
6794
|
+
import_logger10.logger.error("protect position error: %s %s", symbol, err);
|
|
6628
6795
|
return "ORDER_ERROR";
|
|
6629
6796
|
}
|
|
6630
6797
|
return decision.code;
|
|
@@ -6690,7 +6857,11 @@ var executeEntryDecision = async ({
|
|
|
6690
6857
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {},
|
|
6691
6858
|
signal,
|
|
6692
6859
|
beforePlaceOrder,
|
|
6693
|
-
recordRuntimeTrade: recordRuntimeJournal
|
|
6860
|
+
recordRuntimeTrade: recordRuntimeJournal,
|
|
6861
|
+
executionLimits: resolveEntryExecutionLimits(
|
|
6862
|
+
hookCtx.strategyConfig,
|
|
6863
|
+
decision.entryContext.direction
|
|
6864
|
+
)
|
|
6694
6865
|
});
|
|
6695
6866
|
await invokeStageHooks(
|
|
6696
6867
|
"afterPlaceOrder",
|
|
@@ -6712,19 +6883,32 @@ var executeEntryDecision = async ({
|
|
|
6712
6883
|
return signal;
|
|
6713
6884
|
}
|
|
6714
6885
|
await beforePlaceOrder();
|
|
6715
|
-
const
|
|
6886
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
6716
6887
|
connector,
|
|
6717
|
-
symbol
|
|
6718
|
-
});
|
|
6719
|
-
validateEntryProtectionAtArrival({
|
|
6888
|
+
symbol,
|
|
6720
6889
|
direction: decision.entryContext.direction,
|
|
6721
|
-
signalPrice: decision.entryContext.prices.currentPrice,
|
|
6722
|
-
bid: arrivalSnapshot.bid,
|
|
6723
|
-
ask: arrivalSnapshot.ask,
|
|
6724
|
-
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
6725
6890
|
takeProfits: decision.orderPlan.takeProfits,
|
|
6726
|
-
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
6891
|
+
stopLossPrice: decision.orderPlan.stopLossPrice,
|
|
6892
|
+
limits: resolveEntryExecutionLimits(
|
|
6893
|
+
hookCtx.strategyConfig,
|
|
6894
|
+
decision.entryContext.direction
|
|
6895
|
+
)
|
|
6727
6896
|
});
|
|
6897
|
+
if (!beforeSubmit) {
|
|
6898
|
+
const arrivalSnapshot = await getOrderArrivalSnapshot({
|
|
6899
|
+
connector,
|
|
6900
|
+
symbol
|
|
6901
|
+
});
|
|
6902
|
+
validateEntryProtectionAtArrival({
|
|
6903
|
+
direction: decision.entryContext.direction,
|
|
6904
|
+
signalPrice: decision.entryContext.prices.currentPrice,
|
|
6905
|
+
bid: arrivalSnapshot.bid,
|
|
6906
|
+
ask: arrivalSnapshot.ask,
|
|
6907
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
6908
|
+
takeProfits: decision.orderPlan.takeProfits,
|
|
6909
|
+
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
6910
|
+
});
|
|
6911
|
+
}
|
|
6728
6912
|
const orderPlaced = await connector.placeOrder({
|
|
6729
6913
|
symbol,
|
|
6730
6914
|
qty: decision.orderPlan.qty,
|
|
@@ -6732,6 +6916,7 @@ var executeEntryDecision = async ({
|
|
|
6732
6916
|
timestamp: decision.entryContext.timestamp,
|
|
6733
6917
|
direction: decision.entryContext.direction,
|
|
6734
6918
|
positionIntent: decision.orderPlan.positionIntent,
|
|
6919
|
+
...beforeSubmit ? { beforeSubmit } : {},
|
|
6735
6920
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {}
|
|
6736
6921
|
});
|
|
6737
6922
|
if (!orderPlaced) {
|
|
@@ -6773,6 +6958,23 @@ var executeEntryDecision = async ({
|
|
|
6773
6958
|
{ decision, entry, market }
|
|
6774
6959
|
);
|
|
6775
6960
|
} catch (err) {
|
|
6961
|
+
if (err instanceof EntrySubmissionValidationError) {
|
|
6962
|
+
if (signal) {
|
|
6963
|
+
signal.orderStatus = "skipped";
|
|
6964
|
+
signal.orderSkipReason = err.message;
|
|
6965
|
+
signal.allocatorDecision = {
|
|
6966
|
+
stage: "runtime_admission",
|
|
6967
|
+
status: "rejected",
|
|
6968
|
+
reason: err.message
|
|
6969
|
+
};
|
|
6970
|
+
}
|
|
6971
|
+
import_logger10.logger.warn(
|
|
6972
|
+
"entry rejected before submission: %s %s",
|
|
6973
|
+
symbol,
|
|
6974
|
+
err.message
|
|
6975
|
+
);
|
|
6976
|
+
return signal ?? err.message;
|
|
6977
|
+
}
|
|
6776
6978
|
if (signal) {
|
|
6777
6979
|
signal.orderStatus = "failed";
|
|
6778
6980
|
if (typeof signal.orderFailureReason !== "string" || !signal.orderFailureReason.trim()) {
|
|
@@ -6787,9 +6989,9 @@ var executeEntryDecision = async ({
|
|
|
6787
6989
|
market
|
|
6788
6990
|
});
|
|
6789
6991
|
if (err?.message === import_types.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY) {
|
|
6790
|
-
|
|
6992
|
+
import_logger10.logger.warn("order warning: %s %s", symbol, err);
|
|
6791
6993
|
} else {
|
|
6792
|
-
|
|
6994
|
+
import_logger10.logger.error("order error: %s %s", symbol, err);
|
|
6793
6995
|
}
|
|
6794
6996
|
return signal ?? "ORDER_ERROR";
|
|
6795
6997
|
}
|
|
@@ -6954,7 +7156,7 @@ var createStrategyRuntime = ({
|
|
|
6954
7156
|
try {
|
|
6955
7157
|
await projectHook(errorParams);
|
|
6956
7158
|
} catch (hookError) {
|
|
6957
|
-
|
|
7159
|
+
import_logger11.logger.error(
|
|
6958
7160
|
"project hook onRuntimeError failed: %s %s",
|
|
6959
7161
|
strategyName,
|
|
6960
7162
|
hookError
|
|
@@ -6968,7 +7170,7 @@ var createStrategyRuntime = ({
|
|
|
6968
7170
|
try {
|
|
6969
7171
|
await onRuntimeError(errorParams);
|
|
6970
7172
|
} catch (hookError) {
|
|
6971
|
-
|
|
7173
|
+
import_logger11.logger.error(
|
|
6972
7174
|
"runtime hook onRuntimeError failed: %s %s",
|
|
6973
7175
|
strategyName,
|
|
6974
7176
|
hookError
|
|
@@ -6982,7 +7184,7 @@ var createStrategyRuntime = ({
|
|
|
6982
7184
|
try {
|
|
6983
7185
|
return await hook(params);
|
|
6984
7186
|
} catch (error) {
|
|
6985
|
-
|
|
7187
|
+
import_logger11.logger.error(
|
|
6986
7188
|
'strategy hook "%s" failed for %s: %s',
|
|
6987
7189
|
stage,
|
|
6988
7190
|
strategyName,
|