@tradejs/node 1.0.2 → 1.0.4

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.
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  buildMlPayload
3
- } from "./chunk-PXJJPAQT.mjs";
3
+ } from "./chunk-RBE4PZER.mjs";
4
4
  import {
5
5
  require_lodash
6
6
  } from "./chunk-GKDBAF3A.mjs";
7
7
  import {
8
8
  createLoadPineScript
9
- } from "./chunk-OB4CSYDJ.mjs";
9
+ } from "./chunk-7ICOZAKA.mjs";
10
10
  import "./chunk-5YNMSWL3.mjs";
11
11
  import {
12
12
  MAX_AI_SERIES_POINTS,
@@ -15,7 +15,7 @@ import {
15
15
  buildAiPayload,
16
16
  buildAiSystemPrompt,
17
17
  trimSeriesDeep
18
- } from "./chunk-ZIMX3JX2.mjs";
18
+ } from "./chunk-LMAKIC3C.mjs";
19
19
  import {
20
20
  ensureIndicatorPluginsLoaded,
21
21
  ensureStrategyPluginsLoaded,
@@ -28,8 +28,10 @@ import {
28
28
  registerStrategyEntries,
29
29
  resetStrategyRegistryCache,
30
30
  strategies
31
- } from "./chunk-MHCXPD2B.mjs";
32
- import "./chunk-DE7ADBIR.mjs";
31
+ } from "./chunk-ZY6ULOWK.mjs";
32
+ import {
33
+ getTradejsProjectCwd
34
+ } from "./chunk-P2ZUWONT.mjs";
33
35
  import {
34
36
  __toESM
35
37
  } from "./chunk-6DZX6EAA.mjs";
@@ -185,7 +187,8 @@ var enrichSignalWithMl = async ({
185
187
  const mlResult = await fetchMlThreshold({
186
188
  strategy,
187
189
  features,
188
- threshold: ml.mlThreshold
190
+ threshold: ml.mlThreshold,
191
+ projectRoot: getTradejsProjectCwd()
189
192
  });
190
193
  if (mlResult) {
191
194
  signal.ml = mlResult;
@@ -203,7 +206,7 @@ var enrichSignalWithAi = async ({
203
206
  return void 0;
204
207
  }
205
208
  try {
206
- const { askAI: askAI2 } = await import("./ai-NNJ3RLLL.mjs");
209
+ const { askAI: askAI2 } = await import("./ai-G7ATN4YL.mjs");
207
210
  const analysis = await askAI2(signal);
208
211
  if (typeof analysis?.quality === "number") {
209
212
  const normalizedQuality = Math.round(analysis.quality);
@@ -350,10 +353,151 @@ var getEntrySkipReason = ({
350
353
  }
351
354
  return "ENTRY_POLICY_BLOCKED";
352
355
  };
356
+ var buildHookCtx = ({
357
+ connector,
358
+ strategyName,
359
+ userName,
360
+ symbol,
361
+ strategyConfig,
362
+ env,
363
+ isConfigFromBacktest
364
+ }) => ({
365
+ connector,
366
+ strategyName,
367
+ userName,
368
+ symbol,
369
+ strategyConfig,
370
+ env,
371
+ isConfigFromBacktest
372
+ });
373
+ var buildHookEntry = ({
374
+ decision,
375
+ runtime
376
+ }) => ({
377
+ context: decision.entryContext,
378
+ orderPlan: decision.orderPlan,
379
+ signal: decision.signal,
380
+ runtime: {
381
+ raw: decision.runtime,
382
+ resolved: runtime
383
+ }
384
+ });
385
+ var buildHookPolicy = ({
386
+ quality,
387
+ makeOrdersEnabled,
388
+ minAiQuality
389
+ }) => ({
390
+ aiQuality: quality,
391
+ makeOrdersEnabled,
392
+ minAiQuality
393
+ });
394
+ var buildMlHookContext = ({
395
+ signal,
396
+ env,
397
+ ml
398
+ }) => {
399
+ if (env === "BACKTEST") {
400
+ return {
401
+ config: ml,
402
+ attempted: false,
403
+ applied: false,
404
+ skippedReason: "BACKTEST"
405
+ };
406
+ }
407
+ if (!ml) {
408
+ return {
409
+ attempted: false,
410
+ applied: false,
411
+ skippedReason: "NO_RUNTIME"
412
+ };
413
+ }
414
+ if (ml.enabled === false) {
415
+ return {
416
+ config: ml,
417
+ attempted: false,
418
+ applied: false,
419
+ skippedReason: "DISABLED"
420
+ };
421
+ }
422
+ if (!ml.strategyConfig) {
423
+ return {
424
+ config: ml,
425
+ attempted: false,
426
+ applied: false,
427
+ skippedReason: "NO_STRATEGY_CONFIG"
428
+ };
429
+ }
430
+ if (typeof ml.mlThreshold !== "number") {
431
+ return {
432
+ config: ml,
433
+ attempted: false,
434
+ applied: false,
435
+ skippedReason: "NO_THRESHOLD"
436
+ };
437
+ }
438
+ if (signal.ml) {
439
+ return {
440
+ config: ml,
441
+ attempted: true,
442
+ applied: true,
443
+ result: signal.ml
444
+ };
445
+ }
446
+ return {
447
+ config: ml,
448
+ attempted: true,
449
+ applied: false,
450
+ skippedReason: "NO_RESULT"
451
+ };
452
+ };
453
+ var buildAiHookContext = ({
454
+ env,
455
+ ai,
456
+ quality
457
+ }) => {
458
+ if (env === "BACKTEST") {
459
+ return {
460
+ config: ai,
461
+ attempted: false,
462
+ applied: false,
463
+ skippedReason: "BACKTEST"
464
+ };
465
+ }
466
+ if (!ai) {
467
+ return {
468
+ attempted: false,
469
+ applied: false,
470
+ skippedReason: "NO_RUNTIME"
471
+ };
472
+ }
473
+ if (ai.enabled === false) {
474
+ return {
475
+ config: ai,
476
+ attempted: false,
477
+ applied: false,
478
+ skippedReason: "DISABLED"
479
+ };
480
+ }
481
+ if (typeof quality === "number") {
482
+ return {
483
+ config: ai,
484
+ attempted: true,
485
+ applied: true,
486
+ quality
487
+ };
488
+ }
489
+ return {
490
+ config: ai,
491
+ attempted: true,
492
+ applied: false,
493
+ skippedReason: "NO_QUALITY"
494
+ };
495
+ };
353
496
  var handleExitDecision = async ({
354
497
  connector,
355
498
  symbol,
356
499
  decision,
500
+ market,
357
501
  onRuntimeError
358
502
  }) => {
359
503
  try {
@@ -367,7 +511,8 @@ var handleExitDecision = async ({
367
511
  await onRuntimeError?.({
368
512
  stage: "closePosition",
369
513
  error: err,
370
- decision
514
+ decision,
515
+ market
371
516
  });
372
517
  logger3.error("close order error: %s %s", symbol, err);
373
518
  return "ORDER_ERROR";
@@ -380,7 +525,12 @@ var executeEntryDecision = async ({
380
525
  decision,
381
526
  runtime,
382
527
  manifest,
383
- hookBase,
528
+ hookCtx,
529
+ market,
530
+ entry,
531
+ policy,
532
+ ml,
533
+ ai,
384
534
  invokeHook,
385
535
  notifyRuntimeError
386
536
  }) => {
@@ -390,13 +540,15 @@ var executeEntryDecision = async ({
390
540
  "beforePlaceOrder",
391
541
  manifest?.hooks?.beforePlaceOrder,
392
542
  {
393
- ...hookBase,
394
- entryContext: decision.entryContext,
395
- runtime,
543
+ ctx: hookCtx,
544
+ market,
396
545
  decision,
397
- signal
546
+ entry,
547
+ policy,
548
+ ml,
549
+ ai
398
550
  },
399
- { decision, signal }
551
+ { decision, entry, market }
400
552
  );
401
553
  try {
402
554
  await runtime.beforePlaceOrder?.();
@@ -405,7 +557,8 @@ var executeEntryDecision = async ({
405
557
  stage: "runtime.beforePlaceOrder",
406
558
  error,
407
559
  decision,
408
- signal
560
+ entry,
561
+ market
409
562
  });
410
563
  throw error;
411
564
  }
@@ -428,13 +581,18 @@ var executeEntryDecision = async ({
428
581
  "afterPlaceOrder",
429
582
  manifest?.hooks?.afterPlaceOrder,
430
583
  {
431
- ...hookBase,
584
+ ctx: hookCtx,
585
+ market,
432
586
  decision,
433
- runtime,
434
- signal,
435
- orderResult: signal
587
+ entry,
588
+ policy,
589
+ ml,
590
+ ai,
591
+ order: {
592
+ result: signal
593
+ }
436
594
  },
437
- { decision, signal }
595
+ { decision, entry, market }
438
596
  );
439
597
  return signal;
440
598
  }
@@ -454,13 +612,18 @@ var executeEntryDecision = async ({
454
612
  "afterPlaceOrder",
455
613
  manifest?.hooks?.afterPlaceOrder,
456
614
  {
457
- ...hookBase,
615
+ ctx: hookCtx,
616
+ market,
458
617
  decision,
459
- runtime,
460
- signal,
461
- orderResult: decision.code
618
+ entry,
619
+ policy,
620
+ ml,
621
+ ai,
622
+ order: {
623
+ result: decision.code
624
+ }
462
625
  },
463
- { decision, signal }
626
+ { decision, entry, market }
464
627
  );
465
628
  } catch (err) {
466
629
  if (signal) {
@@ -470,7 +633,8 @@ var executeEntryDecision = async ({
470
633
  stage: "placeOrder",
471
634
  error: err,
472
635
  decision,
473
- signal
636
+ entry,
637
+ market
474
638
  });
475
639
  logger3.error("order error: %s %s", symbol, err);
476
640
  return signal ?? "ORDER_ERROR";
@@ -484,6 +648,7 @@ var createStrategyRuntime = ({
484
648
  manifest: staticManifest,
485
649
  strategyDirectory
486
650
  }) => {
651
+ const projectRoot = getTradejsProjectCwd();
487
652
  const resolveManifest = (name) => {
488
653
  if (!name) {
489
654
  return void 0;
@@ -491,11 +656,11 @@ var createStrategyRuntime = ({
491
656
  if (staticManifest?.name === name) {
492
657
  return staticManifest;
493
658
  }
494
- return getStrategyManifest(name);
659
+ return getStrategyManifest(name, projectRoot);
495
660
  };
496
661
  const loadPineScript = createLoadPineScript(
497
662
  strategyDirectory ? path.resolve(strategyDirectory) : path.resolve(
498
- process.cwd(),
663
+ projectRoot,
499
664
  "packages",
500
665
  "strategies",
501
666
  "src",
@@ -526,33 +691,37 @@ var createStrategyRuntime = ({
526
691
  strategyName,
527
692
  userName,
528
693
  symbol,
529
- config,
694
+ strategyConfig: config,
530
695
  env,
531
696
  isConfigFromBacktest
532
697
  };
698
+ const getHookCtx = (name = strategyName) => buildHookCtx({
699
+ ...hookBase,
700
+ strategyName: name
701
+ });
533
702
  const notifyRuntimeError = async ({
534
703
  stage,
535
704
  error,
536
705
  decision,
537
- signal
706
+ entry,
707
+ market
538
708
  }) => {
539
709
  const errorStrategyName = decision?.kind === "entry" ? decision.entryContext.strategy : strategyName;
540
710
  const errorManifest = resolveManifest(errorStrategyName) ?? strategyManifest;
541
- const errorHookBase = {
542
- ...hookBase,
543
- strategyName: errorStrategyName
544
- };
545
711
  const onRuntimeError = errorManifest?.hooks?.onRuntimeError;
546
712
  if (!onRuntimeError) {
547
713
  return;
548
714
  }
549
715
  try {
550
716
  await onRuntimeError({
551
- ...errorHookBase,
552
- stage,
553
- error,
717
+ ctx: getHookCtx(errorStrategyName),
718
+ market,
554
719
  decision,
555
- signal
720
+ entry,
721
+ error: {
722
+ stage,
723
+ cause: error
724
+ }
556
725
  });
557
726
  } catch (hookError) {
558
727
  logger3.error(
@@ -579,7 +748,8 @@ var createStrategyRuntime = ({
579
748
  stage,
580
749
  error,
581
750
  decision: errorContext.decision,
582
- signal: errorContext.signal
751
+ entry: errorContext.entry,
752
+ market: errorContext.market
583
753
  });
584
754
  return void 0;
585
755
  }
@@ -590,7 +760,8 @@ var createStrategyRuntime = ({
590
760
  btcData,
591
761
  btcBinanceData,
592
762
  btcCoinbaseData,
593
- periods: buildDefaultIndicatorPeriods(config)
763
+ periods: buildDefaultIndicatorPeriods(config),
764
+ pluginRegistryScope: projectRoot
594
765
  });
595
766
  const strategyApi = createStrategyAPI({
596
767
  strategy: strategyName,
@@ -617,39 +788,45 @@ var createStrategyRuntime = ({
617
788
  indicatorsState
618
789
  });
619
790
  await invokeHook("onInit", strategyManifest?.hooks?.onInit, {
620
- ...hookBase,
621
- data,
622
- btcData
791
+ ctx: getHookCtx(),
792
+ market: {
793
+ data,
794
+ btcData
795
+ }
623
796
  });
624
797
  return async (candle, btcCandle) => {
625
798
  data.push(candle);
626
799
  btcData.push(btcCandle);
627
800
  indicatorsState.setCurrentBar(candle, btcCandle);
801
+ const market = {
802
+ candle,
803
+ btcCandle
804
+ };
628
805
  const decision = await core(candle, btcCandle);
629
806
  const decisionStrategyName = decision.kind === "entry" ? decision.entryContext.strategy : strategyName;
630
807
  const decisionManifest = resolveManifest(decisionStrategyName) ?? strategyManifest;
631
- const decisionHookBase = {
632
- ...hookBase,
633
- strategyName: decisionStrategyName
634
- };
808
+ const decisionHookCtx = getHookCtx(decisionStrategyName);
635
809
  await invokeHook(
636
810
  "afterCoreDecision",
637
811
  decisionManifest?.hooks?.afterCoreDecision,
638
812
  {
639
- ...decisionHookBase,
640
- decision,
641
- candle,
642
- btcCandle
813
+ ctx: decisionHookCtx,
814
+ market,
815
+ decision
643
816
  },
644
- { decision }
817
+ { decision, market }
645
818
  );
646
819
  if (decision.kind === "skip") {
647
- await invokeHook("onSkip", decisionManifest?.hooks?.onSkip, {
648
- ...decisionHookBase,
649
- decision,
650
- candle,
651
- btcCandle
652
- });
820
+ await invokeHook(
821
+ "onSkip",
822
+ decisionManifest?.hooks?.onSkip,
823
+ {
824
+ ctx: decisionHookCtx,
825
+ market,
826
+ decision
827
+ },
828
+ { decision, market }
829
+ );
653
830
  return decision.code;
654
831
  }
655
832
  const makeOrdersEnabled = typeof config.MAKE_ORDERS === "boolean" ? config.MAKE_ORDERS : true;
@@ -661,10 +838,11 @@ var createStrategyRuntime = ({
661
838
  "beforeClosePosition",
662
839
  decisionManifest?.hooks?.beforeClosePosition,
663
840
  {
664
- ...decisionHookBase,
841
+ ctx: decisionHookCtx,
842
+ market,
665
843
  decision
666
844
  },
667
- { decision }
845
+ { decision, market }
668
846
  );
669
847
  if (closeGate?.allow === false) {
670
848
  return closeGate.reason ? `CLOSE_BLOCKED_BY_HOOK:${closeGate.reason}` : "CLOSE_BLOCKED_BY_HOOK";
@@ -673,11 +851,18 @@ var createStrategyRuntime = ({
673
851
  connector,
674
852
  symbol,
675
853
  decision,
676
- onRuntimeError: async ({ stage, error, decision: exitDecision }) => {
854
+ market,
855
+ onRuntimeError: async ({
856
+ stage,
857
+ error,
858
+ decision: exitDecision,
859
+ market: errorMarket
860
+ }) => {
677
861
  await notifyRuntimeError({
678
862
  stage,
679
863
  error,
680
- decision: exitDecision
864
+ decision: exitDecision,
865
+ market: errorMarket
681
866
  });
682
867
  }
683
868
  });
@@ -688,6 +873,11 @@ var createStrategyRuntime = ({
688
873
  manifest: decisionManifest
689
874
  });
690
875
  const signal = decision.signal;
876
+ const entry = buildHookEntry({
877
+ decision,
878
+ runtime
879
+ });
880
+ let ml;
691
881
  if (signal) {
692
882
  try {
693
883
  await enrichSignalWithMl({
@@ -700,23 +890,31 @@ var createStrategyRuntime = ({
700
890
  stage: "enrichSignalWithMl",
701
891
  error,
702
892
  decision,
703
- signal
893
+ entry,
894
+ market
704
895
  });
705
896
  throw error;
706
897
  }
898
+ ml = buildMlHookContext({
899
+ signal,
900
+ env,
901
+ ml: runtime.ml
902
+ });
707
903
  await invokeHook(
708
904
  "afterEnrichMl",
709
905
  decisionManifest?.hooks?.afterEnrichMl,
710
906
  {
711
- ...decisionHookBase,
907
+ ctx: decisionHookCtx,
908
+ market,
712
909
  decision,
713
- runtime,
714
- signal
910
+ entry,
911
+ ml
715
912
  },
716
- { decision, signal }
913
+ { decision, entry, market }
717
914
  );
718
915
  }
719
916
  let quality;
917
+ let ai;
720
918
  if (signal) {
721
919
  try {
722
920
  quality = await enrichSignalWithAi({
@@ -731,24 +929,36 @@ var createStrategyRuntime = ({
731
929
  stage: "enrichSignalWithAi",
732
930
  error,
733
931
  decision,
734
- signal
932
+ entry,
933
+ market
735
934
  });
736
935
  throw error;
737
936
  }
937
+ ai = buildAiHookContext({
938
+ env,
939
+ ai: runtime.ai,
940
+ quality
941
+ });
738
942
  await invokeHook(
739
943
  "afterEnrichAi",
740
944
  decisionManifest?.hooks?.afterEnrichAi,
741
945
  {
742
- ...decisionHookBase,
946
+ ctx: decisionHookCtx,
947
+ market,
743
948
  decision,
744
- runtime,
745
- signal,
746
- quality
949
+ entry,
950
+ ml: ml ?? buildMlHookContext({ signal, env, ml: runtime.ml }),
951
+ ai
747
952
  },
748
- { decision, signal }
953
+ { decision, entry, market }
749
954
  );
750
955
  }
751
956
  const minAiQuality = runtime.ai?.minQuality ?? 4;
957
+ const policy = buildHookPolicy({
958
+ quality,
959
+ makeOrdersEnabled,
960
+ minAiQuality
961
+ });
752
962
  const shouldMakeOrder = shouldExecuteEntryDecision({
753
963
  makeOrdersEnabled,
754
964
  env,
@@ -772,15 +982,15 @@ var createStrategyRuntime = ({
772
982
  "beforeEntryGate",
773
983
  decisionManifest?.hooks?.beforeEntryGate,
774
984
  {
775
- ...decisionHookBase,
985
+ ctx: decisionHookCtx,
986
+ market,
776
987
  decision,
777
- runtime,
778
- signal,
779
- quality,
780
- makeOrdersEnabled,
781
- minAiQuality
988
+ entry,
989
+ policy,
990
+ ml,
991
+ ai
782
992
  },
783
- { decision, signal }
993
+ { decision, entry, market }
784
994
  );
785
995
  if (entryGate?.allow === false) {
786
996
  const skipReason = entryGate.reason ? `HOOK_BEFORE_ENTRY_GATE:${entryGate.reason}` : "HOOK_BEFORE_ENTRY_GATE";
@@ -796,7 +1006,12 @@ var createStrategyRuntime = ({
796
1006
  decision,
797
1007
  runtime,
798
1008
  manifest: decisionManifest,
799
- hookBase: decisionHookBase,
1009
+ hookCtx: decisionHookCtx,
1010
+ market,
1011
+ entry,
1012
+ policy,
1013
+ ml,
1014
+ ai,
800
1015
  invokeHook,
801
1016
  notifyRuntimeError
802
1017
  });
@@ -808,13 +1023,13 @@ var createStrategyRuntime = ({
808
1023
  var createCloseOppositeBeforePlaceOrderHook = ({
809
1024
  isEnabled
810
1025
  }) => {
811
- return async ({ connector, entryContext, config }) => {
812
- if (!isEnabled(config)) {
1026
+ return async ({ ctx, entry }) => {
1027
+ if (!isEnabled(ctx.strategyConfig)) {
813
1028
  return;
814
1029
  }
815
1030
  await closeOppositePositionsBeforeOpen({
816
- connector,
817
- entryContext
1031
+ connector: ctx.connector,
1032
+ entryContext: entry.context
818
1033
  });
819
1034
  };
820
1035
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/node",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Node-only TradeJS runtime for strategies, backtests, Pine loading, and plugin registries.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -54,15 +54,16 @@
54
54
  "dependencies": {
55
55
  "@langchain/core": "^0.3.68",
56
56
  "@langchain/openai": "^0.6.11",
57
- "@tradejs/core": "^1.0.2",
58
- "@tradejs/infra": "^1.0.2",
59
- "@tradejs/types": "^1.0.2",
57
+ "@tradejs/core": "^1.0.4",
58
+ "@tradejs/infra": "^1.0.4",
59
+ "@tradejs/types": "^1.0.4",
60
60
  "chalk": "4.1.2",
61
61
  "ioredis": "5.8.0",
62
62
  "pinets": "0.8.12",
63
63
  "progress": "^2.0.3",
64
64
  "puppeteer": "24.15.0",
65
- "ts-node": "^10.9.2"
65
+ "ts-node": "^10.9.2",
66
+ "tsconfig-paths": "^4.2.0"
66
67
  },
67
68
  "devDependencies": {
68
69
  "@types/node": "^20",