@tradejs/node 1.0.3 → 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,6 +1,6 @@
1
1
  import {
2
2
  buildMlPayload
3
- } from "./chunk-H4HXW3EZ.mjs";
3
+ } from "./chunk-RBE4PZER.mjs";
4
4
  import {
5
5
  require_lodash
6
6
  } from "./chunk-GKDBAF3A.mjs";
@@ -15,7 +15,7 @@ import {
15
15
  buildAiPayload,
16
16
  buildAiSystemPrompt,
17
17
  trimSeriesDeep
18
- } from "./chunk-QDYCJ2OK.mjs";
18
+ } from "./chunk-LMAKIC3C.mjs";
19
19
  import {
20
20
  ensureIndicatorPluginsLoaded,
21
21
  ensureStrategyPluginsLoaded,
@@ -28,10 +28,10 @@ import {
28
28
  registerStrategyEntries,
29
29
  resetStrategyRegistryCache,
30
30
  strategies
31
- } from "./chunk-CK2PW4L5.mjs";
31
+ } from "./chunk-ZY6ULOWK.mjs";
32
32
  import {
33
33
  getTradejsProjectCwd
34
- } from "./chunk-3C76HVLA.mjs";
34
+ } from "./chunk-P2ZUWONT.mjs";
35
35
  import {
36
36
  __toESM
37
37
  } from "./chunk-6DZX6EAA.mjs";
@@ -206,7 +206,7 @@ var enrichSignalWithAi = async ({
206
206
  return void 0;
207
207
  }
208
208
  try {
209
- const { askAI: askAI2 } = await import("./ai-MDMDKAEE.mjs");
209
+ const { askAI: askAI2 } = await import("./ai-G7ATN4YL.mjs");
210
210
  const analysis = await askAI2(signal);
211
211
  if (typeof analysis?.quality === "number") {
212
212
  const normalizedQuality = Math.round(analysis.quality);
@@ -353,10 +353,151 @@ var getEntrySkipReason = ({
353
353
  }
354
354
  return "ENTRY_POLICY_BLOCKED";
355
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
+ };
356
496
  var handleExitDecision = async ({
357
497
  connector,
358
498
  symbol,
359
499
  decision,
500
+ market,
360
501
  onRuntimeError
361
502
  }) => {
362
503
  try {
@@ -370,7 +511,8 @@ var handleExitDecision = async ({
370
511
  await onRuntimeError?.({
371
512
  stage: "closePosition",
372
513
  error: err,
373
- decision
514
+ decision,
515
+ market
374
516
  });
375
517
  logger3.error("close order error: %s %s", symbol, err);
376
518
  return "ORDER_ERROR";
@@ -383,7 +525,12 @@ var executeEntryDecision = async ({
383
525
  decision,
384
526
  runtime,
385
527
  manifest,
386
- hookBase,
528
+ hookCtx,
529
+ market,
530
+ entry,
531
+ policy,
532
+ ml,
533
+ ai,
387
534
  invokeHook,
388
535
  notifyRuntimeError
389
536
  }) => {
@@ -393,13 +540,15 @@ var executeEntryDecision = async ({
393
540
  "beforePlaceOrder",
394
541
  manifest?.hooks?.beforePlaceOrder,
395
542
  {
396
- ...hookBase,
397
- entryContext: decision.entryContext,
398
- runtime,
543
+ ctx: hookCtx,
544
+ market,
399
545
  decision,
400
- signal
546
+ entry,
547
+ policy,
548
+ ml,
549
+ ai
401
550
  },
402
- { decision, signal }
551
+ { decision, entry, market }
403
552
  );
404
553
  try {
405
554
  await runtime.beforePlaceOrder?.();
@@ -408,7 +557,8 @@ var executeEntryDecision = async ({
408
557
  stage: "runtime.beforePlaceOrder",
409
558
  error,
410
559
  decision,
411
- signal
560
+ entry,
561
+ market
412
562
  });
413
563
  throw error;
414
564
  }
@@ -431,13 +581,18 @@ var executeEntryDecision = async ({
431
581
  "afterPlaceOrder",
432
582
  manifest?.hooks?.afterPlaceOrder,
433
583
  {
434
- ...hookBase,
584
+ ctx: hookCtx,
585
+ market,
435
586
  decision,
436
- runtime,
437
- signal,
438
- orderResult: signal
587
+ entry,
588
+ policy,
589
+ ml,
590
+ ai,
591
+ order: {
592
+ result: signal
593
+ }
439
594
  },
440
- { decision, signal }
595
+ { decision, entry, market }
441
596
  );
442
597
  return signal;
443
598
  }
@@ -457,13 +612,18 @@ var executeEntryDecision = async ({
457
612
  "afterPlaceOrder",
458
613
  manifest?.hooks?.afterPlaceOrder,
459
614
  {
460
- ...hookBase,
615
+ ctx: hookCtx,
616
+ market,
461
617
  decision,
462
- runtime,
463
- signal,
464
- orderResult: decision.code
618
+ entry,
619
+ policy,
620
+ ml,
621
+ ai,
622
+ order: {
623
+ result: decision.code
624
+ }
465
625
  },
466
- { decision, signal }
626
+ { decision, entry, market }
467
627
  );
468
628
  } catch (err) {
469
629
  if (signal) {
@@ -473,7 +633,8 @@ var executeEntryDecision = async ({
473
633
  stage: "placeOrder",
474
634
  error: err,
475
635
  decision,
476
- signal
636
+ entry,
637
+ market
477
638
  });
478
639
  logger3.error("order error: %s %s", symbol, err);
479
640
  return signal ?? "ORDER_ERROR";
@@ -530,33 +691,37 @@ var createStrategyRuntime = ({
530
691
  strategyName,
531
692
  userName,
532
693
  symbol,
533
- config,
694
+ strategyConfig: config,
534
695
  env,
535
696
  isConfigFromBacktest
536
697
  };
698
+ const getHookCtx = (name = strategyName) => buildHookCtx({
699
+ ...hookBase,
700
+ strategyName: name
701
+ });
537
702
  const notifyRuntimeError = async ({
538
703
  stage,
539
704
  error,
540
705
  decision,
541
- signal
706
+ entry,
707
+ market
542
708
  }) => {
543
709
  const errorStrategyName = decision?.kind === "entry" ? decision.entryContext.strategy : strategyName;
544
710
  const errorManifest = resolveManifest(errorStrategyName) ?? strategyManifest;
545
- const errorHookBase = {
546
- ...hookBase,
547
- strategyName: errorStrategyName
548
- };
549
711
  const onRuntimeError = errorManifest?.hooks?.onRuntimeError;
550
712
  if (!onRuntimeError) {
551
713
  return;
552
714
  }
553
715
  try {
554
716
  await onRuntimeError({
555
- ...errorHookBase,
556
- stage,
557
- error,
717
+ ctx: getHookCtx(errorStrategyName),
718
+ market,
558
719
  decision,
559
- signal
720
+ entry,
721
+ error: {
722
+ stage,
723
+ cause: error
724
+ }
560
725
  });
561
726
  } catch (hookError) {
562
727
  logger3.error(
@@ -583,7 +748,8 @@ var createStrategyRuntime = ({
583
748
  stage,
584
749
  error,
585
750
  decision: errorContext.decision,
586
- signal: errorContext.signal
751
+ entry: errorContext.entry,
752
+ market: errorContext.market
587
753
  });
588
754
  return void 0;
589
755
  }
@@ -622,39 +788,45 @@ var createStrategyRuntime = ({
622
788
  indicatorsState
623
789
  });
624
790
  await invokeHook("onInit", strategyManifest?.hooks?.onInit, {
625
- ...hookBase,
626
- data,
627
- btcData
791
+ ctx: getHookCtx(),
792
+ market: {
793
+ data,
794
+ btcData
795
+ }
628
796
  });
629
797
  return async (candle, btcCandle) => {
630
798
  data.push(candle);
631
799
  btcData.push(btcCandle);
632
800
  indicatorsState.setCurrentBar(candle, btcCandle);
801
+ const market = {
802
+ candle,
803
+ btcCandle
804
+ };
633
805
  const decision = await core(candle, btcCandle);
634
806
  const decisionStrategyName = decision.kind === "entry" ? decision.entryContext.strategy : strategyName;
635
807
  const decisionManifest = resolveManifest(decisionStrategyName) ?? strategyManifest;
636
- const decisionHookBase = {
637
- ...hookBase,
638
- strategyName: decisionStrategyName
639
- };
808
+ const decisionHookCtx = getHookCtx(decisionStrategyName);
640
809
  await invokeHook(
641
810
  "afterCoreDecision",
642
811
  decisionManifest?.hooks?.afterCoreDecision,
643
812
  {
644
- ...decisionHookBase,
645
- decision,
646
- candle,
647
- btcCandle
813
+ ctx: decisionHookCtx,
814
+ market,
815
+ decision
648
816
  },
649
- { decision }
817
+ { decision, market }
650
818
  );
651
819
  if (decision.kind === "skip") {
652
- await invokeHook("onSkip", decisionManifest?.hooks?.onSkip, {
653
- ...decisionHookBase,
654
- decision,
655
- candle,
656
- btcCandle
657
- });
820
+ await invokeHook(
821
+ "onSkip",
822
+ decisionManifest?.hooks?.onSkip,
823
+ {
824
+ ctx: decisionHookCtx,
825
+ market,
826
+ decision
827
+ },
828
+ { decision, market }
829
+ );
658
830
  return decision.code;
659
831
  }
660
832
  const makeOrdersEnabled = typeof config.MAKE_ORDERS === "boolean" ? config.MAKE_ORDERS : true;
@@ -666,10 +838,11 @@ var createStrategyRuntime = ({
666
838
  "beforeClosePosition",
667
839
  decisionManifest?.hooks?.beforeClosePosition,
668
840
  {
669
- ...decisionHookBase,
841
+ ctx: decisionHookCtx,
842
+ market,
670
843
  decision
671
844
  },
672
- { decision }
845
+ { decision, market }
673
846
  );
674
847
  if (closeGate?.allow === false) {
675
848
  return closeGate.reason ? `CLOSE_BLOCKED_BY_HOOK:${closeGate.reason}` : "CLOSE_BLOCKED_BY_HOOK";
@@ -678,11 +851,18 @@ var createStrategyRuntime = ({
678
851
  connector,
679
852
  symbol,
680
853
  decision,
681
- onRuntimeError: async ({ stage, error, decision: exitDecision }) => {
854
+ market,
855
+ onRuntimeError: async ({
856
+ stage,
857
+ error,
858
+ decision: exitDecision,
859
+ market: errorMarket
860
+ }) => {
682
861
  await notifyRuntimeError({
683
862
  stage,
684
863
  error,
685
- decision: exitDecision
864
+ decision: exitDecision,
865
+ market: errorMarket
686
866
  });
687
867
  }
688
868
  });
@@ -693,6 +873,11 @@ var createStrategyRuntime = ({
693
873
  manifest: decisionManifest
694
874
  });
695
875
  const signal = decision.signal;
876
+ const entry = buildHookEntry({
877
+ decision,
878
+ runtime
879
+ });
880
+ let ml;
696
881
  if (signal) {
697
882
  try {
698
883
  await enrichSignalWithMl({
@@ -705,23 +890,31 @@ var createStrategyRuntime = ({
705
890
  stage: "enrichSignalWithMl",
706
891
  error,
707
892
  decision,
708
- signal
893
+ entry,
894
+ market
709
895
  });
710
896
  throw error;
711
897
  }
898
+ ml = buildMlHookContext({
899
+ signal,
900
+ env,
901
+ ml: runtime.ml
902
+ });
712
903
  await invokeHook(
713
904
  "afterEnrichMl",
714
905
  decisionManifest?.hooks?.afterEnrichMl,
715
906
  {
716
- ...decisionHookBase,
907
+ ctx: decisionHookCtx,
908
+ market,
717
909
  decision,
718
- runtime,
719
- signal
910
+ entry,
911
+ ml
720
912
  },
721
- { decision, signal }
913
+ { decision, entry, market }
722
914
  );
723
915
  }
724
916
  let quality;
917
+ let ai;
725
918
  if (signal) {
726
919
  try {
727
920
  quality = await enrichSignalWithAi({
@@ -736,24 +929,36 @@ var createStrategyRuntime = ({
736
929
  stage: "enrichSignalWithAi",
737
930
  error,
738
931
  decision,
739
- signal
932
+ entry,
933
+ market
740
934
  });
741
935
  throw error;
742
936
  }
937
+ ai = buildAiHookContext({
938
+ env,
939
+ ai: runtime.ai,
940
+ quality
941
+ });
743
942
  await invokeHook(
744
943
  "afterEnrichAi",
745
944
  decisionManifest?.hooks?.afterEnrichAi,
746
945
  {
747
- ...decisionHookBase,
946
+ ctx: decisionHookCtx,
947
+ market,
748
948
  decision,
749
- runtime,
750
- signal,
751
- quality
949
+ entry,
950
+ ml: ml ?? buildMlHookContext({ signal, env, ml: runtime.ml }),
951
+ ai
752
952
  },
753
- { decision, signal }
953
+ { decision, entry, market }
754
954
  );
755
955
  }
756
956
  const minAiQuality = runtime.ai?.minQuality ?? 4;
957
+ const policy = buildHookPolicy({
958
+ quality,
959
+ makeOrdersEnabled,
960
+ minAiQuality
961
+ });
757
962
  const shouldMakeOrder = shouldExecuteEntryDecision({
758
963
  makeOrdersEnabled,
759
964
  env,
@@ -777,15 +982,15 @@ var createStrategyRuntime = ({
777
982
  "beforeEntryGate",
778
983
  decisionManifest?.hooks?.beforeEntryGate,
779
984
  {
780
- ...decisionHookBase,
985
+ ctx: decisionHookCtx,
986
+ market,
781
987
  decision,
782
- runtime,
783
- signal,
784
- quality,
785
- makeOrdersEnabled,
786
- minAiQuality
988
+ entry,
989
+ policy,
990
+ ml,
991
+ ai
787
992
  },
788
- { decision, signal }
993
+ { decision, entry, market }
789
994
  );
790
995
  if (entryGate?.allow === false) {
791
996
  const skipReason = entryGate.reason ? `HOOK_BEFORE_ENTRY_GATE:${entryGate.reason}` : "HOOK_BEFORE_ENTRY_GATE";
@@ -801,7 +1006,12 @@ var createStrategyRuntime = ({
801
1006
  decision,
802
1007
  runtime,
803
1008
  manifest: decisionManifest,
804
- hookBase: decisionHookBase,
1009
+ hookCtx: decisionHookCtx,
1010
+ market,
1011
+ entry,
1012
+ policy,
1013
+ ml,
1014
+ ai,
805
1015
  invokeHook,
806
1016
  notifyRuntimeError
807
1017
  });
@@ -813,13 +1023,13 @@ var createStrategyRuntime = ({
813
1023
  var createCloseOppositeBeforePlaceOrderHook = ({
814
1024
  isEnabled
815
1025
  }) => {
816
- return async ({ connector, entryContext, config }) => {
817
- if (!isEnabled(config)) {
1026
+ return async ({ ctx, entry }) => {
1027
+ if (!isEnabled(ctx.strategyConfig)) {
818
1028
  return;
819
1029
  }
820
1030
  await closeOppositePositionsBeforeOpen({
821
- connector,
822
- entryContext
1031
+ connector: ctx.connector,
1032
+ entryContext: entry.context
823
1033
  });
824
1034
  };
825
1035
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/node",
3
- "version": "1.0.3",
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.3",
58
- "@tradejs/infra": "^1.0.3",
59
- "@tradejs/types": "^1.0.3",
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",