@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
|
@@ -1048,13 +1048,213 @@ var assessSignalWithJev = async ({
|
|
|
1048
1048
|
// src/strategyRuntime.ts
|
|
1049
1049
|
var import_constants5 = require("@tradejs/core/constants");
|
|
1050
1050
|
var import_strategies6 = require("@tradejs/core/strategies");
|
|
1051
|
-
var
|
|
1051
|
+
var import_logger11 = require("@tradejs/infra/logger");
|
|
1052
1052
|
|
|
1053
1053
|
// src/strategyHelpers/runtime.ts
|
|
1054
|
-
var
|
|
1054
|
+
var import_logger9 = require("@tradejs/infra/logger");
|
|
1055
1055
|
var import_constants3 = require("@tradejs/core/constants");
|
|
1056
1056
|
var import_ml2 = require("@tradejs/infra/ml");
|
|
1057
1057
|
|
|
1058
|
+
// src/strategy/entrySubmissionValidation.ts
|
|
1059
|
+
var import_logger = require("@tradejs/infra/logger");
|
|
1060
|
+
|
|
1061
|
+
// src/strategy/entryExecutionValidation.ts
|
|
1062
|
+
var EntrySubmissionValidationError = class extends Error {
|
|
1063
|
+
constructor() {
|
|
1064
|
+
super(...arguments);
|
|
1065
|
+
this.name = "EntrySubmissionValidationError";
|
|
1066
|
+
}
|
|
1067
|
+
};
|
|
1068
|
+
var resolveEntryExecutionLimits = (config, direction) => {
|
|
1069
|
+
const side = config[direction];
|
|
1070
|
+
const minimum = side && typeof side === "object" && "minRiskRatio" in side ? side.minRiskRatio : void 0;
|
|
1071
|
+
const maximum = config.MAX_LOSS_VALUE;
|
|
1072
|
+
if (minimum == null && maximum == null) return void 0;
|
|
1073
|
+
const minimumNetRiskRatio = minimum == null ? void 0 : Number(minimum);
|
|
1074
|
+
const maxLossValue = maximum == null ? void 0 : Number(maximum);
|
|
1075
|
+
const feeRate = Number(config.RISK_FEE_RATE ?? 0);
|
|
1076
|
+
const slippage = Number(config.RISK_SLIPPAGE_BPS ?? 0);
|
|
1077
|
+
const impact = Number(config.RISK_MARKET_IMPACT_BPS ?? 0);
|
|
1078
|
+
const slippageBps = slippage + impact;
|
|
1079
|
+
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)
|
|
1080
|
+
throw new Error("INVALID_ENTRY_EXECUTION_LIMITS");
|
|
1081
|
+
return { minimumNetRiskRatio, maxLossValue, feeRate, slippageBps };
|
|
1082
|
+
};
|
|
1083
|
+
var validateEntryExecution = ({
|
|
1084
|
+
direction,
|
|
1085
|
+
price,
|
|
1086
|
+
qty,
|
|
1087
|
+
stopLossPrice,
|
|
1088
|
+
takeProfitPrice,
|
|
1089
|
+
limits
|
|
1090
|
+
}) => {
|
|
1091
|
+
if (!Number.isFinite(price) || price <= 0 || !Number.isFinite(qty) || qty <= 0 || stopLossPrice == null || !Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || !Number.isFinite(takeProfitPrice) || takeProfitPrice <= 0)
|
|
1092
|
+
throw new EntrySubmissionValidationError(
|
|
1093
|
+
"ENTRY_ECONOMICS_INPUTS_UNAVAILABLE"
|
|
1094
|
+
);
|
|
1095
|
+
const side = direction === "LONG" ? 1 : -1;
|
|
1096
|
+
if (side * (price - stopLossPrice) <= 0 || side * (takeProfitPrice - price) <= 0)
|
|
1097
|
+
throw new EntrySubmissionValidationError(
|
|
1098
|
+
"INVALID_ENTRY_PROTECTION_AT_SUBMISSION"
|
|
1099
|
+
);
|
|
1100
|
+
const slippage = limits.slippageBps / 1e4;
|
|
1101
|
+
const cost = (value, sign) => value * slippage + value * (1 + sign * slippage) * limits.feeRate;
|
|
1102
|
+
const entryCost = cost(price, side);
|
|
1103
|
+
const lossPerUnit = Math.abs(price - stopLossPrice) + entryCost + cost(stopLossPrice, -side);
|
|
1104
|
+
const rewardPerUnit = Math.max(
|
|
1105
|
+
0,
|
|
1106
|
+
Math.abs(takeProfitPrice - price) - entryCost - cost(takeProfitPrice, -side)
|
|
1107
|
+
);
|
|
1108
|
+
const netRiskRatio = rewardPerUnit / lossPerUnit;
|
|
1109
|
+
const plannedLossValue = qty * lossPerUnit;
|
|
1110
|
+
if (!Number.isFinite(netRiskRatio) || !Number.isFinite(plannedLossValue))
|
|
1111
|
+
throw new EntrySubmissionValidationError(
|
|
1112
|
+
"ENTRY_ECONOMICS_INPUTS_UNAVAILABLE"
|
|
1113
|
+
);
|
|
1114
|
+
if (limits.minimumNetRiskRatio != null && netRiskRatio <= limits.minimumNetRiskRatio)
|
|
1115
|
+
throw new EntrySubmissionValidationError(
|
|
1116
|
+
"ENTRY_RR_BELOW_MIN_AT_SUBMISSION"
|
|
1117
|
+
);
|
|
1118
|
+
if (limits.maxLossValue != null && plannedLossValue > limits.maxLossValue * (1 + 1e-9))
|
|
1119
|
+
throw new EntrySubmissionValidationError(
|
|
1120
|
+
"ENTRY_RISK_EXCEEDS_MAX_LOSS_AT_SUBMISSION"
|
|
1121
|
+
);
|
|
1122
|
+
return { netRiskRatio, plannedLossValue };
|
|
1123
|
+
};
|
|
1124
|
+
|
|
1125
|
+
// src/strategy/entrySubmissionValidation.ts
|
|
1126
|
+
var MAX_ENTRY_QUOTE_AGE_MS = 5e3;
|
|
1127
|
+
var toFiniteNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1128
|
+
var getOrderArrivalSnapshot = async ({
|
|
1129
|
+
connector,
|
|
1130
|
+
symbol,
|
|
1131
|
+
requireTimestamp = false
|
|
1132
|
+
}) => {
|
|
1133
|
+
if (typeof connector.getTopOfBookTicker !== "function") {
|
|
1134
|
+
return {
|
|
1135
|
+
arrivalSnapshotTime: Date.now(),
|
|
1136
|
+
arrivalSource: "unavailable",
|
|
1137
|
+
bid: null,
|
|
1138
|
+
ask: null,
|
|
1139
|
+
arrivalMid: null,
|
|
1140
|
+
spreadBps: null
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
try {
|
|
1144
|
+
const ticker = await connector.getTopOfBookTicker(symbol);
|
|
1145
|
+
const arrivalSnapshotTime = toFiniteNumberOrNull(ticker?.timestamp);
|
|
1146
|
+
const timestampUnavailable = requireTimestamp && arrivalSnapshotTime == null;
|
|
1147
|
+
const bid = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.bidPrice);
|
|
1148
|
+
const ask = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.askPrice);
|
|
1149
|
+
const arrivalMid = bid != null && ask != null ? (bid + ask) / 2 : null;
|
|
1150
|
+
const spreadBps = bid != null && ask != null && arrivalMid != null && arrivalMid > 0 ? (ask - bid) / arrivalMid * 1e4 : null;
|
|
1151
|
+
return {
|
|
1152
|
+
arrivalSnapshotTime: arrivalSnapshotTime ?? Date.now(),
|
|
1153
|
+
arrivalSource: "top_of_book",
|
|
1154
|
+
bid,
|
|
1155
|
+
ask,
|
|
1156
|
+
arrivalMid,
|
|
1157
|
+
spreadBps
|
|
1158
|
+
};
|
|
1159
|
+
} catch (error) {
|
|
1160
|
+
import_logger.logger.warn(
|
|
1161
|
+
"runtime order arrival snapshot failed: %s %s",
|
|
1162
|
+
symbol,
|
|
1163
|
+
error?.message || String(error)
|
|
1164
|
+
);
|
|
1165
|
+
return {
|
|
1166
|
+
arrivalSnapshotTime: Date.now(),
|
|
1167
|
+
arrivalSource: "top_of_book_error",
|
|
1168
|
+
bid: null,
|
|
1169
|
+
ask: null,
|
|
1170
|
+
arrivalMid: null,
|
|
1171
|
+
spreadBps: null
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
};
|
|
1175
|
+
var validateEntryProtectionAtArrival = ({
|
|
1176
|
+
direction,
|
|
1177
|
+
signalPrice,
|
|
1178
|
+
bid,
|
|
1179
|
+
ask,
|
|
1180
|
+
arrivalMid,
|
|
1181
|
+
takeProfits,
|
|
1182
|
+
stopLossPrice
|
|
1183
|
+
}) => {
|
|
1184
|
+
const entryReference = direction === "LONG" ? ask ?? arrivalMid ?? signalPrice : bid ?? arrivalMid ?? signalPrice;
|
|
1185
|
+
const stopReference = direction === "LONG" ? bid ?? arrivalMid ?? signalPrice : ask ?? arrivalMid ?? signalPrice;
|
|
1186
|
+
if (!Number.isFinite(entryReference) || entryReference <= 0 || !Number.isFinite(stopReference) || stopReference <= 0) {
|
|
1187
|
+
throw new Error("INVALID_ENTRY_REFERENCE_AT_ARRIVAL");
|
|
1188
|
+
}
|
|
1189
|
+
for (const takeProfit of takeProfits) {
|
|
1190
|
+
if (!Number.isFinite(takeProfit.price) || takeProfit.price <= 0 || (direction === "LONG" ? takeProfit.price <= entryReference : takeProfit.price >= entryReference)) {
|
|
1191
|
+
throw new Error("TAKE_PROFIT_CROSSED_BEFORE_ENTRY");
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
if (stopLossPrice != null && (!Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || (direction === "LONG" ? stopLossPrice >= stopReference : stopLossPrice <= stopReference))) {
|
|
1195
|
+
throw new Error("STOP_LOSS_CROSSED_BEFORE_ENTRY");
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
1198
|
+
var createEntrySubmissionValidation = ({
|
|
1199
|
+
connector,
|
|
1200
|
+
symbol,
|
|
1201
|
+
direction,
|
|
1202
|
+
takeProfits,
|
|
1203
|
+
stopLossPrice,
|
|
1204
|
+
limits,
|
|
1205
|
+
onValidated
|
|
1206
|
+
}) => {
|
|
1207
|
+
if (!limits) return void 0;
|
|
1208
|
+
if (!connector.capabilities?.orderSubmissionValidation)
|
|
1209
|
+
throw new EntrySubmissionValidationError(
|
|
1210
|
+
"ENTRY_SUBMISSION_VALIDATION_UNSUPPORTED"
|
|
1211
|
+
);
|
|
1212
|
+
return async ({ qty, executionPrice }) => {
|
|
1213
|
+
const simulated = Reflect.get(connector, "__tradejsTestConnector") === true;
|
|
1214
|
+
const snapshot2 = simulated && executionPrice != null ? {
|
|
1215
|
+
arrivalSnapshotTime: Date.now(),
|
|
1216
|
+
arrivalSource: "simulation",
|
|
1217
|
+
bid: executionPrice,
|
|
1218
|
+
ask: executionPrice,
|
|
1219
|
+
arrivalMid: executionPrice,
|
|
1220
|
+
spreadBps: 0
|
|
1221
|
+
} : await getOrderArrivalSnapshot({
|
|
1222
|
+
connector,
|
|
1223
|
+
symbol,
|
|
1224
|
+
requireTimestamp: true
|
|
1225
|
+
});
|
|
1226
|
+
const age = Date.now() - snapshot2.arrivalSnapshotTime;
|
|
1227
|
+
if (snapshot2.bid == null || snapshot2.ask == null || !Number.isFinite(snapshot2.bid) || !Number.isFinite(snapshot2.ask) || snapshot2.bid <= 0 || snapshot2.ask < snapshot2.bid)
|
|
1228
|
+
throw new EntrySubmissionValidationError(
|
|
1229
|
+
"ENTRY_QUOTE_UNAVAILABLE_AT_SUBMISSION"
|
|
1230
|
+
);
|
|
1231
|
+
if (!Number.isFinite(age) || age < -MAX_ENTRY_QUOTE_AGE_MS || age > MAX_ENTRY_QUOTE_AGE_MS)
|
|
1232
|
+
throw new EntrySubmissionValidationError(
|
|
1233
|
+
"ENTRY_QUOTE_STALE_AT_SUBMISSION"
|
|
1234
|
+
);
|
|
1235
|
+
const price = direction === "LONG" ? snapshot2.ask : snapshot2.bid;
|
|
1236
|
+
validateEntryProtectionAtArrival({
|
|
1237
|
+
direction,
|
|
1238
|
+
signalPrice: price,
|
|
1239
|
+
bid: snapshot2.bid,
|
|
1240
|
+
ask: snapshot2.ask,
|
|
1241
|
+
arrivalMid: snapshot2.arrivalMid,
|
|
1242
|
+
takeProfits,
|
|
1243
|
+
stopLossPrice
|
|
1244
|
+
});
|
|
1245
|
+
const prices = takeProfits.map((tp) => tp.price);
|
|
1246
|
+
const economics2 = validateEntryExecution({
|
|
1247
|
+
direction,
|
|
1248
|
+
price,
|
|
1249
|
+
qty,
|
|
1250
|
+
stopLossPrice,
|
|
1251
|
+
takeProfitPrice: direction === "LONG" ? Math.max(...prices) : Math.min(...prices),
|
|
1252
|
+
limits
|
|
1253
|
+
});
|
|
1254
|
+
onValidated?.(snapshot2, economics2);
|
|
1255
|
+
};
|
|
1256
|
+
};
|
|
1257
|
+
|
|
1058
1258
|
// src/ai.ts
|
|
1059
1259
|
var import_aiLanguages2 = require("@tradejs/core/aiLanguages");
|
|
1060
1260
|
var import_redis = require("@tradejs/infra/redis");
|
|
@@ -1639,7 +1839,7 @@ var import_fs = __toESM(require("fs"));
|
|
|
1639
1839
|
var import_path = __toESM(require("path"));
|
|
1640
1840
|
var import_url = require("url");
|
|
1641
1841
|
var import_config = require("@tradejs/core/config");
|
|
1642
|
-
var
|
|
1842
|
+
var import_logger2 = require("@tradejs/infra/logger");
|
|
1643
1843
|
var CONFIG_FILE_NAMES = [
|
|
1644
1844
|
"tradejs.config.ts",
|
|
1645
1845
|
"tradejs.config.mts",
|
|
@@ -1906,11 +2106,11 @@ var loadTradejsConfig = async (cwd = getTradejsProjectCwd()) => {
|
|
|
1906
2106
|
cachedByCwd.set(cwd, config);
|
|
1907
2107
|
if (!announcedConfigFile.has(configFilePath)) {
|
|
1908
2108
|
announcedConfigFile.add(configFilePath);
|
|
1909
|
-
|
|
2109
|
+
import_logger2.logger.log("debug", "Loaded TradeJS config: %s", configFilePath);
|
|
1910
2110
|
}
|
|
1911
2111
|
return config;
|
|
1912
2112
|
} catch (error) {
|
|
1913
|
-
|
|
2113
|
+
import_logger2.logger.log(
|
|
1914
2114
|
"warn",
|
|
1915
2115
|
"Failed to load TradeJS config from %s: %s",
|
|
1916
2116
|
configFilePath,
|
|
@@ -2876,7 +3076,7 @@ var import_node_crypto = require("crypto");
|
|
|
2876
3076
|
var import_constants = require("@tradejs/core/constants");
|
|
2877
3077
|
var import_time = require("@tradejs/core/time");
|
|
2878
3078
|
var import_trade = require("@tradejs/core/trade");
|
|
2879
|
-
var
|
|
3079
|
+
var import_logger3 = require("@tradejs/infra/logger");
|
|
2880
3080
|
var import_redis2 = require("@tradejs/infra/redis");
|
|
2881
3081
|
var now = () => Date.now();
|
|
2882
3082
|
var toRandomOrderSuffix = () => (0, import_node_crypto.randomUUID)().replace(/-/g, "").slice(0, 12).toLowerCase();
|
|
@@ -2935,7 +3135,7 @@ var recordRuntimeTradeOpen = async (params) => {
|
|
|
2935
3135
|
)
|
|
2936
3136
|
]);
|
|
2937
3137
|
} catch (error) {
|
|
2938
|
-
|
|
3138
|
+
import_logger3.logger.error(
|
|
2939
3139
|
"runtime trade open journal failed: %s %s",
|
|
2940
3140
|
record.symbol,
|
|
2941
3141
|
error?.message || String(error)
|
|
@@ -3003,7 +3203,7 @@ var recordRuntimeTradeIncrease = async (params) => {
|
|
|
3003
3203
|
)
|
|
3004
3204
|
]);
|
|
3005
3205
|
} catch (error) {
|
|
3006
|
-
|
|
3206
|
+
import_logger3.logger.error(
|
|
3007
3207
|
"runtime trade increase journal failed: %s %s",
|
|
3008
3208
|
symbol,
|
|
3009
3209
|
error?.message || String(error)
|
|
@@ -3110,7 +3310,7 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
3110
3310
|
)
|
|
3111
3311
|
]);
|
|
3112
3312
|
} catch (error) {
|
|
3113
|
-
|
|
3313
|
+
import_logger3.logger.error(
|
|
3114
3314
|
"runtime trade close journal failed: %s %s",
|
|
3115
3315
|
symbol,
|
|
3116
3316
|
error?.message || String(error)
|
|
@@ -3120,11 +3320,11 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
3120
3320
|
};
|
|
3121
3321
|
|
|
3122
3322
|
// src/strategyHelpers/marketContextStages.ts
|
|
3123
|
-
var
|
|
3323
|
+
var import_logger8 = require("@tradejs/infra/logger");
|
|
3124
3324
|
|
|
3125
3325
|
// src/strategyHelpers/binanceMarketContext.ts
|
|
3126
3326
|
var import_marketContext = require("@tradejs/infra/timescale/marketContext");
|
|
3127
|
-
var
|
|
3327
|
+
var import_logger4 = require("@tradejs/infra/logger");
|
|
3128
3328
|
var import_strategies = require("@tradejs/core/strategies");
|
|
3129
3329
|
|
|
3130
3330
|
// src/binanceBreadthUniverses.ts
|
|
@@ -3488,7 +3688,7 @@ var parseEnabledFlag = (value, env) => {
|
|
|
3488
3688
|
if (normalized === "live") return env !== "BACKTEST";
|
|
3489
3689
|
return false;
|
|
3490
3690
|
};
|
|
3491
|
-
var
|
|
3691
|
+
var toFiniteNumberOrNull2 = (value) => {
|
|
3492
3692
|
const numeric = typeof value === "number" ? value : Number(value);
|
|
3493
3693
|
return Number.isFinite(numeric) ? numeric : null;
|
|
3494
3694
|
};
|
|
@@ -3613,14 +3813,14 @@ var toTradeFlowContext = (row, interval) => row ? {
|
|
|
3613
3813
|
asOfTs: row.ts.getTime(),
|
|
3614
3814
|
ageMs: row.ageMs,
|
|
3615
3815
|
stale: row.stale,
|
|
3616
|
-
trades:
|
|
3617
|
-
buyPressurePct:
|
|
3618
|
-
buyBaseVolume:
|
|
3619
|
-
sellBaseVolume:
|
|
3620
|
-
buyQuoteVolume:
|
|
3621
|
-
sellQuoteVolume:
|
|
3622
|
-
netBaseDelta:
|
|
3623
|
-
netQuoteDelta:
|
|
3816
|
+
trades: toFiniteNumberOrNull2(row.trades),
|
|
3817
|
+
buyPressurePct: toFiniteNumberOrNull2(row.buyPressurePct),
|
|
3818
|
+
buyBaseVolume: toFiniteNumberOrNull2(row.buyBaseVolume),
|
|
3819
|
+
sellBaseVolume: toFiniteNumberOrNull2(row.sellBaseVolume),
|
|
3820
|
+
buyQuoteVolume: toFiniteNumberOrNull2(row.buyQuoteVolume),
|
|
3821
|
+
sellQuoteVolume: toFiniteNumberOrNull2(row.sellQuoteVolume),
|
|
3822
|
+
netBaseDelta: toFiniteNumberOrNull2(row.netBaseDelta),
|
|
3823
|
+
netQuoteDelta: toFiniteNumberOrNull2(row.netQuoteDelta)
|
|
3624
3824
|
} : null;
|
|
3625
3825
|
var getCachedReferenceRows = ({
|
|
3626
3826
|
referenceSymbols,
|
|
@@ -3709,16 +3909,16 @@ var toMarketBreadthContext = (breadth, interval) => ({
|
|
|
3709
3909
|
asOfTs: breadth.ts.getTime(),
|
|
3710
3910
|
ageMs: breadth.ageMs,
|
|
3711
3911
|
stale: breadth.stale,
|
|
3712
|
-
symbolsCount:
|
|
3713
|
-
advancers:
|
|
3714
|
-
decliners:
|
|
3715
|
-
unchanged:
|
|
3716
|
-
advanceDeclineRatio:
|
|
3717
|
-
pctAboveMa20:
|
|
3718
|
-
pctAboveMa50:
|
|
3719
|
-
equalWeightedReturn:
|
|
3720
|
-
volumeWeightedReturn:
|
|
3721
|
-
dispersion:
|
|
3912
|
+
symbolsCount: toFiniteNumberOrNull2(breadth.symbolsCount),
|
|
3913
|
+
advancers: toFiniteNumberOrNull2(breadth.advancers),
|
|
3914
|
+
decliners: toFiniteNumberOrNull2(breadth.decliners),
|
|
3915
|
+
unchanged: toFiniteNumberOrNull2(breadth.unchanged),
|
|
3916
|
+
advanceDeclineRatio: toFiniteNumberOrNull2(breadth.advanceDeclineRatio),
|
|
3917
|
+
pctAboveMa20: toFiniteNumberOrNull2(breadth.pctAboveMa20),
|
|
3918
|
+
pctAboveMa50: toFiniteNumberOrNull2(breadth.pctAboveMa50),
|
|
3919
|
+
equalWeightedReturn: toFiniteNumberOrNull2(breadth.equalWeightedReturn),
|
|
3920
|
+
volumeWeightedReturn: toFiniteNumberOrNull2(breadth.volumeWeightedReturn),
|
|
3921
|
+
dispersion: toFiniteNumberOrNull2(breadth.dispersion)
|
|
3722
3922
|
});
|
|
3723
3923
|
var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
3724
3924
|
const {
|
|
@@ -3819,46 +4019,46 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
3819
4019
|
asOfTs: primaryBreadth.ts.getTime(),
|
|
3820
4020
|
ageMs: primaryBreadth.ageMs,
|
|
3821
4021
|
stale: primaryBreadth.stale,
|
|
3822
|
-
btcReturn1h:
|
|
4022
|
+
btcReturn1h: toFiniteNumberOrNull2(
|
|
3823
4023
|
primaryBreadth.btcReturn1h
|
|
3824
4024
|
),
|
|
3825
|
-
btcReturn4h:
|
|
4025
|
+
btcReturn4h: toFiniteNumberOrNull2(
|
|
3826
4026
|
primaryBreadth.btcReturn4h
|
|
3827
4027
|
),
|
|
3828
|
-
btcReturn24h:
|
|
4028
|
+
btcReturn24h: toFiniteNumberOrNull2(
|
|
3829
4029
|
primaryBreadth.btcReturn24h
|
|
3830
4030
|
),
|
|
3831
|
-
altBasketReturn1h:
|
|
4031
|
+
altBasketReturn1h: toFiniteNumberOrNull2(
|
|
3832
4032
|
primaryBreadth.altBasketReturn1h
|
|
3833
4033
|
),
|
|
3834
|
-
altBasketReturn4h:
|
|
4034
|
+
altBasketReturn4h: toFiniteNumberOrNull2(
|
|
3835
4035
|
primaryBreadth.altBasketReturn4h
|
|
3836
4036
|
),
|
|
3837
|
-
altBasketReturn24h:
|
|
4037
|
+
altBasketReturn24h: toFiniteNumberOrNull2(
|
|
3838
4038
|
primaryBreadth.altBasketReturn24h
|
|
3839
4039
|
),
|
|
3840
|
-
btcVsAltReturn1h:
|
|
4040
|
+
btcVsAltReturn1h: toFiniteNumberOrNull2(
|
|
3841
4041
|
primaryBreadth.btcVsAltReturn1h
|
|
3842
4042
|
),
|
|
3843
|
-
btcVsAltReturn4h:
|
|
4043
|
+
btcVsAltReturn4h: toFiniteNumberOrNull2(
|
|
3844
4044
|
primaryBreadth.btcVsAltReturn4h
|
|
3845
4045
|
),
|
|
3846
|
-
btcVsAltReturn24h:
|
|
4046
|
+
btcVsAltReturn24h: toFiniteNumberOrNull2(
|
|
3847
4047
|
primaryBreadth.btcVsAltReturn24h
|
|
3848
4048
|
),
|
|
3849
|
-
btcTurnoverShare1h:
|
|
4049
|
+
btcTurnoverShare1h: toFiniteNumberOrNull2(
|
|
3850
4050
|
primaryBreadth.btcTurnoverShare1h
|
|
3851
4051
|
),
|
|
3852
|
-
btcTurnoverShare24h:
|
|
4052
|
+
btcTurnoverShare24h: toFiniteNumberOrNull2(
|
|
3853
4053
|
primaryBreadth.btcTurnoverShare24h
|
|
3854
4054
|
),
|
|
3855
|
-
btcTurnoverShareChange24h:
|
|
4055
|
+
btcTurnoverShareChange24h: toFiniteNumberOrNull2(
|
|
3856
4056
|
primaryBreadth.btcTurnoverShareChange24h
|
|
3857
4057
|
),
|
|
3858
|
-
altVolToBtcVol24h:
|
|
4058
|
+
altVolToBtcVol24h: toFiniteNumberOrNull2(
|
|
3859
4059
|
primaryBreadth.altVolToBtcVol24h
|
|
3860
4060
|
),
|
|
3861
|
-
altDispersion24h:
|
|
4061
|
+
altDispersion24h: toFiniteNumberOrNull2(
|
|
3862
4062
|
primaryBreadth.altDispersion24h
|
|
3863
4063
|
),
|
|
3864
4064
|
regime: primaryBreadth.btcAltRegime ?? "unknown"
|
|
@@ -3875,7 +4075,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
3875
4075
|
throw error;
|
|
3876
4076
|
}
|
|
3877
4077
|
binanceMarketContextUnavailable = true;
|
|
3878
|
-
|
|
4078
|
+
import_logger4.logger.warn(
|
|
3879
4079
|
"Binance market context disabled after Timescale read failure: %s",
|
|
3880
4080
|
String(error)
|
|
3881
4081
|
);
|
|
@@ -3885,7 +4085,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
3885
4085
|
|
|
3886
4086
|
// src/strategyHelpers/coinMarketCapContext.ts
|
|
3887
4087
|
var import_strategies2 = require("@tradejs/core/strategies");
|
|
3888
|
-
var
|
|
4088
|
+
var import_logger5 = require("@tradejs/infra/logger");
|
|
3889
4089
|
var import_marketContext2 = require("@tradejs/infra/timescale/marketContext");
|
|
3890
4090
|
var DEFAULT_MAX_AGE_MS = 48 * 60 * 6e4;
|
|
3891
4091
|
var SOURCE_GLOBAL_DAILY = "coinmarketcap_global";
|
|
@@ -3923,7 +4123,7 @@ var asInt = (value, fallback) => {
|
|
|
3923
4123
|
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
3924
4124
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
3925
4125
|
};
|
|
3926
|
-
var
|
|
4126
|
+
var toFiniteNumberOrNull3 = (value) => {
|
|
3927
4127
|
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
|
|
3928
4128
|
return Number.isFinite(numeric) ? numeric : null;
|
|
3929
4129
|
};
|
|
@@ -4157,12 +4357,12 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4157
4357
|
const ethRow = references.get("ETHUSDT") ?? null;
|
|
4158
4358
|
const previousBtcRow = previousReferences.get("BTCUSDT") ?? null;
|
|
4159
4359
|
const previousEthRow = previousReferences.get("ETHUSDT") ?? null;
|
|
4160
|
-
const btcMarketCapUsd =
|
|
4161
|
-
const ethMarketCapUsd =
|
|
4162
|
-
const previousBtcMarketCapUsd =
|
|
4360
|
+
const btcMarketCapUsd = toFiniteNumberOrNull3(btcRow?.marketCapUsd);
|
|
4361
|
+
const ethMarketCapUsd = toFiniteNumberOrNull3(ethRow?.marketCapUsd);
|
|
4362
|
+
const previousBtcMarketCapUsd = toFiniteNumberOrNull3(
|
|
4163
4363
|
previousBtcRow?.marketCapUsd
|
|
4164
4364
|
);
|
|
4165
|
-
const previousEthMarketCapUsd =
|
|
4365
|
+
const previousEthMarketCapUsd = toFiniteNumberOrNull3(
|
|
4166
4366
|
previousEthRow?.marketCapUsd
|
|
4167
4367
|
);
|
|
4168
4368
|
const ethBtcMarketCapRatio = safeDivide(ethMarketCapUsd, btcMarketCapUsd);
|
|
@@ -4171,16 +4371,16 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4171
4371
|
previousBtcMarketCapUsd
|
|
4172
4372
|
);
|
|
4173
4373
|
const ethBtcMarketCapRatioChange24hPct = ethBtcMarketCapRatio != null && previousEthBtcMarketCapRatio != null && previousEthBtcMarketCapRatio > 0 ? (ethBtcMarketCapRatio - previousEthBtcMarketCapRatio) / previousEthBtcMarketCapRatio : null;
|
|
4174
|
-
const btcVolumeUsd =
|
|
4175
|
-
const ethVolumeUsd =
|
|
4374
|
+
const btcVolumeUsd = toFiniteNumberOrNull3(btcRow?.volumeUsd);
|
|
4375
|
+
const ethVolumeUsd = toFiniteNumberOrNull3(ethRow?.volumeUsd);
|
|
4176
4376
|
const referenceStale = btcRow?.stale === true || ethRow?.stale === true || !btcRow || !ethRow;
|
|
4177
|
-
const btcDominanceChange24hPct =
|
|
4377
|
+
const btcDominanceChange24hPct = toFiniteNumberOrNull3(
|
|
4178
4378
|
globalRow?.btcDominanceChange24hPct
|
|
4179
4379
|
);
|
|
4180
|
-
const altMarketCapChange24hPct =
|
|
4380
|
+
const altMarketCapChange24hPct = toFiniteNumberOrNull3(
|
|
4181
4381
|
globalRow?.altMarketCapChange24hPct
|
|
4182
4382
|
);
|
|
4183
|
-
const altVolumeChange24hPct =
|
|
4383
|
+
const altVolumeChange24hPct = toFiniteNumberOrNull3(
|
|
4184
4384
|
globalRow?.altVolumeChange24hPct
|
|
4185
4385
|
);
|
|
4186
4386
|
const altLiquidityRegime = globalRow ? toAltLiquidityRegime({
|
|
@@ -4191,19 +4391,19 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4191
4391
|
}) : "unknown";
|
|
4192
4392
|
const exchangeLiquidityRegime = exchangeLiquidityRow ? toExchangeLiquidityRegime({
|
|
4193
4393
|
stale: exchangeLiquidityRow.stale,
|
|
4194
|
-
totalVolumeChange24hPct:
|
|
4394
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
4195
4395
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
4196
4396
|
),
|
|
4197
4397
|
fallback: exchangeLiquidityRow.liquidityRegime ?? "unknown"
|
|
4198
4398
|
}) : "unknown";
|
|
4199
4399
|
const cmc100Row = indexRows.get("cmc100") ?? null;
|
|
4200
4400
|
const cmc20Row = indexRows.get("cmc20") ?? null;
|
|
4201
|
-
const cmc100Value =
|
|
4202
|
-
const cmc20Value =
|
|
4203
|
-
const cmc100Change24hPct =
|
|
4401
|
+
const cmc100Value = toFiniteNumberOrNull3(cmc100Row?.value);
|
|
4402
|
+
const cmc20Value = toFiniteNumberOrNull3(cmc20Row?.value);
|
|
4403
|
+
const cmc100Change24hPct = toFiniteNumberOrNull3(
|
|
4204
4404
|
cmc100Row?.valueChange24hPct
|
|
4205
4405
|
);
|
|
4206
|
-
const cmc20Change24hPct =
|
|
4406
|
+
const cmc20Change24hPct = toFiniteNumberOrNull3(cmc20Row?.valueChange24hPct);
|
|
4207
4407
|
const cmc20ToCmc100Ratio = safeDivide(cmc20Value, cmc100Value);
|
|
4208
4408
|
const cmc20ToCmc100RatioChange24hPct = cmc20Change24hPct != null && cmc100Change24hPct != null ? (1 + cmc20Change24hPct) / (1 + cmc100Change24hPct) - 1 : null;
|
|
4209
4409
|
const indexStale = cmc100Row?.stale === true || cmc20Row?.stale === true || !cmc100Row || !cmc20Row;
|
|
@@ -4227,41 +4427,41 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4227
4427
|
asOfTs: globalRow.ts.getTime(),
|
|
4228
4428
|
ageMs: globalRow.ageMs,
|
|
4229
4429
|
stale: globalRow.stale,
|
|
4230
|
-
totalMarketCapUsd:
|
|
4430
|
+
totalMarketCapUsd: toFiniteNumberOrNull3(
|
|
4231
4431
|
globalRow.totalMarketCapUsd
|
|
4232
4432
|
),
|
|
4233
|
-
totalVolumeUsd:
|
|
4433
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
4234
4434
|
globalRow.totalVolumeUsd
|
|
4235
4435
|
),
|
|
4236
|
-
totalVolumeReportedUsd:
|
|
4436
|
+
totalVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
4237
4437
|
globalRow.totalVolumeReportedUsd
|
|
4238
4438
|
),
|
|
4239
|
-
altMarketCapUsd:
|
|
4439
|
+
altMarketCapUsd: toFiniteNumberOrNull3(
|
|
4240
4440
|
globalRow.altMarketCapUsd
|
|
4241
4441
|
),
|
|
4242
|
-
altVolumeUsd:
|
|
4243
|
-
altVolumeReportedUsd:
|
|
4442
|
+
altVolumeUsd: toFiniteNumberOrNull3(globalRow.altVolumeUsd),
|
|
4443
|
+
altVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
4244
4444
|
globalRow.altVolumeReportedUsd
|
|
4245
4445
|
),
|
|
4246
|
-
btcDominancePct:
|
|
4446
|
+
btcDominancePct: toFiniteNumberOrNull3(
|
|
4247
4447
|
globalRow.btcDominancePct
|
|
4248
4448
|
),
|
|
4249
|
-
ethDominancePct:
|
|
4449
|
+
ethDominancePct: toFiniteNumberOrNull3(
|
|
4250
4450
|
globalRow.ethDominancePct
|
|
4251
4451
|
),
|
|
4252
4452
|
btcDominanceChange24hPct,
|
|
4253
|
-
ethDominanceChange24hPct:
|
|
4453
|
+
ethDominanceChange24hPct: toFiniteNumberOrNull3(
|
|
4254
4454
|
globalRow.ethDominanceChange24hPct
|
|
4255
4455
|
),
|
|
4256
4456
|
altMarketCapChange24hPct,
|
|
4257
4457
|
altVolumeChange24hPct,
|
|
4258
|
-
activeCryptocurrencies:
|
|
4458
|
+
activeCryptocurrencies: toFiniteNumberOrNull3(
|
|
4259
4459
|
globalRow.activeCryptocurrencies
|
|
4260
4460
|
),
|
|
4261
|
-
activeExchanges:
|
|
4461
|
+
activeExchanges: toFiniteNumberOrNull3(
|
|
4262
4462
|
globalRow.activeExchanges
|
|
4263
4463
|
),
|
|
4264
|
-
activeMarketPairs:
|
|
4464
|
+
activeMarketPairs: toFiniteNumberOrNull3(
|
|
4265
4465
|
globalRow.activeMarketPairs
|
|
4266
4466
|
),
|
|
4267
4467
|
altLiquidityRegime
|
|
@@ -4306,22 +4506,22 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4306
4506
|
asOfTs: exchangeLiquidityRow.ts.getTime(),
|
|
4307
4507
|
ageMs: exchangeLiquidityRow.ageMs,
|
|
4308
4508
|
stale: exchangeLiquidityRow.stale,
|
|
4309
|
-
exchangesCount:
|
|
4509
|
+
exchangesCount: toFiniteNumberOrNull3(
|
|
4310
4510
|
exchangeLiquidityRow.exchangesCount
|
|
4311
4511
|
),
|
|
4312
|
-
totalVolumeUsd:
|
|
4512
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
4313
4513
|
exchangeLiquidityRow.totalVolumeUsd
|
|
4314
4514
|
),
|
|
4315
|
-
totalVolumeChange24hPct:
|
|
4515
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
4316
4516
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
4317
4517
|
),
|
|
4318
|
-
binanceVolumeUsd:
|
|
4518
|
+
binanceVolumeUsd: toFiniteNumberOrNull3(
|
|
4319
4519
|
exchangeLiquidityRow.binanceVolumeUsd
|
|
4320
4520
|
),
|
|
4321
|
-
binanceVolumeShare:
|
|
4521
|
+
binanceVolumeShare: toFiniteNumberOrNull3(
|
|
4322
4522
|
exchangeLiquidityRow.binanceVolumeShare
|
|
4323
4523
|
),
|
|
4324
|
-
topExchangeVolumeShare:
|
|
4524
|
+
topExchangeVolumeShare: toFiniteNumberOrNull3(
|
|
4325
4525
|
exchangeLiquidityRow.topExchangeVolumeShare
|
|
4326
4526
|
),
|
|
4327
4527
|
liquidityRegime: exchangeLiquidityRegime
|
|
@@ -4334,11 +4534,11 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4334
4534
|
asOfTs: fearGreedRow.ts.getTime(),
|
|
4335
4535
|
ageMs: fearGreedRow.ageMs,
|
|
4336
4536
|
stale: fearGreedRow.stale,
|
|
4337
|
-
value:
|
|
4338
|
-
valueChange24h:
|
|
4537
|
+
value: toFiniteNumberOrNull3(fearGreedRow.value),
|
|
4538
|
+
valueChange24h: toFiniteNumberOrNull3(
|
|
4339
4539
|
fearGreedRow.valueChange24h
|
|
4340
4540
|
),
|
|
4341
|
-
valueChange7d:
|
|
4541
|
+
valueChange7d: toFiniteNumberOrNull3(
|
|
4342
4542
|
fearGreedRow.valueChange7d
|
|
4343
4543
|
),
|
|
4344
4544
|
classification: fearGreedRow.classification ?? "Unknown",
|
|
@@ -4358,13 +4558,13 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4358
4558
|
cmc100Value,
|
|
4359
4559
|
cmc100Change24hPct,
|
|
4360
4560
|
cmc100TopConstituentSymbol: cmc100Row?.topConstituentSymbol ?? null,
|
|
4361
|
-
cmc100TopConstituentWeightPct:
|
|
4561
|
+
cmc100TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
4362
4562
|
cmc100Row?.topConstituentWeightPct
|
|
4363
4563
|
),
|
|
4364
4564
|
cmc20Value,
|
|
4365
4565
|
cmc20Change24hPct,
|
|
4366
4566
|
cmc20TopConstituentSymbol: cmc20Row?.topConstituentSymbol ?? null,
|
|
4367
|
-
cmc20TopConstituentWeightPct:
|
|
4567
|
+
cmc20TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
4368
4568
|
cmc20Row?.topConstituentWeightPct
|
|
4369
4569
|
),
|
|
4370
4570
|
cmc20ToCmc100Ratio,
|
|
@@ -4382,7 +4582,7 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4382
4582
|
throw error;
|
|
4383
4583
|
}
|
|
4384
4584
|
coinMarketCapContextUnavailable = true;
|
|
4385
|
-
|
|
4585
|
+
import_logger5.logger.warn(
|
|
4386
4586
|
"CoinMarketCap context disabled after Timescale read failure: %s",
|
|
4387
4587
|
String(error)
|
|
4388
4588
|
);
|
|
@@ -4396,7 +4596,7 @@ var import_data2 = require("@tradejs/core/data");
|
|
|
4396
4596
|
var import_strategies3 = require("@tradejs/core/strategies");
|
|
4397
4597
|
var import_constants2 = require("@tradejs/core/constants");
|
|
4398
4598
|
var import_derivatives = require("@tradejs/infra/timescale/derivatives");
|
|
4399
|
-
var
|
|
4599
|
+
var import_logger6 = require("@tradejs/infra/logger");
|
|
4400
4600
|
var STORED_INTERVALS = ["15m", "1h"];
|
|
4401
4601
|
var CONTEXT_INTERVALS = ["15m", "1h"];
|
|
4402
4602
|
var DEFAULT_LOOKBACK_HOURS = 48;
|
|
@@ -4451,7 +4651,7 @@ var resolvePrimaryReferenceSymbol2 = () => PRIMARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
|
4451
4651
|
var resolveSecondaryReferenceSymbol = () => SECONDARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
4452
4652
|
var getPrimaryIntervalContext = (context) => context?.intervals["15m"] ?? context?.intervals["1h"] ?? null;
|
|
4453
4653
|
var hasDerivativesSymbolData = (context) => Object.keys(context.intervals).length > 0 && !context.summary.riskFlags.includes("missing_derivatives");
|
|
4454
|
-
var
|
|
4654
|
+
var toFiniteNumberOrNull4 = (value) => {
|
|
4455
4655
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
4456
4656
|
if (typeof value === "string" && value.trim()) {
|
|
4457
4657
|
const parsed = Number(value);
|
|
@@ -4465,8 +4665,8 @@ var roundNullable = (value, digits = 4) => {
|
|
|
4465
4665
|
return Math.round(value * multiplier) / multiplier;
|
|
4466
4666
|
};
|
|
4467
4667
|
var deltaNullable = (targetValue, referenceValue) => {
|
|
4468
|
-
const target =
|
|
4469
|
-
const reference =
|
|
4668
|
+
const target = toFiniteNumberOrNull4(targetValue);
|
|
4669
|
+
const reference = toFiniteNumberOrNull4(referenceValue);
|
|
4470
4670
|
return target == null || reference == null ? null : roundNullable(target - reference);
|
|
4471
4671
|
};
|
|
4472
4672
|
var buildTargetDerivedContext = (params) => {
|
|
@@ -4628,7 +4828,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
4628
4828
|
throw error;
|
|
4629
4829
|
}
|
|
4630
4830
|
derivativesContextUnavailable = true;
|
|
4631
|
-
|
|
4831
|
+
import_logger6.logger.warn(
|
|
4632
4832
|
"Derivatives context disabled after Timescale read failure: %s",
|
|
4633
4833
|
String(error)
|
|
4634
4834
|
);
|
|
@@ -4640,7 +4840,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
4640
4840
|
var import_data3 = require("@tradejs/core/data");
|
|
4641
4841
|
var import_strategies4 = require("@tradejs/core/strategies");
|
|
4642
4842
|
var import_hyperliquidWhales2 = require("@tradejs/infra/timescale/hyperliquidWhales");
|
|
4643
|
-
var
|
|
4843
|
+
var import_logger7 = require("@tradejs/infra/logger");
|
|
4644
4844
|
|
|
4645
4845
|
// src/hyperliquidWhaleUniverse.ts
|
|
4646
4846
|
var import_node_crypto3 = require("crypto");
|
|
@@ -5160,7 +5360,7 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
5160
5360
|
throw error;
|
|
5161
5361
|
}
|
|
5162
5362
|
hyperliquidWhaleContextUnavailable = true;
|
|
5163
|
-
|
|
5363
|
+
import_logger7.logger.warn(
|
|
5164
5364
|
"Hyperliquid whale context disabled after Timescale read failure: %s",
|
|
5165
5365
|
String(error)
|
|
5166
5366
|
);
|
|
@@ -5243,7 +5443,7 @@ var runMarketContextStage = async ({
|
|
|
5243
5443
|
elapsedMs: Date.now() - startedAt
|
|
5244
5444
|
};
|
|
5245
5445
|
if (status === "timed_out") {
|
|
5246
|
-
|
|
5446
|
+
import_logger8.logger.warn(
|
|
5247
5447
|
"Market context stage timed out: %s after %sms",
|
|
5248
5448
|
stage,
|
|
5249
5449
|
result.elapsedMs
|
|
@@ -5433,79 +5633,10 @@ var enrichSignalWithAi = async ({
|
|
|
5433
5633
|
signal.aiAnalysis = analysis;
|
|
5434
5634
|
return resolveAiQuality(analysis, direction);
|
|
5435
5635
|
} catch (err) {
|
|
5436
|
-
|
|
5636
|
+
import_logger9.logger.error("AI analysis error: %s %s", symbol, formatAiError(err));
|
|
5437
5637
|
}
|
|
5438
5638
|
return void 0;
|
|
5439
5639
|
};
|
|
5440
|
-
var toFiniteNumberOrNull4 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
5441
|
-
var getOrderArrivalSnapshot = async ({
|
|
5442
|
-
connector,
|
|
5443
|
-
symbol
|
|
5444
|
-
}) => {
|
|
5445
|
-
if (typeof connector.getTopOfBookTicker !== "function") {
|
|
5446
|
-
return {
|
|
5447
|
-
arrivalSnapshotTime: Date.now(),
|
|
5448
|
-
arrivalSource: "unavailable",
|
|
5449
|
-
bid: null,
|
|
5450
|
-
ask: null,
|
|
5451
|
-
arrivalMid: null,
|
|
5452
|
-
spreadBps: null
|
|
5453
|
-
};
|
|
5454
|
-
}
|
|
5455
|
-
try {
|
|
5456
|
-
const ticker = await connector.getTopOfBookTicker(symbol);
|
|
5457
|
-
const arrivalSnapshotTime = toFiniteNumberOrNull4(ticker?.timestamp);
|
|
5458
|
-
const bid = toFiniteNumberOrNull4(ticker?.bidPrice);
|
|
5459
|
-
const ask = toFiniteNumberOrNull4(ticker?.askPrice);
|
|
5460
|
-
const arrivalMid = bid != null && ask != null ? (bid + ask) / 2 : null;
|
|
5461
|
-
const spreadBps = bid != null && ask != null && arrivalMid != null && arrivalMid > 0 ? (ask - bid) / arrivalMid * 1e4 : null;
|
|
5462
|
-
return {
|
|
5463
|
-
arrivalSnapshotTime: arrivalSnapshotTime ?? Date.now(),
|
|
5464
|
-
arrivalSource: "top_of_book",
|
|
5465
|
-
bid,
|
|
5466
|
-
ask,
|
|
5467
|
-
arrivalMid,
|
|
5468
|
-
spreadBps
|
|
5469
|
-
};
|
|
5470
|
-
} catch (error) {
|
|
5471
|
-
import_logger8.logger.warn(
|
|
5472
|
-
"runtime order arrival snapshot failed: %s %s",
|
|
5473
|
-
symbol,
|
|
5474
|
-
error?.message || String(error)
|
|
5475
|
-
);
|
|
5476
|
-
return {
|
|
5477
|
-
arrivalSnapshotTime: Date.now(),
|
|
5478
|
-
arrivalSource: "top_of_book_error",
|
|
5479
|
-
bid: null,
|
|
5480
|
-
ask: null,
|
|
5481
|
-
arrivalMid: null,
|
|
5482
|
-
spreadBps: null
|
|
5483
|
-
};
|
|
5484
|
-
}
|
|
5485
|
-
};
|
|
5486
|
-
var validateEntryProtectionAtArrival = ({
|
|
5487
|
-
direction,
|
|
5488
|
-
signalPrice,
|
|
5489
|
-
bid,
|
|
5490
|
-
ask,
|
|
5491
|
-
arrivalMid,
|
|
5492
|
-
takeProfits,
|
|
5493
|
-
stopLossPrice
|
|
5494
|
-
}) => {
|
|
5495
|
-
const entryReference = direction === "LONG" ? ask ?? arrivalMid ?? signalPrice : bid ?? arrivalMid ?? signalPrice;
|
|
5496
|
-
const stopReference = direction === "LONG" ? bid ?? arrivalMid ?? signalPrice : ask ?? arrivalMid ?? signalPrice;
|
|
5497
|
-
if (!Number.isFinite(entryReference) || entryReference <= 0 || !Number.isFinite(stopReference) || stopReference <= 0) {
|
|
5498
|
-
throw new Error("INVALID_ENTRY_REFERENCE_AT_ARRIVAL");
|
|
5499
|
-
}
|
|
5500
|
-
for (const takeProfit of takeProfits) {
|
|
5501
|
-
if (!Number.isFinite(takeProfit.price) || takeProfit.price <= 0 || (direction === "LONG" ? takeProfit.price <= entryReference : takeProfit.price >= entryReference)) {
|
|
5502
|
-
throw new Error("TAKE_PROFIT_CROSSED_BEFORE_ENTRY");
|
|
5503
|
-
}
|
|
5504
|
-
}
|
|
5505
|
-
if (stopLossPrice != null && (!Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || (direction === "LONG" ? stopLossPrice >= stopReference : stopLossPrice <= stopReference))) {
|
|
5506
|
-
throw new Error("STOP_LOSS_CROSSED_BEFORE_ENTRY");
|
|
5507
|
-
}
|
|
5508
|
-
};
|
|
5509
5640
|
var resolveRuntimeTelemetryQuality = ({
|
|
5510
5641
|
signalClosePrice,
|
|
5511
5642
|
arrivalMid,
|
|
@@ -5582,7 +5713,8 @@ var executeEntryOrder = async ({
|
|
|
5582
5713
|
signal,
|
|
5583
5714
|
beforePlaceOrder,
|
|
5584
5715
|
recordRuntimeTrade = true,
|
|
5585
|
-
leverage
|
|
5716
|
+
leverage,
|
|
5717
|
+
executionLimits
|
|
5586
5718
|
}) => {
|
|
5587
5719
|
await beforePlaceOrder?.();
|
|
5588
5720
|
const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
|
|
@@ -5593,23 +5725,57 @@ var executeEntryOrder = async ({
|
|
|
5593
5725
|
signal.orderQty = qty;
|
|
5594
5726
|
signal.orderValue = qty * currentPrice;
|
|
5595
5727
|
signal.orderFailureReason = void 0;
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5728
|
+
let arrivalSnapshot = {
|
|
5729
|
+
arrivalSnapshotTime: Date.now(),
|
|
5730
|
+
arrivalSource: "unavailable",
|
|
5731
|
+
bid: null,
|
|
5732
|
+
ask: null,
|
|
5733
|
+
arrivalMid: null,
|
|
5734
|
+
spreadBps: null
|
|
5735
|
+
};
|
|
5736
|
+
let orderSubmitTime = Date.now();
|
|
5737
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
5738
|
+
connector,
|
|
5739
|
+
symbol,
|
|
5740
|
+
direction,
|
|
5741
|
+
takeProfits,
|
|
5742
|
+
stopLossPrice,
|
|
5743
|
+
limits: executionLimits,
|
|
5744
|
+
onValidated: (snapshot2, economics2) => {
|
|
5745
|
+
arrivalSnapshot = snapshot2;
|
|
5746
|
+
orderSubmitTime = Date.now();
|
|
5747
|
+
if (executionLimits?.maxLossValue != null) {
|
|
5748
|
+
signal.riskDecision = {
|
|
5749
|
+
stage: "order_plan",
|
|
5750
|
+
status: "approved",
|
|
5751
|
+
reason: "PLANNED_LOSS_WITHIN_MAX_LOSS_VALUE",
|
|
5752
|
+
plannedLossValue: economics2.plannedLossValue,
|
|
5753
|
+
maxLossValue: executionLimits.maxLossValue,
|
|
5754
|
+
limitUtilization: economics2.plannedLossValue / executionLimits.maxLossValue,
|
|
5755
|
+
enforced: true
|
|
5756
|
+
};
|
|
5757
|
+
}
|
|
5758
|
+
}
|
|
5759
|
+
});
|
|
5760
|
+
if (!beforeSubmit) {
|
|
5761
|
+
arrivalSnapshot = await getOrderArrivalSnapshot({ connector, symbol });
|
|
5762
|
+
try {
|
|
5763
|
+
validateEntryProtectionAtArrival({
|
|
5764
|
+
direction,
|
|
5765
|
+
signalPrice: currentPrice,
|
|
5766
|
+
bid: arrivalSnapshot.bid,
|
|
5767
|
+
ask: arrivalSnapshot.ask,
|
|
5768
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
5769
|
+
takeProfits,
|
|
5770
|
+
stopLossPrice
|
|
5771
|
+
});
|
|
5772
|
+
} catch (error) {
|
|
5773
|
+
signal.orderStatus = "failed";
|
|
5774
|
+
signal.orderFailureReason = error.message;
|
|
5775
|
+
throw error;
|
|
5776
|
+
}
|
|
5777
|
+
orderSubmitTime = Date.now();
|
|
5611
5778
|
}
|
|
5612
|
-
const orderSubmitTime = Date.now();
|
|
5613
5779
|
const orderPlaced = await connector.placeOrder({
|
|
5614
5780
|
symbol,
|
|
5615
5781
|
qty,
|
|
@@ -5620,7 +5786,8 @@ var executeEntryOrder = async ({
|
|
|
5620
5786
|
direction,
|
|
5621
5787
|
...typeof leverage === "number" && Number.isFinite(leverage) ? { leverage } : {},
|
|
5622
5788
|
orderId,
|
|
5623
|
-
signal
|
|
5789
|
+
signal,
|
|
5790
|
+
...beforeSubmit ? { beforeSubmit } : {}
|
|
5624
5791
|
});
|
|
5625
5792
|
const orderAckTime = Date.now();
|
|
5626
5793
|
const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
|
|
@@ -6449,7 +6616,7 @@ var canUseSharedReplayState = ({
|
|
|
6449
6616
|
}) => (env === "BACKTEST" || env === "PARITY") && Boolean(sharedReplayKey);
|
|
6450
6617
|
|
|
6451
6618
|
// src/strategy/runtimeExecution.ts
|
|
6452
|
-
var
|
|
6619
|
+
var import_logger10 = require("@tradejs/infra/logger");
|
|
6453
6620
|
var import_types = require("@tradejs/types");
|
|
6454
6621
|
var buildExitOrderSignal = ({
|
|
6455
6622
|
strategyName,
|
|
@@ -6501,7 +6668,7 @@ var handleExitDecision = async ({
|
|
|
6501
6668
|
deploymentId: connector.deploymentId
|
|
6502
6669
|
});
|
|
6503
6670
|
if (!activeTrade) {
|
|
6504
|
-
|
|
6671
|
+
import_logger10.logger.warn(
|
|
6505
6672
|
"[%s] blocked closePosition for untracked runtime position: %s",
|
|
6506
6673
|
strategyName ?? "unknown",
|
|
6507
6674
|
symbol
|
|
@@ -6509,7 +6676,7 @@ var handleExitDecision = async ({
|
|
|
6509
6676
|
return "CLOSE_BLOCKED_BY_UNTRACKED_POSITION";
|
|
6510
6677
|
}
|
|
6511
6678
|
if (!strategyName || activeTrade.strategy !== strategyName) {
|
|
6512
|
-
|
|
6679
|
+
import_logger10.logger.warn(
|
|
6513
6680
|
"[%s] blocked closePosition for foreign runtime position: %s ownedBy=%s",
|
|
6514
6681
|
strategyName ?? "unknown",
|
|
6515
6682
|
symbol,
|
|
@@ -6562,7 +6729,7 @@ var handleExitDecision = async ({
|
|
|
6562
6729
|
exitType: closedTrade?.exitType ?? "exit"
|
|
6563
6730
|
});
|
|
6564
6731
|
} catch (notificationError) {
|
|
6565
|
-
|
|
6732
|
+
import_logger10.logger.error(
|
|
6566
6733
|
"runtime close notification error: %s %s",
|
|
6567
6734
|
symbol,
|
|
6568
6735
|
notificationError
|
|
@@ -6576,7 +6743,7 @@ var handleExitDecision = async ({
|
|
|
6576
6743
|
decision,
|
|
6577
6744
|
market
|
|
6578
6745
|
});
|
|
6579
|
-
|
|
6746
|
+
import_logger10.logger.error("close order error: %s %s", symbol, err);
|
|
6580
6747
|
return "ORDER_ERROR";
|
|
6581
6748
|
}
|
|
6582
6749
|
return decision.code;
|
|
@@ -6603,7 +6770,7 @@ var handleProtectDecision = async ({
|
|
|
6603
6770
|
decision,
|
|
6604
6771
|
market
|
|
6605
6772
|
});
|
|
6606
|
-
|
|
6773
|
+
import_logger10.logger.error("protect position error: %s %s", symbol, err);
|
|
6607
6774
|
return "ORDER_ERROR";
|
|
6608
6775
|
}
|
|
6609
6776
|
return decision.code;
|
|
@@ -6669,7 +6836,11 @@ var executeEntryDecision = async ({
|
|
|
6669
6836
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {},
|
|
6670
6837
|
signal,
|
|
6671
6838
|
beforePlaceOrder,
|
|
6672
|
-
recordRuntimeTrade: recordRuntimeJournal
|
|
6839
|
+
recordRuntimeTrade: recordRuntimeJournal,
|
|
6840
|
+
executionLimits: resolveEntryExecutionLimits(
|
|
6841
|
+
hookCtx.strategyConfig,
|
|
6842
|
+
decision.entryContext.direction
|
|
6843
|
+
)
|
|
6673
6844
|
});
|
|
6674
6845
|
await invokeStageHooks(
|
|
6675
6846
|
"afterPlaceOrder",
|
|
@@ -6691,19 +6862,32 @@ var executeEntryDecision = async ({
|
|
|
6691
6862
|
return signal;
|
|
6692
6863
|
}
|
|
6693
6864
|
await beforePlaceOrder();
|
|
6694
|
-
const
|
|
6865
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
6695
6866
|
connector,
|
|
6696
|
-
symbol
|
|
6697
|
-
});
|
|
6698
|
-
validateEntryProtectionAtArrival({
|
|
6867
|
+
symbol,
|
|
6699
6868
|
direction: decision.entryContext.direction,
|
|
6700
|
-
signalPrice: decision.entryContext.prices.currentPrice,
|
|
6701
|
-
bid: arrivalSnapshot.bid,
|
|
6702
|
-
ask: arrivalSnapshot.ask,
|
|
6703
|
-
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
6704
6869
|
takeProfits: decision.orderPlan.takeProfits,
|
|
6705
|
-
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
6870
|
+
stopLossPrice: decision.orderPlan.stopLossPrice,
|
|
6871
|
+
limits: resolveEntryExecutionLimits(
|
|
6872
|
+
hookCtx.strategyConfig,
|
|
6873
|
+
decision.entryContext.direction
|
|
6874
|
+
)
|
|
6706
6875
|
});
|
|
6876
|
+
if (!beforeSubmit) {
|
|
6877
|
+
const arrivalSnapshot = await getOrderArrivalSnapshot({
|
|
6878
|
+
connector,
|
|
6879
|
+
symbol
|
|
6880
|
+
});
|
|
6881
|
+
validateEntryProtectionAtArrival({
|
|
6882
|
+
direction: decision.entryContext.direction,
|
|
6883
|
+
signalPrice: decision.entryContext.prices.currentPrice,
|
|
6884
|
+
bid: arrivalSnapshot.bid,
|
|
6885
|
+
ask: arrivalSnapshot.ask,
|
|
6886
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
6887
|
+
takeProfits: decision.orderPlan.takeProfits,
|
|
6888
|
+
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
6889
|
+
});
|
|
6890
|
+
}
|
|
6707
6891
|
const orderPlaced = await connector.placeOrder({
|
|
6708
6892
|
symbol,
|
|
6709
6893
|
qty: decision.orderPlan.qty,
|
|
@@ -6711,6 +6895,7 @@ var executeEntryDecision = async ({
|
|
|
6711
6895
|
timestamp: decision.entryContext.timestamp,
|
|
6712
6896
|
direction: decision.entryContext.direction,
|
|
6713
6897
|
positionIntent: decision.orderPlan.positionIntent,
|
|
6898
|
+
...beforeSubmit ? { beforeSubmit } : {},
|
|
6714
6899
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {}
|
|
6715
6900
|
});
|
|
6716
6901
|
if (!orderPlaced) {
|
|
@@ -6752,6 +6937,23 @@ var executeEntryDecision = async ({
|
|
|
6752
6937
|
{ decision, entry, market }
|
|
6753
6938
|
);
|
|
6754
6939
|
} catch (err) {
|
|
6940
|
+
if (err instanceof EntrySubmissionValidationError) {
|
|
6941
|
+
if (signal) {
|
|
6942
|
+
signal.orderStatus = "skipped";
|
|
6943
|
+
signal.orderSkipReason = err.message;
|
|
6944
|
+
signal.allocatorDecision = {
|
|
6945
|
+
stage: "runtime_admission",
|
|
6946
|
+
status: "rejected",
|
|
6947
|
+
reason: err.message
|
|
6948
|
+
};
|
|
6949
|
+
}
|
|
6950
|
+
import_logger10.logger.warn(
|
|
6951
|
+
"entry rejected before submission: %s %s",
|
|
6952
|
+
symbol,
|
|
6953
|
+
err.message
|
|
6954
|
+
);
|
|
6955
|
+
return signal ?? err.message;
|
|
6956
|
+
}
|
|
6755
6957
|
if (signal) {
|
|
6756
6958
|
signal.orderStatus = "failed";
|
|
6757
6959
|
if (typeof signal.orderFailureReason !== "string" || !signal.orderFailureReason.trim()) {
|
|
@@ -6766,9 +6968,9 @@ var executeEntryDecision = async ({
|
|
|
6766
6968
|
market
|
|
6767
6969
|
});
|
|
6768
6970
|
if (err?.message === import_types.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY) {
|
|
6769
|
-
|
|
6971
|
+
import_logger10.logger.warn("order warning: %s %s", symbol, err);
|
|
6770
6972
|
} else {
|
|
6771
|
-
|
|
6973
|
+
import_logger10.logger.error("order error: %s %s", symbol, err);
|
|
6772
6974
|
}
|
|
6773
6975
|
return signal ?? "ORDER_ERROR";
|
|
6774
6976
|
}
|
|
@@ -6933,7 +7135,7 @@ var createStrategyRuntime = ({
|
|
|
6933
7135
|
try {
|
|
6934
7136
|
await projectHook(errorParams);
|
|
6935
7137
|
} catch (hookError) {
|
|
6936
|
-
|
|
7138
|
+
import_logger11.logger.error(
|
|
6937
7139
|
"project hook onRuntimeError failed: %s %s",
|
|
6938
7140
|
strategyName,
|
|
6939
7141
|
hookError
|
|
@@ -6947,7 +7149,7 @@ var createStrategyRuntime = ({
|
|
|
6947
7149
|
try {
|
|
6948
7150
|
await onRuntimeError(errorParams);
|
|
6949
7151
|
} catch (hookError) {
|
|
6950
|
-
|
|
7152
|
+
import_logger11.logger.error(
|
|
6951
7153
|
"runtime hook onRuntimeError failed: %s %s",
|
|
6952
7154
|
strategyName,
|
|
6953
7155
|
hookError
|
|
@@ -6961,7 +7163,7 @@ var createStrategyRuntime = ({
|
|
|
6961
7163
|
try {
|
|
6962
7164
|
return await hook(params);
|
|
6963
7165
|
} catch (error) {
|
|
6964
|
-
|
|
7166
|
+
import_logger11.logger.error(
|
|
6965
7167
|
'strategy hook "%s" failed for %s: %s',
|
|
6966
7168
|
stage,
|
|
6967
7169
|
strategyName,
|