@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/runtimeDashboard.js
CHANGED
|
@@ -781,7 +781,7 @@ __export(runtimeDashboard_exports, {
|
|
|
781
781
|
});
|
|
782
782
|
module.exports = __toCommonJS(runtimeDashboard_exports);
|
|
783
783
|
var import_time4 = require("@tradejs/core/time");
|
|
784
|
-
var
|
|
784
|
+
var import_logger15 = require("@tradejs/infra/logger");
|
|
785
785
|
var import_runtimeDeploymentEvents = require("@tradejs/infra/runtimeDeploymentEvents");
|
|
786
786
|
var import_tradingAccounts3 = require("@tradejs/infra/tradingAccounts");
|
|
787
787
|
var import_redis4 = require("@tradejs/infra/redis");
|
|
@@ -2342,13 +2342,213 @@ var assessSignalWithJev = async ({
|
|
|
2342
2342
|
// src/strategyRuntime.ts
|
|
2343
2343
|
var import_constants7 = require("@tradejs/core/constants");
|
|
2344
2344
|
var import_strategies6 = require("@tradejs/core/strategies");
|
|
2345
|
-
var
|
|
2345
|
+
var import_logger14 = require("@tradejs/infra/logger");
|
|
2346
2346
|
|
|
2347
2347
|
// src/strategyHelpers/runtime.ts
|
|
2348
|
-
var
|
|
2348
|
+
var import_logger12 = require("@tradejs/infra/logger");
|
|
2349
2349
|
var import_constants5 = require("@tradejs/core/constants");
|
|
2350
2350
|
var import_ml2 = require("@tradejs/infra/ml");
|
|
2351
2351
|
|
|
2352
|
+
// src/strategy/entrySubmissionValidation.ts
|
|
2353
|
+
var import_logger5 = require("@tradejs/infra/logger");
|
|
2354
|
+
|
|
2355
|
+
// src/strategy/entryExecutionValidation.ts
|
|
2356
|
+
var EntrySubmissionValidationError = class extends Error {
|
|
2357
|
+
constructor() {
|
|
2358
|
+
super(...arguments);
|
|
2359
|
+
this.name = "EntrySubmissionValidationError";
|
|
2360
|
+
}
|
|
2361
|
+
};
|
|
2362
|
+
var resolveEntryExecutionLimits = (config, direction) => {
|
|
2363
|
+
const side = config[direction];
|
|
2364
|
+
const minimum = side && typeof side === "object" && "minRiskRatio" in side ? side.minRiskRatio : void 0;
|
|
2365
|
+
const maximum = config.MAX_LOSS_VALUE;
|
|
2366
|
+
if (minimum == null && maximum == null) return void 0;
|
|
2367
|
+
const minimumNetRiskRatio = minimum == null ? void 0 : Number(minimum);
|
|
2368
|
+
const maxLossValue = maximum == null ? void 0 : Number(maximum);
|
|
2369
|
+
const feeRate = Number(config.RISK_FEE_RATE ?? 0);
|
|
2370
|
+
const slippage = Number(config.RISK_SLIPPAGE_BPS ?? 0);
|
|
2371
|
+
const impact = Number(config.RISK_MARKET_IMPACT_BPS ?? 0);
|
|
2372
|
+
const slippageBps = slippage + impact;
|
|
2373
|
+
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)
|
|
2374
|
+
throw new Error("INVALID_ENTRY_EXECUTION_LIMITS");
|
|
2375
|
+
return { minimumNetRiskRatio, maxLossValue, feeRate, slippageBps };
|
|
2376
|
+
};
|
|
2377
|
+
var validateEntryExecution = ({
|
|
2378
|
+
direction,
|
|
2379
|
+
price,
|
|
2380
|
+
qty,
|
|
2381
|
+
stopLossPrice,
|
|
2382
|
+
takeProfitPrice,
|
|
2383
|
+
limits
|
|
2384
|
+
}) => {
|
|
2385
|
+
if (!Number.isFinite(price) || price <= 0 || !Number.isFinite(qty) || qty <= 0 || stopLossPrice == null || !Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || !Number.isFinite(takeProfitPrice) || takeProfitPrice <= 0)
|
|
2386
|
+
throw new EntrySubmissionValidationError(
|
|
2387
|
+
"ENTRY_ECONOMICS_INPUTS_UNAVAILABLE"
|
|
2388
|
+
);
|
|
2389
|
+
const side = direction === "LONG" ? 1 : -1;
|
|
2390
|
+
if (side * (price - stopLossPrice) <= 0 || side * (takeProfitPrice - price) <= 0)
|
|
2391
|
+
throw new EntrySubmissionValidationError(
|
|
2392
|
+
"INVALID_ENTRY_PROTECTION_AT_SUBMISSION"
|
|
2393
|
+
);
|
|
2394
|
+
const slippage = limits.slippageBps / 1e4;
|
|
2395
|
+
const cost = (value, sign) => value * slippage + value * (1 + sign * slippage) * limits.feeRate;
|
|
2396
|
+
const entryCost = cost(price, side);
|
|
2397
|
+
const lossPerUnit = Math.abs(price - stopLossPrice) + entryCost + cost(stopLossPrice, -side);
|
|
2398
|
+
const rewardPerUnit = Math.max(
|
|
2399
|
+
0,
|
|
2400
|
+
Math.abs(takeProfitPrice - price) - entryCost - cost(takeProfitPrice, -side)
|
|
2401
|
+
);
|
|
2402
|
+
const netRiskRatio = rewardPerUnit / lossPerUnit;
|
|
2403
|
+
const plannedLossValue = qty * lossPerUnit;
|
|
2404
|
+
if (!Number.isFinite(netRiskRatio) || !Number.isFinite(plannedLossValue))
|
|
2405
|
+
throw new EntrySubmissionValidationError(
|
|
2406
|
+
"ENTRY_ECONOMICS_INPUTS_UNAVAILABLE"
|
|
2407
|
+
);
|
|
2408
|
+
if (limits.minimumNetRiskRatio != null && netRiskRatio <= limits.minimumNetRiskRatio)
|
|
2409
|
+
throw new EntrySubmissionValidationError(
|
|
2410
|
+
"ENTRY_RR_BELOW_MIN_AT_SUBMISSION"
|
|
2411
|
+
);
|
|
2412
|
+
if (limits.maxLossValue != null && plannedLossValue > limits.maxLossValue * (1 + 1e-9))
|
|
2413
|
+
throw new EntrySubmissionValidationError(
|
|
2414
|
+
"ENTRY_RISK_EXCEEDS_MAX_LOSS_AT_SUBMISSION"
|
|
2415
|
+
);
|
|
2416
|
+
return { netRiskRatio, plannedLossValue };
|
|
2417
|
+
};
|
|
2418
|
+
|
|
2419
|
+
// src/strategy/entrySubmissionValidation.ts
|
|
2420
|
+
var MAX_ENTRY_QUOTE_AGE_MS = 5e3;
|
|
2421
|
+
var toFiniteNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
2422
|
+
var getOrderArrivalSnapshot = async ({
|
|
2423
|
+
connector,
|
|
2424
|
+
symbol,
|
|
2425
|
+
requireTimestamp = false
|
|
2426
|
+
}) => {
|
|
2427
|
+
if (typeof connector.getTopOfBookTicker !== "function") {
|
|
2428
|
+
return {
|
|
2429
|
+
arrivalSnapshotTime: Date.now(),
|
|
2430
|
+
arrivalSource: "unavailable",
|
|
2431
|
+
bid: null,
|
|
2432
|
+
ask: null,
|
|
2433
|
+
arrivalMid: null,
|
|
2434
|
+
spreadBps: null
|
|
2435
|
+
};
|
|
2436
|
+
}
|
|
2437
|
+
try {
|
|
2438
|
+
const ticker = await connector.getTopOfBookTicker(symbol);
|
|
2439
|
+
const arrivalSnapshotTime = toFiniteNumberOrNull(ticker?.timestamp);
|
|
2440
|
+
const timestampUnavailable = requireTimestamp && arrivalSnapshotTime == null;
|
|
2441
|
+
const bid = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.bidPrice);
|
|
2442
|
+
const ask = timestampUnavailable ? null : toFiniteNumberOrNull(ticker?.askPrice);
|
|
2443
|
+
const arrivalMid = bid != null && ask != null ? (bid + ask) / 2 : null;
|
|
2444
|
+
const spreadBps = bid != null && ask != null && arrivalMid != null && arrivalMid > 0 ? (ask - bid) / arrivalMid * 1e4 : null;
|
|
2445
|
+
return {
|
|
2446
|
+
arrivalSnapshotTime: arrivalSnapshotTime ?? Date.now(),
|
|
2447
|
+
arrivalSource: "top_of_book",
|
|
2448
|
+
bid,
|
|
2449
|
+
ask,
|
|
2450
|
+
arrivalMid,
|
|
2451
|
+
spreadBps
|
|
2452
|
+
};
|
|
2453
|
+
} catch (error) {
|
|
2454
|
+
import_logger5.logger.warn(
|
|
2455
|
+
"runtime order arrival snapshot failed: %s %s",
|
|
2456
|
+
symbol,
|
|
2457
|
+
error?.message || String(error)
|
|
2458
|
+
);
|
|
2459
|
+
return {
|
|
2460
|
+
arrivalSnapshotTime: Date.now(),
|
|
2461
|
+
arrivalSource: "top_of_book_error",
|
|
2462
|
+
bid: null,
|
|
2463
|
+
ask: null,
|
|
2464
|
+
arrivalMid: null,
|
|
2465
|
+
spreadBps: null
|
|
2466
|
+
};
|
|
2467
|
+
}
|
|
2468
|
+
};
|
|
2469
|
+
var validateEntryProtectionAtArrival = ({
|
|
2470
|
+
direction,
|
|
2471
|
+
signalPrice,
|
|
2472
|
+
bid,
|
|
2473
|
+
ask,
|
|
2474
|
+
arrivalMid,
|
|
2475
|
+
takeProfits,
|
|
2476
|
+
stopLossPrice
|
|
2477
|
+
}) => {
|
|
2478
|
+
const entryReference = direction === "LONG" ? ask ?? arrivalMid ?? signalPrice : bid ?? arrivalMid ?? signalPrice;
|
|
2479
|
+
const stopReference = direction === "LONG" ? bid ?? arrivalMid ?? signalPrice : ask ?? arrivalMid ?? signalPrice;
|
|
2480
|
+
if (!Number.isFinite(entryReference) || entryReference <= 0 || !Number.isFinite(stopReference) || stopReference <= 0) {
|
|
2481
|
+
throw new Error("INVALID_ENTRY_REFERENCE_AT_ARRIVAL");
|
|
2482
|
+
}
|
|
2483
|
+
for (const takeProfit of takeProfits) {
|
|
2484
|
+
if (!Number.isFinite(takeProfit.price) || takeProfit.price <= 0 || (direction === "LONG" ? takeProfit.price <= entryReference : takeProfit.price >= entryReference)) {
|
|
2485
|
+
throw new Error("TAKE_PROFIT_CROSSED_BEFORE_ENTRY");
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
if (stopLossPrice != null && (!Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || (direction === "LONG" ? stopLossPrice >= stopReference : stopLossPrice <= stopReference))) {
|
|
2489
|
+
throw new Error("STOP_LOSS_CROSSED_BEFORE_ENTRY");
|
|
2490
|
+
}
|
|
2491
|
+
};
|
|
2492
|
+
var createEntrySubmissionValidation = ({
|
|
2493
|
+
connector,
|
|
2494
|
+
symbol,
|
|
2495
|
+
direction,
|
|
2496
|
+
takeProfits,
|
|
2497
|
+
stopLossPrice,
|
|
2498
|
+
limits,
|
|
2499
|
+
onValidated
|
|
2500
|
+
}) => {
|
|
2501
|
+
if (!limits) return void 0;
|
|
2502
|
+
if (!connector.capabilities?.orderSubmissionValidation)
|
|
2503
|
+
throw new EntrySubmissionValidationError(
|
|
2504
|
+
"ENTRY_SUBMISSION_VALIDATION_UNSUPPORTED"
|
|
2505
|
+
);
|
|
2506
|
+
return async ({ qty, executionPrice }) => {
|
|
2507
|
+
const simulated = Reflect.get(connector, "__tradejsTestConnector") === true;
|
|
2508
|
+
const snapshot2 = simulated && executionPrice != null ? {
|
|
2509
|
+
arrivalSnapshotTime: Date.now(),
|
|
2510
|
+
arrivalSource: "simulation",
|
|
2511
|
+
bid: executionPrice,
|
|
2512
|
+
ask: executionPrice,
|
|
2513
|
+
arrivalMid: executionPrice,
|
|
2514
|
+
spreadBps: 0
|
|
2515
|
+
} : await getOrderArrivalSnapshot({
|
|
2516
|
+
connector,
|
|
2517
|
+
symbol,
|
|
2518
|
+
requireTimestamp: true
|
|
2519
|
+
});
|
|
2520
|
+
const age = Date.now() - snapshot2.arrivalSnapshotTime;
|
|
2521
|
+
if (snapshot2.bid == null || snapshot2.ask == null || !Number.isFinite(snapshot2.bid) || !Number.isFinite(snapshot2.ask) || snapshot2.bid <= 0 || snapshot2.ask < snapshot2.bid)
|
|
2522
|
+
throw new EntrySubmissionValidationError(
|
|
2523
|
+
"ENTRY_QUOTE_UNAVAILABLE_AT_SUBMISSION"
|
|
2524
|
+
);
|
|
2525
|
+
if (!Number.isFinite(age) || age < -MAX_ENTRY_QUOTE_AGE_MS || age > MAX_ENTRY_QUOTE_AGE_MS)
|
|
2526
|
+
throw new EntrySubmissionValidationError(
|
|
2527
|
+
"ENTRY_QUOTE_STALE_AT_SUBMISSION"
|
|
2528
|
+
);
|
|
2529
|
+
const price = direction === "LONG" ? snapshot2.ask : snapshot2.bid;
|
|
2530
|
+
validateEntryProtectionAtArrival({
|
|
2531
|
+
direction,
|
|
2532
|
+
signalPrice: price,
|
|
2533
|
+
bid: snapshot2.bid,
|
|
2534
|
+
ask: snapshot2.ask,
|
|
2535
|
+
arrivalMid: snapshot2.arrivalMid,
|
|
2536
|
+
takeProfits,
|
|
2537
|
+
stopLossPrice
|
|
2538
|
+
});
|
|
2539
|
+
const prices = takeProfits.map((tp) => tp.price);
|
|
2540
|
+
const economics2 = validateEntryExecution({
|
|
2541
|
+
direction,
|
|
2542
|
+
price,
|
|
2543
|
+
qty,
|
|
2544
|
+
stopLossPrice,
|
|
2545
|
+
takeProfitPrice: direction === "LONG" ? Math.max(...prices) : Math.min(...prices),
|
|
2546
|
+
limits
|
|
2547
|
+
});
|
|
2548
|
+
onValidated?.(snapshot2, economics2);
|
|
2549
|
+
};
|
|
2550
|
+
};
|
|
2551
|
+
|
|
2352
2552
|
// src/ai.ts
|
|
2353
2553
|
var import_aiLanguages2 = require("@tradejs/core/aiLanguages");
|
|
2354
2554
|
var import_redis2 = require("@tradejs/infra/redis");
|
|
@@ -3881,7 +4081,7 @@ var import_node_crypto = require("crypto");
|
|
|
3881
4081
|
var import_constants3 = require("@tradejs/core/constants");
|
|
3882
4082
|
var import_time3 = require("@tradejs/core/time");
|
|
3883
4083
|
var import_trade = require("@tradejs/core/trade");
|
|
3884
|
-
var
|
|
4084
|
+
var import_logger6 = require("@tradejs/infra/logger");
|
|
3885
4085
|
var import_redis3 = require("@tradejs/infra/redis");
|
|
3886
4086
|
var now = () => Date.now();
|
|
3887
4087
|
var toRandomOrderSuffix = () => (0, import_node_crypto.randomUUID)().replace(/-/g, "").slice(0, 12).toLowerCase();
|
|
@@ -3940,7 +4140,7 @@ var recordRuntimeTradeOpen = async (params) => {
|
|
|
3940
4140
|
)
|
|
3941
4141
|
]);
|
|
3942
4142
|
} catch (error) {
|
|
3943
|
-
|
|
4143
|
+
import_logger6.logger.error(
|
|
3944
4144
|
"runtime trade open journal failed: %s %s",
|
|
3945
4145
|
record.symbol,
|
|
3946
4146
|
error?.message || String(error)
|
|
@@ -4008,7 +4208,7 @@ var recordRuntimeTradeIncrease = async (params) => {
|
|
|
4008
4208
|
)
|
|
4009
4209
|
]);
|
|
4010
4210
|
} catch (error) {
|
|
4011
|
-
|
|
4211
|
+
import_logger6.logger.error(
|
|
4012
4212
|
"runtime trade increase journal failed: %s %s",
|
|
4013
4213
|
symbol,
|
|
4014
4214
|
error?.message || String(error)
|
|
@@ -4115,7 +4315,7 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
4115
4315
|
)
|
|
4116
4316
|
]);
|
|
4117
4317
|
} catch (error) {
|
|
4118
|
-
|
|
4318
|
+
import_logger6.logger.error(
|
|
4119
4319
|
"runtime trade close journal failed: %s %s",
|
|
4120
4320
|
symbol,
|
|
4121
4321
|
error?.message || String(error)
|
|
@@ -4125,11 +4325,11 @@ var markRuntimeTradeClosed = async (params) => {
|
|
|
4125
4325
|
};
|
|
4126
4326
|
|
|
4127
4327
|
// src/strategyHelpers/marketContextStages.ts
|
|
4128
|
-
var
|
|
4328
|
+
var import_logger11 = require("@tradejs/infra/logger");
|
|
4129
4329
|
|
|
4130
4330
|
// src/strategyHelpers/binanceMarketContext.ts
|
|
4131
4331
|
var import_marketContext = require("@tradejs/infra/timescale/marketContext");
|
|
4132
|
-
var
|
|
4332
|
+
var import_logger7 = require("@tradejs/infra/logger");
|
|
4133
4333
|
var import_strategies = require("@tradejs/core/strategies");
|
|
4134
4334
|
|
|
4135
4335
|
// src/binanceBreadthUniverses.ts
|
|
@@ -4493,7 +4693,7 @@ var parseEnabledFlag = (value, env) => {
|
|
|
4493
4693
|
if (normalized === "live") return env !== "BACKTEST";
|
|
4494
4694
|
return false;
|
|
4495
4695
|
};
|
|
4496
|
-
var
|
|
4696
|
+
var toFiniteNumberOrNull2 = (value) => {
|
|
4497
4697
|
const numeric = typeof value === "number" ? value : Number(value);
|
|
4498
4698
|
return Number.isFinite(numeric) ? numeric : null;
|
|
4499
4699
|
};
|
|
@@ -4618,14 +4818,14 @@ var toTradeFlowContext = (row, interval) => row ? {
|
|
|
4618
4818
|
asOfTs: row.ts.getTime(),
|
|
4619
4819
|
ageMs: row.ageMs,
|
|
4620
4820
|
stale: row.stale,
|
|
4621
|
-
trades:
|
|
4622
|
-
buyPressurePct:
|
|
4623
|
-
buyBaseVolume:
|
|
4624
|
-
sellBaseVolume:
|
|
4625
|
-
buyQuoteVolume:
|
|
4626
|
-
sellQuoteVolume:
|
|
4627
|
-
netBaseDelta:
|
|
4628
|
-
netQuoteDelta:
|
|
4821
|
+
trades: toFiniteNumberOrNull2(row.trades),
|
|
4822
|
+
buyPressurePct: toFiniteNumberOrNull2(row.buyPressurePct),
|
|
4823
|
+
buyBaseVolume: toFiniteNumberOrNull2(row.buyBaseVolume),
|
|
4824
|
+
sellBaseVolume: toFiniteNumberOrNull2(row.sellBaseVolume),
|
|
4825
|
+
buyQuoteVolume: toFiniteNumberOrNull2(row.buyQuoteVolume),
|
|
4826
|
+
sellQuoteVolume: toFiniteNumberOrNull2(row.sellQuoteVolume),
|
|
4827
|
+
netBaseDelta: toFiniteNumberOrNull2(row.netBaseDelta),
|
|
4828
|
+
netQuoteDelta: toFiniteNumberOrNull2(row.netQuoteDelta)
|
|
4629
4829
|
} : null;
|
|
4630
4830
|
var getCachedReferenceRows = ({
|
|
4631
4831
|
referenceSymbols,
|
|
@@ -4714,16 +4914,16 @@ var toMarketBreadthContext = (breadth, interval) => ({
|
|
|
4714
4914
|
asOfTs: breadth.ts.getTime(),
|
|
4715
4915
|
ageMs: breadth.ageMs,
|
|
4716
4916
|
stale: breadth.stale,
|
|
4717
|
-
symbolsCount:
|
|
4718
|
-
advancers:
|
|
4719
|
-
decliners:
|
|
4720
|
-
unchanged:
|
|
4721
|
-
advanceDeclineRatio:
|
|
4722
|
-
pctAboveMa20:
|
|
4723
|
-
pctAboveMa50:
|
|
4724
|
-
equalWeightedReturn:
|
|
4725
|
-
volumeWeightedReturn:
|
|
4726
|
-
dispersion:
|
|
4917
|
+
symbolsCount: toFiniteNumberOrNull2(breadth.symbolsCount),
|
|
4918
|
+
advancers: toFiniteNumberOrNull2(breadth.advancers),
|
|
4919
|
+
decliners: toFiniteNumberOrNull2(breadth.decliners),
|
|
4920
|
+
unchanged: toFiniteNumberOrNull2(breadth.unchanged),
|
|
4921
|
+
advanceDeclineRatio: toFiniteNumberOrNull2(breadth.advanceDeclineRatio),
|
|
4922
|
+
pctAboveMa20: toFiniteNumberOrNull2(breadth.pctAboveMa20),
|
|
4923
|
+
pctAboveMa50: toFiniteNumberOrNull2(breadth.pctAboveMa50),
|
|
4924
|
+
equalWeightedReturn: toFiniteNumberOrNull2(breadth.equalWeightedReturn),
|
|
4925
|
+
volumeWeightedReturn: toFiniteNumberOrNull2(breadth.volumeWeightedReturn),
|
|
4926
|
+
dispersion: toFiniteNumberOrNull2(breadth.dispersion)
|
|
4727
4927
|
});
|
|
4728
4928
|
var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
4729
4929
|
const {
|
|
@@ -4824,46 +5024,46 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
4824
5024
|
asOfTs: primaryBreadth.ts.getTime(),
|
|
4825
5025
|
ageMs: primaryBreadth.ageMs,
|
|
4826
5026
|
stale: primaryBreadth.stale,
|
|
4827
|
-
btcReturn1h:
|
|
5027
|
+
btcReturn1h: toFiniteNumberOrNull2(
|
|
4828
5028
|
primaryBreadth.btcReturn1h
|
|
4829
5029
|
),
|
|
4830
|
-
btcReturn4h:
|
|
5030
|
+
btcReturn4h: toFiniteNumberOrNull2(
|
|
4831
5031
|
primaryBreadth.btcReturn4h
|
|
4832
5032
|
),
|
|
4833
|
-
btcReturn24h:
|
|
5033
|
+
btcReturn24h: toFiniteNumberOrNull2(
|
|
4834
5034
|
primaryBreadth.btcReturn24h
|
|
4835
5035
|
),
|
|
4836
|
-
altBasketReturn1h:
|
|
5036
|
+
altBasketReturn1h: toFiniteNumberOrNull2(
|
|
4837
5037
|
primaryBreadth.altBasketReturn1h
|
|
4838
5038
|
),
|
|
4839
|
-
altBasketReturn4h:
|
|
5039
|
+
altBasketReturn4h: toFiniteNumberOrNull2(
|
|
4840
5040
|
primaryBreadth.altBasketReturn4h
|
|
4841
5041
|
),
|
|
4842
|
-
altBasketReturn24h:
|
|
5042
|
+
altBasketReturn24h: toFiniteNumberOrNull2(
|
|
4843
5043
|
primaryBreadth.altBasketReturn24h
|
|
4844
5044
|
),
|
|
4845
|
-
btcVsAltReturn1h:
|
|
5045
|
+
btcVsAltReturn1h: toFiniteNumberOrNull2(
|
|
4846
5046
|
primaryBreadth.btcVsAltReturn1h
|
|
4847
5047
|
),
|
|
4848
|
-
btcVsAltReturn4h:
|
|
5048
|
+
btcVsAltReturn4h: toFiniteNumberOrNull2(
|
|
4849
5049
|
primaryBreadth.btcVsAltReturn4h
|
|
4850
5050
|
),
|
|
4851
|
-
btcVsAltReturn24h:
|
|
5051
|
+
btcVsAltReturn24h: toFiniteNumberOrNull2(
|
|
4852
5052
|
primaryBreadth.btcVsAltReturn24h
|
|
4853
5053
|
),
|
|
4854
|
-
btcTurnoverShare1h:
|
|
5054
|
+
btcTurnoverShare1h: toFiniteNumberOrNull2(
|
|
4855
5055
|
primaryBreadth.btcTurnoverShare1h
|
|
4856
5056
|
),
|
|
4857
|
-
btcTurnoverShare24h:
|
|
5057
|
+
btcTurnoverShare24h: toFiniteNumberOrNull2(
|
|
4858
5058
|
primaryBreadth.btcTurnoverShare24h
|
|
4859
5059
|
),
|
|
4860
|
-
btcTurnoverShareChange24h:
|
|
5060
|
+
btcTurnoverShareChange24h: toFiniteNumberOrNull2(
|
|
4861
5061
|
primaryBreadth.btcTurnoverShareChange24h
|
|
4862
5062
|
),
|
|
4863
|
-
altVolToBtcVol24h:
|
|
5063
|
+
altVolToBtcVol24h: toFiniteNumberOrNull2(
|
|
4864
5064
|
primaryBreadth.altVolToBtcVol24h
|
|
4865
5065
|
),
|
|
4866
|
-
altDispersion24h:
|
|
5066
|
+
altDispersion24h: toFiniteNumberOrNull2(
|
|
4867
5067
|
primaryBreadth.altDispersion24h
|
|
4868
5068
|
),
|
|
4869
5069
|
regime: primaryBreadth.btcAltRegime ?? "unknown"
|
|
@@ -4880,7 +5080,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
4880
5080
|
throw error;
|
|
4881
5081
|
}
|
|
4882
5082
|
binanceMarketContextUnavailable = true;
|
|
4883
|
-
|
|
5083
|
+
import_logger7.logger.warn(
|
|
4884
5084
|
"Binance market context disabled after Timescale read failure: %s",
|
|
4885
5085
|
String(error)
|
|
4886
5086
|
);
|
|
@@ -4890,7 +5090,7 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
4890
5090
|
|
|
4891
5091
|
// src/strategyHelpers/coinMarketCapContext.ts
|
|
4892
5092
|
var import_strategies2 = require("@tradejs/core/strategies");
|
|
4893
|
-
var
|
|
5093
|
+
var import_logger8 = require("@tradejs/infra/logger");
|
|
4894
5094
|
var import_marketContext2 = require("@tradejs/infra/timescale/marketContext");
|
|
4895
5095
|
var DEFAULT_MAX_AGE_MS = 48 * 60 * 6e4;
|
|
4896
5096
|
var SOURCE_GLOBAL_DAILY = "coinmarketcap_global";
|
|
@@ -4928,7 +5128,7 @@ var asInt = (value, fallback) => {
|
|
|
4928
5128
|
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
4929
5129
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
4930
5130
|
};
|
|
4931
|
-
var
|
|
5131
|
+
var toFiniteNumberOrNull3 = (value) => {
|
|
4932
5132
|
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
|
|
4933
5133
|
return Number.isFinite(numeric) ? numeric : null;
|
|
4934
5134
|
};
|
|
@@ -5162,12 +5362,12 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
5162
5362
|
const ethRow = references.get("ETHUSDT") ?? null;
|
|
5163
5363
|
const previousBtcRow = previousReferences.get("BTCUSDT") ?? null;
|
|
5164
5364
|
const previousEthRow = previousReferences.get("ETHUSDT") ?? null;
|
|
5165
|
-
const btcMarketCapUsd =
|
|
5166
|
-
const ethMarketCapUsd =
|
|
5167
|
-
const previousBtcMarketCapUsd =
|
|
5365
|
+
const btcMarketCapUsd = toFiniteNumberOrNull3(btcRow?.marketCapUsd);
|
|
5366
|
+
const ethMarketCapUsd = toFiniteNumberOrNull3(ethRow?.marketCapUsd);
|
|
5367
|
+
const previousBtcMarketCapUsd = toFiniteNumberOrNull3(
|
|
5168
5368
|
previousBtcRow?.marketCapUsd
|
|
5169
5369
|
);
|
|
5170
|
-
const previousEthMarketCapUsd =
|
|
5370
|
+
const previousEthMarketCapUsd = toFiniteNumberOrNull3(
|
|
5171
5371
|
previousEthRow?.marketCapUsd
|
|
5172
5372
|
);
|
|
5173
5373
|
const ethBtcMarketCapRatio = safeDivide(ethMarketCapUsd, btcMarketCapUsd);
|
|
@@ -5176,16 +5376,16 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
5176
5376
|
previousBtcMarketCapUsd
|
|
5177
5377
|
);
|
|
5178
5378
|
const ethBtcMarketCapRatioChange24hPct = ethBtcMarketCapRatio != null && previousEthBtcMarketCapRatio != null && previousEthBtcMarketCapRatio > 0 ? (ethBtcMarketCapRatio - previousEthBtcMarketCapRatio) / previousEthBtcMarketCapRatio : null;
|
|
5179
|
-
const btcVolumeUsd =
|
|
5180
|
-
const ethVolumeUsd =
|
|
5379
|
+
const btcVolumeUsd = toFiniteNumberOrNull3(btcRow?.volumeUsd);
|
|
5380
|
+
const ethVolumeUsd = toFiniteNumberOrNull3(ethRow?.volumeUsd);
|
|
5181
5381
|
const referenceStale = btcRow?.stale === true || ethRow?.stale === true || !btcRow || !ethRow;
|
|
5182
|
-
const btcDominanceChange24hPct =
|
|
5382
|
+
const btcDominanceChange24hPct = toFiniteNumberOrNull3(
|
|
5183
5383
|
globalRow?.btcDominanceChange24hPct
|
|
5184
5384
|
);
|
|
5185
|
-
const altMarketCapChange24hPct =
|
|
5385
|
+
const altMarketCapChange24hPct = toFiniteNumberOrNull3(
|
|
5186
5386
|
globalRow?.altMarketCapChange24hPct
|
|
5187
5387
|
);
|
|
5188
|
-
const altVolumeChange24hPct =
|
|
5388
|
+
const altVolumeChange24hPct = toFiniteNumberOrNull3(
|
|
5189
5389
|
globalRow?.altVolumeChange24hPct
|
|
5190
5390
|
);
|
|
5191
5391
|
const altLiquidityRegime = globalRow ? toAltLiquidityRegime({
|
|
@@ -5196,19 +5396,19 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
5196
5396
|
}) : "unknown";
|
|
5197
5397
|
const exchangeLiquidityRegime = exchangeLiquidityRow ? toExchangeLiquidityRegime({
|
|
5198
5398
|
stale: exchangeLiquidityRow.stale,
|
|
5199
|
-
totalVolumeChange24hPct:
|
|
5399
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
5200
5400
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
5201
5401
|
),
|
|
5202
5402
|
fallback: exchangeLiquidityRow.liquidityRegime ?? "unknown"
|
|
5203
5403
|
}) : "unknown";
|
|
5204
5404
|
const cmc100Row = indexRows.get("cmc100") ?? null;
|
|
5205
5405
|
const cmc20Row = indexRows.get("cmc20") ?? null;
|
|
5206
|
-
const cmc100Value =
|
|
5207
|
-
const cmc20Value =
|
|
5208
|
-
const cmc100Change24hPct =
|
|
5406
|
+
const cmc100Value = toFiniteNumberOrNull3(cmc100Row?.value);
|
|
5407
|
+
const cmc20Value = toFiniteNumberOrNull3(cmc20Row?.value);
|
|
5408
|
+
const cmc100Change24hPct = toFiniteNumberOrNull3(
|
|
5209
5409
|
cmc100Row?.valueChange24hPct
|
|
5210
5410
|
);
|
|
5211
|
-
const cmc20Change24hPct =
|
|
5411
|
+
const cmc20Change24hPct = toFiniteNumberOrNull3(cmc20Row?.valueChange24hPct);
|
|
5212
5412
|
const cmc20ToCmc100Ratio = safeDivide(cmc20Value, cmc100Value);
|
|
5213
5413
|
const cmc20ToCmc100RatioChange24hPct = cmc20Change24hPct != null && cmc100Change24hPct != null ? (1 + cmc20Change24hPct) / (1 + cmc100Change24hPct) - 1 : null;
|
|
5214
5414
|
const indexStale = cmc100Row?.stale === true || cmc20Row?.stale === true || !cmc100Row || !cmc20Row;
|
|
@@ -5232,41 +5432,41 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
5232
5432
|
asOfTs: globalRow.ts.getTime(),
|
|
5233
5433
|
ageMs: globalRow.ageMs,
|
|
5234
5434
|
stale: globalRow.stale,
|
|
5235
|
-
totalMarketCapUsd:
|
|
5435
|
+
totalMarketCapUsd: toFiniteNumberOrNull3(
|
|
5236
5436
|
globalRow.totalMarketCapUsd
|
|
5237
5437
|
),
|
|
5238
|
-
totalVolumeUsd:
|
|
5438
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
5239
5439
|
globalRow.totalVolumeUsd
|
|
5240
5440
|
),
|
|
5241
|
-
totalVolumeReportedUsd:
|
|
5441
|
+
totalVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
5242
5442
|
globalRow.totalVolumeReportedUsd
|
|
5243
5443
|
),
|
|
5244
|
-
altMarketCapUsd:
|
|
5444
|
+
altMarketCapUsd: toFiniteNumberOrNull3(
|
|
5245
5445
|
globalRow.altMarketCapUsd
|
|
5246
5446
|
),
|
|
5247
|
-
altVolumeUsd:
|
|
5248
|
-
altVolumeReportedUsd:
|
|
5447
|
+
altVolumeUsd: toFiniteNumberOrNull3(globalRow.altVolumeUsd),
|
|
5448
|
+
altVolumeReportedUsd: toFiniteNumberOrNull3(
|
|
5249
5449
|
globalRow.altVolumeReportedUsd
|
|
5250
5450
|
),
|
|
5251
|
-
btcDominancePct:
|
|
5451
|
+
btcDominancePct: toFiniteNumberOrNull3(
|
|
5252
5452
|
globalRow.btcDominancePct
|
|
5253
5453
|
),
|
|
5254
|
-
ethDominancePct:
|
|
5454
|
+
ethDominancePct: toFiniteNumberOrNull3(
|
|
5255
5455
|
globalRow.ethDominancePct
|
|
5256
5456
|
),
|
|
5257
5457
|
btcDominanceChange24hPct,
|
|
5258
|
-
ethDominanceChange24hPct:
|
|
5458
|
+
ethDominanceChange24hPct: toFiniteNumberOrNull3(
|
|
5259
5459
|
globalRow.ethDominanceChange24hPct
|
|
5260
5460
|
),
|
|
5261
5461
|
altMarketCapChange24hPct,
|
|
5262
5462
|
altVolumeChange24hPct,
|
|
5263
|
-
activeCryptocurrencies:
|
|
5463
|
+
activeCryptocurrencies: toFiniteNumberOrNull3(
|
|
5264
5464
|
globalRow.activeCryptocurrencies
|
|
5265
5465
|
),
|
|
5266
|
-
activeExchanges:
|
|
5466
|
+
activeExchanges: toFiniteNumberOrNull3(
|
|
5267
5467
|
globalRow.activeExchanges
|
|
5268
5468
|
),
|
|
5269
|
-
activeMarketPairs:
|
|
5469
|
+
activeMarketPairs: toFiniteNumberOrNull3(
|
|
5270
5470
|
globalRow.activeMarketPairs
|
|
5271
5471
|
),
|
|
5272
5472
|
altLiquidityRegime
|
|
@@ -5311,22 +5511,22 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
5311
5511
|
asOfTs: exchangeLiquidityRow.ts.getTime(),
|
|
5312
5512
|
ageMs: exchangeLiquidityRow.ageMs,
|
|
5313
5513
|
stale: exchangeLiquidityRow.stale,
|
|
5314
|
-
exchangesCount:
|
|
5514
|
+
exchangesCount: toFiniteNumberOrNull3(
|
|
5315
5515
|
exchangeLiquidityRow.exchangesCount
|
|
5316
5516
|
),
|
|
5317
|
-
totalVolumeUsd:
|
|
5517
|
+
totalVolumeUsd: toFiniteNumberOrNull3(
|
|
5318
5518
|
exchangeLiquidityRow.totalVolumeUsd
|
|
5319
5519
|
),
|
|
5320
|
-
totalVolumeChange24hPct:
|
|
5520
|
+
totalVolumeChange24hPct: toFiniteNumberOrNull3(
|
|
5321
5521
|
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
5322
5522
|
),
|
|
5323
|
-
binanceVolumeUsd:
|
|
5523
|
+
binanceVolumeUsd: toFiniteNumberOrNull3(
|
|
5324
5524
|
exchangeLiquidityRow.binanceVolumeUsd
|
|
5325
5525
|
),
|
|
5326
|
-
binanceVolumeShare:
|
|
5526
|
+
binanceVolumeShare: toFiniteNumberOrNull3(
|
|
5327
5527
|
exchangeLiquidityRow.binanceVolumeShare
|
|
5328
5528
|
),
|
|
5329
|
-
topExchangeVolumeShare:
|
|
5529
|
+
topExchangeVolumeShare: toFiniteNumberOrNull3(
|
|
5330
5530
|
exchangeLiquidityRow.topExchangeVolumeShare
|
|
5331
5531
|
),
|
|
5332
5532
|
liquidityRegime: exchangeLiquidityRegime
|
|
@@ -5339,11 +5539,11 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
5339
5539
|
asOfTs: fearGreedRow.ts.getTime(),
|
|
5340
5540
|
ageMs: fearGreedRow.ageMs,
|
|
5341
5541
|
stale: fearGreedRow.stale,
|
|
5342
|
-
value:
|
|
5343
|
-
valueChange24h:
|
|
5542
|
+
value: toFiniteNumberOrNull3(fearGreedRow.value),
|
|
5543
|
+
valueChange24h: toFiniteNumberOrNull3(
|
|
5344
5544
|
fearGreedRow.valueChange24h
|
|
5345
5545
|
),
|
|
5346
|
-
valueChange7d:
|
|
5546
|
+
valueChange7d: toFiniteNumberOrNull3(
|
|
5347
5547
|
fearGreedRow.valueChange7d
|
|
5348
5548
|
),
|
|
5349
5549
|
classification: fearGreedRow.classification ?? "Unknown",
|
|
@@ -5363,13 +5563,13 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
5363
5563
|
cmc100Value,
|
|
5364
5564
|
cmc100Change24hPct,
|
|
5365
5565
|
cmc100TopConstituentSymbol: cmc100Row?.topConstituentSymbol ?? null,
|
|
5366
|
-
cmc100TopConstituentWeightPct:
|
|
5566
|
+
cmc100TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
5367
5567
|
cmc100Row?.topConstituentWeightPct
|
|
5368
5568
|
),
|
|
5369
5569
|
cmc20Value,
|
|
5370
5570
|
cmc20Change24hPct,
|
|
5371
5571
|
cmc20TopConstituentSymbol: cmc20Row?.topConstituentSymbol ?? null,
|
|
5372
|
-
cmc20TopConstituentWeightPct:
|
|
5572
|
+
cmc20TopConstituentWeightPct: toFiniteNumberOrNull3(
|
|
5373
5573
|
cmc20Row?.topConstituentWeightPct
|
|
5374
5574
|
),
|
|
5375
5575
|
cmc20ToCmc100Ratio,
|
|
@@ -5387,7 +5587,7 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
5387
5587
|
throw error;
|
|
5388
5588
|
}
|
|
5389
5589
|
coinMarketCapContextUnavailable = true;
|
|
5390
|
-
|
|
5590
|
+
import_logger8.logger.warn(
|
|
5391
5591
|
"CoinMarketCap context disabled after Timescale read failure: %s",
|
|
5392
5592
|
String(error)
|
|
5393
5593
|
);
|
|
@@ -5401,7 +5601,7 @@ var import_data3 = require("@tradejs/core/data");
|
|
|
5401
5601
|
var import_strategies3 = require("@tradejs/core/strategies");
|
|
5402
5602
|
var import_constants4 = require("@tradejs/core/constants");
|
|
5403
5603
|
var import_derivatives = require("@tradejs/infra/timescale/derivatives");
|
|
5404
|
-
var
|
|
5604
|
+
var import_logger9 = require("@tradejs/infra/logger");
|
|
5405
5605
|
var STORED_INTERVALS = ["15m", "1h"];
|
|
5406
5606
|
var CONTEXT_INTERVALS = ["15m", "1h"];
|
|
5407
5607
|
var DEFAULT_LOOKBACK_HOURS = 48;
|
|
@@ -5456,7 +5656,7 @@ var resolvePrimaryReferenceSymbol2 = () => PRIMARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
|
5456
5656
|
var resolveSecondaryReferenceSymbol = () => SECONDARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
5457
5657
|
var getPrimaryIntervalContext = (context) => context?.intervals["15m"] ?? context?.intervals["1h"] ?? null;
|
|
5458
5658
|
var hasDerivativesSymbolData = (context) => Object.keys(context.intervals).length > 0 && !context.summary.riskFlags.includes("missing_derivatives");
|
|
5459
|
-
var
|
|
5659
|
+
var toFiniteNumberOrNull4 = (value) => {
|
|
5460
5660
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
5461
5661
|
if (typeof value === "string" && value.trim()) {
|
|
5462
5662
|
const parsed = Number(value);
|
|
@@ -5470,8 +5670,8 @@ var roundNullable = (value, digits = 4) => {
|
|
|
5470
5670
|
return Math.round(value * multiplier) / multiplier;
|
|
5471
5671
|
};
|
|
5472
5672
|
var deltaNullable = (targetValue, referenceValue) => {
|
|
5473
|
-
const target =
|
|
5474
|
-
const reference =
|
|
5673
|
+
const target = toFiniteNumberOrNull4(targetValue);
|
|
5674
|
+
const reference = toFiniteNumberOrNull4(referenceValue);
|
|
5475
5675
|
return target == null || reference == null ? null : roundNullable(target - reference);
|
|
5476
5676
|
};
|
|
5477
5677
|
var buildTargetDerivedContext = (params) => {
|
|
@@ -5633,7 +5833,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
5633
5833
|
throw error;
|
|
5634
5834
|
}
|
|
5635
5835
|
derivativesContextUnavailable = true;
|
|
5636
|
-
|
|
5836
|
+
import_logger9.logger.warn(
|
|
5637
5837
|
"Derivatives context disabled after Timescale read failure: %s",
|
|
5638
5838
|
String(error)
|
|
5639
5839
|
);
|
|
@@ -5645,7 +5845,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
5645
5845
|
var import_data4 = require("@tradejs/core/data");
|
|
5646
5846
|
var import_strategies4 = require("@tradejs/core/strategies");
|
|
5647
5847
|
var import_hyperliquidWhales2 = require("@tradejs/infra/timescale/hyperliquidWhales");
|
|
5648
|
-
var
|
|
5848
|
+
var import_logger10 = require("@tradejs/infra/logger");
|
|
5649
5849
|
|
|
5650
5850
|
// src/hyperliquidWhaleUniverse.ts
|
|
5651
5851
|
var import_node_crypto3 = require("crypto");
|
|
@@ -6165,7 +6365,7 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
6165
6365
|
throw error;
|
|
6166
6366
|
}
|
|
6167
6367
|
hyperliquidWhaleContextUnavailable = true;
|
|
6168
|
-
|
|
6368
|
+
import_logger10.logger.warn(
|
|
6169
6369
|
"Hyperliquid whale context disabled after Timescale read failure: %s",
|
|
6170
6370
|
String(error)
|
|
6171
6371
|
);
|
|
@@ -6248,7 +6448,7 @@ var runMarketContextStage = async ({
|
|
|
6248
6448
|
elapsedMs: Date.now() - startedAt
|
|
6249
6449
|
};
|
|
6250
6450
|
if (status === "timed_out") {
|
|
6251
|
-
|
|
6451
|
+
import_logger11.logger.warn(
|
|
6252
6452
|
"Market context stage timed out: %s after %sms",
|
|
6253
6453
|
stage,
|
|
6254
6454
|
result.elapsedMs
|
|
@@ -6438,79 +6638,10 @@ var enrichSignalWithAi = async ({
|
|
|
6438
6638
|
signal.aiAnalysis = analysis;
|
|
6439
6639
|
return resolveAiQuality(analysis, direction);
|
|
6440
6640
|
} catch (err) {
|
|
6441
|
-
|
|
6641
|
+
import_logger12.logger.error("AI analysis error: %s %s", symbol, formatAiError(err));
|
|
6442
6642
|
}
|
|
6443
6643
|
return void 0;
|
|
6444
6644
|
};
|
|
6445
|
-
var toFiniteNumberOrNull4 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
6446
|
-
var getOrderArrivalSnapshot = async ({
|
|
6447
|
-
connector,
|
|
6448
|
-
symbol
|
|
6449
|
-
}) => {
|
|
6450
|
-
if (typeof connector.getTopOfBookTicker !== "function") {
|
|
6451
|
-
return {
|
|
6452
|
-
arrivalSnapshotTime: Date.now(),
|
|
6453
|
-
arrivalSource: "unavailable",
|
|
6454
|
-
bid: null,
|
|
6455
|
-
ask: null,
|
|
6456
|
-
arrivalMid: null,
|
|
6457
|
-
spreadBps: null
|
|
6458
|
-
};
|
|
6459
|
-
}
|
|
6460
|
-
try {
|
|
6461
|
-
const ticker = await connector.getTopOfBookTicker(symbol);
|
|
6462
|
-
const arrivalSnapshotTime = toFiniteNumberOrNull4(ticker?.timestamp);
|
|
6463
|
-
const bid = toFiniteNumberOrNull4(ticker?.bidPrice);
|
|
6464
|
-
const ask = toFiniteNumberOrNull4(ticker?.askPrice);
|
|
6465
|
-
const arrivalMid = bid != null && ask != null ? (bid + ask) / 2 : null;
|
|
6466
|
-
const spreadBps = bid != null && ask != null && arrivalMid != null && arrivalMid > 0 ? (ask - bid) / arrivalMid * 1e4 : null;
|
|
6467
|
-
return {
|
|
6468
|
-
arrivalSnapshotTime: arrivalSnapshotTime ?? Date.now(),
|
|
6469
|
-
arrivalSource: "top_of_book",
|
|
6470
|
-
bid,
|
|
6471
|
-
ask,
|
|
6472
|
-
arrivalMid,
|
|
6473
|
-
spreadBps
|
|
6474
|
-
};
|
|
6475
|
-
} catch (error) {
|
|
6476
|
-
import_logger11.logger.warn(
|
|
6477
|
-
"runtime order arrival snapshot failed: %s %s",
|
|
6478
|
-
symbol,
|
|
6479
|
-
error?.message || String(error)
|
|
6480
|
-
);
|
|
6481
|
-
return {
|
|
6482
|
-
arrivalSnapshotTime: Date.now(),
|
|
6483
|
-
arrivalSource: "top_of_book_error",
|
|
6484
|
-
bid: null,
|
|
6485
|
-
ask: null,
|
|
6486
|
-
arrivalMid: null,
|
|
6487
|
-
spreadBps: null
|
|
6488
|
-
};
|
|
6489
|
-
}
|
|
6490
|
-
};
|
|
6491
|
-
var validateEntryProtectionAtArrival = ({
|
|
6492
|
-
direction,
|
|
6493
|
-
signalPrice,
|
|
6494
|
-
bid,
|
|
6495
|
-
ask,
|
|
6496
|
-
arrivalMid,
|
|
6497
|
-
takeProfits,
|
|
6498
|
-
stopLossPrice
|
|
6499
|
-
}) => {
|
|
6500
|
-
const entryReference = direction === "LONG" ? ask ?? arrivalMid ?? signalPrice : bid ?? arrivalMid ?? signalPrice;
|
|
6501
|
-
const stopReference = direction === "LONG" ? bid ?? arrivalMid ?? signalPrice : ask ?? arrivalMid ?? signalPrice;
|
|
6502
|
-
if (!Number.isFinite(entryReference) || entryReference <= 0 || !Number.isFinite(stopReference) || stopReference <= 0) {
|
|
6503
|
-
throw new Error("INVALID_ENTRY_REFERENCE_AT_ARRIVAL");
|
|
6504
|
-
}
|
|
6505
|
-
for (const takeProfit of takeProfits) {
|
|
6506
|
-
if (!Number.isFinite(takeProfit.price) || takeProfit.price <= 0 || (direction === "LONG" ? takeProfit.price <= entryReference : takeProfit.price >= entryReference)) {
|
|
6507
|
-
throw new Error("TAKE_PROFIT_CROSSED_BEFORE_ENTRY");
|
|
6508
|
-
}
|
|
6509
|
-
}
|
|
6510
|
-
if (stopLossPrice != null && (!Number.isFinite(stopLossPrice) || stopLossPrice <= 0 || (direction === "LONG" ? stopLossPrice >= stopReference : stopLossPrice <= stopReference))) {
|
|
6511
|
-
throw new Error("STOP_LOSS_CROSSED_BEFORE_ENTRY");
|
|
6512
|
-
}
|
|
6513
|
-
};
|
|
6514
6645
|
var resolveRuntimeTelemetryQuality = ({
|
|
6515
6646
|
signalClosePrice,
|
|
6516
6647
|
arrivalMid,
|
|
@@ -6587,7 +6718,8 @@ var executeEntryOrder = async ({
|
|
|
6587
6718
|
signal,
|
|
6588
6719
|
beforePlaceOrder,
|
|
6589
6720
|
recordRuntimeTrade = true,
|
|
6590
|
-
leverage
|
|
6721
|
+
leverage,
|
|
6722
|
+
executionLimits
|
|
6591
6723
|
}) => {
|
|
6592
6724
|
await beforePlaceOrder?.();
|
|
6593
6725
|
const previousPosition = positionIntent === "increase" ? await connector.getPosition(symbol) : null;
|
|
@@ -6598,23 +6730,57 @@ var executeEntryOrder = async ({
|
|
|
6598
6730
|
signal.orderQty = qty;
|
|
6599
6731
|
signal.orderValue = qty * currentPrice;
|
|
6600
6732
|
signal.orderFailureReason = void 0;
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6733
|
+
let arrivalSnapshot = {
|
|
6734
|
+
arrivalSnapshotTime: Date.now(),
|
|
6735
|
+
arrivalSource: "unavailable",
|
|
6736
|
+
bid: null,
|
|
6737
|
+
ask: null,
|
|
6738
|
+
arrivalMid: null,
|
|
6739
|
+
spreadBps: null
|
|
6740
|
+
};
|
|
6741
|
+
let orderSubmitTime = Date.now();
|
|
6742
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
6743
|
+
connector,
|
|
6744
|
+
symbol,
|
|
6745
|
+
direction,
|
|
6746
|
+
takeProfits,
|
|
6747
|
+
stopLossPrice,
|
|
6748
|
+
limits: executionLimits,
|
|
6749
|
+
onValidated: (snapshot2, economics2) => {
|
|
6750
|
+
arrivalSnapshot = snapshot2;
|
|
6751
|
+
orderSubmitTime = Date.now();
|
|
6752
|
+
if (executionLimits?.maxLossValue != null) {
|
|
6753
|
+
signal.riskDecision = {
|
|
6754
|
+
stage: "order_plan",
|
|
6755
|
+
status: "approved",
|
|
6756
|
+
reason: "PLANNED_LOSS_WITHIN_MAX_LOSS_VALUE",
|
|
6757
|
+
plannedLossValue: economics2.plannedLossValue,
|
|
6758
|
+
maxLossValue: executionLimits.maxLossValue,
|
|
6759
|
+
limitUtilization: economics2.plannedLossValue / executionLimits.maxLossValue,
|
|
6760
|
+
enforced: true
|
|
6761
|
+
};
|
|
6762
|
+
}
|
|
6763
|
+
}
|
|
6764
|
+
});
|
|
6765
|
+
if (!beforeSubmit) {
|
|
6766
|
+
arrivalSnapshot = await getOrderArrivalSnapshot({ connector, symbol });
|
|
6767
|
+
try {
|
|
6768
|
+
validateEntryProtectionAtArrival({
|
|
6769
|
+
direction,
|
|
6770
|
+
signalPrice: currentPrice,
|
|
6771
|
+
bid: arrivalSnapshot.bid,
|
|
6772
|
+
ask: arrivalSnapshot.ask,
|
|
6773
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
6774
|
+
takeProfits,
|
|
6775
|
+
stopLossPrice
|
|
6776
|
+
});
|
|
6777
|
+
} catch (error) {
|
|
6778
|
+
signal.orderStatus = "failed";
|
|
6779
|
+
signal.orderFailureReason = error.message;
|
|
6780
|
+
throw error;
|
|
6781
|
+
}
|
|
6782
|
+
orderSubmitTime = Date.now();
|
|
6616
6783
|
}
|
|
6617
|
-
const orderSubmitTime = Date.now();
|
|
6618
6784
|
const orderPlaced = await connector.placeOrder({
|
|
6619
6785
|
symbol,
|
|
6620
6786
|
qty,
|
|
@@ -6625,7 +6791,8 @@ var executeEntryOrder = async ({
|
|
|
6625
6791
|
direction,
|
|
6626
6792
|
...typeof leverage === "number" && Number.isFinite(leverage) ? { leverage } : {},
|
|
6627
6793
|
orderId,
|
|
6628
|
-
signal
|
|
6794
|
+
signal,
|
|
6795
|
+
...beforeSubmit ? { beforeSubmit } : {}
|
|
6629
6796
|
});
|
|
6630
6797
|
const orderAckTime = Date.now();
|
|
6631
6798
|
const placedQty = typeof signal.orderQty === "number" && Number.isFinite(signal.orderQty) && signal.orderQty > 0 ? signal.orderQty : qty;
|
|
@@ -7454,7 +7621,7 @@ var canUseSharedReplayState = ({
|
|
|
7454
7621
|
}) => (env === "BACKTEST" || env === "PARITY") && Boolean(sharedReplayKey);
|
|
7455
7622
|
|
|
7456
7623
|
// src/strategy/runtimeExecution.ts
|
|
7457
|
-
var
|
|
7624
|
+
var import_logger13 = require("@tradejs/infra/logger");
|
|
7458
7625
|
var import_types = require("@tradejs/types");
|
|
7459
7626
|
var buildExitOrderSignal = ({
|
|
7460
7627
|
strategyName,
|
|
@@ -7506,7 +7673,7 @@ var handleExitDecision = async ({
|
|
|
7506
7673
|
deploymentId: connector.deploymentId
|
|
7507
7674
|
});
|
|
7508
7675
|
if (!activeTrade) {
|
|
7509
|
-
|
|
7676
|
+
import_logger13.logger.warn(
|
|
7510
7677
|
"[%s] blocked closePosition for untracked runtime position: %s",
|
|
7511
7678
|
strategyName ?? "unknown",
|
|
7512
7679
|
symbol
|
|
@@ -7514,7 +7681,7 @@ var handleExitDecision = async ({
|
|
|
7514
7681
|
return "CLOSE_BLOCKED_BY_UNTRACKED_POSITION";
|
|
7515
7682
|
}
|
|
7516
7683
|
if (!strategyName || activeTrade.strategy !== strategyName) {
|
|
7517
|
-
|
|
7684
|
+
import_logger13.logger.warn(
|
|
7518
7685
|
"[%s] blocked closePosition for foreign runtime position: %s ownedBy=%s",
|
|
7519
7686
|
strategyName ?? "unknown",
|
|
7520
7687
|
symbol,
|
|
@@ -7567,7 +7734,7 @@ var handleExitDecision = async ({
|
|
|
7567
7734
|
exitType: closedTrade?.exitType ?? "exit"
|
|
7568
7735
|
});
|
|
7569
7736
|
} catch (notificationError) {
|
|
7570
|
-
|
|
7737
|
+
import_logger13.logger.error(
|
|
7571
7738
|
"runtime close notification error: %s %s",
|
|
7572
7739
|
symbol,
|
|
7573
7740
|
notificationError
|
|
@@ -7581,7 +7748,7 @@ var handleExitDecision = async ({
|
|
|
7581
7748
|
decision,
|
|
7582
7749
|
market
|
|
7583
7750
|
});
|
|
7584
|
-
|
|
7751
|
+
import_logger13.logger.error("close order error: %s %s", symbol, err);
|
|
7585
7752
|
return "ORDER_ERROR";
|
|
7586
7753
|
}
|
|
7587
7754
|
return decision.code;
|
|
@@ -7608,7 +7775,7 @@ var handleProtectDecision = async ({
|
|
|
7608
7775
|
decision,
|
|
7609
7776
|
market
|
|
7610
7777
|
});
|
|
7611
|
-
|
|
7778
|
+
import_logger13.logger.error("protect position error: %s %s", symbol, err);
|
|
7612
7779
|
return "ORDER_ERROR";
|
|
7613
7780
|
}
|
|
7614
7781
|
return decision.code;
|
|
@@ -7674,7 +7841,11 @@ var executeEntryDecision = async ({
|
|
|
7674
7841
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {},
|
|
7675
7842
|
signal,
|
|
7676
7843
|
beforePlaceOrder,
|
|
7677
|
-
recordRuntimeTrade: recordRuntimeJournal
|
|
7844
|
+
recordRuntimeTrade: recordRuntimeJournal,
|
|
7845
|
+
executionLimits: resolveEntryExecutionLimits(
|
|
7846
|
+
hookCtx.strategyConfig,
|
|
7847
|
+
decision.entryContext.direction
|
|
7848
|
+
)
|
|
7678
7849
|
});
|
|
7679
7850
|
await invokeStageHooks(
|
|
7680
7851
|
"afterPlaceOrder",
|
|
@@ -7696,19 +7867,32 @@ var executeEntryDecision = async ({
|
|
|
7696
7867
|
return signal;
|
|
7697
7868
|
}
|
|
7698
7869
|
await beforePlaceOrder();
|
|
7699
|
-
const
|
|
7870
|
+
const beforeSubmit = createEntrySubmissionValidation({
|
|
7700
7871
|
connector,
|
|
7701
|
-
symbol
|
|
7702
|
-
});
|
|
7703
|
-
validateEntryProtectionAtArrival({
|
|
7872
|
+
symbol,
|
|
7704
7873
|
direction: decision.entryContext.direction,
|
|
7705
|
-
signalPrice: decision.entryContext.prices.currentPrice,
|
|
7706
|
-
bid: arrivalSnapshot.bid,
|
|
7707
|
-
ask: arrivalSnapshot.ask,
|
|
7708
|
-
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
7709
7874
|
takeProfits: decision.orderPlan.takeProfits,
|
|
7710
|
-
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
7875
|
+
stopLossPrice: decision.orderPlan.stopLossPrice,
|
|
7876
|
+
limits: resolveEntryExecutionLimits(
|
|
7877
|
+
hookCtx.strategyConfig,
|
|
7878
|
+
decision.entryContext.direction
|
|
7879
|
+
)
|
|
7711
7880
|
});
|
|
7881
|
+
if (!beforeSubmit) {
|
|
7882
|
+
const arrivalSnapshot = await getOrderArrivalSnapshot({
|
|
7883
|
+
connector,
|
|
7884
|
+
symbol
|
|
7885
|
+
});
|
|
7886
|
+
validateEntryProtectionAtArrival({
|
|
7887
|
+
direction: decision.entryContext.direction,
|
|
7888
|
+
signalPrice: decision.entryContext.prices.currentPrice,
|
|
7889
|
+
bid: arrivalSnapshot.bid,
|
|
7890
|
+
ask: arrivalSnapshot.ask,
|
|
7891
|
+
arrivalMid: arrivalSnapshot.arrivalMid,
|
|
7892
|
+
takeProfits: decision.orderPlan.takeProfits,
|
|
7893
|
+
stopLossPrice: decision.orderPlan.stopLossPrice
|
|
7894
|
+
});
|
|
7895
|
+
}
|
|
7712
7896
|
const orderPlaced = await connector.placeOrder({
|
|
7713
7897
|
symbol,
|
|
7714
7898
|
qty: decision.orderPlan.qty,
|
|
@@ -7716,6 +7900,7 @@ var executeEntryDecision = async ({
|
|
|
7716
7900
|
timestamp: decision.entryContext.timestamp,
|
|
7717
7901
|
direction: decision.entryContext.direction,
|
|
7718
7902
|
positionIntent: decision.orderPlan.positionIntent,
|
|
7903
|
+
...beforeSubmit ? { beforeSubmit } : {},
|
|
7719
7904
|
...Number.isFinite(Number(hookCtx.strategyConfig.LEVERAGE)) ? { leverage: Number(hookCtx.strategyConfig.LEVERAGE) } : {}
|
|
7720
7905
|
});
|
|
7721
7906
|
if (!orderPlaced) {
|
|
@@ -7757,6 +7942,23 @@ var executeEntryDecision = async ({
|
|
|
7757
7942
|
{ decision, entry, market }
|
|
7758
7943
|
);
|
|
7759
7944
|
} catch (err) {
|
|
7945
|
+
if (err instanceof EntrySubmissionValidationError) {
|
|
7946
|
+
if (signal) {
|
|
7947
|
+
signal.orderStatus = "skipped";
|
|
7948
|
+
signal.orderSkipReason = err.message;
|
|
7949
|
+
signal.allocatorDecision = {
|
|
7950
|
+
stage: "runtime_admission",
|
|
7951
|
+
status: "rejected",
|
|
7952
|
+
reason: err.message
|
|
7953
|
+
};
|
|
7954
|
+
}
|
|
7955
|
+
import_logger13.logger.warn(
|
|
7956
|
+
"entry rejected before submission: %s %s",
|
|
7957
|
+
symbol,
|
|
7958
|
+
err.message
|
|
7959
|
+
);
|
|
7960
|
+
return signal ?? err.message;
|
|
7961
|
+
}
|
|
7760
7962
|
if (signal) {
|
|
7761
7963
|
signal.orderStatus = "failed";
|
|
7762
7964
|
if (typeof signal.orderFailureReason !== "string" || !signal.orderFailureReason.trim()) {
|
|
@@ -7771,9 +7973,9 @@ var executeEntryDecision = async ({
|
|
|
7771
7973
|
market
|
|
7772
7974
|
});
|
|
7773
7975
|
if (err?.message === import_types.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY) {
|
|
7774
|
-
|
|
7976
|
+
import_logger13.logger.warn("order warning: %s %s", symbol, err);
|
|
7775
7977
|
} else {
|
|
7776
|
-
|
|
7978
|
+
import_logger13.logger.error("order error: %s %s", symbol, err);
|
|
7777
7979
|
}
|
|
7778
7980
|
return signal ?? "ORDER_ERROR";
|
|
7779
7981
|
}
|
|
@@ -7938,7 +8140,7 @@ var createStrategyRuntime = ({
|
|
|
7938
8140
|
try {
|
|
7939
8141
|
await projectHook(errorParams);
|
|
7940
8142
|
} catch (hookError) {
|
|
7941
|
-
|
|
8143
|
+
import_logger14.logger.error(
|
|
7942
8144
|
"project hook onRuntimeError failed: %s %s",
|
|
7943
8145
|
strategyName,
|
|
7944
8146
|
hookError
|
|
@@ -7952,7 +8154,7 @@ var createStrategyRuntime = ({
|
|
|
7952
8154
|
try {
|
|
7953
8155
|
await onRuntimeError(errorParams);
|
|
7954
8156
|
} catch (hookError) {
|
|
7955
|
-
|
|
8157
|
+
import_logger14.logger.error(
|
|
7956
8158
|
"runtime hook onRuntimeError failed: %s %s",
|
|
7957
8159
|
strategyName,
|
|
7958
8160
|
hookError
|
|
@@ -7966,7 +8168,7 @@ var createStrategyRuntime = ({
|
|
|
7966
8168
|
try {
|
|
7967
8169
|
return await hook(params);
|
|
7968
8170
|
} catch (error) {
|
|
7969
|
-
|
|
8171
|
+
import_logger14.logger.error(
|
|
7970
8172
|
'strategy hook "%s" failed for %s: %s',
|
|
7971
8173
|
stage,
|
|
7972
8174
|
strategyName,
|
|
@@ -8165,8 +8367,11 @@ var createStrategyRuntime = ({
|
|
|
8165
8367
|
return enrichedBaseContext;
|
|
8166
8368
|
}
|
|
8167
8369
|
});
|
|
8370
|
+
const coreConfig = { ...config };
|
|
8371
|
+
delete coreConfig.RECORD_RUNTIME_TRADES;
|
|
8372
|
+
delete coreConfig.SIMULATE_ORDERS;
|
|
8168
8373
|
const core = await createCore({
|
|
8169
|
-
config,
|
|
8374
|
+
config: coreConfig,
|
|
8170
8375
|
data,
|
|
8171
8376
|
strategyApi,
|
|
8172
8377
|
indicatorsState
|
|
@@ -9517,7 +9722,7 @@ var loadExchangeRange = async ({
|
|
|
9517
9722
|
} catch (error) {
|
|
9518
9723
|
const message = error?.message || String(error);
|
|
9519
9724
|
errors?.push(`${label}: ${message}`);
|
|
9520
|
-
|
|
9725
|
+
import_logger15.logger.warn("strategies runtime: %s failed: %s", label, message);
|
|
9521
9726
|
return [];
|
|
9522
9727
|
}
|
|
9523
9728
|
};
|
|
@@ -9557,7 +9762,7 @@ var loadClosedPnlRows = async ({
|
|
|
9557
9762
|
} catch (error) {
|
|
9558
9763
|
const message = error?.message || String(error);
|
|
9559
9764
|
errors?.push(`getClosedPnl: ${message}`);
|
|
9560
|
-
|
|
9765
|
+
import_logger15.logger.warn("strategies runtime: getClosedPnl failed: %s", message);
|
|
9561
9766
|
return [];
|
|
9562
9767
|
}
|
|
9563
9768
|
};
|
|
@@ -9590,7 +9795,7 @@ var loadExchangeEntryRows = async ({
|
|
|
9590
9795
|
} catch (error) {
|
|
9591
9796
|
const message = error?.message || String(error);
|
|
9592
9797
|
errors?.push(`getEntryExecutions: ${message}`);
|
|
9593
|
-
|
|
9798
|
+
import_logger15.logger.warn("strategies runtime: getEntryExecutions failed: %s", message);
|
|
9594
9799
|
return [];
|
|
9595
9800
|
}
|
|
9596
9801
|
};
|
|
@@ -9606,7 +9811,7 @@ var loadOpenPositions = async (connector, errors) => {
|
|
|
9606
9811
|
} catch (error) {
|
|
9607
9812
|
const message = error?.message || String(error);
|
|
9608
9813
|
errors?.push(`getOpenPositionPnl: ${message}`);
|
|
9609
|
-
|
|
9814
|
+
import_logger15.logger.warn("strategies runtime: getOpenPositionPnl failed: %s", message);
|
|
9610
9815
|
return { positions: [], reliable: false };
|
|
9611
9816
|
}
|
|
9612
9817
|
};
|