@tradejs/node 3.1.29-beta.288 → 3.1.29-beta.290
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backtest.js +442 -234
- package/dist/backtest.mjs +10 -5
- package/dist/{chunk-F7CLF3PT.mjs → chunk-PB2KHBGH.mjs} +415 -212
- package/dist/cli.js +3 -2
- package/dist/cli.mjs +3 -2
- package/dist/diagnostics.js +416 -213
- package/dist/diagnostics.mjs +1 -1
- package/dist/mcp.d.mts +3 -1
- package/dist/mcp.d.ts +3 -1
- package/dist/mcp.js +455 -230
- package/dist/mcp.mjs +24 -3
- package/dist/registry.js +416 -213
- package/dist/registry.mjs +1 -1
- package/dist/runtimeDashboard.js +418 -215
- package/dist/runtimeDashboard.mjs +1 -1
- package/dist/runtimeStrategies.js +416 -213
- package/dist/runtimeStrategies.mjs +1 -1
- package/dist/strategies.d.mts +32 -22
- package/dist/strategies.d.ts +32 -22
- package/dist/strategies.js +424 -221
- package/dist/strategies.mjs +1 -1
- package/package.json +5 -5
package/dist/backtest.js
CHANGED
|
@@ -799,7 +799,7 @@ var import_indicators3 = require("@tradejs/core/indicators");
|
|
|
799
799
|
var import_constants9 = require("@tradejs/core/constants");
|
|
800
800
|
var import_strategies7 = require("@tradejs/core/strategies");
|
|
801
801
|
var import_time3 = require("@tradejs/core/time");
|
|
802
|
-
var
|
|
802
|
+
var import_logger15 = require("@tradejs/infra/logger");
|
|
803
803
|
|
|
804
804
|
// src/strategyRuntime.ts
|
|
805
805
|
var import_jev6 = require("@tradejs/core/jev");
|
|
@@ -1054,13 +1054,210 @@ var assessSignalWithJev = async ({
|
|
|
1054
1054
|
// src/strategyRuntime.ts
|
|
1055
1055
|
var import_constants5 = require("@tradejs/core/constants");
|
|
1056
1056
|
var import_strategies6 = require("@tradejs/core/strategies");
|
|
1057
|
-
var
|
|
1057
|
+
var import_logger11 = require("@tradejs/infra/logger");
|
|
1058
1058
|
|
|
1059
1059
|
// src/strategyHelpers/runtime.ts
|
|
1060
|
-
var
|
|
1060
|
+
var import_logger9 = require("@tradejs/infra/logger");
|
|
1061
1061
|
var import_constants3 = require("@tradejs/core/constants");
|
|
1062
1062
|
var import_ml2 = require("@tradejs/infra/ml");
|
|
1063
1063
|
|
|
1064
|
+
// src/strategy/entrySubmissionValidation.ts
|
|
1065
|
+
var import_logger = require("@tradejs/infra/logger");
|
|
1066
|
+
|
|
1067
|
+
// src/strategy/entryExecutionValidation.ts
|
|
1068
|
+
var EntrySubmissionValidationError = class extends Error {
|
|
1069
|
+
constructor() {
|
|
1070
|
+
super(...arguments);
|
|
1071
|
+
this.name = "EntrySubmissionValidationError";
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
var resolveEntryExecutionLimits = (config, direction) => {
|
|
1075
|
+
const side = config[direction];
|
|
1076
|
+
const minimum = side && typeof side === "object" && "minRiskRatio" in side ? side.minRiskRatio : void 0;
|
|
1077
|
+
const maximum = config.MAX_LOSS_VALUE;
|
|
1078
|
+
if (minimum == null && maximum == null) return void 0;
|
|
1079
|
+
const minimumNetRiskRatio = minimum == null ? void 0 : Number(minimum);
|
|
1080
|
+
const maxLossValue = maximum == null ? void 0 : Number(maximum);
|
|
1081
|
+
const feeRate = Number(config.RISK_FEE_RATE ?? 0);
|
|
1082
|
+
const slippage = Number(config.RISK_SLIPPAGE_BPS ?? 0);
|
|
1083
|
+
const impact = Number(config.RISK_MARKET_IMPACT_BPS ?? 0);
|
|
1084
|
+
const slippageBps = slippage + impact;
|
|
1085
|
+
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)
|
|
1086
|
+
throw new Error("INVALID_ENTRY_EXECUTION_LIMITS");
|
|
1087
|
+
return { minimumNetRiskRatio, maxLossValue, feeRate, slippageBps };
|
|
1088
|
+
};
|
|
1089
|
+
var validateEntryExecution = ({
|
|
1090
|
+
direction,
|
|
1091
|
+
price,
|
|
1092
|
+
qty,
|
|
1093
|
+
stopLossPrice,
|
|
1094
|
+
takeProfitPrice,
|
|
1095
|
+
limits
|
|
1096
|
+
}) => {
|
|
1097
|
+
if (!Number.isFinite(price) || price <= 0 || !Number.isFinite(qty) || qty <= 0 || stopLossPrice == null || !Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || !Number.isFinite(takeProfitPrice) || takeProfitPrice <= 0)
|
|
1098
|
+
throw new EntrySubmissionValidationError(
|
|
1099
|
+
"ENTRY_ECONOMICS_INPUTS_UNAVAILABLE"
|
|
1100
|
+
);
|
|
1101
|
+
const side = direction === "LONG" ? 1 : -1;
|
|
1102
|
+
if (side * (price - stopLossPrice) <= 0 || side * (takeProfitPrice - price) <= 0)
|
|
1103
|
+
throw new EntrySubmissionValidationError(
|
|
1104
|
+
"INVALID_ENTRY_PROTECTION_AT_SUBMISSION"
|
|
1105
|
+
);
|
|
1106
|
+
const slippage = limits.slippageBps / 1e4;
|
|
1107
|
+
const cost = (value, sign) => value * slippage + value * (1 + sign * slippage) * limits.feeRate;
|
|
1108
|
+
const entryCost = cost(price, side);
|
|
1109
|
+
const lossPerUnit = Math.abs(price - stopLossPrice) + entryCost + cost(stopLossPrice, -side);
|
|
1110
|
+
const rewardPerUnit = Math.max(
|
|
1111
|
+
0,
|
|
1112
|
+
Math.abs(takeProfitPrice - price) - entryCost - cost(takeProfitPrice, -side)
|
|
1113
|
+
);
|
|
1114
|
+
const netRiskRatio = rewardPerUnit / lossPerUnit;
|
|
1115
|
+
const plannedLossValue = qty * lossPerUnit;
|
|
1116
|
+
if (!Number.isFinite(netRiskRatio) || !Number.isFinite(plannedLossValue))
|
|
1117
|
+
throw new EntrySubmissionValidationError(
|
|
1118
|
+
"ENTRY_ECONOMICS_INPUTS_UNAVAILABLE"
|
|
1119
|
+
);
|
|
1120
|
+
if (limits.minimumNetRiskRatio != null && netRiskRatio <= limits.minimumNetRiskRatio)
|
|
1121
|
+
throw new EntrySubmissionValidationError(
|
|
1122
|
+
"ENTRY_RR_BELOW_MIN_AT_SUBMISSION"
|
|
1123
|
+
);
|
|
1124
|
+
const riskExceeded = limits.maxLossValue != null && plannedLossValue > limits.maxLossValue * (1 + 1e-9);
|
|
1125
|
+
return { netRiskRatio, plannedLossValue, riskExceeded };
|
|
1126
|
+
};
|
|
1127
|
+
|
|
1128
|
+
// src/strategy/entrySubmissionValidation.ts
|
|
1129
|
+
var MAX_ENTRY_QUOTE_AGE_MS = 5e3;
|
|
1130
|
+
var toFiniteNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1131
|
+
var getOrderArrivalSnapshot = async ({
|
|
1132
|
+
connector,
|
|
1133
|
+
symbol,
|
|
1134
|
+
requireTimestamp = false
|
|
1135
|
+
}) => {
|
|
1136
|
+
if (typeof connector.getTopOfBookTicker !== "function") {
|
|
1137
|
+
return {
|
|
1138
|
+
arrivalSnapshotTime: Date.now(),
|
|
1139
|
+
arrivalSource: "unavailable",
|
|
1140
|
+
bid: null,
|
|
1141
|
+
ask: null,
|
|
1142
|
+
arrivalMid: null,
|
|
1143
|
+
spreadBps: null
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
try {
|
|
1147
|
+
const ticker = await connector.getTopOfBookTicker(symbol);
|
|
1148
|
+
const arrivalSnapshotTime = toFiniteNumberOrNull(ticker?.timestamp);
|
|
1149
|
+
const timestampUnavailable = requireTimestamp && arrivalSnapshotTime == null;
|
|
1150
|
+
const bid = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.bidPrice);
|
|
1151
|
+
const ask = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.askPrice);
|
|
1152
|
+
const arrivalMid = bid != null && ask != null ? (bid + ask) / 2 : null;
|
|
1153
|
+
const spreadBps = bid != null && ask != null && arrivalMid != null && arrivalMid > 0 ? (ask - bid) / arrivalMid * 1e4 : null;
|
|
1154
|
+
return {
|
|
1155
|
+
arrivalSnapshotTime: arrivalSnapshotTime ?? Date.now(),
|
|
1156
|
+
arrivalSource: "top_of_book",
|
|
1157
|
+
bid,
|
|
1158
|
+
ask,
|
|
1159
|
+
arrivalMid,
|
|
1160
|
+
spreadBps
|
|
1161
|
+
};
|
|
1162
|
+
} catch (error) {
|
|
1163
|
+
import_logger.logger.warn(
|
|
1164
|
+
"runtime order arrival snapshot failed: %s %s",
|
|
1165
|
+
symbol,
|
|
1166
|
+
error?.message || String(error)
|
|
1167
|
+
);
|
|
1168
|
+
return {
|
|
1169
|
+
arrivalSnapshotTime: Date.now(),
|
|
1170
|
+
arrivalSource: "top_of_book_error",
|
|
1171
|
+
bid: null,
|
|
1172
|
+
ask: null,
|
|
1173
|
+
arrivalMid: null,
|
|
1174
|
+
spreadBps: null
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
};
|
|
1178
|
+
var validateEntryProtectionAtArrival = ({
|
|
1179
|
+
direction,
|
|
1180
|
+
signalPrice,
|
|
1181
|
+
bid,
|
|
1182
|
+
ask,
|
|
1183
|
+
arrivalMid,
|
|
1184
|
+
takeProfits,
|
|
1185
|
+
stopLossPrice
|
|
1186
|
+
}) => {
|
|
1187
|
+
const entryReference = direction === "LONG" ? ask ?? arrivalMid ?? signalPrice : bid ?? arrivalMid ?? signalPrice;
|
|
1188
|
+
const stopReference = direction === "LONG" ? bid ?? arrivalMid ?? signalPrice : ask ?? arrivalMid ?? signalPrice;
|
|
1189
|
+
if (!Number.isFinite(entryReference) || entryReference <= 0 || !Number.isFinite(stopReference) || stopReference <= 0) {
|
|
1190
|
+
throw new Error("INVALID_ENTRY_REFERENCE_AT_ARRIVAL");
|
|
1191
|
+
}
|
|
1192
|
+
for (const takeProfit of takeProfits) {
|
|
1193
|
+
if (!Number.isFinite(takeProfit.price) || takeProfit.price <= 0 || (direction === "LONG" ? takeProfit.price <= entryReference : takeProfit.price >= entryReference)) {
|
|
1194
|
+
throw new Error("TAKE_PROFIT_CROSSED_BEFORE_ENTRY");
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
if (stopLossPrice != null && (!Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || (direction === "LONG" ? stopLossPrice >= stopReference : stopLossPrice <= stopReference))) {
|
|
1198
|
+
throw new Error("STOP_LOSS_CROSSED_BEFORE_ENTRY");
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
var createEntrySubmissionValidation = ({
|
|
1202
|
+
connector,
|
|
1203
|
+
symbol,
|
|
1204
|
+
direction,
|
|
1205
|
+
takeProfits,
|
|
1206
|
+
stopLossPrice,
|
|
1207
|
+
limits,
|
|
1208
|
+
onValidated
|
|
1209
|
+
}) => {
|
|
1210
|
+
if (!limits) return void 0;
|
|
1211
|
+
if (!connector.capabilities?.orderSubmissionValidation)
|
|
1212
|
+
throw new EntrySubmissionValidationError(
|
|
1213
|
+
"ENTRY_SUBMISSION_VALIDATION_UNSUPPORTED"
|
|
1214
|
+
);
|
|
1215
|
+
return async ({ qty, executionPrice }) => {
|
|
1216
|
+
const simulated = Reflect.get(connector, "__tradejsTestConnector") === true;
|
|
1217
|
+
const snapshot2 = simulated && executionPrice != null ? {
|
|
1218
|
+
arrivalSnapshotTime: Date.now(),
|
|
1219
|
+
arrivalSource: "simulation",
|
|
1220
|
+
bid: executionPrice,
|
|
1221
|
+
ask: executionPrice,
|
|
1222
|
+
arrivalMid: executionPrice,
|
|
1223
|
+
spreadBps: 0
|
|
1224
|
+
} : await getOrderArrivalSnapshot({
|
|
1225
|
+
connector,
|
|
1226
|
+
symbol,
|
|
1227
|
+
requireTimestamp: true
|
|
1228
|
+
});
|
|
1229
|
+
const age = Date.now() - snapshot2.arrivalSnapshotTime;
|
|
1230
|
+
if (snapshot2.bid == null || snapshot2.ask == null || !Number.isFinite(snapshot2.bid) || !Number.isFinite(snapshot2.ask) || snapshot2.bid <= 0 || snapshot2.ask < snapshot2.bid)
|
|
1231
|
+
throw new EntrySubmissionValidationError(
|
|
1232
|
+
"ENTRY_QUOTE_UNAVAILABLE_AT_SUBMISSION"
|
|
1233
|
+
);
|
|
1234
|
+
if (!Number.isFinite(age) || age < -MAX_ENTRY_QUOTE_AGE_MS || age > MAX_ENTRY_QUOTE_AGE_MS)
|
|
1235
|
+
throw new EntrySubmissionValidationError(
|
|
1236
|
+
"ENTRY_QUOTE_STALE_AT_SUBMISSION"
|
|
1237
|
+
);
|
|
1238
|
+
const price = direction === "LONG" ? snapshot2.ask : snapshot2.bid;
|
|
1239
|
+
validateEntryProtectionAtArrival({
|
|
1240
|
+
direction,
|
|
1241
|
+
signalPrice: price,
|
|
1242
|
+
bid: snapshot2.bid,
|
|
1243
|
+
ask: snapshot2.ask,
|
|
1244
|
+
arrivalMid: snapshot2.arrivalMid,
|
|
1245
|
+
takeProfits,
|
|
1246
|
+
stopLossPrice
|
|
1247
|
+
});
|
|
1248
|
+
const prices = takeProfits.map((tp) => tp.price);
|
|
1249
|
+
const economics2 = validateEntryExecution({
|
|
1250
|
+
direction,
|
|
1251
|
+
price,
|
|
1252
|
+
qty,
|
|
1253
|
+
stopLossPrice,
|
|
1254
|
+
takeProfitPrice: direction === "LONG" ? Math.max(...prices) : Math.min(...prices),
|
|
1255
|
+
limits
|
|
1256
|
+
});
|
|
1257
|
+
onValidated?.(snapshot2, economics2);
|
|
1258
|
+
};
|
|
1259
|
+
};
|
|
1260
|
+
|
|
1064
1261
|
// src/ai.ts
|
|
1065
1262
|
var import_aiLanguages2 = require("@tradejs/core/aiLanguages");
|
|
1066
1263
|
var import_redis = require("@tradejs/infra/redis");
|
|
@@ -1645,7 +1842,7 @@ var import_fs = __toESM(require("fs"));
|
|
|
1645
1842
|
var import_path = __toESM(require("path"));
|
|
1646
1843
|
var import_url = require("url");
|
|
1647
1844
|
var import_config = require("@tradejs/core/config");
|
|
1648
|
-
var
|
|
1845
|
+
var import_logger2 = require("@tradejs/infra/logger");
|
|
1649
1846
|
var CONFIG_FILE_NAMES = [
|
|
1650
1847
|
"tradejs.config.ts",
|
|
1651
1848
|
"tradejs.config.mts",
|
|
@@ -1912,11 +2109,11 @@ var loadTradejsConfig = async (cwd = getTradejsProjectCwd()) => {
|
|
|
1912
2109
|
cachedByCwd.set(cwd, config);
|
|
1913
2110
|
if (!announcedConfigFile.has(configFilePath)) {
|
|
1914
2111
|
announcedConfigFile.add(configFilePath);
|
|
1915
|
-
|
|
2112
|
+
import_logger2.logger.log("debug", "Loaded TradeJS config: %s", configFilePath);
|
|
1916
2113
|
}
|
|
1917
2114
|
return config;
|
|
1918
2115
|
} catch (error) {
|
|
1919
|
-
|
|
2116
|
+
import_logger2.logger.log(
|
|
1920
2117
|
"warn",
|
|
1921
2118
|
"Failed to load TradeJS config from %s: %s",
|
|
1922
2119
|
configFilePath,
|
|
@@ -2864,7 +3061,7 @@ var import_node_crypto = require("crypto");
|
|
|
2864
3061
|
var import_constants = require("@tradejs/core/constants");
|
|
2865
3062
|
var import_time = require("@tradejs/core/time");
|
|
2866
3063
|
var import_trade = require("@tradejs/core/trade");
|
|
2867
|
-
var
|
|
3064
|
+
var import_logger3 = require("@tradejs/infra/logger");
|
|
2868
3065
|
var import_redis2 = require("@tradejs/infra/redis");
|
|
2869
3066
|
var now = () => Date.now();
|
|
2870
3067
|
var toRandomOrderSuffix = () => (0, import_node_crypto.randomUUID)().replace(/-/g, "").slice(0, 12).toLowerCase();
|
|
@@ -2923,7 +3120,7 @@ var recordRuntimeTradeOpen = async (params) => {
|
|
|
2923
3120
|
)
|
|
2924
3121
|
]);
|
|
2925
3122
|
} catch (error) {
|
|
2926
|
-
|
|
3123
|
+
import_logger3.logger.error(
|
|
2927
3124
|
"runtime trade open journal failed: %s %s",
|
|
2928
3125
|
record.symbol,
|
|
2929
3126
|
error?.message || String(error)
|
|
@@ -2991,7 +3188,7 @@ var recordRuntimeTradeIncrease = async (params) => {
|
|
|
2991
3188
|
)
|
|
2992
3189
|
]);
|
|
2993
3190
|
} catch (error) {
|
|
2994
|
-
|
|
3191
|
+
import_logger3.logger.error(
|
|
2995
3192
|
"runtime trade increase journal failed: %s %s",
|
|
2996
3193
|
symbol,
|
|
2997
3194
|
error?.message || String(error)
|
|
@@ -3098,7 +3295,7 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
3098
3295
|
)
|
|
3099
3296
|
]);
|
|
3100
3297
|
} catch (error) {
|
|
3101
|
-
|
|
3298
|
+
import_logger3.logger.error(
|
|
3102
3299
|
"runtime trade close journal failed: %s %s",
|
|
3103
3300
|
symbol,
|
|
3104
3301
|
error?.message || String(error)
|
|
@@ -3108,11 +3305,11 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
3108
3305
|
};
|
|
3109
3306
|
|
|
3110
3307
|
// src/strategyHelpers/marketContextStages.ts
|
|
3111
|
-
var
|
|
3308
|
+
var import_logger8 = require("@tradejs/infra/logger");
|
|
3112
3309
|
|
|
3113
3310
|
// src/strategyHelpers/binanceMarketContext.ts
|
|
3114
3311
|
var import_marketContext = require("@tradejs/infra/timescale/marketContext");
|
|
3115
|
-
var
|
|
3312
|
+
var import_logger4 = require("@tradejs/infra/logger");
|
|
3116
3313
|
var import_strategies = require("@tradejs/core/strategies");
|
|
3117
3314
|
|
|
3118
3315
|
// src/binanceBreadthUniverses.ts
|
|
@@ -3476,7 +3673,7 @@ var parseEnabledFlag = (value, env) => {
|
|
|
3476
3673
|
if (normalized === "live") return env !== "BACKTEST";
|
|
3477
3674
|
return false;
|
|
3478
3675
|
};
|
|
3479
|
-
var
|
|
3676
|
+
var toFiniteNumberOrNull2 = (value) => {
|
|
3480
3677
|
const numeric = typeof value === "number" ? value : Number(value);
|
|
3481
3678
|
return Number.isFinite(numeric) ? numeric : null;
|
|
3482
3679
|
};
|
|
@@ -3601,14 +3798,14 @@ var toTradeFlowContext = (row, interval) => row ? {
|
|
|
3601
3798
|
asOfTs: row.ts.getTime(),
|
|
3602
3799
|
ageMs: row.ageMs,
|
|
3603
3800
|
stale: row.stale,
|
|
3604
|
-
trades:
|
|
3605
|
-
buyPressurePct:
|
|
3606
|
-
buyBaseVolume:
|
|
3607
|
-
sellBaseVolume:
|
|
3608
|
-
buyQuoteVolume:
|
|
3609
|
-
sellQuoteVolume:
|
|
3610
|
-
netBaseDelta:
|
|
3611
|
-
netQuoteDelta:
|
|
3801
|
+
trades: toFiniteNumberOrNull2(row.trades),
|
|
3802
|
+
buyPressurePct: toFiniteNumberOrNull2(row.buyPressurePct),
|
|
3803
|
+
buyBaseVolume: toFiniteNumberOrNull2(row.buyBaseVolume),
|
|
3804
|
+
sellBaseVolume: toFiniteNumberOrNull2(row.sellBaseVolume),
|
|
3805
|
+
buyQuoteVolume: toFiniteNumberOrNull2(row.buyQuoteVolume),
|
|
3806
|
+
sellQuoteVolume: toFiniteNumberOrNull2(row.sellQuoteVolume),
|
|
3807
|
+
netBaseDelta: toFiniteNumberOrNull2(row.netBaseDelta),
|
|
3808
|
+
netQuoteDelta: toFiniteNumberOrNull2(row.netQuoteDelta)
|
|
3612
3809
|
} : null;
|
|
3613
3810
|
var getCachedReferenceRows = ({
|
|
3614
3811
|
referenceSymbols,
|
|
@@ -3697,16 +3894,16 @@ var toMarketBreadthContext = (breadth, interval) => ({
|
|
|
3697
3894
|
asOfTs: breadth.ts.getTime(),
|
|
3698
3895
|
ageMs: breadth.ageMs,
|
|
3699
3896
|
stale: breadth.stale,
|
|
3700
|
-
symbolsCount:
|
|
3701
|
-
advancers:
|
|
3702
|
-
decliners:
|
|
3703
|
-
unchanged:
|
|
3704
|
-
advanceDeclineRatio:
|
|
3705
|
-
pctAboveMa20:
|
|
3706
|
-
pctAboveMa50:
|
|
3707
|
-
equalWeightedReturn:
|
|
3708
|
-
volumeWeightedReturn:
|
|
3709
|
-
dispersion:
|
|
3897
|
+
symbolsCount: toFiniteNumberOrNull2(breadth.symbolsCount),
|
|
3898
|
+
advancers: toFiniteNumberOrNull2(breadth.advancers),
|
|
3899
|
+
decliners: toFiniteNumberOrNull2(breadth.decliners),
|
|
3900
|
+
unchanged: toFiniteNumberOrNull2(breadth.unchanged),
|
|
3901
|
+
advanceDeclineRatio: toFiniteNumberOrNull2(breadth.advanceDeclineRatio),
|
|
3902
|
+
pctAboveMa20: toFiniteNumberOrNull2(breadth.pctAboveMa20),
|
|
3903
|
+
pctAboveMa50: toFiniteNumberOrNull2(breadth.pctAboveMa50),
|
|
3904
|
+
equalWeightedReturn: toFiniteNumberOrNull2(breadth.equalWeightedReturn),
|
|
3905
|
+
volumeWeightedReturn: toFiniteNumberOrNull2(breadth.volumeWeightedReturn),
|
|
3906
|
+
dispersion: toFiniteNumberOrNull2(breadth.dispersion)
|
|
3710
3907
|
});
|
|
3711
3908
|
var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
3712
3909
|
const {
|
|
@@ -3807,46 +4004,46 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
3807
4004
|
asOfTs: primaryBreadth.ts.getTime(),
|
|
3808
4005
|
ageMs: primaryBreadth.ageMs,
|
|
3809
4006
|
stale: primaryBreadth.stale,
|
|
3810
|
-
btcReturn1h:
|
|
4007
|
+
btcReturn1h: toFiniteNumberOrNull2(
|
|
3811
4008
|
primaryBreadth.btcReturn1h
|
|
3812
4009
|
),
|
|
3813
|
-
btcReturn4h:
|
|
4010
|
+
btcReturn4h: toFiniteNumberOrNull2(
|
|
3814
4011
|
primaryBreadth.btcReturn4h
|
|
3815
4012
|
),
|
|
3816
|
-
btcReturn24h:
|
|
4013
|
+
btcReturn24h: toFiniteNumberOrNull2(
|
|
3817
4014
|
primaryBreadth.btcReturn24h
|
|
3818
4015
|
),
|
|
3819
|
-
altBasketReturn1h:
|
|
4016
|
+
altBasketReturn1h: toFiniteNumberOrNull2(
|
|
3820
4017
|
primaryBreadth.altBasketReturn1h
|
|
3821
4018
|
),
|
|
3822
|
-
altBasketReturn4h:
|
|
4019
|
+
altBasketReturn4h: toFiniteNumberOrNull2(
|
|
3823
4020
|
primaryBreadth.altBasketReturn4h
|
|
3824
4021
|
),
|
|
3825
|
-
altBasketReturn24h:
|
|
4022
|
+
altBasketReturn24h: toFiniteNumberOrNull2(
|
|
3826
4023
|
primaryBreadth.altBasketReturn24h
|
|
3827
4024
|
),
|
|
3828
|
-
btcVsAltReturn1h:
|
|
4025
|
+
btcVsAltReturn1h: toFiniteNumberOrNull2(
|
|
3829
4026
|
primaryBreadth.btcVsAltReturn1h
|
|
3830
4027
|
),
|
|
3831
|
-
btcVsAltReturn4h:
|
|
4028
|
+
btcVsAltReturn4h: toFiniteNumberOrNull2(
|
|
3832
4029
|
primaryBreadth.btcVsAltReturn4h
|
|
3833
4030
|
),
|
|
3834
|
-
btcVsAltReturn24h:
|
|
4031
|
+
btcVsAltReturn24h: toFiniteNumberOrNull2(
|
|
3835
4032
|
primaryBreadth.btcVsAltReturn24h
|
|
3836
4033
|
),
|
|
3837
|
-
btcTurnoverShare1h:
|
|
4034
|
+
btcTurnoverShare1h: toFiniteNumberOrNull2(
|
|
3838
4035
|
primaryBreadth.btcTurnoverShare1h
|
|
3839
4036
|
),
|
|
3840
|
-
btcTurnoverShare24h:
|
|
4037
|
+
btcTurnoverShare24h: toFiniteNumberOrNull2(
|
|
3841
4038
|
primaryBreadth.btcTurnoverShare24h
|
|
3842
4039
|
),
|
|
3843
|
-
btcTurnoverShareChange24h:
|
|
4040
|
+
btcTurnoverShareChange24h: toFiniteNumberOrNull2(
|
|
3844
4041
|
primaryBreadth.btcTurnoverShareChange24h
|
|
3845
4042
|
),
|
|
3846
|
-
altVolToBtcVol24h:
|
|
4043
|
+
altVolToBtcVol24h: toFiniteNumberOrNull2(
|
|
3847
4044
|
primaryBreadth.altVolToBtcVol24h
|
|
3848
4045
|
),
|
|
3849
|
-
altDispersion24h:
|
|
4046
|
+
altDispersion24h: toFiniteNumberOrNull2(
|
|
3850
4047
|
primaryBreadth.altDispersion24h
|
|
3851
4048
|
),
|
|
3852
4049
|
regime: primaryBreadth.btcAltRegime ?? "unknown"
|
|
@@ -3863,7 +4060,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
3863
4060
|
throw error;
|
|
3864
4061
|
}
|
|
3865
4062
|
binanceMarketContextUnavailable = true;
|
|
3866
|
-
|
|
4063
|
+
import_logger4.logger.warn(
|
|
3867
4064
|
"Binance market context disabled after Timescale read failure: %s",
|
|
3868
4065
|
String(error)
|
|
3869
4066
|
);
|
|
@@ -3873,7 +4070,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
3873
4070
|
|
|
3874
4071
|
// src/strategyHelpers/coinMarketCapContext.ts
|
|
3875
4072
|
var import_strategies2 = require("@tradejs/core/strategies");
|
|
3876
|
-
var
|
|
4073
|
+
var import_logger5 = require("@tradejs/infra/logger");
|
|
3877
4074
|
var import_marketContext2 = require("@tradejs/infra/timescale/marketContext");
|
|
3878
4075
|
var DEFAULT_MAX_AGE_MS = 48 * 60 * 6e4;
|
|
3879
4076
|
var SOURCE_GLOBAL_DAILY = "coinmarketcap_global";
|
|
@@ -3911,7 +4108,7 @@ var asInt = (value, fallback) => {
|
|
|
3911
4108
|
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
3912
4109
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
3913
4110
|
};
|
|
3914
|
-
var
|
|
4111
|
+
var toFiniteNumberOrNull3 = (value) => {
|
|
3915
4112
|
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
|
|
3916
4113
|
return Number.isFinite(numeric) ? numeric : null;
|
|
3917
4114
|
};
|
|
@@ -4145,12 +4342,12 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4145
4342
|
const ethRow = references.get("ETHUSDT") ?? null;
|
|
4146
4343
|
const previousBtcRow = previousReferences.get("BTCUSDT") ?? null;
|
|
4147
4344
|
const previousEthRow = previousReferences.get("ETHUSDT") ?? null;
|
|
4148
|
-
const btcMarketCapUsd =
|
|
4149
|
-
const ethMarketCapUsd =
|
|
4150
|
-
const previousBtcMarketCapUsd =
|
|
4345
|
+
const btcMarketCapUsd = toFiniteNumberOrNull3(btcRow?.marketCapUsd);
|
|
4346
|
+
const ethMarketCapUsd = toFiniteNumberOrNull3(ethRow?.marketCapUsd);
|
|
4347
|
+
const previousBtcMarketCapUsd = toFiniteNumberOrNull3(
|
|
4151
4348
|
previousBtcRow?.marketCapUsd
|
|
4152
4349
|
);
|
|
4153
|
-
const previousEthMarketCapUsd =
|
|
4350
|
+
const previousEthMarketCapUsd = toFiniteNumberOrNull3(
|
|
4154
4351
|
previousEthRow?.marketCapUsd
|
|
4155
4352
|
);
|
|
4156
4353
|
const ethBtcMarketCapRatio = safeDivide(ethMarketCapUsd, btcMarketCapUsd);
|
|
@@ -4159,16 +4356,16 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4159
4356
|
previousBtcMarketCapUsd
|
|
4160
4357
|
);
|
|
4161
4358
|
const ethBtcMarketCapRatioChange24hPct = ethBtcMarketCapRatio != null && previousEthBtcMarketCapRatio != null && previousEthBtcMarketCapRatio > 0 ? (ethBtcMarketCapRatio - previousEthBtcMarketCapRatio) / previousEthBtcMarketCapRatio : null;
|
|
4162
|
-
const btcVolumeUsd =
|
|
4163
|
-
const ethVolumeUsd =
|
|
4359
|
+
const btcVolumeUsd = toFiniteNumberOrNull3(btcRow?.volumeUsd);
|
|
4360
|
+
const ethVolumeUsd = toFiniteNumberOrNull3(ethRow?.volumeUsd);
|
|
4164
4361
|
const referenceStale = btcRow?.stale === true || ethRow?.stale === true || !btcRow || !ethRow;
|
|
4165
|
-
const btcDominanceChange24hPct =
|
|
4362
|
+
const btcDominanceChange24hPct = toFiniteNumberOrNull3(
|
|
4166
4363
|
globalRow?.btcDominanceChange24hPct
|
|
4167
4364
|
);
|
|
4168
|
-
const altMarketCapChange24hPct =
|
|
4365
|
+
const altMarketCapChange24hPct = toFiniteNumberOrNull3(
|
|
4169
4366
|
globalRow?.altMarketCapChange24hPct
|
|
4170
4367
|
);
|
|
4171
|
-
const altVolumeChange24hPct =
|
|
4368
|
+
const altVolumeChange24hPct = toFiniteNumberOrNull3(
|
|
4172
4369
|
globalRow?.altVolumeChange24hPct
|
|
4173
4370
|
);
|
|
4174
4371
|
const altLiquidityRegime = globalRow ? toAltLiquidityRegime({
|
|
@@ -4179,19 +4376,19 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4179
4376
|
}) : "unknown";
|
|
4180
4377
|
const exchangeLiquidityRegime = exchangeLiquidityRow ? toExchangeLiquidityRegime({
|
|
4181
4378
|
stale: exchangeLiquidityRow.stale,
|
|
4182
|
-
totalVolumeChange24hPct:
|
|
4379
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
4183
4380
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
4184
4381
|
),
|
|
4185
4382
|
fallback: exchangeLiquidityRow.liquidityRegime ?? "unknown"
|
|
4186
4383
|
}) : "unknown";
|
|
4187
4384
|
const cmc100Row = indexRows.get("cmc100") ?? null;
|
|
4188
4385
|
const cmc20Row = indexRows.get("cmc20") ?? null;
|
|
4189
|
-
const cmc100Value =
|
|
4190
|
-
const cmc20Value =
|
|
4191
|
-
const cmc100Change24hPct =
|
|
4386
|
+
const cmc100Value = toFiniteNumberOrNull3(cmc100Row?.value);
|
|
4387
|
+
const cmc20Value = toFiniteNumberOrNull3(cmc20Row?.value);
|
|
4388
|
+
const cmc100Change24hPct = toFiniteNumberOrNull3(
|
|
4192
4389
|
cmc100Row?.valueChange24hPct
|
|
4193
4390
|
);
|
|
4194
|
-
const cmc20Change24hPct =
|
|
4391
|
+
const cmc20Change24hPct = toFiniteNumberOrNull3(cmc20Row?.valueChange24hPct);
|
|
4195
4392
|
const cmc20ToCmc100Ratio = safeDivide(cmc20Value, cmc100Value);
|
|
4196
4393
|
const cmc20ToCmc100RatioChange24hPct = cmc20Change24hPct != null && cmc100Change24hPct != null ? (1 + cmc20Change24hPct) / (1 + cmc100Change24hPct) - 1 : null;
|
|
4197
4394
|
const indexStale = cmc100Row?.stale === true || cmc20Row?.stale === true || !cmc100Row || !cmc20Row;
|
|
@@ -4215,41 +4412,41 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4215
4412
|
asOfTs: globalRow.ts.getTime(),
|
|
4216
4413
|
ageMs: globalRow.ageMs,
|
|
4217
4414
|
stale: globalRow.stale,
|
|
4218
|
-
totalMarketCapUsd:
|
|
4415
|
+
totalMarketCapUsd: toFiniteNumberOrNull3(
|
|
4219
4416
|
globalRow.totalMarketCapUsd
|
|
4220
4417
|
),
|
|
4221
|
-
totalVolumeUsd:
|
|
4418
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
4222
4419
|
globalRow.totalVolumeUsd
|
|
4223
4420
|
),
|
|
4224
|
-
totalVolumeReportedUsd:
|
|
4421
|
+
totalVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
4225
4422
|
globalRow.totalVolumeReportedUsd
|
|
4226
4423
|
),
|
|
4227
|
-
altMarketCapUsd:
|
|
4424
|
+
altMarketCapUsd: toFiniteNumberOrNull3(
|
|
4228
4425
|
globalRow.altMarketCapUsd
|
|
4229
4426
|
),
|
|
4230
|
-
altVolumeUsd:
|
|
4231
|
-
altVolumeReportedUsd:
|
|
4427
|
+
altVolumeUsd: toFiniteNumberOrNull3(globalRow.altVolumeUsd),
|
|
4428
|
+
altVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
4232
4429
|
globalRow.altVolumeReportedUsd
|
|
4233
4430
|
),
|
|
4234
|
-
btcDominancePct:
|
|
4431
|
+
btcDominancePct: toFiniteNumberOrNull3(
|
|
4235
4432
|
globalRow.btcDominancePct
|
|
4236
4433
|
),
|
|
4237
|
-
ethDominancePct:
|
|
4434
|
+
ethDominancePct: toFiniteNumberOrNull3(
|
|
4238
4435
|
globalRow.ethDominancePct
|
|
4239
4436
|
),
|
|
4240
4437
|
btcDominanceChange24hPct,
|
|
4241
|
-
ethDominanceChange24hPct:
|
|
4438
|
+
ethDominanceChange24hPct: toFiniteNumberOrNull3(
|
|
4242
4439
|
globalRow.ethDominanceChange24hPct
|
|
4243
4440
|
),
|
|
4244
4441
|
altMarketCapChange24hPct,
|
|
4245
4442
|
altVolumeChange24hPct,
|
|
4246
|
-
activeCryptocurrencies:
|
|
4443
|
+
activeCryptocurrencies: toFiniteNumberOrNull3(
|
|
4247
4444
|
globalRow.activeCryptocurrencies
|
|
4248
4445
|
),
|
|
4249
|
-
activeExchanges:
|
|
4446
|
+
activeExchanges: toFiniteNumberOrNull3(
|
|
4250
4447
|
globalRow.activeExchanges
|
|
4251
4448
|
),
|
|
4252
|
-
activeMarketPairs:
|
|
4449
|
+
activeMarketPairs: toFiniteNumberOrNull3(
|
|
4253
4450
|
globalRow.activeMarketPairs
|
|
4254
4451
|
),
|
|
4255
4452
|
altLiquidityRegime
|
|
@@ -4294,22 +4491,22 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4294
4491
|
asOfTs: exchangeLiquidityRow.ts.getTime(),
|
|
4295
4492
|
ageMs: exchangeLiquidityRow.ageMs,
|
|
4296
4493
|
stale: exchangeLiquidityRow.stale,
|
|
4297
|
-
exchangesCount:
|
|
4494
|
+
exchangesCount: toFiniteNumberOrNull3(
|
|
4298
4495
|
exchangeLiquidityRow.exchangesCount
|
|
4299
4496
|
),
|
|
4300
|
-
totalVolumeUsd:
|
|
4497
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
4301
4498
|
exchangeLiquidityRow.totalVolumeUsd
|
|
4302
4499
|
),
|
|
4303
|
-
totalVolumeChange24hPct:
|
|
4500
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
4304
4501
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
4305
4502
|
),
|
|
4306
|
-
binanceVolumeUsd:
|
|
4503
|
+
binanceVolumeUsd: toFiniteNumberOrNull3(
|
|
4307
4504
|
exchangeLiquidityRow.binanceVolumeUsd
|
|
4308
4505
|
),
|
|
4309
|
-
binanceVolumeShare:
|
|
4506
|
+
binanceVolumeShare: toFiniteNumberOrNull3(
|
|
4310
4507
|
exchangeLiquidityRow.binanceVolumeShare
|
|
4311
4508
|
),
|
|
4312
|
-
topExchangeVolumeShare:
|
|
4509
|
+
topExchangeVolumeShare: toFiniteNumberOrNull3(
|
|
4313
4510
|
exchangeLiquidityRow.topExchangeVolumeShare
|
|
4314
4511
|
),
|
|
4315
4512
|
liquidityRegime: exchangeLiquidityRegime
|
|
@@ -4322,11 +4519,11 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4322
4519
|
asOfTs: fearGreedRow.ts.getTime(),
|
|
4323
4520
|
ageMs: fearGreedRow.ageMs,
|
|
4324
4521
|
stale: fearGreedRow.stale,
|
|
4325
|
-
value:
|
|
4326
|
-
valueChange24h:
|
|
4522
|
+
value: toFiniteNumberOrNull3(fearGreedRow.value),
|
|
4523
|
+
valueChange24h: toFiniteNumberOrNull3(
|
|
4327
4524
|
fearGreedRow.valueChange24h
|
|
4328
4525
|
),
|
|
4329
|
-
valueChange7d:
|
|
4526
|
+
valueChange7d: toFiniteNumberOrNull3(
|
|
4330
4527
|
fearGreedRow.valueChange7d
|
|
4331
4528
|
),
|
|
4332
4529
|
classification: fearGreedRow.classification ?? "Unknown",
|
|
@@ -4346,13 +4543,13 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4346
4543
|
cmc100Value,
|
|
4347
4544
|
cmc100Change24hPct,
|
|
4348
4545
|
cmc100TopConstituentSymbol: cmc100Row?.topConstituentSymbol ?? null,
|
|
4349
|
-
cmc100TopConstituentWeightPct:
|
|
4546
|
+
cmc100TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
4350
4547
|
cmc100Row?.topConstituentWeightPct
|
|
4351
4548
|
),
|
|
4352
4549
|
cmc20Value,
|
|
4353
4550
|
cmc20Change24hPct,
|
|
4354
4551
|
cmc20TopConstituentSymbol: cmc20Row?.topConstituentSymbol ?? null,
|
|
4355
|
-
cmc20TopConstituentWeightPct:
|
|
4552
|
+
cmc20TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
4356
4553
|
cmc20Row?.topConstituentWeightPct
|
|
4357
4554
|
),
|
|
4358
4555
|
cmc20ToCmc100Ratio,
|
|
@@ -4370,7 +4567,7 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4370
4567
|
throw error;
|
|
4371
4568
|
}
|
|
4372
4569
|
coinMarketCapContextUnavailable = true;
|
|
4373
|
-
|
|
4570
|
+
import_logger5.logger.warn(
|
|
4374
4571
|
"CoinMarketCap context disabled after Timescale read failure: %s",
|
|
4375
4572
|
String(error)
|
|
4376
4573
|
);
|
|
@@ -4384,7 +4581,7 @@ var import_data2 = require("@tradejs/core/data");
|
|
|
4384
4581
|
var import_strategies3 = require("@tradejs/core/strategies");
|
|
4385
4582
|
var import_constants2 = require("@tradejs/core/constants");
|
|
4386
4583
|
var import_derivatives = require("@tradejs/infra/timescale/derivatives");
|
|
4387
|
-
var
|
|
4584
|
+
var import_logger6 = require("@tradejs/infra/logger");
|
|
4388
4585
|
var STORED_INTERVALS = ["15m", "1h"];
|
|
4389
4586
|
var CONTEXT_INTERVALS = ["15m", "1h"];
|
|
4390
4587
|
var DEFAULT_LOOKBACK_HOURS = 48;
|
|
@@ -4439,7 +4636,7 @@ var resolvePrimaryReferenceSymbol2 = () => PRIMARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
|
4439
4636
|
var resolveSecondaryReferenceSymbol = () => SECONDARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
4440
4637
|
var getPrimaryIntervalContext = (context) => context?.intervals["15m"] ?? context?.intervals["1h"] ?? null;
|
|
4441
4638
|
var hasDerivativesSymbolData = (context) => Object.keys(context.intervals).length > 0 && !context.summary.riskFlags.includes("missing_derivatives");
|
|
4442
|
-
var
|
|
4639
|
+
var toFiniteNumberOrNull4 = (value) => {
|
|
4443
4640
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
4444
4641
|
if (typeof value === "string" && value.trim()) {
|
|
4445
4642
|
const parsed = Number(value);
|
|
@@ -4453,8 +4650,8 @@ var roundNullable = (value, digits = 4) => {
|
|
|
4453
4650
|
return Math.round(value * multiplier) / multiplier;
|
|
4454
4651
|
};
|
|
4455
4652
|
var deltaNullable = (targetValue, referenceValue) => {
|
|
4456
|
-
const target =
|
|
4457
|
-
const reference =
|
|
4653
|
+
const target = toFiniteNumberOrNull4(targetValue);
|
|
4654
|
+
const reference = toFiniteNumberOrNull4(referenceValue);
|
|
4458
4655
|
return target == null || reference == null ? null : roundNullable(target - reference);
|
|
4459
4656
|
};
|
|
4460
4657
|
var buildTargetDerivedContext = (params) => {
|
|
@@ -4616,7 +4813,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
4616
4813
|
throw error;
|
|
4617
4814
|
}
|
|
4618
4815
|
derivativesContextUnavailable = true;
|
|
4619
|
-
|
|
4816
|
+
import_logger6.logger.warn(
|
|
4620
4817
|
"Derivatives context disabled after Timescale read failure: %s",
|
|
4621
4818
|
String(error)
|
|
4622
4819
|
);
|
|
@@ -4628,7 +4825,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
4628
4825
|
var import_data3 = require("@tradejs/core/data");
|
|
4629
4826
|
var import_strategies4 = require("@tradejs/core/strategies");
|
|
4630
4827
|
var import_hyperliquidWhales2 = require("@tradejs/infra/timescale/hyperliquidWhales");
|
|
4631
|
-
var
|
|
4828
|
+
var import_logger7 = require("@tradejs/infra/logger");
|
|
4632
4829
|
|
|
4633
4830
|
// src/hyperliquidWhaleUniverse.ts
|
|
4634
4831
|
var import_node_crypto3 = require("crypto");
|
|
@@ -5148,7 +5345,7 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
5148
5345
|
throw error;
|
|
5149
5346
|
}
|
|
5150
5347
|
hyperliquidWhaleContextUnavailable = true;
|
|
5151
|
-
|
|
5348
|
+
import_logger7.logger.warn(
|
|
5152
5349
|
"Hyperliquid whale context disabled after Timescale read failure: %s",
|
|
5153
5350
|
String(error)
|
|
5154
5351
|
);
|
|
@@ -5231,7 +5428,7 @@ var runMarketContextStage = async ({
|
|
|
5231
5428
|
elapsedMs: Date.now() - startedAt
|
|
5232
5429
|
};
|
|
5233
5430
|
if (status === "timed_out") {
|
|
5234
|
-
|
|
5431
|
+
import_logger8.logger.warn(
|
|
5235
5432
|
"Market context stage timed out: %s after %sms",
|
|
5236
5433
|
stage,
|
|
5237
5434
|
result.elapsedMs
|
|
@@ -5421,79 +5618,10 @@ var enrichSignalWithAi = async ({
|
|
|
5421
5618
|
signal.aiAnalysis = analysis;
|
|
5422
5619
|
return resolveAiQuality(analysis, direction);
|
|
5423
5620
|
} catch (err) {
|
|
5424
|
-
|
|
5621
|
+
import_logger9.logger.error("AI analysis error: %s %s", symbol, formatAiError(err));
|
|
5425
5622
|
}
|
|
5426
5623
|
return void 0;
|
|
5427
5624
|
};
|
|
5428
|
-
var toFiniteNumberOrNull4 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
5429
|
-
var getOrderArrivalSnapshot = async ({
|
|
5430
|
-
connector,
|
|
5431
|
-
symbol
|
|
5432
|
-
}) => {
|
|
5433
|
-
if (typeof connector.getTopOfBookTicker !== "function") {
|
|
5434
|
-
return {
|
|
5435
|
-
arrivalSnapshotTime: Date.now(),
|
|
5436
|
-
arrivalSource: "unavailable",
|
|
5437
|
-
bid: null,
|
|
5438
|
-
ask: null,
|
|
5439
|
-
arrivalMid: null,
|
|
5440
|
-
spreadBps: null
|
|
5441
|
-
};
|
|
5442
|
-
}
|
|
5443
|
-
try {
|
|
5444
|
-
const ticker = await connector.getTopOfBookTicker(symbol);
|
|
5445
|
-
const arrivalSnapshotTime = toFiniteNumberOrNull4(ticker?.timestamp);
|
|
5446
|
-
const bid = toFiniteNumberOrNull4(ticker?.bidPrice);
|
|
5447
|
-
const ask = toFiniteNumberOrNull4(ticker?.askPrice);
|
|
5448
|
-
const arrivalMid = bid != null && ask != null ? (bid + ask) / 2 : null;
|
|
5449
|
-
const spreadBps = bid != null && ask != null && arrivalMid != null && arrivalMid > 0 ? (ask - bid) / arrivalMid * 1e4 : null;
|
|
5450
|
-
return {
|
|
5451
|
-
arrivalSnapshotTime: arrivalSnapshotTime ?? Date.now(),
|
|
5452
|
-
arrivalSource: "top_of_book",
|
|
5453
|
-
bid,
|
|
5454
|
-
ask,
|
|
5455
|
-
arrivalMid,
|
|
5456
|
-
spreadBps
|
|
5457
|
-
};
|
|
5458
|
-
} catch (error) {
|
|
5459
|
-
import_logger8.logger.warn(
|
|
5460
|
-
"runtime order arrival snapshot failed: %s %s",
|
|
5461
|
-
symbol,
|
|
5462
|
-
error?.message || String(error)
|
|
5463
|
-
);
|
|
5464
|
-
return {
|
|
5465
|
-
arrivalSnapshotTime: Date.now(),
|
|
5466
|
-
arrivalSource: "top_of_book_error",
|
|
5467
|
-
bid: null,
|
|
5468
|
-
ask: null,
|
|
5469
|
-
arrivalMid: null,
|
|
5470
|
-
spreadBps: null
|
|
5471
|
-
};
|
|
5472
|
-
}
|
|
5473
|
-
};
|
|
5474
|
-
var validateEntryProtectionAtArrival = ({
|
|
5475
|
-
direction,
|
|
5476
|
-
signalPrice,
|
|
5477
|
-
bid,
|
|
5478
|
-
ask,
|
|
5479
|
-
arrivalMid,
|
|
5480
|
-
takeProfits,
|
|
5481
|
-
stopLossPrice
|
|
5482
|
-
}) => {
|
|
5483
|
-
const entryReference = direction === "LONG" ? ask ?? arrivalMid ?? signalPrice : bid ?? arrivalMid ?? signalPrice;
|
|
5484
|
-
const stopReference = direction === "LONG" ? bid ?? arrivalMid ?? signalPrice : ask ?? arrivalMid ?? signalPrice;
|
|
5485
|
-
if (!Number.isFinite(entryReference) || entryReference <= 0 || !Number.isFinite(stopReference) || stopReference <= 0) {
|
|
5486
|
-
throw new Error("INVALID_ENTRY_REFERENCE_AT_ARRIVAL");
|
|
5487
|
-
}
|
|
5488
|
-
for (const takeProfit of takeProfits) {
|
|
5489
|
-
if (!Number.isFinite(takeProfit.price) || takeProfit.price <= 0 || (direction === "LONG" ? takeProfit.price <= entryReference : takeProfit.price >= entryReference)) {
|
|
5490
|
-
throw new Error("TAKE_PROFIT_CROSSED_BEFORE_ENTRY");
|
|
5491
|
-
}
|
|
5492
|
-
}
|
|
5493
|
-
if (stopLossPrice != null && (!Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || (direction === "LONG" ? stopLossPrice >= stopReference : stopLossPrice <= stopReference))) {
|
|
5494
|
-
throw new Error("STOP_LOSS_CROSSED_BEFORE_ENTRY");
|
|
5495
|
-
}
|
|
5496
|
-
};
|
|
5497
5625
|
var resolveRuntimeTelemetryQuality = ({
|
|
5498
5626
|
signalClosePrice,
|
|
5499
5627
|
arrivalMid,
|
|
@@ -5570,7 +5698,8 @@ var executeEntryOrder = async ({
|
|
|
5570
5698
|
signal,
|
|
5571
5699
|
beforePlaceOrder,
|
|
5572
5700
|
recordRuntimeTrade = true,
|
|
5573
|
-
leverage
|
|
5701
|
+
leverage,
|
|
5702
|
+
executionLimits
|
|
5574
5703
|
}) => {
|
|
5575
5704
|
await beforePlaceOrder?.();
|
|
5576
5705
|
const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
|
|
@@ -5581,23 +5710,57 @@ var executeEntryOrder = async ({
|
|
|
5581
5710
|
signal.orderQty = qty;
|
|
5582
5711
|
signal.orderValue = qty * currentPrice;
|
|
5583
5712
|
signal.orderFailureReason = void 0;
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5713
|
+
let arrivalSnapshot = {
|
|
5714
|
+
arrivalSnapshotTime: Date.now(),
|
|
5715
|
+
arrivalSource: "unavailable",
|
|
5716
|
+
bid: null,
|
|
5717
|
+
ask: null,
|
|
5718
|
+
arrivalMid: null,
|
|
5719
|
+
spreadBps: null
|
|
5720
|
+
};
|
|
5721
|
+
let orderSubmitTime = Date.now();
|
|
5722
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
5723
|
+
connector,
|
|
5724
|
+
symbol,
|
|
5725
|
+
direction,
|
|
5726
|
+
takeProfits,
|
|
5727
|
+
stopLossPrice,
|
|
5728
|
+
limits: executionLimits,
|
|
5729
|
+
onValidated: (snapshot2, economics2) => {
|
|
5730
|
+
arrivalSnapshot = snapshot2;
|
|
5731
|
+
orderSubmitTime = Date.now();
|
|
5732
|
+
if (executionLimits?.maxLossValue != null) {
|
|
5733
|
+
signal.riskDecision = {
|
|
5734
|
+
stage: "order_plan",
|
|
5735
|
+
status: economics2.riskExceeded ? "warning" : "approved",
|
|
5736
|
+
reason: economics2.riskExceeded ? "ENTRY_RISK_EXCEEDS_MAX_LOSS_AT_SUBMISSION" : "PLANNED_LOSS_WITHIN_MAX_LOSS_VALUE",
|
|
5737
|
+
plannedLossValue: economics2.plannedLossValue,
|
|
5738
|
+
maxLossValue: executionLimits.maxLossValue,
|
|
5739
|
+
limitUtilization: economics2.plannedLossValue / executionLimits.maxLossValue,
|
|
5740
|
+
enforced: false
|
|
5741
|
+
};
|
|
5742
|
+
}
|
|
5743
|
+
}
|
|
5744
|
+
});
|
|
5745
|
+
if (!beforeSubmit) {
|
|
5746
|
+
arrivalSnapshot = await getOrderArrivalSnapshot({ connector, symbol });
|
|
5747
|
+
try {
|
|
5748
|
+
validateEntryProtectionAtArrival({
|
|
5749
|
+
direction,
|
|
5750
|
+
signalPrice: currentPrice,
|
|
5751
|
+
bid: arrivalSnapshot.bid,
|
|
5752
|
+
ask: arrivalSnapshot.ask,
|
|
5753
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
5754
|
+
takeProfits,
|
|
5755
|
+
stopLossPrice
|
|
5756
|
+
});
|
|
5757
|
+
} catch (error) {
|
|
5758
|
+
signal.orderStatus = "failed";
|
|
5759
|
+
signal.orderFailureReason = error.message;
|
|
5760
|
+
throw error;
|
|
5761
|
+
}
|
|
5762
|
+
orderSubmitTime = Date.now();
|
|
5599
5763
|
}
|
|
5600
|
-
const orderSubmitTime = Date.now();
|
|
5601
5764
|
const orderPlaced = await connector.placeOrder({
|
|
5602
5765
|
symbol,
|
|
5603
5766
|
qty,
|
|
@@ -5608,7 +5771,8 @@ var executeEntryOrder = async ({
|
|
|
5608
5771
|
direction,
|
|
5609
5772
|
...typeof leverage === "number" && Number.isFinite(leverage) ? { leverage } : {},
|
|
5610
5773
|
orderId,
|
|
5611
|
-
signal
|
|
5774
|
+
signal,
|
|
5775
|
+
...beforeSubmit ? { beforeSubmit } : {}
|
|
5612
5776
|
});
|
|
5613
5777
|
const orderAckTime = Date.now();
|
|
5614
5778
|
const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
|
|
@@ -6440,7 +6604,7 @@ var canUseSharedReplayState = ({
|
|
|
6440
6604
|
}) => (env === "BACKTEST" || env === "PARITY") && Boolean(sharedReplayKey);
|
|
6441
6605
|
|
|
6442
6606
|
// src/strategy/runtimeExecution.ts
|
|
6443
|
-
var
|
|
6607
|
+
var import_logger10 = require("@tradejs/infra/logger");
|
|
6444
6608
|
var import_types = require("@tradejs/types");
|
|
6445
6609
|
var buildExitOrderSignal = ({
|
|
6446
6610
|
strategyName,
|
|
@@ -6492,7 +6656,7 @@ var handleExitDecision = async ({
|
|
|
6492
6656
|
deploymentId: connector.deploymentId
|
|
6493
6657
|
});
|
|
6494
6658
|
if (!activeTrade) {
|
|
6495
|
-
|
|
6659
|
+
import_logger10.logger.warn(
|
|
6496
6660
|
"[%s] blocked closePosition for untracked runtime position: %s",
|
|
6497
6661
|
strategyName ?? "unknown",
|
|
6498
6662
|
symbol
|
|
@@ -6500,7 +6664,7 @@ var handleExitDecision = async ({
|
|
|
6500
6664
|
return "CLOSE_BLOCKED_BY_UNTRACKED_POSITION";
|
|
6501
6665
|
}
|
|
6502
6666
|
if (!strategyName || activeTrade.strategy !== strategyName) {
|
|
6503
|
-
|
|
6667
|
+
import_logger10.logger.warn(
|
|
6504
6668
|
"[%s] blocked closePosition for foreign runtime position: %s ownedBy=%s",
|
|
6505
6669
|
strategyName ?? "unknown",
|
|
6506
6670
|
symbol,
|
|
@@ -6553,7 +6717,7 @@ var handleExitDecision = async ({
|
|
|
6553
6717
|
exitType: closedTrade?.exitType ?? "exit"
|
|
6554
6718
|
});
|
|
6555
6719
|
} catch (notificationError) {
|
|
6556
|
-
|
|
6720
|
+
import_logger10.logger.error(
|
|
6557
6721
|
"runtime close notification error: %s %s",
|
|
6558
6722
|
symbol,
|
|
6559
6723
|
notificationError
|
|
@@ -6567,7 +6731,7 @@ var handleExitDecision = async ({
|
|
|
6567
6731
|
decision,
|
|
6568
6732
|
market
|
|
6569
6733
|
});
|
|
6570
|
-
|
|
6734
|
+
import_logger10.logger.error("close order error: %s %s", symbol, err);
|
|
6571
6735
|
return "ORDER_ERROR";
|
|
6572
6736
|
}
|
|
6573
6737
|
return decision.code;
|
|
@@ -6594,7 +6758,7 @@ var handleProtectDecision = async ({
|
|
|
6594
6758
|
decision,
|
|
6595
6759
|
market
|
|
6596
6760
|
});
|
|
6597
|
-
|
|
6761
|
+
import_logger10.logger.error("protect position error: %s %s", symbol, err);
|
|
6598
6762
|
return "ORDER_ERROR";
|
|
6599
6763
|
}
|
|
6600
6764
|
return decision.code;
|
|
@@ -6660,7 +6824,11 @@ var executeEntryDecision = async ({
|
|
|
6660
6824
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {},
|
|
6661
6825
|
signal,
|
|
6662
6826
|
beforePlaceOrder,
|
|
6663
|
-
recordRuntimeTrade: recordRuntimeJournal
|
|
6827
|
+
recordRuntimeTrade: recordRuntimeJournal,
|
|
6828
|
+
executionLimits: resolveEntryExecutionLimits(
|
|
6829
|
+
hookCtx.strategyConfig,
|
|
6830
|
+
decision.entryContext.direction
|
|
6831
|
+
)
|
|
6664
6832
|
});
|
|
6665
6833
|
await invokeStageHooks(
|
|
6666
6834
|
"afterPlaceOrder",
|
|
@@ -6682,19 +6850,32 @@ var executeEntryDecision = async ({
|
|
|
6682
6850
|
return signal;
|
|
6683
6851
|
}
|
|
6684
6852
|
await beforePlaceOrder();
|
|
6685
|
-
const
|
|
6853
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
6686
6854
|
connector,
|
|
6687
|
-
symbol
|
|
6688
|
-
});
|
|
6689
|
-
validateEntryProtectionAtArrival({
|
|
6855
|
+
symbol,
|
|
6690
6856
|
direction: decision.entryContext.direction,
|
|
6691
|
-
signalPrice: decision.entryContext.prices.currentPrice,
|
|
6692
|
-
bid: arrivalSnapshot.bid,
|
|
6693
|
-
ask: arrivalSnapshot.ask,
|
|
6694
|
-
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
6695
6857
|
takeProfits: decision.orderPlan.takeProfits,
|
|
6696
|
-
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
6858
|
+
stopLossPrice: decision.orderPlan.stopLossPrice,
|
|
6859
|
+
limits: resolveEntryExecutionLimits(
|
|
6860
|
+
hookCtx.strategyConfig,
|
|
6861
|
+
decision.entryContext.direction
|
|
6862
|
+
)
|
|
6697
6863
|
});
|
|
6864
|
+
if (!beforeSubmit) {
|
|
6865
|
+
const arrivalSnapshot = await getOrderArrivalSnapshot({
|
|
6866
|
+
connector,
|
|
6867
|
+
symbol
|
|
6868
|
+
});
|
|
6869
|
+
validateEntryProtectionAtArrival({
|
|
6870
|
+
direction: decision.entryContext.direction,
|
|
6871
|
+
signalPrice: decision.entryContext.prices.currentPrice,
|
|
6872
|
+
bid: arrivalSnapshot.bid,
|
|
6873
|
+
ask: arrivalSnapshot.ask,
|
|
6874
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
6875
|
+
takeProfits: decision.orderPlan.takeProfits,
|
|
6876
|
+
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
6877
|
+
});
|
|
6878
|
+
}
|
|
6698
6879
|
const orderPlaced = await connector.placeOrder({
|
|
6699
6880
|
symbol,
|
|
6700
6881
|
qty: decision.orderPlan.qty,
|
|
@@ -6702,6 +6883,7 @@ var executeEntryDecision = async ({
|
|
|
6702
6883
|
timestamp: decision.entryContext.timestamp,
|
|
6703
6884
|
direction: decision.entryContext.direction,
|
|
6704
6885
|
positionIntent: decision.orderPlan.positionIntent,
|
|
6886
|
+
...beforeSubmit ? { beforeSubmit } : {},
|
|
6705
6887
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {}
|
|
6706
6888
|
});
|
|
6707
6889
|
if (!orderPlaced) {
|
|
@@ -6743,6 +6925,23 @@ var executeEntryDecision = async ({
|
|
|
6743
6925
|
{ decision, entry, market }
|
|
6744
6926
|
);
|
|
6745
6927
|
} catch (err) {
|
|
6928
|
+
if (err instanceof EntrySubmissionValidationError) {
|
|
6929
|
+
if (signal) {
|
|
6930
|
+
signal.orderStatus = "skipped";
|
|
6931
|
+
signal.orderSkipReason = err.message;
|
|
6932
|
+
signal.allocatorDecision = {
|
|
6933
|
+
stage: "runtime_admission",
|
|
6934
|
+
status: "rejected",
|
|
6935
|
+
reason: err.message
|
|
6936
|
+
};
|
|
6937
|
+
}
|
|
6938
|
+
import_logger10.logger.warn(
|
|
6939
|
+
"entry rejected before submission: %s %s",
|
|
6940
|
+
symbol,
|
|
6941
|
+
err.message
|
|
6942
|
+
);
|
|
6943
|
+
return signal ?? err.message;
|
|
6944
|
+
}
|
|
6746
6945
|
if (signal) {
|
|
6747
6946
|
signal.orderStatus = "failed";
|
|
6748
6947
|
if (typeof signal.orderFailureReason !== "string" || !signal.orderFailureReason.trim()) {
|
|
@@ -6757,9 +6956,9 @@ var executeEntryDecision = async ({
|
|
|
6757
6956
|
market
|
|
6758
6957
|
});
|
|
6759
6958
|
if (err?.message === import_types.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY) {
|
|
6760
|
-
|
|
6959
|
+
import_logger10.logger.warn("order warning: %s %s", symbol, err);
|
|
6761
6960
|
} else {
|
|
6762
|
-
|
|
6961
|
+
import_logger10.logger.error("order error: %s %s", symbol, err);
|
|
6763
6962
|
}
|
|
6764
6963
|
return signal ?? "ORDER_ERROR";
|
|
6765
6964
|
}
|
|
@@ -6867,6 +7066,9 @@ var createStrategyRuntime = ({
|
|
|
6867
7066
|
});
|
|
6868
7067
|
const strategyPolicyProfile = getPolicyProfile();
|
|
6869
7068
|
const indicatorPeriods = (0, import_strategies6.buildDefaultIndicatorPeriods)(config);
|
|
7069
|
+
const strategyConfig = { ...config };
|
|
7070
|
+
delete strategyConfig.RECORD_RUNTIME_TRADES;
|
|
7071
|
+
delete strategyConfig.SIMULATE_ORDERS;
|
|
6870
7072
|
const hookBase = {
|
|
6871
7073
|
connector,
|
|
6872
7074
|
strategyName,
|
|
@@ -6877,7 +7079,7 @@ var createStrategyRuntime = ({
|
|
|
6877
7079
|
accountId,
|
|
6878
7080
|
deploymentId,
|
|
6879
7081
|
policyProfileId: strategyPolicyProfile?.id ?? requestedPolicyProfileId,
|
|
6880
|
-
strategyConfig
|
|
7082
|
+
strategyConfig,
|
|
6881
7083
|
env,
|
|
6882
7084
|
isConfigFromBacktest
|
|
6883
7085
|
};
|
|
@@ -6924,7 +7126,7 @@ var createStrategyRuntime = ({
|
|
|
6924
7126
|
try {
|
|
6925
7127
|
await projectHook(errorParams);
|
|
6926
7128
|
} catch (hookError) {
|
|
6927
|
-
|
|
7129
|
+
import_logger11.logger.error(
|
|
6928
7130
|
"project hook onRuntimeError failed: %s %s",
|
|
6929
7131
|
strategyName,
|
|
6930
7132
|
hookError
|
|
@@ -6938,21 +7140,21 @@ var createStrategyRuntime = ({
|
|
|
6938
7140
|
try {
|
|
6939
7141
|
await onRuntimeError(errorParams);
|
|
6940
7142
|
} catch (hookError) {
|
|
6941
|
-
|
|
7143
|
+
import_logger11.logger.error(
|
|
6942
7144
|
"runtime hook onRuntimeError failed: %s %s",
|
|
6943
7145
|
strategyName,
|
|
6944
7146
|
hookError
|
|
6945
7147
|
);
|
|
6946
7148
|
}
|
|
6947
7149
|
};
|
|
6948
|
-
const invokeHook = async (stage, hook, params, errorContext = {}) => {
|
|
7150
|
+
const invokeHook = async (stage, hook, params, errorContext = {}, failureResult) => {
|
|
6949
7151
|
if (!hook) {
|
|
6950
7152
|
return void 0;
|
|
6951
7153
|
}
|
|
6952
7154
|
try {
|
|
6953
7155
|
return await hook(params);
|
|
6954
7156
|
} catch (error) {
|
|
6955
|
-
|
|
7157
|
+
import_logger11.logger.error(
|
|
6956
7158
|
'strategy hook "%s" failed for %s: %s',
|
|
6957
7159
|
stage,
|
|
6958
7160
|
strategyName,
|
|
@@ -6965,17 +7167,18 @@ var createStrategyRuntime = ({
|
|
|
6965
7167
|
entry: errorContext.entry,
|
|
6966
7168
|
market: errorContext.market
|
|
6967
7169
|
});
|
|
6968
|
-
return
|
|
7170
|
+
return failureResult;
|
|
6969
7171
|
}
|
|
6970
7172
|
};
|
|
6971
|
-
const invokeProjectHooks = async (stage, params, errorContext = {}) => {
|
|
7173
|
+
const invokeProjectHooks = async (stage, params, errorContext = {}, failureResult) => {
|
|
6972
7174
|
const results = [];
|
|
6973
7175
|
for (const hook of getProjectHookList(stage)) {
|
|
6974
7176
|
const result = await invokeHook(
|
|
6975
7177
|
stage,
|
|
6976
7178
|
hook,
|
|
6977
7179
|
params,
|
|
6978
|
-
errorContext
|
|
7180
|
+
errorContext,
|
|
7181
|
+
failureResult
|
|
6979
7182
|
);
|
|
6980
7183
|
if (result !== void 0) {
|
|
6981
7184
|
results.push(result);
|
|
@@ -6990,10 +7193,12 @@ var createStrategyRuntime = ({
|
|
|
6990
7193
|
return invokeHook(stage, hook, params, errorContext);
|
|
6991
7194
|
};
|
|
6992
7195
|
const invokeGateHooks = async (stage, hook, params, errorContext = {}) => {
|
|
7196
|
+
const failureResult = stage === "beforeEntryGate" ? { allow: false, reason: "ERROR" } : void 0;
|
|
6993
7197
|
const projectResults = await invokeProjectHooks(
|
|
6994
7198
|
stage,
|
|
6995
7199
|
params,
|
|
6996
|
-
errorContext
|
|
7200
|
+
errorContext,
|
|
7201
|
+
failureResult
|
|
6997
7202
|
);
|
|
6998
7203
|
const projectBlock = projectResults.find(
|
|
6999
7204
|
(result) => result?.allow === false
|
|
@@ -7005,7 +7210,8 @@ var createStrategyRuntime = ({
|
|
|
7005
7210
|
stage,
|
|
7006
7211
|
hook,
|
|
7007
7212
|
params,
|
|
7008
|
-
errorContext
|
|
7213
|
+
errorContext,
|
|
7214
|
+
failureResult
|
|
7009
7215
|
);
|
|
7010
7216
|
};
|
|
7011
7217
|
const applyProjectAfterCoreDecisionHooks = async ({
|
|
@@ -7151,11 +7357,8 @@ var createStrategyRuntime = ({
|
|
|
7151
7357
|
return enrichedBaseContext;
|
|
7152
7358
|
}
|
|
7153
7359
|
});
|
|
7154
|
-
const coreConfig = { ...config };
|
|
7155
|
-
delete coreConfig.RECORD_RUNTIME_TRADES;
|
|
7156
|
-
delete coreConfig.SIMULATE_ORDERS;
|
|
7157
7360
|
const core = await createCore({
|
|
7158
|
-
config:
|
|
7361
|
+
config: strategyConfig,
|
|
7159
7362
|
data,
|
|
7160
7363
|
strategyApi,
|
|
7161
7364
|
indicatorsState
|
|
@@ -7774,10 +7977,10 @@ var createStrategyRuntime = ({
|
|
|
7774
7977
|
setStrategyRuntimeFactory(createStrategyRuntime);
|
|
7775
7978
|
|
|
7776
7979
|
// src/connectorsRegistry.ts
|
|
7777
|
-
var
|
|
7980
|
+
var import_logger14 = require("@tradejs/infra/logger");
|
|
7778
7981
|
|
|
7779
7982
|
// src/connectorRuntime.ts
|
|
7780
|
-
var
|
|
7983
|
+
var import_logger13 = require("@tradejs/infra/logger");
|
|
7781
7984
|
var import_tradingAccounts = require("@tradejs/infra/tradingAccounts");
|
|
7782
7985
|
|
|
7783
7986
|
// src/timescaleCachedKline.ts
|
|
@@ -7785,7 +7988,7 @@ var import_constants6 = require("@tradejs/core/constants");
|
|
|
7785
7988
|
var import_data5 = require("@tradejs/core/data");
|
|
7786
7989
|
var import_time2 = require("@tradejs/core/time");
|
|
7787
7990
|
var import_async = require("@tradejs/core/async");
|
|
7788
|
-
var
|
|
7991
|
+
var import_logger12 = require("@tradejs/infra/logger");
|
|
7789
7992
|
var import_candles = require("@tradejs/infra/timescale/candles");
|
|
7790
7993
|
var DEFAULT_LIMIT = 1e3;
|
|
7791
7994
|
var DEFAULT_CACHE_FALLBACK_WINDOW = 1e3;
|
|
@@ -7895,7 +8098,7 @@ var createTimescaleCachedKline = ({
|
|
|
7895
8098
|
}) => {
|
|
7896
8099
|
const intMinutes = intervalToMinutes(interval);
|
|
7897
8100
|
if (!intMinutes) {
|
|
7898
|
-
|
|
8101
|
+
import_logger12.logger.log("error", "refreshTail: invalid interval %s", interval);
|
|
7899
8102
|
return;
|
|
7900
8103
|
}
|
|
7901
8104
|
const intervalMs = intervalMsOf(intMinutes);
|
|
@@ -7926,7 +8129,7 @@ var createTimescaleCachedKline = ({
|
|
|
7926
8129
|
}) => {
|
|
7927
8130
|
const intMinutes = intervalToMinutes(interval);
|
|
7928
8131
|
if (!intMinutes) {
|
|
7929
|
-
|
|
8132
|
+
import_logger12.logger.log("error", "kline: invalid interval %s", interval);
|
|
7930
8133
|
return [];
|
|
7931
8134
|
}
|
|
7932
8135
|
if (defaultStart !== void 0 && defaultEnd !== void 0 && defaultEnd <= defaultStart) {
|
|
@@ -8035,7 +8238,7 @@ var createTimescaleCachedKline = ({
|
|
|
8035
8238
|
if (warmOnly) {
|
|
8036
8239
|
if (isTimescaleFallbackMode) {
|
|
8037
8240
|
isTimescaleFallbackMode = false;
|
|
8038
|
-
|
|
8241
|
+
import_logger12.logger.log("info", "TimescaleDB connection restored for kline cache");
|
|
8039
8242
|
}
|
|
8040
8243
|
return [];
|
|
8041
8244
|
}
|
|
@@ -8053,13 +8256,13 @@ var createTimescaleCachedKline = ({
|
|
|
8053
8256
|
);
|
|
8054
8257
|
if (isTimescaleFallbackMode) {
|
|
8055
8258
|
isTimescaleFallbackMode = false;
|
|
8056
|
-
|
|
8259
|
+
import_logger12.logger.log("info", "TimescaleDB connection restored for kline cache");
|
|
8057
8260
|
}
|
|
8058
8261
|
return dbData;
|
|
8059
8262
|
} catch (error) {
|
|
8060
8263
|
if (!isTimescaleFallbackMode) {
|
|
8061
8264
|
isTimescaleFallbackMode = true;
|
|
8062
|
-
|
|
8265
|
+
import_logger12.logger.log(
|
|
8063
8266
|
"warn",
|
|
8064
8267
|
"TimescaleDB unavailable for %s %s: %s. Falling back to exchange API.",
|
|
8065
8268
|
symbol,
|
|
@@ -8083,7 +8286,7 @@ var createTimescaleCachedKline = ({
|
|
|
8083
8286
|
|
|
8084
8287
|
// src/connectorRuntime.ts
|
|
8085
8288
|
var connectorRuntime = {
|
|
8086
|
-
logger:
|
|
8289
|
+
logger: import_logger13.logger,
|
|
8087
8290
|
resolveTradingAccount: import_tradingAccounts.resolveTradingAccount,
|
|
8088
8291
|
createCachedKline: createTimescaleCachedKline
|
|
8089
8292
|
};
|
|
@@ -8144,7 +8347,7 @@ var normalizeProviders = (providers, connectorName) => {
|
|
|
8144
8347
|
var registerProvider = (provider, connectorName, source, providerToConnectorName) => {
|
|
8145
8348
|
const existing = providerToConnectorName.get(provider);
|
|
8146
8349
|
if (existing && existing !== connectorName) {
|
|
8147
|
-
|
|
8350
|
+
import_logger14.logger.warn(
|
|
8148
8351
|
'Skip duplicate connector provider "%s" from %s: already mapped to %s',
|
|
8149
8352
|
provider,
|
|
8150
8353
|
source,
|
|
@@ -8157,11 +8360,11 @@ var registerProvider = (provider, connectorName, source, providerToConnectorName
|
|
|
8157
8360
|
var registerEntry = (entry, source, state) => {
|
|
8158
8361
|
const connectorName = String(entry?.name ?? "").trim();
|
|
8159
8362
|
if (!connectorName) {
|
|
8160
|
-
|
|
8363
|
+
import_logger14.logger.warn("Skip connector entry without name from %s", source);
|
|
8161
8364
|
return;
|
|
8162
8365
|
}
|
|
8163
8366
|
if (typeof entry.creator !== "function") {
|
|
8164
|
-
|
|
8367
|
+
import_logger14.logger.warn(
|
|
8165
8368
|
'Skip connector entry "%s" from %s: creator must be a function',
|
|
8166
8369
|
connectorName,
|
|
8167
8370
|
source
|
|
@@ -8173,7 +8376,7 @@ var registerEntry = (entry, source, state) => {
|
|
|
8173
8376
|
state.connectorCreators
|
|
8174
8377
|
);
|
|
8175
8378
|
if (existingByName) {
|
|
8176
|
-
|
|
8379
|
+
import_logger14.logger.warn(
|
|
8177
8380
|
'Skip duplicate connector "%s" from %s: already registered as %s',
|
|
8178
8381
|
connectorName,
|
|
8179
8382
|
source,
|
|
@@ -8248,7 +8451,7 @@ var ensureConnectorPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
|
|
|
8248
8451
|
);
|
|
8249
8452
|
const pluginDefinition = extractConnectorPluginDefinition(moduleExport);
|
|
8250
8453
|
if (!pluginDefinition) {
|
|
8251
|
-
|
|
8454
|
+
import_logger14.logger.warn(
|
|
8252
8455
|
'Skip connector plugin "%s": export { connectorEntries } is missing',
|
|
8253
8456
|
moduleName
|
|
8254
8457
|
);
|
|
@@ -8256,7 +8459,7 @@ var ensureConnectorPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
|
|
|
8256
8459
|
}
|
|
8257
8460
|
registerEntries2(pluginDefinition.connectorEntries, moduleName, state);
|
|
8258
8461
|
} catch (error) {
|
|
8259
|
-
|
|
8462
|
+
import_logger14.logger.warn(
|
|
8260
8463
|
'Failed to load connector plugin "%s": %s',
|
|
8261
8464
|
moduleName,
|
|
8262
8465
|
String(error)
|
|
@@ -8953,7 +9156,10 @@ var createTestConnector = (connector, context) => {
|
|
|
8953
9156
|
});
|
|
8954
9157
|
return {
|
|
8955
9158
|
__tradejsTestConnector: true,
|
|
8956
|
-
capabilities:
|
|
9159
|
+
capabilities: {
|
|
9160
|
+
...connector.capabilities,
|
|
9161
|
+
orderSubmissionValidation: true
|
|
9162
|
+
},
|
|
8957
9163
|
universe: connector.universe,
|
|
8958
9164
|
accountId: connector.accountId,
|
|
8959
9165
|
deploymentId: connector.deploymentId,
|
|
@@ -9235,6 +9441,7 @@ var createTestConnector = (connector, context) => {
|
|
|
9235
9441
|
}
|
|
9236
9442
|
},
|
|
9237
9443
|
placeOrder: async (order) => {
|
|
9444
|
+
const { beforeSubmit, ...submittedOrder } = order;
|
|
9238
9445
|
const isPositionIncrease = Boolean(
|
|
9239
9446
|
currentPosition && order.positionIntent === "increase" && currentPosition.direction === order.direction
|
|
9240
9447
|
);
|
|
@@ -9271,6 +9478,7 @@ var createTestConnector = (connector, context) => {
|
|
|
9271
9478
|
signal: order.signal
|
|
9272
9479
|
});
|
|
9273
9480
|
const previousPosition = currentPosition;
|
|
9481
|
+
await beforeSubmit?.({ qty: orderQty, executionPrice: entryPrice });
|
|
9274
9482
|
const previousQty = previousPosition?.qty ?? 0;
|
|
9275
9483
|
const resultingQty = previousQty + orderQty;
|
|
9276
9484
|
const resultingEntryPrice = previousPosition ? getWeightedAverage(
|
|
@@ -9283,7 +9491,7 @@ var createTestConnector = (connector, context) => {
|
|
|
9283
9491
|
...previousPosition,
|
|
9284
9492
|
qty: resultingQty,
|
|
9285
9493
|
price: resultingEntryPrice
|
|
9286
|
-
} : { ...
|
|
9494
|
+
} : { ...submittedOrder, qty: orderQty, price: entryPrice, amount };
|
|
9287
9495
|
originalQty = resultingQty;
|
|
9288
9496
|
if (isPositionIncrease) {
|
|
9289
9497
|
const { fee: fee2, profit: profit2 } = getNetProfit({
|
|
@@ -9383,7 +9591,7 @@ var createTestConnector = (connector, context) => {
|
|
|
9383
9591
|
};
|
|
9384
9592
|
}
|
|
9385
9593
|
logOrder({
|
|
9386
|
-
...
|
|
9594
|
+
...submittedOrder,
|
|
9387
9595
|
qty: orderQty,
|
|
9388
9596
|
price: entryPrice,
|
|
9389
9597
|
profit: profit2,
|
|
@@ -9424,7 +9632,7 @@ var createTestConnector = (connector, context) => {
|
|
|
9424
9632
|
currentEntryLegResults = [openTradeResult];
|
|
9425
9633
|
currentTradeResult = currentSignalId ? { ...openTradeResult, signalId: currentSignalId } : null;
|
|
9426
9634
|
logOrder({
|
|
9427
|
-
...
|
|
9635
|
+
...submittedOrder,
|
|
9428
9636
|
qty: orderQty,
|
|
9429
9637
|
price: entryPrice,
|
|
9430
9638
|
profit,
|
|
@@ -10180,7 +10388,7 @@ var prepareTestingData = async (params) => {
|
|
|
10180
10388
|
throw new Error(`Unknown connector: ${connectorName}`);
|
|
10181
10389
|
}
|
|
10182
10390
|
if (!binanceConnector || !coinbaseConnector) {
|
|
10183
|
-
|
|
10391
|
+
import_logger15.logger.warn(
|
|
10184
10392
|
"Binance/Coinbase connectors are unavailable. Reusing %s for BTC references.",
|
|
10185
10393
|
connectorName
|
|
10186
10394
|
);
|