@tradejs/node 1.0.6 → 1.0.8

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.mjs CHANGED
@@ -1,19 +1,20 @@
1
1
  import {
2
2
  BUILTIN_CONNECTOR_NAMES,
3
3
  getConnectorCreatorByName
4
- } from "./chunk-XW5L327F.mjs";
4
+ } from "./chunk-JMDYEKIO.mjs";
5
5
  import {
6
- buildMlPayload
7
- } from "./chunk-PQH5PAFC.mjs";
6
+ buildMlPayload,
7
+ enrichSignalWithDerivativesContext
8
+ } from "./chunk-JRRG3YQG.mjs";
8
9
  import {
9
10
  buildAiPayload
10
- } from "./chunk-72FKXJ2I.mjs";
11
+ } from "./chunk-2JKX3DM7.mjs";
11
12
  import {
12
13
  getStrategyCreator
13
- } from "./chunk-EOZSJKUM.mjs";
14
+ } from "./chunk-WGOYR6AB.mjs";
14
15
  import {
15
16
  getTradejsProjectCwd
16
- } from "./chunk-CGJ2UU6H.mjs";
17
+ } from "./chunk-JU77QVJ3.mjs";
17
18
  import "./chunk-6DZX6EAA.mjs";
18
19
 
19
20
  // src/backtest.ts
@@ -21,8 +22,7 @@ export * from "@tradejs/core/backtest";
21
22
 
22
23
  // src/testing.ts
23
24
  import { alignSortedCandlesByTimestamp } from "@tradejs/core/indicators";
24
- import { PRELOAD_DAYS } from "@tradejs/core/constants";
25
- import { getTimestamp } from "@tradejs/core/time";
25
+ import { getBacktestPreloadStart } from "@tradejs/core/time";
26
26
  import { appendAiDatasetRow } from "@tradejs/infra/ai";
27
27
  import {
28
28
  appendMlDatasetRow,
@@ -92,6 +92,7 @@ var createTestConnector = (connector, context) => {
92
92
  currentPositionProfit = 0;
93
93
  };
94
94
  return {
95
+ __tradejsTestConnector: true,
95
96
  getState: async () => state,
96
97
  setState: async (newState) => {
97
98
  state = {
@@ -114,6 +115,24 @@ var createTestConnector = (connector, context) => {
114
115
  };
115
116
  },
116
117
  getPosition: async () => currentPosition || null,
118
+ getOpenPositionPnl: async () => {
119
+ if (typeof connector.getOpenPositionPnl === "function") {
120
+ return connector.getOpenPositionPnl();
121
+ }
122
+ if (!currentPosition) {
123
+ return [];
124
+ }
125
+ return [
126
+ {
127
+ symbol: currentPosition.symbol,
128
+ qty: currentPosition.qty,
129
+ price: currentPosition.price,
130
+ currentPrice: currentPosition.price,
131
+ unrealizedPnl: 0,
132
+ direction: currentPosition.direction
133
+ }
134
+ ];
135
+ },
117
136
  checkTp: async (candle) => {
118
137
  if (!candle || !currentPosition || !currentPosition.qty) {
119
138
  return;
@@ -231,7 +250,6 @@ var createTestConnector = (connector, context) => {
231
250
  };
232
251
 
233
252
  // src/testing.ts
234
- var preloadStart = getTimestamp(PRELOAD_DAYS);
235
253
  var createTestingKlineCacheState = () => ({
236
254
  coinKlineCache: /* @__PURE__ */ new Map(),
237
255
  btcKlineCache: /* @__PURE__ */ new Map(),
@@ -254,7 +272,15 @@ var getTestingKlineCacheState = (cwd = getTradejsProjectCwd()) => {
254
272
  };
255
273
  };
256
274
  var getKlineCacheKey = (params) => {
257
- const { userName, connectorName, symbol, end, interval, cacheOnly } = params;
275
+ const {
276
+ userName,
277
+ connectorName,
278
+ symbol,
279
+ preloadStart,
280
+ end,
281
+ interval,
282
+ cacheOnly
283
+ } = params;
258
284
  return [
259
285
  userName,
260
286
  connectorName,
@@ -270,6 +296,7 @@ var getPreparedDataCacheKey = (params) => {
270
296
  userName,
271
297
  connectorName,
272
298
  symbol,
299
+ preloadStart,
273
300
  start,
274
301
  end,
275
302
  interval,
@@ -280,6 +307,7 @@ var getPreparedDataCacheKey = (params) => {
280
307
  userName,
281
308
  connectorName,
282
309
  symbol,
310
+ preloadStart,
283
311
  start,
284
312
  end,
285
313
  interval,
@@ -288,7 +316,7 @@ var getPreparedDataCacheKey = (params) => {
288
316
  ].join(":");
289
317
  };
290
318
  var getConnectorCacheKey = (params) => [params.userName, params.connectorName].join(":");
291
- var splitCandlesForTesting = (candles, start) => {
319
+ var splitCandlesForTesting = (candles, start, preloadStart) => {
292
320
  const prevData = [];
293
321
  const testData = [];
294
322
  for (const candle of candles) {
@@ -349,6 +377,7 @@ var testing = async ({
349
377
  if (!start) {
350
378
  throw new Error("no start");
351
379
  }
380
+ const preloadStart = getBacktestPreloadStart(start);
352
381
  const startedAt = Date.now();
353
382
  const formatTimeoutMessage = (stage) => `Test ${name} (${symbol}) timed out after ${timeoutMs}ms during ${stage}`;
354
383
  const getRemainingTimeoutMs = (stage) => {
@@ -435,6 +464,7 @@ var testing = async ({
435
464
  userName,
436
465
  connectorName,
437
466
  symbol,
467
+ preloadStart,
438
468
  end,
439
469
  interval,
440
470
  cacheOnly
@@ -443,6 +473,7 @@ var testing = async ({
443
473
  userName,
444
474
  connectorName,
445
475
  symbol: "BTCUSDT",
476
+ preloadStart,
446
477
  end,
447
478
  interval,
448
479
  cacheOnly
@@ -455,6 +486,7 @@ var testing = async ({
455
486
  userName,
456
487
  connectorName: btcBinanceConnectorName,
457
488
  symbol: "BTCUSDT",
489
+ preloadStart,
458
490
  end,
459
491
  interval,
460
492
  cacheOnly
@@ -463,6 +495,7 @@ var testing = async ({
463
495
  userName,
464
496
  connectorName: btcCoinbaseConnectorName,
465
497
  symbol: "BTCUSDT",
498
+ preloadStart,
466
499
  end,
467
500
  interval,
468
501
  cacheOnly
@@ -473,6 +506,7 @@ var testing = async ({
473
506
  userName,
474
507
  connectorName,
475
508
  symbol,
509
+ preloadStart,
476
510
  start,
477
511
  end,
478
512
  interval,
@@ -530,15 +564,17 @@ var testing = async ({
530
564
  if (!cachedBtcCoinbaseData) {
531
565
  state.btcCoinbaseKlineCache.set(btcCoinbaseCacheKey, btcCoinbaseData);
532
566
  }
533
- const { prevData: prevDataRaw, testData: testDataRaw } = splitCandlesForTesting(data, start);
534
- const { prevData: btcPrevDataRaw, testData: btcTestDataRaw } = splitCandlesForTesting(btcData, start);
567
+ const { prevData: prevDataRaw, testData: testDataRaw } = splitCandlesForTesting(data, start, preloadStart);
568
+ const { prevData: btcPrevDataRaw, testData: btcTestDataRaw } = splitCandlesForTesting(btcData, start, preloadStart);
535
569
  const { prevData: btcBinancePrevDataRaw } = splitCandlesForTesting(
536
570
  btcBinanceData,
537
- start
571
+ start,
572
+ preloadStart
538
573
  );
539
574
  const { prevData: btcCoinbasePrevDataRaw } = splitCandlesForTesting(
540
575
  btcCoinbaseData,
541
- start
576
+ start,
577
+ preloadStart
542
578
  );
543
579
  const { alignedCoinCandles: prevData2, alignedBtcCandles: btcPrevData2 } = alignSortedCandlesByTimestamp(prevDataRaw, btcPrevDataRaw);
544
580
  const { alignedCoinCandles: testData2, alignedBtcCandles: btcTestData2 } = alignSortedCandlesByTimestamp(testDataRaw, btcTestDataRaw);
@@ -585,6 +621,7 @@ var testing = async ({
585
621
  );
586
622
  const pendingMlPayloadBySignalId = /* @__PURE__ */ new Map();
587
623
  const pendingAiRowBySignalId = /* @__PURE__ */ new Map();
624
+ const replaySignalEvaluations = [];
588
625
  const flushClosedResultsBatch = async () => {
589
626
  if (!ml && !ai) return;
590
627
  const batch = await testConnector.drainMlResultsBatch();
@@ -629,6 +666,47 @@ var testing = async ({
629
666
  "strategy signal",
630
667
  strategy(candle, btcCandle)
631
668
  );
669
+ if (!signal || typeof signal === "string") {
670
+ replaySignalEvaluations.push({
671
+ evaluationId: `${testId}:${strategyName}:${symbol}:${candle.timestamp}`,
672
+ userName,
673
+ strategy: strategyName,
674
+ symbol,
675
+ interval,
676
+ timestamp: candle.timestamp,
677
+ evaluatedAt: candle.timestamp,
678
+ status: "skip",
679
+ reason: typeof signal === "string" && signal.trim() ? signal : "NO_SIGNAL"
680
+ });
681
+ } else {
682
+ replaySignalEvaluations.push({
683
+ evaluationId: `${signal.signalId || testId}:${strategyName}:${symbol}:${signal.timestamp || candle.timestamp}`,
684
+ userName,
685
+ strategy: signal.strategy || strategyName,
686
+ symbol: signal.symbol || symbol,
687
+ interval: signal.interval || interval,
688
+ timestamp: typeof signal.timestamp === "number" && Number.isFinite(signal.timestamp) ? signal.timestamp : candle.timestamp,
689
+ evaluatedAt: candle.timestamp,
690
+ status: "signal",
691
+ reason: signal.orderSkipReason || signal.orderStatus,
692
+ signalId: signal.signalId,
693
+ direction: signal.direction,
694
+ orderStatus: signal.orderStatus,
695
+ orderSkipReason: signal.orderSkipReason,
696
+ aiAnalysis: signal.aiAnalysis ?? null,
697
+ ml: signal.ml
698
+ });
699
+ }
700
+ const shouldCapturePayload = signal && typeof signal !== "string" && signal.signalId && (ml || ai);
701
+ if (shouldCapturePayload) {
702
+ await withTimeout(
703
+ "derivatives context",
704
+ enrichSignalWithDerivativesContext({
705
+ signal,
706
+ env: "BACKTEST"
707
+ })
708
+ );
709
+ }
632
710
  if (ml && signal && typeof signal !== "string" && signal.signalId) {
633
711
  const payload = buildMlPayload({
634
712
  signal,
@@ -661,7 +739,11 @@ var testing = async ({
661
739
  }
662
740
  }
663
741
  await withTimeout("flush closed results", flushClosedResultsBatch());
664
- return await withTimeout("collect result", testConnector.getResult());
742
+ const result = await withTimeout("collect result", testConnector.getResult());
743
+ return {
744
+ ...result,
745
+ inlineReplaySignalEvaluations: replaySignalEvaluations
746
+ };
665
747
  };
666
748
  export {
667
749
  createTestConnector,