@tradejs/node 3.1.29-beta.287 → 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 +434 -224
- package/dist/backtest.mjs +10 -5
- package/dist/{chunk-KOV3U6ZZ.mjs → chunk-KYGJBMKE.mjs} +407 -202
- package/dist/diagnostics.js +408 -203
- package/dist/diagnostics.mjs +1 -1
- package/dist/mcp.d.mts +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +423 -218
- package/dist/mcp.mjs +1 -1
- package/dist/registry.js +408 -203
- package/dist/registry.mjs +1 -1
- package/dist/runtimeDashboard.js +410 -205
- package/dist/runtimeDashboard.mjs +1 -1
- package/dist/runtimeStrategies.js +408 -203
- package/dist/runtimeStrategies.mjs +1 -1
- package/dist/strategies.d.mts +32 -22
- package/dist/strategies.d.ts +32 -22
- package/dist/strategies.js +416 -211
- 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,213 @@ 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
|
+
if (limits.maxLossValue != null && plannedLossValue > limits.maxLossValue * (1 + 1e-9))
|
|
1125
|
+
throw new EntrySubmissionValidationError(
|
|
1126
|
+
"ENTRY_RISK_EXCEEDS_MAX_LOSS_AT_SUBMISSION"
|
|
1127
|
+
);
|
|
1128
|
+
return { netRiskRatio, plannedLossValue };
|
|
1129
|
+
};
|
|
1130
|
+
|
|
1131
|
+
// src/strategy/entrySubmissionValidation.ts
|
|
1132
|
+
var MAX_ENTRY_QUOTE_AGE_MS = 5e3;
|
|
1133
|
+
var toFiniteNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1134
|
+
var getOrderArrivalSnapshot = async ({
|
|
1135
|
+
connector,
|
|
1136
|
+
symbol,
|
|
1137
|
+
requireTimestamp = false
|
|
1138
|
+
}) => {
|
|
1139
|
+
if (typeof connector.getTopOfBookTicker !== "function") {
|
|
1140
|
+
return {
|
|
1141
|
+
arrivalSnapshotTime: Date.now(),
|
|
1142
|
+
arrivalSource: "unavailable",
|
|
1143
|
+
bid: null,
|
|
1144
|
+
ask: null,
|
|
1145
|
+
arrivalMid: null,
|
|
1146
|
+
spreadBps: null
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
try {
|
|
1150
|
+
const ticker = await connector.getTopOfBookTicker(symbol);
|
|
1151
|
+
const arrivalSnapshotTime = toFiniteNumberOrNull(ticker?.timestamp);
|
|
1152
|
+
const timestampUnavailable = requireTimestamp && arrivalSnapshotTime == null;
|
|
1153
|
+
const bid = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.bidPrice);
|
|
1154
|
+
const ask = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.askPrice);
|
|
1155
|
+
const arrivalMid = bid != null && ask != null ? (bid + ask) / 2 : null;
|
|
1156
|
+
const spreadBps = bid != null && ask != null && arrivalMid != null && arrivalMid > 0 ? (ask - bid) / arrivalMid * 1e4 : null;
|
|
1157
|
+
return {
|
|
1158
|
+
arrivalSnapshotTime: arrivalSnapshotTime ?? Date.now(),
|
|
1159
|
+
arrivalSource: "top_of_book",
|
|
1160
|
+
bid,
|
|
1161
|
+
ask,
|
|
1162
|
+
arrivalMid,
|
|
1163
|
+
spreadBps
|
|
1164
|
+
};
|
|
1165
|
+
} catch (error) {
|
|
1166
|
+
import_logger.logger.warn(
|
|
1167
|
+
"runtime order arrival snapshot failed: %s %s",
|
|
1168
|
+
symbol,
|
|
1169
|
+
error?.message || String(error)
|
|
1170
|
+
);
|
|
1171
|
+
return {
|
|
1172
|
+
arrivalSnapshotTime: Date.now(),
|
|
1173
|
+
arrivalSource: "top_of_book_error",
|
|
1174
|
+
bid: null,
|
|
1175
|
+
ask: null,
|
|
1176
|
+
arrivalMid: null,
|
|
1177
|
+
spreadBps: null
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
};
|
|
1181
|
+
var validateEntryProtectionAtArrival = ({
|
|
1182
|
+
direction,
|
|
1183
|
+
signalPrice,
|
|
1184
|
+
bid,
|
|
1185
|
+
ask,
|
|
1186
|
+
arrivalMid,
|
|
1187
|
+
takeProfits,
|
|
1188
|
+
stopLossPrice
|
|
1189
|
+
}) => {
|
|
1190
|
+
const entryReference = direction === "LONG" ? ask ?? arrivalMid ?? signalPrice : bid ?? arrivalMid ?? signalPrice;
|
|
1191
|
+
const stopReference = direction === "LONG" ? bid ?? arrivalMid ?? signalPrice : ask ?? arrivalMid ?? signalPrice;
|
|
1192
|
+
if (!Number.isFinite(entryReference) || entryReference <= 0 || !Number.isFinite(stopReference) || stopReference <= 0) {
|
|
1193
|
+
throw new Error("INVALID_ENTRY_REFERENCE_AT_ARRIVAL");
|
|
1194
|
+
}
|
|
1195
|
+
for (const takeProfit of takeProfits) {
|
|
1196
|
+
if (!Number.isFinite(takeProfit.price) || takeProfit.price <= 0 || (direction === "LONG" ? takeProfit.price <= entryReference : takeProfit.price >= entryReference)) {
|
|
1197
|
+
throw new Error("TAKE_PROFIT_CROSSED_BEFORE_ENTRY");
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
if (stopLossPrice != null && (!Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || (direction === "LONG" ? stopLossPrice >= stopReference : stopLossPrice <= stopReference))) {
|
|
1201
|
+
throw new Error("STOP_LOSS_CROSSED_BEFORE_ENTRY");
|
|
1202
|
+
}
|
|
1203
|
+
};
|
|
1204
|
+
var createEntrySubmissionValidation = ({
|
|
1205
|
+
connector,
|
|
1206
|
+
symbol,
|
|
1207
|
+
direction,
|
|
1208
|
+
takeProfits,
|
|
1209
|
+
stopLossPrice,
|
|
1210
|
+
limits,
|
|
1211
|
+
onValidated
|
|
1212
|
+
}) => {
|
|
1213
|
+
if (!limits) return void 0;
|
|
1214
|
+
if (!connector.capabilities?.orderSubmissionValidation)
|
|
1215
|
+
throw new EntrySubmissionValidationError(
|
|
1216
|
+
"ENTRY_SUBMISSION_VALIDATION_UNSUPPORTED"
|
|
1217
|
+
);
|
|
1218
|
+
return async ({ qty, executionPrice }) => {
|
|
1219
|
+
const simulated = Reflect.get(connector, "__tradejsTestConnector") === true;
|
|
1220
|
+
const snapshot2 = simulated && executionPrice != null ? {
|
|
1221
|
+
arrivalSnapshotTime: Date.now(),
|
|
1222
|
+
arrivalSource: "simulation",
|
|
1223
|
+
bid: executionPrice,
|
|
1224
|
+
ask: executionPrice,
|
|
1225
|
+
arrivalMid: executionPrice,
|
|
1226
|
+
spreadBps: 0
|
|
1227
|
+
} : await getOrderArrivalSnapshot({
|
|
1228
|
+
connector,
|
|
1229
|
+
symbol,
|
|
1230
|
+
requireTimestamp: true
|
|
1231
|
+
});
|
|
1232
|
+
const age = Date.now() - snapshot2.arrivalSnapshotTime;
|
|
1233
|
+
if (snapshot2.bid == null || snapshot2.ask == null || !Number.isFinite(snapshot2.bid) || !Number.isFinite(snapshot2.ask) || snapshot2.bid <= 0 || snapshot2.ask < snapshot2.bid)
|
|
1234
|
+
throw new EntrySubmissionValidationError(
|
|
1235
|
+
"ENTRY_QUOTE_UNAVAILABLE_AT_SUBMISSION"
|
|
1236
|
+
);
|
|
1237
|
+
if (!Number.isFinite(age) || age < -MAX_ENTRY_QUOTE_AGE_MS || age > MAX_ENTRY_QUOTE_AGE_MS)
|
|
1238
|
+
throw new EntrySubmissionValidationError(
|
|
1239
|
+
"ENTRY_QUOTE_STALE_AT_SUBMISSION"
|
|
1240
|
+
);
|
|
1241
|
+
const price = direction === "LONG" ? snapshot2.ask : snapshot2.bid;
|
|
1242
|
+
validateEntryProtectionAtArrival({
|
|
1243
|
+
direction,
|
|
1244
|
+
signalPrice: price,
|
|
1245
|
+
bid: snapshot2.bid,
|
|
1246
|
+
ask: snapshot2.ask,
|
|
1247
|
+
arrivalMid: snapshot2.arrivalMid,
|
|
1248
|
+
takeProfits,
|
|
1249
|
+
stopLossPrice
|
|
1250
|
+
});
|
|
1251
|
+
const prices = takeProfits.map((tp) => tp.price);
|
|
1252
|
+
const economics2 = validateEntryExecution({
|
|
1253
|
+
direction,
|
|
1254
|
+
price,
|
|
1255
|
+
qty,
|
|
1256
|
+
stopLossPrice,
|
|
1257
|
+
takeProfitPrice: direction === "LONG" ? Math.max(...prices) : Math.min(...prices),
|
|
1258
|
+
limits
|
|
1259
|
+
});
|
|
1260
|
+
onValidated?.(snapshot2, economics2);
|
|
1261
|
+
};
|
|
1262
|
+
};
|
|
1263
|
+
|
|
1064
1264
|
// src/ai.ts
|
|
1065
1265
|
var import_aiLanguages2 = require("@tradejs/core/aiLanguages");
|
|
1066
1266
|
var import_redis = require("@tradejs/infra/redis");
|
|
@@ -1645,7 +1845,7 @@ var import_fs = __toESM(require("fs"));
|
|
|
1645
1845
|
var import_path = __toESM(require("path"));
|
|
1646
1846
|
var import_url = require("url");
|
|
1647
1847
|
var import_config = require("@tradejs/core/config");
|
|
1648
|
-
var
|
|
1848
|
+
var import_logger2 = require("@tradejs/infra/logger");
|
|
1649
1849
|
var CONFIG_FILE_NAMES = [
|
|
1650
1850
|
"tradejs.config.ts",
|
|
1651
1851
|
"tradejs.config.mts",
|
|
@@ -1912,11 +2112,11 @@ var loadTradejsConfig = async (cwd = getTradejsProjectCwd()) => {
|
|
|
1912
2112
|
cachedByCwd.set(cwd, config);
|
|
1913
2113
|
if (!announcedConfigFile.has(configFilePath)) {
|
|
1914
2114
|
announcedConfigFile.add(configFilePath);
|
|
1915
|
-
|
|
2115
|
+
import_logger2.logger.log("debug", "Loaded TradeJS config: %s", configFilePath);
|
|
1916
2116
|
}
|
|
1917
2117
|
return config;
|
|
1918
2118
|
} catch (error) {
|
|
1919
|
-
|
|
2119
|
+
import_logger2.logger.log(
|
|
1920
2120
|
"warn",
|
|
1921
2121
|
"Failed to load TradeJS config from %s: %s",
|
|
1922
2122
|
configFilePath,
|
|
@@ -2864,7 +3064,7 @@ var import_node_crypto = require("crypto");
|
|
|
2864
3064
|
var import_constants = require("@tradejs/core/constants");
|
|
2865
3065
|
var import_time = require("@tradejs/core/time");
|
|
2866
3066
|
var import_trade = require("@tradejs/core/trade");
|
|
2867
|
-
var
|
|
3067
|
+
var import_logger3 = require("@tradejs/infra/logger");
|
|
2868
3068
|
var import_redis2 = require("@tradejs/infra/redis");
|
|
2869
3069
|
var now = () => Date.now();
|
|
2870
3070
|
var toRandomOrderSuffix = () => (0, import_node_crypto.randomUUID)().replace(/-/g, "").slice(0, 12).toLowerCase();
|
|
@@ -2923,7 +3123,7 @@ var recordRuntimeTradeOpen = async (params) => {
|
|
|
2923
3123
|
)
|
|
2924
3124
|
]);
|
|
2925
3125
|
} catch (error) {
|
|
2926
|
-
|
|
3126
|
+
import_logger3.logger.error(
|
|
2927
3127
|
"runtime trade open journal failed: %s %s",
|
|
2928
3128
|
record.symbol,
|
|
2929
3129
|
error?.message || String(error)
|
|
@@ -2991,7 +3191,7 @@ var recordRuntimeTradeIncrease = async (params) => {
|
|
|
2991
3191
|
)
|
|
2992
3192
|
]);
|
|
2993
3193
|
} catch (error) {
|
|
2994
|
-
|
|
3194
|
+
import_logger3.logger.error(
|
|
2995
3195
|
"runtime trade increase journal failed: %s %s",
|
|
2996
3196
|
symbol,
|
|
2997
3197
|
error?.message || String(error)
|
|
@@ -3098,7 +3298,7 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
3098
3298
|
)
|
|
3099
3299
|
]);
|
|
3100
3300
|
} catch (error) {
|
|
3101
|
-
|
|
3301
|
+
import_logger3.logger.error(
|
|
3102
3302
|
"runtime trade close journal failed: %s %s",
|
|
3103
3303
|
symbol,
|
|
3104
3304
|
error?.message || String(error)
|
|
@@ -3108,11 +3308,11 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
3108
3308
|
};
|
|
3109
3309
|
|
|
3110
3310
|
// src/strategyHelpers/marketContextStages.ts
|
|
3111
|
-
var
|
|
3311
|
+
var import_logger8 = require("@tradejs/infra/logger");
|
|
3112
3312
|
|
|
3113
3313
|
// src/strategyHelpers/binanceMarketContext.ts
|
|
3114
3314
|
var import_marketContext = require("@tradejs/infra/timescale/marketContext");
|
|
3115
|
-
var
|
|
3315
|
+
var import_logger4 = require("@tradejs/infra/logger");
|
|
3116
3316
|
var import_strategies = require("@tradejs/core/strategies");
|
|
3117
3317
|
|
|
3118
3318
|
// src/binanceBreadthUniverses.ts
|
|
@@ -3476,7 +3676,7 @@ var parseEnabledFlag = (value, env) => {
|
|
|
3476
3676
|
if (normalized === "live") return env !== "BACKTEST";
|
|
3477
3677
|
return false;
|
|
3478
3678
|
};
|
|
3479
|
-
var
|
|
3679
|
+
var toFiniteNumberOrNull2 = (value) => {
|
|
3480
3680
|
const numeric = typeof value === "number" ? value : Number(value);
|
|
3481
3681
|
return Number.isFinite(numeric) ? numeric : null;
|
|
3482
3682
|
};
|
|
@@ -3601,14 +3801,14 @@ var toTradeFlowContext = (row, interval) => row ? {
|
|
|
3601
3801
|
asOfTs: row.ts.getTime(),
|
|
3602
3802
|
ageMs: row.ageMs,
|
|
3603
3803
|
stale: row.stale,
|
|
3604
|
-
trades:
|
|
3605
|
-
buyPressurePct:
|
|
3606
|
-
buyBaseVolume:
|
|
3607
|
-
sellBaseVolume:
|
|
3608
|
-
buyQuoteVolume:
|
|
3609
|
-
sellQuoteVolume:
|
|
3610
|
-
netBaseDelta:
|
|
3611
|
-
netQuoteDelta:
|
|
3804
|
+
trades: toFiniteNumberOrNull2(row.trades),
|
|
3805
|
+
buyPressurePct: toFiniteNumberOrNull2(row.buyPressurePct),
|
|
3806
|
+
buyBaseVolume: toFiniteNumberOrNull2(row.buyBaseVolume),
|
|
3807
|
+
sellBaseVolume: toFiniteNumberOrNull2(row.sellBaseVolume),
|
|
3808
|
+
buyQuoteVolume: toFiniteNumberOrNull2(row.buyQuoteVolume),
|
|
3809
|
+
sellQuoteVolume: toFiniteNumberOrNull2(row.sellQuoteVolume),
|
|
3810
|
+
netBaseDelta: toFiniteNumberOrNull2(row.netBaseDelta),
|
|
3811
|
+
netQuoteDelta: toFiniteNumberOrNull2(row.netQuoteDelta)
|
|
3612
3812
|
} : null;
|
|
3613
3813
|
var getCachedReferenceRows = ({
|
|
3614
3814
|
referenceSymbols,
|
|
@@ -3697,16 +3897,16 @@ var toMarketBreadthContext = (breadth, interval) => ({
|
|
|
3697
3897
|
asOfTs: breadth.ts.getTime(),
|
|
3698
3898
|
ageMs: breadth.ageMs,
|
|
3699
3899
|
stale: breadth.stale,
|
|
3700
|
-
symbolsCount:
|
|
3701
|
-
advancers:
|
|
3702
|
-
decliners:
|
|
3703
|
-
unchanged:
|
|
3704
|
-
advanceDeclineRatio:
|
|
3705
|
-
pctAboveMa20:
|
|
3706
|
-
pctAboveMa50:
|
|
3707
|
-
equalWeightedReturn:
|
|
3708
|
-
volumeWeightedReturn:
|
|
3709
|
-
dispersion:
|
|
3900
|
+
symbolsCount: toFiniteNumberOrNull2(breadth.symbolsCount),
|
|
3901
|
+
advancers: toFiniteNumberOrNull2(breadth.advancers),
|
|
3902
|
+
decliners: toFiniteNumberOrNull2(breadth.decliners),
|
|
3903
|
+
unchanged: toFiniteNumberOrNull2(breadth.unchanged),
|
|
3904
|
+
advanceDeclineRatio: toFiniteNumberOrNull2(breadth.advanceDeclineRatio),
|
|
3905
|
+
pctAboveMa20: toFiniteNumberOrNull2(breadth.pctAboveMa20),
|
|
3906
|
+
pctAboveMa50: toFiniteNumberOrNull2(breadth.pctAboveMa50),
|
|
3907
|
+
equalWeightedReturn: toFiniteNumberOrNull2(breadth.equalWeightedReturn),
|
|
3908
|
+
volumeWeightedReturn: toFiniteNumberOrNull2(breadth.volumeWeightedReturn),
|
|
3909
|
+
dispersion: toFiniteNumberOrNull2(breadth.dispersion)
|
|
3710
3910
|
});
|
|
3711
3911
|
var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
3712
3912
|
const {
|
|
@@ -3807,46 +4007,46 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
3807
4007
|
asOfTs: primaryBreadth.ts.getTime(),
|
|
3808
4008
|
ageMs: primaryBreadth.ageMs,
|
|
3809
4009
|
stale: primaryBreadth.stale,
|
|
3810
|
-
btcReturn1h:
|
|
4010
|
+
btcReturn1h: toFiniteNumberOrNull2(
|
|
3811
4011
|
primaryBreadth.btcReturn1h
|
|
3812
4012
|
),
|
|
3813
|
-
btcReturn4h:
|
|
4013
|
+
btcReturn4h: toFiniteNumberOrNull2(
|
|
3814
4014
|
primaryBreadth.btcReturn4h
|
|
3815
4015
|
),
|
|
3816
|
-
btcReturn24h:
|
|
4016
|
+
btcReturn24h: toFiniteNumberOrNull2(
|
|
3817
4017
|
primaryBreadth.btcReturn24h
|
|
3818
4018
|
),
|
|
3819
|
-
altBasketReturn1h:
|
|
4019
|
+
altBasketReturn1h: toFiniteNumberOrNull2(
|
|
3820
4020
|
primaryBreadth.altBasketReturn1h
|
|
3821
4021
|
),
|
|
3822
|
-
altBasketReturn4h:
|
|
4022
|
+
altBasketReturn4h: toFiniteNumberOrNull2(
|
|
3823
4023
|
primaryBreadth.altBasketReturn4h
|
|
3824
4024
|
),
|
|
3825
|
-
altBasketReturn24h:
|
|
4025
|
+
altBasketReturn24h: toFiniteNumberOrNull2(
|
|
3826
4026
|
primaryBreadth.altBasketReturn24h
|
|
3827
4027
|
),
|
|
3828
|
-
btcVsAltReturn1h:
|
|
4028
|
+
btcVsAltReturn1h: toFiniteNumberOrNull2(
|
|
3829
4029
|
primaryBreadth.btcVsAltReturn1h
|
|
3830
4030
|
),
|
|
3831
|
-
btcVsAltReturn4h:
|
|
4031
|
+
btcVsAltReturn4h: toFiniteNumberOrNull2(
|
|
3832
4032
|
primaryBreadth.btcVsAltReturn4h
|
|
3833
4033
|
),
|
|
3834
|
-
btcVsAltReturn24h:
|
|
4034
|
+
btcVsAltReturn24h: toFiniteNumberOrNull2(
|
|
3835
4035
|
primaryBreadth.btcVsAltReturn24h
|
|
3836
4036
|
),
|
|
3837
|
-
btcTurnoverShare1h:
|
|
4037
|
+
btcTurnoverShare1h: toFiniteNumberOrNull2(
|
|
3838
4038
|
primaryBreadth.btcTurnoverShare1h
|
|
3839
4039
|
),
|
|
3840
|
-
btcTurnoverShare24h:
|
|
4040
|
+
btcTurnoverShare24h: toFiniteNumberOrNull2(
|
|
3841
4041
|
primaryBreadth.btcTurnoverShare24h
|
|
3842
4042
|
),
|
|
3843
|
-
btcTurnoverShareChange24h:
|
|
4043
|
+
btcTurnoverShareChange24h: toFiniteNumberOrNull2(
|
|
3844
4044
|
primaryBreadth.btcTurnoverShareChange24h
|
|
3845
4045
|
),
|
|
3846
|
-
altVolToBtcVol24h:
|
|
4046
|
+
altVolToBtcVol24h: toFiniteNumberOrNull2(
|
|
3847
4047
|
primaryBreadth.altVolToBtcVol24h
|
|
3848
4048
|
),
|
|
3849
|
-
altDispersion24h:
|
|
4049
|
+
altDispersion24h: toFiniteNumberOrNull2(
|
|
3850
4050
|
primaryBreadth.altDispersion24h
|
|
3851
4051
|
),
|
|
3852
4052
|
regime: primaryBreadth.btcAltRegime ?? "unknown"
|
|
@@ -3863,7 +4063,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
3863
4063
|
throw error;
|
|
3864
4064
|
}
|
|
3865
4065
|
binanceMarketContextUnavailable = true;
|
|
3866
|
-
|
|
4066
|
+
import_logger4.logger.warn(
|
|
3867
4067
|
"Binance market context disabled after Timescale read failure: %s",
|
|
3868
4068
|
String(error)
|
|
3869
4069
|
);
|
|
@@ -3873,7 +4073,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
3873
4073
|
|
|
3874
4074
|
// src/strategyHelpers/coinMarketCapContext.ts
|
|
3875
4075
|
var import_strategies2 = require("@tradejs/core/strategies");
|
|
3876
|
-
var
|
|
4076
|
+
var import_logger5 = require("@tradejs/infra/logger");
|
|
3877
4077
|
var import_marketContext2 = require("@tradejs/infra/timescale/marketContext");
|
|
3878
4078
|
var DEFAULT_MAX_AGE_MS = 48 * 60 * 6e4;
|
|
3879
4079
|
var SOURCE_GLOBAL_DAILY = "coinmarketcap_global";
|
|
@@ -3911,7 +4111,7 @@ var asInt = (value, fallback) => {
|
|
|
3911
4111
|
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
3912
4112
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
3913
4113
|
};
|
|
3914
|
-
var
|
|
4114
|
+
var toFiniteNumberOrNull3 = (value) => {
|
|
3915
4115
|
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
|
|
3916
4116
|
return Number.isFinite(numeric) ? numeric : null;
|
|
3917
4117
|
};
|
|
@@ -4145,12 +4345,12 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4145
4345
|
const ethRow = references.get("ETHUSDT") ?? null;
|
|
4146
4346
|
const previousBtcRow = previousReferences.get("BTCUSDT") ?? null;
|
|
4147
4347
|
const previousEthRow = previousReferences.get("ETHUSDT") ?? null;
|
|
4148
|
-
const btcMarketCapUsd =
|
|
4149
|
-
const ethMarketCapUsd =
|
|
4150
|
-
const previousBtcMarketCapUsd =
|
|
4348
|
+
const btcMarketCapUsd = toFiniteNumberOrNull3(btcRow?.marketCapUsd);
|
|
4349
|
+
const ethMarketCapUsd = toFiniteNumberOrNull3(ethRow?.marketCapUsd);
|
|
4350
|
+
const previousBtcMarketCapUsd = toFiniteNumberOrNull3(
|
|
4151
4351
|
previousBtcRow?.marketCapUsd
|
|
4152
4352
|
);
|
|
4153
|
-
const previousEthMarketCapUsd =
|
|
4353
|
+
const previousEthMarketCapUsd = toFiniteNumberOrNull3(
|
|
4154
4354
|
previousEthRow?.marketCapUsd
|
|
4155
4355
|
);
|
|
4156
4356
|
const ethBtcMarketCapRatio = safeDivide(ethMarketCapUsd, btcMarketCapUsd);
|
|
@@ -4159,16 +4359,16 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4159
4359
|
previousBtcMarketCapUsd
|
|
4160
4360
|
);
|
|
4161
4361
|
const ethBtcMarketCapRatioChange24hPct = ethBtcMarketCapRatio != null && previousEthBtcMarketCapRatio != null && previousEthBtcMarketCapRatio > 0 ? (ethBtcMarketCapRatio - previousEthBtcMarketCapRatio) / previousEthBtcMarketCapRatio : null;
|
|
4162
|
-
const btcVolumeUsd =
|
|
4163
|
-
const ethVolumeUsd =
|
|
4362
|
+
const btcVolumeUsd = toFiniteNumberOrNull3(btcRow?.volumeUsd);
|
|
4363
|
+
const ethVolumeUsd = toFiniteNumberOrNull3(ethRow?.volumeUsd);
|
|
4164
4364
|
const referenceStale = btcRow?.stale === true || ethRow?.stale === true || !btcRow || !ethRow;
|
|
4165
|
-
const btcDominanceChange24hPct =
|
|
4365
|
+
const btcDominanceChange24hPct = toFiniteNumberOrNull3(
|
|
4166
4366
|
globalRow?.btcDominanceChange24hPct
|
|
4167
4367
|
);
|
|
4168
|
-
const altMarketCapChange24hPct =
|
|
4368
|
+
const altMarketCapChange24hPct = toFiniteNumberOrNull3(
|
|
4169
4369
|
globalRow?.altMarketCapChange24hPct
|
|
4170
4370
|
);
|
|
4171
|
-
const altVolumeChange24hPct =
|
|
4371
|
+
const altVolumeChange24hPct = toFiniteNumberOrNull3(
|
|
4172
4372
|
globalRow?.altVolumeChange24hPct
|
|
4173
4373
|
);
|
|
4174
4374
|
const altLiquidityRegime = globalRow ? toAltLiquidityRegime({
|
|
@@ -4179,19 +4379,19 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4179
4379
|
}) : "unknown";
|
|
4180
4380
|
const exchangeLiquidityRegime = exchangeLiquidityRow ? toExchangeLiquidityRegime({
|
|
4181
4381
|
stale: exchangeLiquidityRow.stale,
|
|
4182
|
-
totalVolumeChange24hPct:
|
|
4382
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
4183
4383
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
4184
4384
|
),
|
|
4185
4385
|
fallback: exchangeLiquidityRow.liquidityRegime ?? "unknown"
|
|
4186
4386
|
}) : "unknown";
|
|
4187
4387
|
const cmc100Row = indexRows.get("cmc100") ?? null;
|
|
4188
4388
|
const cmc20Row = indexRows.get("cmc20") ?? null;
|
|
4189
|
-
const cmc100Value =
|
|
4190
|
-
const cmc20Value =
|
|
4191
|
-
const cmc100Change24hPct =
|
|
4389
|
+
const cmc100Value = toFiniteNumberOrNull3(cmc100Row?.value);
|
|
4390
|
+
const cmc20Value = toFiniteNumberOrNull3(cmc20Row?.value);
|
|
4391
|
+
const cmc100Change24hPct = toFiniteNumberOrNull3(
|
|
4192
4392
|
cmc100Row?.valueChange24hPct
|
|
4193
4393
|
);
|
|
4194
|
-
const cmc20Change24hPct =
|
|
4394
|
+
const cmc20Change24hPct = toFiniteNumberOrNull3(cmc20Row?.valueChange24hPct);
|
|
4195
4395
|
const cmc20ToCmc100Ratio = safeDivide(cmc20Value, cmc100Value);
|
|
4196
4396
|
const cmc20ToCmc100RatioChange24hPct = cmc20Change24hPct != null && cmc100Change24hPct != null ? (1 + cmc20Change24hPct) / (1 + cmc100Change24hPct) - 1 : null;
|
|
4197
4397
|
const indexStale = cmc100Row?.stale === true || cmc20Row?.stale === true || !cmc100Row || !cmc20Row;
|
|
@@ -4215,41 +4415,41 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4215
4415
|
asOfTs: globalRow.ts.getTime(),
|
|
4216
4416
|
ageMs: globalRow.ageMs,
|
|
4217
4417
|
stale: globalRow.stale,
|
|
4218
|
-
totalMarketCapUsd:
|
|
4418
|
+
totalMarketCapUsd: toFiniteNumberOrNull3(
|
|
4219
4419
|
globalRow.totalMarketCapUsd
|
|
4220
4420
|
),
|
|
4221
|
-
totalVolumeUsd:
|
|
4421
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
4222
4422
|
globalRow.totalVolumeUsd
|
|
4223
4423
|
),
|
|
4224
|
-
totalVolumeReportedUsd:
|
|
4424
|
+
totalVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
4225
4425
|
globalRow.totalVolumeReportedUsd
|
|
4226
4426
|
),
|
|
4227
|
-
altMarketCapUsd:
|
|
4427
|
+
altMarketCapUsd: toFiniteNumberOrNull3(
|
|
4228
4428
|
globalRow.altMarketCapUsd
|
|
4229
4429
|
),
|
|
4230
|
-
altVolumeUsd:
|
|
4231
|
-
altVolumeReportedUsd:
|
|
4430
|
+
altVolumeUsd: toFiniteNumberOrNull3(globalRow.altVolumeUsd),
|
|
4431
|
+
altVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
4232
4432
|
globalRow.altVolumeReportedUsd
|
|
4233
4433
|
),
|
|
4234
|
-
btcDominancePct:
|
|
4434
|
+
btcDominancePct: toFiniteNumberOrNull3(
|
|
4235
4435
|
globalRow.btcDominancePct
|
|
4236
4436
|
),
|
|
4237
|
-
ethDominancePct:
|
|
4437
|
+
ethDominancePct: toFiniteNumberOrNull3(
|
|
4238
4438
|
globalRow.ethDominancePct
|
|
4239
4439
|
),
|
|
4240
4440
|
btcDominanceChange24hPct,
|
|
4241
|
-
ethDominanceChange24hPct:
|
|
4441
|
+
ethDominanceChange24hPct: toFiniteNumberOrNull3(
|
|
4242
4442
|
globalRow.ethDominanceChange24hPct
|
|
4243
4443
|
),
|
|
4244
4444
|
altMarketCapChange24hPct,
|
|
4245
4445
|
altVolumeChange24hPct,
|
|
4246
|
-
activeCryptocurrencies:
|
|
4446
|
+
activeCryptocurrencies: toFiniteNumberOrNull3(
|
|
4247
4447
|
globalRow.activeCryptocurrencies
|
|
4248
4448
|
),
|
|
4249
|
-
activeExchanges:
|
|
4449
|
+
activeExchanges: toFiniteNumberOrNull3(
|
|
4250
4450
|
globalRow.activeExchanges
|
|
4251
4451
|
),
|
|
4252
|
-
activeMarketPairs:
|
|
4452
|
+
activeMarketPairs: toFiniteNumberOrNull3(
|
|
4253
4453
|
globalRow.activeMarketPairs
|
|
4254
4454
|
),
|
|
4255
4455
|
altLiquidityRegime
|
|
@@ -4294,22 +4494,22 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4294
4494
|
asOfTs: exchangeLiquidityRow.ts.getTime(),
|
|
4295
4495
|
ageMs: exchangeLiquidityRow.ageMs,
|
|
4296
4496
|
stale: exchangeLiquidityRow.stale,
|
|
4297
|
-
exchangesCount:
|
|
4497
|
+
exchangesCount: toFiniteNumberOrNull3(
|
|
4298
4498
|
exchangeLiquidityRow.exchangesCount
|
|
4299
4499
|
),
|
|
4300
|
-
totalVolumeUsd:
|
|
4500
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
4301
4501
|
exchangeLiquidityRow.totalVolumeUsd
|
|
4302
4502
|
),
|
|
4303
|
-
totalVolumeChange24hPct:
|
|
4503
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
4304
4504
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
4305
4505
|
),
|
|
4306
|
-
binanceVolumeUsd:
|
|
4506
|
+
binanceVolumeUsd: toFiniteNumberOrNull3(
|
|
4307
4507
|
exchangeLiquidityRow.binanceVolumeUsd
|
|
4308
4508
|
),
|
|
4309
|
-
binanceVolumeShare:
|
|
4509
|
+
binanceVolumeShare: toFiniteNumberOrNull3(
|
|
4310
4510
|
exchangeLiquidityRow.binanceVolumeShare
|
|
4311
4511
|
),
|
|
4312
|
-
topExchangeVolumeShare:
|
|
4512
|
+
topExchangeVolumeShare: toFiniteNumberOrNull3(
|
|
4313
4513
|
exchangeLiquidityRow.topExchangeVolumeShare
|
|
4314
4514
|
),
|
|
4315
4515
|
liquidityRegime: exchangeLiquidityRegime
|
|
@@ -4322,11 +4522,11 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4322
4522
|
asOfTs: fearGreedRow.ts.getTime(),
|
|
4323
4523
|
ageMs: fearGreedRow.ageMs,
|
|
4324
4524
|
stale: fearGreedRow.stale,
|
|
4325
|
-
value:
|
|
4326
|
-
valueChange24h:
|
|
4525
|
+
value: toFiniteNumberOrNull3(fearGreedRow.value),
|
|
4526
|
+
valueChange24h: toFiniteNumberOrNull3(
|
|
4327
4527
|
fearGreedRow.valueChange24h
|
|
4328
4528
|
),
|
|
4329
|
-
valueChange7d:
|
|
4529
|
+
valueChange7d: toFiniteNumberOrNull3(
|
|
4330
4530
|
fearGreedRow.valueChange7d
|
|
4331
4531
|
),
|
|
4332
4532
|
classification: fearGreedRow.classification ?? "Unknown",
|
|
@@ -4346,13 +4546,13 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4346
4546
|
cmc100Value,
|
|
4347
4547
|
cmc100Change24hPct,
|
|
4348
4548
|
cmc100TopConstituentSymbol: cmc100Row?.topConstituentSymbol ?? null,
|
|
4349
|
-
cmc100TopConstituentWeightPct:
|
|
4549
|
+
cmc100TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
4350
4550
|
cmc100Row?.topConstituentWeightPct
|
|
4351
4551
|
),
|
|
4352
4552
|
cmc20Value,
|
|
4353
4553
|
cmc20Change24hPct,
|
|
4354
4554
|
cmc20TopConstituentSymbol: cmc20Row?.topConstituentSymbol ?? null,
|
|
4355
|
-
cmc20TopConstituentWeightPct:
|
|
4555
|
+
cmc20TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
4356
4556
|
cmc20Row?.topConstituentWeightPct
|
|
4357
4557
|
),
|
|
4358
4558
|
cmc20ToCmc100Ratio,
|
|
@@ -4370,7 +4570,7 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4370
4570
|
throw error;
|
|
4371
4571
|
}
|
|
4372
4572
|
coinMarketCapContextUnavailable = true;
|
|
4373
|
-
|
|
4573
|
+
import_logger5.logger.warn(
|
|
4374
4574
|
"CoinMarketCap context disabled after Timescale read failure: %s",
|
|
4375
4575
|
String(error)
|
|
4376
4576
|
);
|
|
@@ -4384,7 +4584,7 @@ var import_data2 = require("@tradejs/core/data");
|
|
|
4384
4584
|
var import_strategies3 = require("@tradejs/core/strategies");
|
|
4385
4585
|
var import_constants2 = require("@tradejs/core/constants");
|
|
4386
4586
|
var import_derivatives = require("@tradejs/infra/timescale/derivatives");
|
|
4387
|
-
var
|
|
4587
|
+
var import_logger6 = require("@tradejs/infra/logger");
|
|
4388
4588
|
var STORED_INTERVALS = ["15m", "1h"];
|
|
4389
4589
|
var CONTEXT_INTERVALS = ["15m", "1h"];
|
|
4390
4590
|
var DEFAULT_LOOKBACK_HOURS = 48;
|
|
@@ -4439,7 +4639,7 @@ var resolvePrimaryReferenceSymbol2 = () => PRIMARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
|
4439
4639
|
var resolveSecondaryReferenceSymbol = () => SECONDARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
4440
4640
|
var getPrimaryIntervalContext = (context) => context?.intervals["15m"] ?? context?.intervals["1h"] ?? null;
|
|
4441
4641
|
var hasDerivativesSymbolData = (context) => Object.keys(context.intervals).length > 0 && !context.summary.riskFlags.includes("missing_derivatives");
|
|
4442
|
-
var
|
|
4642
|
+
var toFiniteNumberOrNull4 = (value) => {
|
|
4443
4643
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
4444
4644
|
if (typeof value === "string" && value.trim()) {
|
|
4445
4645
|
const parsed = Number(value);
|
|
@@ -4453,8 +4653,8 @@ var roundNullable = (value, digits = 4) => {
|
|
|
4453
4653
|
return Math.round(value * multiplier) / multiplier;
|
|
4454
4654
|
};
|
|
4455
4655
|
var deltaNullable = (targetValue, referenceValue) => {
|
|
4456
|
-
const target =
|
|
4457
|
-
const reference =
|
|
4656
|
+
const target = toFiniteNumberOrNull4(targetValue);
|
|
4657
|
+
const reference = toFiniteNumberOrNull4(referenceValue);
|
|
4458
4658
|
return target == null || reference == null ? null : roundNullable(target - reference);
|
|
4459
4659
|
};
|
|
4460
4660
|
var buildTargetDerivedContext = (params) => {
|
|
@@ -4616,7 +4816,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
4616
4816
|
throw error;
|
|
4617
4817
|
}
|
|
4618
4818
|
derivativesContextUnavailable = true;
|
|
4619
|
-
|
|
4819
|
+
import_logger6.logger.warn(
|
|
4620
4820
|
"Derivatives context disabled after Timescale read failure: %s",
|
|
4621
4821
|
String(error)
|
|
4622
4822
|
);
|
|
@@ -4628,7 +4828,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
4628
4828
|
var import_data3 = require("@tradejs/core/data");
|
|
4629
4829
|
var import_strategies4 = require("@tradejs/core/strategies");
|
|
4630
4830
|
var import_hyperliquidWhales2 = require("@tradejs/infra/timescale/hyperliquidWhales");
|
|
4631
|
-
var
|
|
4831
|
+
var import_logger7 = require("@tradejs/infra/logger");
|
|
4632
4832
|
|
|
4633
4833
|
// src/hyperliquidWhaleUniverse.ts
|
|
4634
4834
|
var import_node_crypto3 = require("crypto");
|
|
@@ -5148,7 +5348,7 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
5148
5348
|
throw error;
|
|
5149
5349
|
}
|
|
5150
5350
|
hyperliquidWhaleContextUnavailable = true;
|
|
5151
|
-
|
|
5351
|
+
import_logger7.logger.warn(
|
|
5152
5352
|
"Hyperliquid whale context disabled after Timescale read failure: %s",
|
|
5153
5353
|
String(error)
|
|
5154
5354
|
);
|
|
@@ -5231,7 +5431,7 @@ var runMarketContextStage = async ({
|
|
|
5231
5431
|
elapsedMs: Date.now() - startedAt
|
|
5232
5432
|
};
|
|
5233
5433
|
if (status === "timed_out") {
|
|
5234
|
-
|
|
5434
|
+
import_logger8.logger.warn(
|
|
5235
5435
|
"Market context stage timed out: %s after %sms",
|
|
5236
5436
|
stage,
|
|
5237
5437
|
result.elapsedMs
|
|
@@ -5421,79 +5621,10 @@ var enrichSignalWithAi = async ({
|
|
|
5421
5621
|
signal.aiAnalysis = analysis;
|
|
5422
5622
|
return resolveAiQuality(analysis, direction);
|
|
5423
5623
|
} catch (err) {
|
|
5424
|
-
|
|
5624
|
+
import_logger9.logger.error("AI analysis error: %s %s", symbol, formatAiError(err));
|
|
5425
5625
|
}
|
|
5426
5626
|
return void 0;
|
|
5427
5627
|
};
|
|
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
5628
|
var resolveRuntimeTelemetryQuality = ({
|
|
5498
5629
|
signalClosePrice,
|
|
5499
5630
|
arrivalMid,
|
|
@@ -5570,7 +5701,8 @@ var executeEntryOrder = async ({
|
|
|
5570
5701
|
signal,
|
|
5571
5702
|
beforePlaceOrder,
|
|
5572
5703
|
recordRuntimeTrade = true,
|
|
5573
|
-
leverage
|
|
5704
|
+
leverage,
|
|
5705
|
+
executionLimits
|
|
5574
5706
|
}) => {
|
|
5575
5707
|
await beforePlaceOrder?.();
|
|
5576
5708
|
const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
|
|
@@ -5581,23 +5713,57 @@ var executeEntryOrder = async ({
|
|
|
5581
5713
|
signal.orderQty = qty;
|
|
5582
5714
|
signal.orderValue = qty * currentPrice;
|
|
5583
5715
|
signal.orderFailureReason = void 0;
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5716
|
+
let arrivalSnapshot = {
|
|
5717
|
+
arrivalSnapshotTime: Date.now(),
|
|
5718
|
+
arrivalSource: "unavailable",
|
|
5719
|
+
bid: null,
|
|
5720
|
+
ask: null,
|
|
5721
|
+
arrivalMid: null,
|
|
5722
|
+
spreadBps: null
|
|
5723
|
+
};
|
|
5724
|
+
let orderSubmitTime = Date.now();
|
|
5725
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
5726
|
+
connector,
|
|
5727
|
+
symbol,
|
|
5728
|
+
direction,
|
|
5729
|
+
takeProfits,
|
|
5730
|
+
stopLossPrice,
|
|
5731
|
+
limits: executionLimits,
|
|
5732
|
+
onValidated: (snapshot2, economics2) => {
|
|
5733
|
+
arrivalSnapshot = snapshot2;
|
|
5734
|
+
orderSubmitTime = Date.now();
|
|
5735
|
+
if (executionLimits?.maxLossValue != null) {
|
|
5736
|
+
signal.riskDecision = {
|
|
5737
|
+
stage: "order_plan",
|
|
5738
|
+
status: "approved",
|
|
5739
|
+
reason: "PLANNED_LOSS_WITHIN_MAX_LOSS_VALUE",
|
|
5740
|
+
plannedLossValue: economics2.plannedLossValue,
|
|
5741
|
+
maxLossValue: executionLimits.maxLossValue,
|
|
5742
|
+
limitUtilization: economics2.plannedLossValue / executionLimits.maxLossValue,
|
|
5743
|
+
enforced: true
|
|
5744
|
+
};
|
|
5745
|
+
}
|
|
5746
|
+
}
|
|
5747
|
+
});
|
|
5748
|
+
if (!beforeSubmit) {
|
|
5749
|
+
arrivalSnapshot = await getOrderArrivalSnapshot({ connector, symbol });
|
|
5750
|
+
try {
|
|
5751
|
+
validateEntryProtectionAtArrival({
|
|
5752
|
+
direction,
|
|
5753
|
+
signalPrice: currentPrice,
|
|
5754
|
+
bid: arrivalSnapshot.bid,
|
|
5755
|
+
ask: arrivalSnapshot.ask,
|
|
5756
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
5757
|
+
takeProfits,
|
|
5758
|
+
stopLossPrice
|
|
5759
|
+
});
|
|
5760
|
+
} catch (error) {
|
|
5761
|
+
signal.orderStatus = "failed";
|
|
5762
|
+
signal.orderFailureReason = error.message;
|
|
5763
|
+
throw error;
|
|
5764
|
+
}
|
|
5765
|
+
orderSubmitTime = Date.now();
|
|
5599
5766
|
}
|
|
5600
|
-
const orderSubmitTime = Date.now();
|
|
5601
5767
|
const orderPlaced = await connector.placeOrder({
|
|
5602
5768
|
symbol,
|
|
5603
5769
|
qty,
|
|
@@ -5608,7 +5774,8 @@ var executeEntryOrder = async ({
|
|
|
5608
5774
|
direction,
|
|
5609
5775
|
...typeof leverage === "number" && Number.isFinite(leverage) ? { leverage } : {},
|
|
5610
5776
|
orderId,
|
|
5611
|
-
signal
|
|
5777
|
+
signal,
|
|
5778
|
+
...beforeSubmit ? { beforeSubmit } : {}
|
|
5612
5779
|
});
|
|
5613
5780
|
const orderAckTime = Date.now();
|
|
5614
5781
|
const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
|
|
@@ -6440,7 +6607,7 @@ var canUseSharedReplayState = ({
|
|
|
6440
6607
|
}) => (env === "BACKTEST" || env === "PARITY") && Boolean(sharedReplayKey);
|
|
6441
6608
|
|
|
6442
6609
|
// src/strategy/runtimeExecution.ts
|
|
6443
|
-
var
|
|
6610
|
+
var import_logger10 = require("@tradejs/infra/logger");
|
|
6444
6611
|
var import_types = require("@tradejs/types");
|
|
6445
6612
|
var buildExitOrderSignal = ({
|
|
6446
6613
|
strategyName,
|
|
@@ -6492,7 +6659,7 @@ var handleExitDecision = async ({
|
|
|
6492
6659
|
deploymentId: connector.deploymentId
|
|
6493
6660
|
});
|
|
6494
6661
|
if (!activeTrade) {
|
|
6495
|
-
|
|
6662
|
+
import_logger10.logger.warn(
|
|
6496
6663
|
"[%s] blocked closePosition for untracked runtime position: %s",
|
|
6497
6664
|
strategyName ?? "unknown",
|
|
6498
6665
|
symbol
|
|
@@ -6500,7 +6667,7 @@ var handleExitDecision = async ({
|
|
|
6500
6667
|
return "CLOSE_BLOCKED_BY_UNTRACKED_POSITION";
|
|
6501
6668
|
}
|
|
6502
6669
|
if (!strategyName || activeTrade.strategy !== strategyName) {
|
|
6503
|
-
|
|
6670
|
+
import_logger10.logger.warn(
|
|
6504
6671
|
"[%s] blocked closePosition for foreign runtime position: %s ownedBy=%s",
|
|
6505
6672
|
strategyName ?? "unknown",
|
|
6506
6673
|
symbol,
|
|
@@ -6553,7 +6720,7 @@ var handleExitDecision = async ({
|
|
|
6553
6720
|
exitType: closedTrade?.exitType ?? "exit"
|
|
6554
6721
|
});
|
|
6555
6722
|
} catch (notificationError) {
|
|
6556
|
-
|
|
6723
|
+
import_logger10.logger.error(
|
|
6557
6724
|
"runtime close notification error: %s %s",
|
|
6558
6725
|
symbol,
|
|
6559
6726
|
notificationError
|
|
@@ -6567,7 +6734,7 @@ var handleExitDecision = async ({
|
|
|
6567
6734
|
decision,
|
|
6568
6735
|
market
|
|
6569
6736
|
});
|
|
6570
|
-
|
|
6737
|
+
import_logger10.logger.error("close order error: %s %s", symbol, err);
|
|
6571
6738
|
return "ORDER_ERROR";
|
|
6572
6739
|
}
|
|
6573
6740
|
return decision.code;
|
|
@@ -6594,7 +6761,7 @@ var handleProtectDecision = async ({
|
|
|
6594
6761
|
decision,
|
|
6595
6762
|
market
|
|
6596
6763
|
});
|
|
6597
|
-
|
|
6764
|
+
import_logger10.logger.error("protect position error: %s %s", symbol, err);
|
|
6598
6765
|
return "ORDER_ERROR";
|
|
6599
6766
|
}
|
|
6600
6767
|
return decision.code;
|
|
@@ -6660,7 +6827,11 @@ var executeEntryDecision = async ({
|
|
|
6660
6827
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {},
|
|
6661
6828
|
signal,
|
|
6662
6829
|
beforePlaceOrder,
|
|
6663
|
-
recordRuntimeTrade: recordRuntimeJournal
|
|
6830
|
+
recordRuntimeTrade: recordRuntimeJournal,
|
|
6831
|
+
executionLimits: resolveEntryExecutionLimits(
|
|
6832
|
+
hookCtx.strategyConfig,
|
|
6833
|
+
decision.entryContext.direction
|
|
6834
|
+
)
|
|
6664
6835
|
});
|
|
6665
6836
|
await invokeStageHooks(
|
|
6666
6837
|
"afterPlaceOrder",
|
|
@@ -6682,19 +6853,32 @@ var executeEntryDecision = async ({
|
|
|
6682
6853
|
return signal;
|
|
6683
6854
|
}
|
|
6684
6855
|
await beforePlaceOrder();
|
|
6685
|
-
const
|
|
6856
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
6686
6857
|
connector,
|
|
6687
|
-
symbol
|
|
6688
|
-
});
|
|
6689
|
-
validateEntryProtectionAtArrival({
|
|
6858
|
+
symbol,
|
|
6690
6859
|
direction: decision.entryContext.direction,
|
|
6691
|
-
signalPrice: decision.entryContext.prices.currentPrice,
|
|
6692
|
-
bid: arrivalSnapshot.bid,
|
|
6693
|
-
ask: arrivalSnapshot.ask,
|
|
6694
|
-
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
6695
6860
|
takeProfits: decision.orderPlan.takeProfits,
|
|
6696
|
-
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
6861
|
+
stopLossPrice: decision.orderPlan.stopLossPrice,
|
|
6862
|
+
limits: resolveEntryExecutionLimits(
|
|
6863
|
+
hookCtx.strategyConfig,
|
|
6864
|
+
decision.entryContext.direction
|
|
6865
|
+
)
|
|
6697
6866
|
});
|
|
6867
|
+
if (!beforeSubmit) {
|
|
6868
|
+
const arrivalSnapshot = await getOrderArrivalSnapshot({
|
|
6869
|
+
connector,
|
|
6870
|
+
symbol
|
|
6871
|
+
});
|
|
6872
|
+
validateEntryProtectionAtArrival({
|
|
6873
|
+
direction: decision.entryContext.direction,
|
|
6874
|
+
signalPrice: decision.entryContext.prices.currentPrice,
|
|
6875
|
+
bid: arrivalSnapshot.bid,
|
|
6876
|
+
ask: arrivalSnapshot.ask,
|
|
6877
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
6878
|
+
takeProfits: decision.orderPlan.takeProfits,
|
|
6879
|
+
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
6880
|
+
});
|
|
6881
|
+
}
|
|
6698
6882
|
const orderPlaced = await connector.placeOrder({
|
|
6699
6883
|
symbol,
|
|
6700
6884
|
qty: decision.orderPlan.qty,
|
|
@@ -6702,6 +6886,7 @@ var executeEntryDecision = async ({
|
|
|
6702
6886
|
timestamp: decision.entryContext.timestamp,
|
|
6703
6887
|
direction: decision.entryContext.direction,
|
|
6704
6888
|
positionIntent: decision.orderPlan.positionIntent,
|
|
6889
|
+
...beforeSubmit ? { beforeSubmit } : {},
|
|
6705
6890
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {}
|
|
6706
6891
|
});
|
|
6707
6892
|
if (!orderPlaced) {
|
|
@@ -6743,6 +6928,23 @@ var executeEntryDecision = async ({
|
|
|
6743
6928
|
{ decision, entry, market }
|
|
6744
6929
|
);
|
|
6745
6930
|
} catch (err) {
|
|
6931
|
+
if (err instanceof EntrySubmissionValidationError) {
|
|
6932
|
+
if (signal) {
|
|
6933
|
+
signal.orderStatus = "skipped";
|
|
6934
|
+
signal.orderSkipReason = err.message;
|
|
6935
|
+
signal.allocatorDecision = {
|
|
6936
|
+
stage: "runtime_admission",
|
|
6937
|
+
status: "rejected",
|
|
6938
|
+
reason: err.message
|
|
6939
|
+
};
|
|
6940
|
+
}
|
|
6941
|
+
import_logger10.logger.warn(
|
|
6942
|
+
"entry rejected before submission: %s %s",
|
|
6943
|
+
symbol,
|
|
6944
|
+
err.message
|
|
6945
|
+
);
|
|
6946
|
+
return signal ?? err.message;
|
|
6947
|
+
}
|
|
6746
6948
|
if (signal) {
|
|
6747
6949
|
signal.orderStatus = "failed";
|
|
6748
6950
|
if (typeof signal.orderFailureReason !== "string" || !signal.orderFailureReason.trim()) {
|
|
@@ -6757,9 +6959,9 @@ var executeEntryDecision = async ({
|
|
|
6757
6959
|
market
|
|
6758
6960
|
});
|
|
6759
6961
|
if (err?.message === import_types.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY) {
|
|
6760
|
-
|
|
6962
|
+
import_logger10.logger.warn("order warning: %s %s", symbol, err);
|
|
6761
6963
|
} else {
|
|
6762
|
-
|
|
6964
|
+
import_logger10.logger.error("order error: %s %s", symbol, err);
|
|
6763
6965
|
}
|
|
6764
6966
|
return signal ?? "ORDER_ERROR";
|
|
6765
6967
|
}
|
|
@@ -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,7 +7140,7 @@ 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
|
|
@@ -6952,7 +7154,7 @@ var createStrategyRuntime = ({
|
|
|
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,
|
|
@@ -7151,8 +7353,11 @@ var createStrategyRuntime = ({
|
|
|
7151
7353
|
return enrichedBaseContext;
|
|
7152
7354
|
}
|
|
7153
7355
|
});
|
|
7356
|
+
const coreConfig = { ...config };
|
|
7357
|
+
delete coreConfig.RECORD_RUNTIME_TRADES;
|
|
7358
|
+
delete coreConfig.SIMULATE_ORDERS;
|
|
7154
7359
|
const core = await createCore({
|
|
7155
|
-
config,
|
|
7360
|
+
config: coreConfig,
|
|
7156
7361
|
data,
|
|
7157
7362
|
strategyApi,
|
|
7158
7363
|
indicatorsState
|
|
@@ -7771,10 +7976,10 @@ var createStrategyRuntime = ({
|
|
|
7771
7976
|
setStrategyRuntimeFactory(createStrategyRuntime);
|
|
7772
7977
|
|
|
7773
7978
|
// src/connectorsRegistry.ts
|
|
7774
|
-
var
|
|
7979
|
+
var import_logger14 = require("@tradejs/infra/logger");
|
|
7775
7980
|
|
|
7776
7981
|
// src/connectorRuntime.ts
|
|
7777
|
-
var
|
|
7982
|
+
var import_logger13 = require("@tradejs/infra/logger");
|
|
7778
7983
|
var import_tradingAccounts = require("@tradejs/infra/tradingAccounts");
|
|
7779
7984
|
|
|
7780
7985
|
// src/timescaleCachedKline.ts
|
|
@@ -7782,7 +7987,7 @@ var import_constants6 = require("@tradejs/core/constants");
|
|
|
7782
7987
|
var import_data5 = require("@tradejs/core/data");
|
|
7783
7988
|
var import_time2 = require("@tradejs/core/time");
|
|
7784
7989
|
var import_async = require("@tradejs/core/async");
|
|
7785
|
-
var
|
|
7990
|
+
var import_logger12 = require("@tradejs/infra/logger");
|
|
7786
7991
|
var import_candles = require("@tradejs/infra/timescale/candles");
|
|
7787
7992
|
var DEFAULT_LIMIT = 1e3;
|
|
7788
7993
|
var DEFAULT_CACHE_FALLBACK_WINDOW = 1e3;
|
|
@@ -7892,7 +8097,7 @@ var createTimescaleCachedKline = ({
|
|
|
7892
8097
|
}) => {
|
|
7893
8098
|
const intMinutes = intervalToMinutes(interval);
|
|
7894
8099
|
if (!intMinutes) {
|
|
7895
|
-
|
|
8100
|
+
import_logger12.logger.log("error", "refreshTail: invalid interval %s", interval);
|
|
7896
8101
|
return;
|
|
7897
8102
|
}
|
|
7898
8103
|
const intervalMs = intervalMsOf(intMinutes);
|
|
@@ -7923,7 +8128,7 @@ var createTimescaleCachedKline = ({
|
|
|
7923
8128
|
}) => {
|
|
7924
8129
|
const intMinutes = intervalToMinutes(interval);
|
|
7925
8130
|
if (!intMinutes) {
|
|
7926
|
-
|
|
8131
|
+
import_logger12.logger.log("error", "kline: invalid interval %s", interval);
|
|
7927
8132
|
return [];
|
|
7928
8133
|
}
|
|
7929
8134
|
if (defaultStart !== void 0 && defaultEnd !== void 0 && defaultEnd <= defaultStart) {
|
|
@@ -8032,7 +8237,7 @@ var createTimescaleCachedKline = ({
|
|
|
8032
8237
|
if (warmOnly) {
|
|
8033
8238
|
if (isTimescaleFallbackMode) {
|
|
8034
8239
|
isTimescaleFallbackMode = false;
|
|
8035
|
-
|
|
8240
|
+
import_logger12.logger.log("info", "TimescaleDB connection restored for kline cache");
|
|
8036
8241
|
}
|
|
8037
8242
|
return [];
|
|
8038
8243
|
}
|
|
@@ -8050,13 +8255,13 @@ var createTimescaleCachedKline = ({
|
|
|
8050
8255
|
);
|
|
8051
8256
|
if (isTimescaleFallbackMode) {
|
|
8052
8257
|
isTimescaleFallbackMode = false;
|
|
8053
|
-
|
|
8258
|
+
import_logger12.logger.log("info", "TimescaleDB connection restored for kline cache");
|
|
8054
8259
|
}
|
|
8055
8260
|
return dbData;
|
|
8056
8261
|
} catch (error) {
|
|
8057
8262
|
if (!isTimescaleFallbackMode) {
|
|
8058
8263
|
isTimescaleFallbackMode = true;
|
|
8059
|
-
|
|
8264
|
+
import_logger12.logger.log(
|
|
8060
8265
|
"warn",
|
|
8061
8266
|
"TimescaleDB unavailable for %s %s: %s. Falling back to exchange API.",
|
|
8062
8267
|
symbol,
|
|
@@ -8080,7 +8285,7 @@ var createTimescaleCachedKline = ({
|
|
|
8080
8285
|
|
|
8081
8286
|
// src/connectorRuntime.ts
|
|
8082
8287
|
var connectorRuntime = {
|
|
8083
|
-
logger:
|
|
8288
|
+
logger: import_logger13.logger,
|
|
8084
8289
|
resolveTradingAccount: import_tradingAccounts.resolveTradingAccount,
|
|
8085
8290
|
createCachedKline: createTimescaleCachedKline
|
|
8086
8291
|
};
|
|
@@ -8141,7 +8346,7 @@ var normalizeProviders = (providers, connectorName) => {
|
|
|
8141
8346
|
var registerProvider = (provider, connectorName, source, providerToConnectorName) => {
|
|
8142
8347
|
const existing = providerToConnectorName.get(provider);
|
|
8143
8348
|
if (existing && existing !== connectorName) {
|
|
8144
|
-
|
|
8349
|
+
import_logger14.logger.warn(
|
|
8145
8350
|
'Skip duplicate connector provider "%s" from %s: already mapped to %s',
|
|
8146
8351
|
provider,
|
|
8147
8352
|
source,
|
|
@@ -8154,11 +8359,11 @@ var registerProvider = (provider, connectorName, source, providerToConnectorName
|
|
|
8154
8359
|
var registerEntry = (entry, source, state) => {
|
|
8155
8360
|
const connectorName = String(entry?.name ?? "").trim();
|
|
8156
8361
|
if (!connectorName) {
|
|
8157
|
-
|
|
8362
|
+
import_logger14.logger.warn("Skip connector entry without name from %s", source);
|
|
8158
8363
|
return;
|
|
8159
8364
|
}
|
|
8160
8365
|
if (typeof entry.creator !== "function") {
|
|
8161
|
-
|
|
8366
|
+
import_logger14.logger.warn(
|
|
8162
8367
|
'Skip connector entry "%s" from %s: creator must be a function',
|
|
8163
8368
|
connectorName,
|
|
8164
8369
|
source
|
|
@@ -8170,7 +8375,7 @@ var registerEntry = (entry, source, state) => {
|
|
|
8170
8375
|
state.connectorCreators
|
|
8171
8376
|
);
|
|
8172
8377
|
if (existingByName) {
|
|
8173
|
-
|
|
8378
|
+
import_logger14.logger.warn(
|
|
8174
8379
|
'Skip duplicate connector "%s" from %s: already registered as %s',
|
|
8175
8380
|
connectorName,
|
|
8176
8381
|
source,
|
|
@@ -8245,7 +8450,7 @@ var ensureConnectorPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
|
|
|
8245
8450
|
);
|
|
8246
8451
|
const pluginDefinition = extractConnectorPluginDefinition(moduleExport);
|
|
8247
8452
|
if (!pluginDefinition) {
|
|
8248
|
-
|
|
8453
|
+
import_logger14.logger.warn(
|
|
8249
8454
|
'Skip connector plugin "%s": export { connectorEntries } is missing',
|
|
8250
8455
|
moduleName
|
|
8251
8456
|
);
|
|
@@ -8253,7 +8458,7 @@ var ensureConnectorPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
|
|
|
8253
8458
|
}
|
|
8254
8459
|
registerEntries2(pluginDefinition.connectorEntries, moduleName, state);
|
|
8255
8460
|
} catch (error) {
|
|
8256
|
-
|
|
8461
|
+
import_logger14.logger.warn(
|
|
8257
8462
|
'Failed to load connector plugin "%s": %s',
|
|
8258
8463
|
moduleName,
|
|
8259
8464
|
String(error)
|
|
@@ -8950,7 +9155,10 @@ var createTestConnector = (connector, context) => {
|
|
|
8950
9155
|
});
|
|
8951
9156
|
return {
|
|
8952
9157
|
__tradejsTestConnector: true,
|
|
8953
|
-
capabilities:
|
|
9158
|
+
capabilities: {
|
|
9159
|
+
...connector.capabilities,
|
|
9160
|
+
orderSubmissionValidation: true
|
|
9161
|
+
},
|
|
8954
9162
|
universe: connector.universe,
|
|
8955
9163
|
accountId: connector.accountId,
|
|
8956
9164
|
deploymentId: connector.deploymentId,
|
|
@@ -9232,6 +9440,7 @@ var createTestConnector = (connector, context) => {
|
|
|
9232
9440
|
}
|
|
9233
9441
|
},
|
|
9234
9442
|
placeOrder: async (order) => {
|
|
9443
|
+
const { beforeSubmit, ...submittedOrder } = order;
|
|
9235
9444
|
const isPositionIncrease = Boolean(
|
|
9236
9445
|
currentPosition && order.positionIntent === "increase" && currentPosition.direction === order.direction
|
|
9237
9446
|
);
|
|
@@ -9268,6 +9477,7 @@ var createTestConnector = (connector, context) => {
|
|
|
9268
9477
|
signal: order.signal
|
|
9269
9478
|
});
|
|
9270
9479
|
const previousPosition = currentPosition;
|
|
9480
|
+
await beforeSubmit?.({ qty: orderQty, executionPrice: entryPrice });
|
|
9271
9481
|
const previousQty = previousPosition?.qty ?? 0;
|
|
9272
9482
|
const resultingQty = previousQty + orderQty;
|
|
9273
9483
|
const resultingEntryPrice = previousPosition ? getWeightedAverage(
|
|
@@ -9280,7 +9490,7 @@ var createTestConnector = (connector, context) => {
|
|
|
9280
9490
|
...previousPosition,
|
|
9281
9491
|
qty: resultingQty,
|
|
9282
9492
|
price: resultingEntryPrice
|
|
9283
|
-
} : { ...
|
|
9493
|
+
} : { ...submittedOrder, qty: orderQty, price: entryPrice, amount };
|
|
9284
9494
|
originalQty = resultingQty;
|
|
9285
9495
|
if (isPositionIncrease) {
|
|
9286
9496
|
const { fee: fee2, profit: profit2 } = getNetProfit({
|
|
@@ -9380,7 +9590,7 @@ var createTestConnector = (connector, context) => {
|
|
|
9380
9590
|
};
|
|
9381
9591
|
}
|
|
9382
9592
|
logOrder({
|
|
9383
|
-
...
|
|
9593
|
+
...submittedOrder,
|
|
9384
9594
|
qty: orderQty,
|
|
9385
9595
|
price: entryPrice,
|
|
9386
9596
|
profit: profit2,
|
|
@@ -9421,7 +9631,7 @@ var createTestConnector = (connector, context) => {
|
|
|
9421
9631
|
currentEntryLegResults = [openTradeResult];
|
|
9422
9632
|
currentTradeResult = currentSignalId ? { ...openTradeResult, signalId: currentSignalId } : null;
|
|
9423
9633
|
logOrder({
|
|
9424
|
-
...
|
|
9634
|
+
...submittedOrder,
|
|
9425
9635
|
qty: orderQty,
|
|
9426
9636
|
price: entryPrice,
|
|
9427
9637
|
profit,
|
|
@@ -10177,7 +10387,7 @@ var prepareTestingData = async (params) => {
|
|
|
10177
10387
|
throw new Error(`Unknown connector: ${connectorName}`);
|
|
10178
10388
|
}
|
|
10179
10389
|
if (!binanceConnector || !coinbaseConnector) {
|
|
10180
|
-
|
|
10390
|
+
import_logger15.logger.warn(
|
|
10181
10391
|
"Binance/Coinbase connectors are unavailable. Reusing %s for BTC references.",
|
|
10182
10392
|
connectorName
|
|
10183
10393
|
);
|