@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.
@@ -5546,7 +5546,7 @@ var init_aiShared = __esm({
5546
5546
  });
5547
5547
 
5548
5548
  // src/tradejsConfig.ts
5549
- var import_fs, import_path, import_module, import_url, import_logger2, CONFIG_FILE_NAMES, TS_MODULE_RE, cachedByCwd, announcedConfigFile, tsNodeRegistered, getTradejsProjectCwd, normalizeConfig, getRequireFn, ensureTsNodeRegistered, toImportSpecifier, isTsModulePath, isRelativeModulePath, isBareModuleSpecifier, importConfigFile, importTradejsModule, resolveExportedConfig, findConfigFilePath, resolvePluginModuleSpecifier, loadTradejsConfig;
5549
+ var import_fs, import_path, import_module, import_url, import_logger2, CONFIG_FILE_NAMES, TS_MODULE_RE, cachedByCwd, announcedConfigFile, tsNodeRegistered, tsconfigPathsRegisteredByCwd, getTradejsProjectCwd, normalizeConfig, getRequireFn, ensureTsNodeRegistered, ensureTsconfigPathsRegistered, toImportSpecifier, isTsModulePath, isRelativeModulePath, isBareModuleSpecifier, importConfigFile, importTradejsModule, resolveExportedConfig, findConfigFilePath, resolvePluginModuleSpecifier, loadTradejsConfig;
5550
5550
  var init_tradejsConfig = __esm({
5551
5551
  "src/tradejsConfig.ts"() {
5552
5552
  "use strict";
@@ -5566,6 +5566,7 @@ var init_tradejsConfig = __esm({
5566
5566
  cachedByCwd = /* @__PURE__ */ new Map();
5567
5567
  announcedConfigFile = /* @__PURE__ */ new Set();
5568
5568
  tsNodeRegistered = false;
5569
+ tsconfigPathsRegisteredByCwd = /* @__PURE__ */ new Set();
5569
5570
  getTradejsProjectCwd = (cwd) => {
5570
5571
  const explicit = String(cwd ?? "").trim();
5571
5572
  if (explicit) {
@@ -5607,6 +5608,28 @@ var init_tradejsConfig = __esm({
5607
5608
  });
5608
5609
  tsNodeRegistered = true;
5609
5610
  };
5611
+ ensureTsconfigPathsRegistered = async (cwd = getTradejsProjectCwd()) => {
5612
+ const projectRoot = getTradejsProjectCwd(cwd);
5613
+ if (tsconfigPathsRegisteredByCwd.has(projectRoot)) {
5614
+ return;
5615
+ }
5616
+ const tsconfigPathsModule = await import("tsconfig-paths");
5617
+ const loadConfig = tsconfigPathsModule.loadConfig;
5618
+ const register = tsconfigPathsModule.register;
5619
+ if (typeof loadConfig !== "function" || typeof register !== "function") {
5620
+ return;
5621
+ }
5622
+ const loadedConfig = loadConfig(projectRoot);
5623
+ if (loadedConfig.resultType !== "success") {
5624
+ return;
5625
+ }
5626
+ register({
5627
+ baseUrl: loadedConfig.absoluteBaseUrl,
5628
+ paths: loadedConfig.paths,
5629
+ addMatchAll: false
5630
+ });
5631
+ tsconfigPathsRegisteredByCwd.add(projectRoot);
5632
+ };
5610
5633
  toImportSpecifier = (moduleName) => {
5611
5634
  if (moduleName.startsWith("file://")) {
5612
5635
  return moduleName;
@@ -5634,6 +5657,7 @@ var init_tradejsConfig = __esm({
5634
5657
  if (ext === ".ts" || ext === ".mts") {
5635
5658
  const requireFn = getRequireFn(import_path.default.dirname(configFilePath));
5636
5659
  await ensureTsNodeRegistered();
5660
+ await ensureTsconfigPathsRegistered(import_path.default.dirname(configFilePath));
5637
5661
  return requireFn(configFilePath);
5638
5662
  }
5639
5663
  return import(
@@ -5641,7 +5665,7 @@ var init_tradejsConfig = __esm({
5641
5665
  configFileUrl
5642
5666
  );
5643
5667
  };
5644
- importTradejsModule = async (moduleName) => {
5668
+ importTradejsModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
5645
5669
  const normalized = String(moduleName ?? "").trim();
5646
5670
  if (!normalized) {
5647
5671
  return {};
@@ -5655,13 +5679,15 @@ var init_tradejsConfig = __esm({
5655
5679
  }
5656
5680
  }
5657
5681
  const requireFn = getRequireFn(
5658
- import_path.default.isAbsolute(modulePath) ? import_path.default.dirname(modulePath) : getTradejsProjectCwd()
5682
+ import_path.default.isAbsolute(modulePath) ? import_path.default.dirname(modulePath) : cwd
5659
5683
  );
5660
5684
  if (isTsModulePath(modulePath)) {
5661
5685
  await ensureTsNodeRegistered();
5686
+ await ensureTsconfigPathsRegistered(cwd);
5662
5687
  return requireFn(modulePath);
5663
5688
  }
5664
5689
  if (isBareModuleSpecifier(normalized)) {
5690
+ await ensureTsconfigPathsRegistered(cwd);
5665
5691
  return requireFn(normalized);
5666
5692
  }
5667
5693
  try {
@@ -5672,6 +5698,7 @@ var init_tradejsConfig = __esm({
5672
5698
  } catch (error) {
5673
5699
  if (isTsModulePath(modulePath)) {
5674
5700
  await ensureTsNodeRegistered();
5701
+ await ensureTsconfigPathsRegistered(cwd);
5675
5702
  return requireFn(modulePath);
5676
5703
  }
5677
5704
  throw error;
@@ -5751,7 +5778,7 @@ var init_tradejsConfig = __esm({
5751
5778
  });
5752
5779
 
5753
5780
  // src/strategy/manifests.ts
5754
- var import_indicators, import_logger3, strategyCreators, strategyManifestsMap, pluginsLoadPromise, toUniqueModules, getConfiguredPluginModuleNames, extractModuleEntries, extractStrategyPluginDefinition, extractIndicatorPluginDefinition, registerEntries, importStrategyPluginModule, ensureStrategyPluginsLoaded, ensureIndicatorPluginsLoaded, getStrategyCreator, getAvailableStrategyNames, getRegisteredStrategies, getRegisteredManifests, getStrategyManifest, isKnownStrategy, registerStrategyEntries, resetStrategyRegistryCache, strategies;
5781
+ var import_indicators, import_logger3, createStrategyRegistryState, registryStateByProjectRoot, getStrategyRegistryState, toUniqueModules, getConfiguredPluginModuleNames, extractModuleEntries, extractStrategyPluginDefinition, extractIndicatorPluginDefinition, registerEntries, importStrategyPluginModule, ensureStrategyPluginsLoaded, ensureIndicatorPluginsLoaded, getStrategyCreator, getAvailableStrategyNames, getRegisteredStrategies, getRegisteredManifests, getStrategyManifest, isKnownStrategy, registerStrategyEntries, resetStrategyRegistryCache, strategies;
5755
5782
  var init_manifests = __esm({
5756
5783
  "src/strategy/manifests.ts"() {
5757
5784
  "use strict";
@@ -5759,14 +5786,29 @@ var init_manifests = __esm({
5759
5786
  import_logger3 = require("@tradejs/infra/logger");
5760
5787
  init_tradejsConfig();
5761
5788
  init_tradejsConfig();
5762
- strategyCreators = /* @__PURE__ */ new Map();
5763
- strategyManifestsMap = /* @__PURE__ */ new Map();
5764
- pluginsLoadPromise = null;
5789
+ createStrategyRegistryState = () => ({
5790
+ strategyCreators: /* @__PURE__ */ new Map(),
5791
+ strategyManifestsMap: /* @__PURE__ */ new Map(),
5792
+ pluginsLoadPromise: null
5793
+ });
5794
+ registryStateByProjectRoot = /* @__PURE__ */ new Map();
5795
+ getStrategyRegistryState = (cwd = getTradejsProjectCwd()) => {
5796
+ const projectRoot = getTradejsProjectCwd(cwd);
5797
+ let state = registryStateByProjectRoot.get(projectRoot);
5798
+ if (!state) {
5799
+ state = createStrategyRegistryState();
5800
+ registryStateByProjectRoot.set(projectRoot, state);
5801
+ }
5802
+ return {
5803
+ projectRoot,
5804
+ state
5805
+ };
5806
+ };
5765
5807
  toUniqueModules = (modules = []) => [
5766
5808
  ...new Set(modules.map((moduleName) => moduleName.trim()).filter(Boolean))
5767
5809
  ];
5768
- getConfiguredPluginModuleNames = async () => {
5769
- const config = await loadTradejsConfig();
5810
+ getConfiguredPluginModuleNames = async (cwd = getTradejsProjectCwd()) => {
5811
+ const config = await loadTradejsConfig(cwd);
5770
5812
  return {
5771
5813
  strategyModules: toUniqueModules(config.strategies),
5772
5814
  indicatorModules: toUniqueModules(config.indicators)
@@ -5800,14 +5842,14 @@ var init_manifests = __esm({
5800
5842
  );
5801
5843
  return indicatorEntries ? { indicatorEntries } : null;
5802
5844
  };
5803
- registerEntries = (entries, source) => {
5845
+ registerEntries = (entries, source, state) => {
5804
5846
  for (const entry of entries) {
5805
5847
  const strategyName = entry.manifest?.name;
5806
5848
  if (!strategyName) {
5807
5849
  import_logger3.logger.warn("Skip strategy entry without name from %s", source);
5808
5850
  continue;
5809
5851
  }
5810
- if (strategyCreators.has(strategyName)) {
5852
+ if (state.strategyCreators.has(strategyName)) {
5811
5853
  import_logger3.logger.warn(
5812
5854
  'Skip duplicate strategy "%s" from %s: already registered',
5813
5855
  strategyName,
@@ -5815,106 +5857,135 @@ var init_manifests = __esm({
5815
5857
  );
5816
5858
  continue;
5817
5859
  }
5818
- strategyCreators.set(strategyName, entry.creator);
5819
- strategyManifestsMap.set(strategyName, entry.manifest);
5860
+ state.strategyCreators.set(strategyName, entry.creator);
5861
+ state.strategyManifestsMap.set(strategyName, entry.manifest);
5820
5862
  }
5821
5863
  };
5822
- importStrategyPluginModule = async (moduleName) => {
5864
+ importStrategyPluginModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
5823
5865
  if (typeof importTradejsModule === "function") {
5824
- return importTradejsModule(moduleName);
5866
+ return importTradejsModule(moduleName, cwd);
5825
5867
  }
5826
5868
  return import(
5827
5869
  /* webpackIgnore: true */
5828
5870
  moduleName
5829
5871
  );
5830
5872
  };
5831
- ensureStrategyPluginsLoaded = async () => {
5832
- if (pluginsLoadPromise) {
5833
- return pluginsLoadPromise;
5834
- }
5835
- pluginsLoadPromise = (async () => {
5836
- const { strategyModules, indicatorModules } = await getConfiguredPluginModuleNames();
5837
- const strategySet = new Set(strategyModules);
5838
- const indicatorSet = new Set(indicatorModules);
5839
- const pluginModuleNames = [
5840
- .../* @__PURE__ */ new Set([...strategyModules, ...indicatorModules])
5841
- ];
5842
- if (!pluginModuleNames.length) return;
5843
- for (const moduleName of pluginModuleNames) {
5844
- try {
5845
- const resolvedModuleName = resolvePluginModuleSpecifier(moduleName);
5846
- const moduleExport = await importStrategyPluginModule(resolvedModuleName);
5847
- if (strategySet.has(moduleName)) {
5848
- const pluginDefinition = extractStrategyPluginDefinition(moduleExport);
5849
- if (!pluginDefinition) {
5850
- import_logger3.logger.warn(
5851
- 'Skip strategy plugin "%s": export { strategyEntries } is missing',
5852
- moduleName
5853
- );
5854
- } else {
5855
- registerEntries(pluginDefinition.strategyEntries, moduleName);
5873
+ ensureStrategyPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
5874
+ const { projectRoot, state } = getStrategyRegistryState(cwd);
5875
+ if (!state.pluginsLoadPromise) {
5876
+ (0, import_indicators.resetIndicatorRegistryCache)(projectRoot);
5877
+ state.pluginsLoadPromise = (async () => {
5878
+ const { strategyModules, indicatorModules } = await getConfiguredPluginModuleNames(projectRoot);
5879
+ const strategySet = new Set(strategyModules);
5880
+ const indicatorSet = new Set(indicatorModules);
5881
+ const pluginModuleNames = [
5882
+ .../* @__PURE__ */ new Set([...strategyModules, ...indicatorModules])
5883
+ ];
5884
+ if (!pluginModuleNames.length) {
5885
+ return;
5886
+ }
5887
+ for (const moduleName of pluginModuleNames) {
5888
+ try {
5889
+ const resolvedModuleName = resolvePluginModuleSpecifier(
5890
+ moduleName,
5891
+ projectRoot
5892
+ );
5893
+ const moduleExport = await importStrategyPluginModule(
5894
+ resolvedModuleName,
5895
+ projectRoot
5896
+ );
5897
+ if (strategySet.has(moduleName)) {
5898
+ const pluginDefinition = extractStrategyPluginDefinition(moduleExport);
5899
+ if (!pluginDefinition) {
5900
+ import_logger3.logger.warn(
5901
+ 'Skip strategy plugin "%s": export { strategyEntries } is missing',
5902
+ moduleName
5903
+ );
5904
+ } else {
5905
+ registerEntries(
5906
+ pluginDefinition.strategyEntries,
5907
+ moduleName,
5908
+ state
5909
+ );
5910
+ }
5856
5911
  }
5857
- }
5858
- if (indicatorSet.has(moduleName)) {
5859
- const indicatorPluginDefinition = extractIndicatorPluginDefinition(moduleExport);
5860
- if (!indicatorPluginDefinition) {
5912
+ if (indicatorSet.has(moduleName)) {
5913
+ const indicatorPluginDefinition = extractIndicatorPluginDefinition(moduleExport);
5914
+ if (!indicatorPluginDefinition) {
5915
+ import_logger3.logger.warn(
5916
+ 'Skip indicator plugin "%s": export { indicatorEntries } is missing',
5917
+ moduleName
5918
+ );
5919
+ } else {
5920
+ (0, import_indicators.registerIndicatorEntries)(
5921
+ indicatorPluginDefinition.indicatorEntries,
5922
+ moduleName,
5923
+ projectRoot
5924
+ );
5925
+ }
5926
+ }
5927
+ if (!strategySet.has(moduleName) && !indicatorSet.has(moduleName)) {
5861
5928
  import_logger3.logger.warn(
5862
- 'Skip indicator plugin "%s": export { indicatorEntries } is missing',
5863
- moduleName
5864
- );
5865
- } else {
5866
- (0, import_indicators.registerIndicatorEntries)(
5867
- indicatorPluginDefinition.indicatorEntries,
5929
+ 'Skip plugin "%s": no strategy/indicator sections requested in config',
5868
5930
  moduleName
5869
5931
  );
5870
5932
  }
5871
- }
5872
- if (!strategySet.has(moduleName) && !indicatorSet.has(moduleName)) {
5933
+ } catch (error) {
5873
5934
  import_logger3.logger.warn(
5874
- 'Skip plugin "%s": no strategy/indicator sections requested in config',
5875
- moduleName
5935
+ 'Failed to load plugin "%s": %s',
5936
+ moduleName,
5937
+ String(error)
5876
5938
  );
5877
5939
  }
5878
- } catch (error) {
5879
- import_logger3.logger.warn(
5880
- 'Failed to load plugin "%s": %s',
5881
- moduleName,
5882
- String(error)
5883
- );
5884
5940
  }
5885
- }
5886
- })();
5887
- return pluginsLoadPromise;
5941
+ })();
5942
+ }
5943
+ await state.pluginsLoadPromise;
5888
5944
  };
5889
- ensureIndicatorPluginsLoaded = ensureStrategyPluginsLoaded;
5890
- getStrategyCreator = async (name) => {
5891
- await ensureStrategyPluginsLoaded();
5892
- return strategyCreators.get(name);
5945
+ ensureIndicatorPluginsLoaded = async (cwd = getTradejsProjectCwd()) => ensureStrategyPluginsLoaded(cwd);
5946
+ getStrategyCreator = async (name, cwd = getTradejsProjectCwd()) => {
5947
+ await ensureStrategyPluginsLoaded(cwd);
5948
+ const { state } = getStrategyRegistryState(cwd);
5949
+ return state.strategyCreators.get(name);
5893
5950
  };
5894
- getAvailableStrategyNames = async () => {
5895
- await ensureStrategyPluginsLoaded();
5896
- return [...strategyCreators.keys()].sort((a, b) => a.localeCompare(b));
5951
+ getAvailableStrategyNames = async (cwd = getTradejsProjectCwd()) => {
5952
+ await ensureStrategyPluginsLoaded(cwd);
5953
+ const { state } = getStrategyRegistryState(cwd);
5954
+ return [...state.strategyCreators.keys()].sort((a, b) => a.localeCompare(b));
5897
5955
  };
5898
- getRegisteredStrategies = () => {
5899
- return Object.fromEntries(strategyCreators.entries());
5956
+ getRegisteredStrategies = (cwd = getTradejsProjectCwd()) => {
5957
+ const { state } = getStrategyRegistryState(cwd);
5958
+ return Object.fromEntries(state.strategyCreators.entries());
5900
5959
  };
5901
- getRegisteredManifests = () => {
5902
- return [...strategyManifestsMap.values()];
5960
+ getRegisteredManifests = (cwd = getTradejsProjectCwd()) => {
5961
+ const { state } = getStrategyRegistryState(cwd);
5962
+ return [...state.strategyManifestsMap.values()];
5903
5963
  };
5904
- getStrategyManifest = (name) => {
5905
- return name ? strategyManifestsMap.get(name) : void 0;
5964
+ getStrategyManifest = (name, cwd = getTradejsProjectCwd()) => {
5965
+ if (!name) {
5966
+ return void 0;
5967
+ }
5968
+ const { state } = getStrategyRegistryState(cwd);
5969
+ return state.strategyManifestsMap.get(name);
5906
5970
  };
5907
- isKnownStrategy = (name) => {
5908
- return strategyCreators.has(name);
5971
+ isKnownStrategy = (name, cwd = getTradejsProjectCwd()) => {
5972
+ const { state } = getStrategyRegistryState(cwd);
5973
+ return state.strategyCreators.has(name);
5909
5974
  };
5910
- registerStrategyEntries = (entries) => {
5911
- registerEntries(entries, "runtime");
5975
+ registerStrategyEntries = (entries, cwd = getTradejsProjectCwd()) => {
5976
+ const { state } = getStrategyRegistryState(cwd);
5977
+ registerEntries(entries, "runtime", state);
5912
5978
  };
5913
- resetStrategyRegistryCache = () => {
5914
- strategyCreators.clear();
5915
- strategyManifestsMap.clear();
5916
- (0, import_indicators.resetIndicatorRegistryCache)();
5917
- pluginsLoadPromise = null;
5979
+ resetStrategyRegistryCache = (cwd) => {
5980
+ const normalizedCwd = String(cwd ?? "").trim();
5981
+ if (!normalizedCwd) {
5982
+ registryStateByProjectRoot.clear();
5983
+ (0, import_indicators.resetIndicatorRegistryCache)();
5984
+ return;
5985
+ }
5986
+ const projectRoot = getTradejsProjectCwd(normalizedCwd);
5987
+ registryStateByProjectRoot.delete(projectRoot);
5988
+ (0, import_indicators.resetIndicatorRegistryCache)(projectRoot);
5918
5989
  };
5919
5990
  strategies = new Proxy(
5920
5991
  {},
@@ -5923,10 +5994,10 @@ var init_manifests = __esm({
5923
5994
  if (typeof property !== "string") {
5924
5995
  return void 0;
5925
5996
  }
5926
- return strategyCreators.get(property);
5997
+ return getStrategyRegistryState().state.strategyCreators.get(property);
5927
5998
  },
5928
5999
  ownKeys: () => {
5929
- return [...strategyCreators.keys()];
6000
+ return [...getStrategyRegistryState().state.strategyCreators.keys()];
5930
6001
  },
5931
6002
  getOwnPropertyDescriptor: () => ({
5932
6003
  enumerable: true,
@@ -6399,6 +6470,7 @@ var buildMlPayload = (payload) => {
6399
6470
  };
6400
6471
 
6401
6472
  // src/strategyHelpers/runtime.ts
6473
+ init_tradejsConfig();
6402
6474
  var formatAiError = (err) => {
6403
6475
  const error = err;
6404
6476
  const safeJson = (value) => {
@@ -6440,7 +6512,8 @@ var enrichSignalWithMl = async ({
6440
6512
  const mlResult = await (0, import_ml2.fetchMlThreshold)({
6441
6513
  strategy,
6442
6514
  features,
6443
- threshold: ml.mlThreshold
6515
+ threshold: ml.mlThreshold,
6516
+ projectRoot: getTradejsProjectCwd()
6444
6517
  });
6445
6518
  if (mlResult) {
6446
6519
  signal.ml = mlResult;
@@ -6545,6 +6618,7 @@ var createLoadPineScript = (baseDir) => {
6545
6618
 
6546
6619
  // src/strategyRuntime.ts
6547
6620
  init_manifests();
6621
+ init_tradejsConfig();
6548
6622
 
6549
6623
  // src/strategyHelpers/config.ts
6550
6624
  var import_lodash2 = __toESM(require_lodash());
@@ -6634,10 +6708,151 @@ var getEntrySkipReason = ({
6634
6708
  }
6635
6709
  return "ENTRY_POLICY_BLOCKED";
6636
6710
  };
6711
+ var buildHookCtx = ({
6712
+ connector,
6713
+ strategyName,
6714
+ userName,
6715
+ symbol,
6716
+ strategyConfig,
6717
+ env,
6718
+ isConfigFromBacktest
6719
+ }) => ({
6720
+ connector,
6721
+ strategyName,
6722
+ userName,
6723
+ symbol,
6724
+ strategyConfig,
6725
+ env,
6726
+ isConfigFromBacktest
6727
+ });
6728
+ var buildHookEntry = ({
6729
+ decision,
6730
+ runtime
6731
+ }) => ({
6732
+ context: decision.entryContext,
6733
+ orderPlan: decision.orderPlan,
6734
+ signal: decision.signal,
6735
+ runtime: {
6736
+ raw: decision.runtime,
6737
+ resolved: runtime
6738
+ }
6739
+ });
6740
+ var buildHookPolicy = ({
6741
+ quality,
6742
+ makeOrdersEnabled,
6743
+ minAiQuality
6744
+ }) => ({
6745
+ aiQuality: quality,
6746
+ makeOrdersEnabled,
6747
+ minAiQuality
6748
+ });
6749
+ var buildMlHookContext = ({
6750
+ signal,
6751
+ env,
6752
+ ml
6753
+ }) => {
6754
+ if (env === "BACKTEST") {
6755
+ return {
6756
+ config: ml,
6757
+ attempted: false,
6758
+ applied: false,
6759
+ skippedReason: "BACKTEST"
6760
+ };
6761
+ }
6762
+ if (!ml) {
6763
+ return {
6764
+ attempted: false,
6765
+ applied: false,
6766
+ skippedReason: "NO_RUNTIME"
6767
+ };
6768
+ }
6769
+ if (ml.enabled === false) {
6770
+ return {
6771
+ config: ml,
6772
+ attempted: false,
6773
+ applied: false,
6774
+ skippedReason: "DISABLED"
6775
+ };
6776
+ }
6777
+ if (!ml.strategyConfig) {
6778
+ return {
6779
+ config: ml,
6780
+ attempted: false,
6781
+ applied: false,
6782
+ skippedReason: "NO_STRATEGY_CONFIG"
6783
+ };
6784
+ }
6785
+ if (typeof ml.mlThreshold !== "number") {
6786
+ return {
6787
+ config: ml,
6788
+ attempted: false,
6789
+ applied: false,
6790
+ skippedReason: "NO_THRESHOLD"
6791
+ };
6792
+ }
6793
+ if (signal.ml) {
6794
+ return {
6795
+ config: ml,
6796
+ attempted: true,
6797
+ applied: true,
6798
+ result: signal.ml
6799
+ };
6800
+ }
6801
+ return {
6802
+ config: ml,
6803
+ attempted: true,
6804
+ applied: false,
6805
+ skippedReason: "NO_RESULT"
6806
+ };
6807
+ };
6808
+ var buildAiHookContext = ({
6809
+ env,
6810
+ ai,
6811
+ quality
6812
+ }) => {
6813
+ if (env === "BACKTEST") {
6814
+ return {
6815
+ config: ai,
6816
+ attempted: false,
6817
+ applied: false,
6818
+ skippedReason: "BACKTEST"
6819
+ };
6820
+ }
6821
+ if (!ai) {
6822
+ return {
6823
+ attempted: false,
6824
+ applied: false,
6825
+ skippedReason: "NO_RUNTIME"
6826
+ };
6827
+ }
6828
+ if (ai.enabled === false) {
6829
+ return {
6830
+ config: ai,
6831
+ attempted: false,
6832
+ applied: false,
6833
+ skippedReason: "DISABLED"
6834
+ };
6835
+ }
6836
+ if (typeof quality === "number") {
6837
+ return {
6838
+ config: ai,
6839
+ attempted: true,
6840
+ applied: true,
6841
+ quality
6842
+ };
6843
+ }
6844
+ return {
6845
+ config: ai,
6846
+ attempted: true,
6847
+ applied: false,
6848
+ skippedReason: "NO_QUALITY"
6849
+ };
6850
+ };
6637
6851
  var handleExitDecision = async ({
6638
6852
  connector,
6639
6853
  symbol,
6640
6854
  decision,
6855
+ market,
6641
6856
  onRuntimeError
6642
6857
  }) => {
6643
6858
  try {
@@ -6651,7 +6866,8 @@ var handleExitDecision = async ({
6651
6866
  await onRuntimeError?.({
6652
6867
  stage: "closePosition",
6653
6868
  error: err,
6654
- decision
6869
+ decision,
6870
+ market
6655
6871
  });
6656
6872
  import_logger5.logger.error("close order error: %s %s", symbol, err);
6657
6873
  return "ORDER_ERROR";
@@ -6664,7 +6880,12 @@ var executeEntryDecision = async ({
6664
6880
  decision,
6665
6881
  runtime,
6666
6882
  manifest,
6667
- hookBase,
6883
+ hookCtx,
6884
+ market,
6885
+ entry,
6886
+ policy,
6887
+ ml,
6888
+ ai,
6668
6889
  invokeHook,
6669
6890
  notifyRuntimeError
6670
6891
  }) => {
@@ -6674,13 +6895,15 @@ var executeEntryDecision = async ({
6674
6895
  "beforePlaceOrder",
6675
6896
  manifest?.hooks?.beforePlaceOrder,
6676
6897
  {
6677
- ...hookBase,
6678
- entryContext: decision.entryContext,
6679
- runtime,
6898
+ ctx: hookCtx,
6899
+ market,
6680
6900
  decision,
6681
- signal
6901
+ entry,
6902
+ policy,
6903
+ ml,
6904
+ ai
6682
6905
  },
6683
- { decision, signal }
6906
+ { decision, entry, market }
6684
6907
  );
6685
6908
  try {
6686
6909
  await runtime.beforePlaceOrder?.();
@@ -6689,7 +6912,8 @@ var executeEntryDecision = async ({
6689
6912
  stage: "runtime.beforePlaceOrder",
6690
6913
  error,
6691
6914
  decision,
6692
- signal
6915
+ entry,
6916
+ market
6693
6917
  });
6694
6918
  throw error;
6695
6919
  }
@@ -6712,13 +6936,18 @@ var executeEntryDecision = async ({
6712
6936
  "afterPlaceOrder",
6713
6937
  manifest?.hooks?.afterPlaceOrder,
6714
6938
  {
6715
- ...hookBase,
6939
+ ctx: hookCtx,
6940
+ market,
6716
6941
  decision,
6717
- runtime,
6718
- signal,
6719
- orderResult: signal
6942
+ entry,
6943
+ policy,
6944
+ ml,
6945
+ ai,
6946
+ order: {
6947
+ result: signal
6948
+ }
6720
6949
  },
6721
- { decision, signal }
6950
+ { decision, entry, market }
6722
6951
  );
6723
6952
  return signal;
6724
6953
  }
@@ -6738,13 +6967,18 @@ var executeEntryDecision = async ({
6738
6967
  "afterPlaceOrder",
6739
6968
  manifest?.hooks?.afterPlaceOrder,
6740
6969
  {
6741
- ...hookBase,
6970
+ ctx: hookCtx,
6971
+ market,
6742
6972
  decision,
6743
- runtime,
6744
- signal,
6745
- orderResult: decision.code
6973
+ entry,
6974
+ policy,
6975
+ ml,
6976
+ ai,
6977
+ order: {
6978
+ result: decision.code
6979
+ }
6746
6980
  },
6747
- { decision, signal }
6981
+ { decision, entry, market }
6748
6982
  );
6749
6983
  } catch (err) {
6750
6984
  if (signal) {
@@ -6754,7 +6988,8 @@ var executeEntryDecision = async ({
6754
6988
  stage: "placeOrder",
6755
6989
  error: err,
6756
6990
  decision,
6757
- signal
6991
+ entry,
6992
+ market
6758
6993
  });
6759
6994
  import_logger5.logger.error("order error: %s %s", symbol, err);
6760
6995
  return signal ?? "ORDER_ERROR";
@@ -6768,6 +7003,7 @@ var createStrategyRuntime = ({
6768
7003
  manifest: staticManifest,
6769
7004
  strategyDirectory
6770
7005
  }) => {
7006
+ const projectRoot = getTradejsProjectCwd();
6771
7007
  const resolveManifest = (name) => {
6772
7008
  if (!name) {
6773
7009
  return void 0;
@@ -6775,11 +7011,11 @@ var createStrategyRuntime = ({
6775
7011
  if (staticManifest?.name === name) {
6776
7012
  return staticManifest;
6777
7013
  }
6778
- return getStrategyManifest(name);
7014
+ return getStrategyManifest(name, projectRoot);
6779
7015
  };
6780
7016
  const loadPineScript2 = createLoadPineScript(
6781
7017
  strategyDirectory ? import_node_path2.default.resolve(strategyDirectory) : import_node_path2.default.resolve(
6782
- process.cwd(),
7018
+ projectRoot,
6783
7019
  "packages",
6784
7020
  "strategies",
6785
7021
  "src",
@@ -6810,33 +7046,37 @@ var createStrategyRuntime = ({
6810
7046
  strategyName,
6811
7047
  userName,
6812
7048
  symbol,
6813
- config,
7049
+ strategyConfig: config,
6814
7050
  env,
6815
7051
  isConfigFromBacktest
6816
7052
  };
7053
+ const getHookCtx = (name = strategyName) => buildHookCtx({
7054
+ ...hookBase,
7055
+ strategyName: name
7056
+ });
6817
7057
  const notifyRuntimeError = async ({
6818
7058
  stage,
6819
7059
  error,
6820
7060
  decision,
6821
- signal
7061
+ entry,
7062
+ market
6822
7063
  }) => {
6823
7064
  const errorStrategyName = decision?.kind === "entry" ? decision.entryContext.strategy : strategyName;
6824
7065
  const errorManifest = resolveManifest(errorStrategyName) ?? strategyManifest;
6825
- const errorHookBase = {
6826
- ...hookBase,
6827
- strategyName: errorStrategyName
6828
- };
6829
7066
  const onRuntimeError = errorManifest?.hooks?.onRuntimeError;
6830
7067
  if (!onRuntimeError) {
6831
7068
  return;
6832
7069
  }
6833
7070
  try {
6834
7071
  await onRuntimeError({
6835
- ...errorHookBase,
6836
- stage,
6837
- error,
7072
+ ctx: getHookCtx(errorStrategyName),
7073
+ market,
6838
7074
  decision,
6839
- signal
7075
+ entry,
7076
+ error: {
7077
+ stage,
7078
+ cause: error
7079
+ }
6840
7080
  });
6841
7081
  } catch (hookError) {
6842
7082
  import_logger5.logger.error(
@@ -6863,7 +7103,8 @@ var createStrategyRuntime = ({
6863
7103
  stage,
6864
7104
  error,
6865
7105
  decision: errorContext.decision,
6866
- signal: errorContext.signal
7106
+ entry: errorContext.entry,
7107
+ market: errorContext.market
6867
7108
  });
6868
7109
  return void 0;
6869
7110
  }
@@ -6874,7 +7115,8 @@ var createStrategyRuntime = ({
6874
7115
  btcData,
6875
7116
  btcBinanceData,
6876
7117
  btcCoinbaseData,
6877
- periods: (0, import_strategies.buildDefaultIndicatorPeriods)(config)
7118
+ periods: (0, import_strategies.buildDefaultIndicatorPeriods)(config),
7119
+ pluginRegistryScope: projectRoot
6878
7120
  });
6879
7121
  const strategyApi = (0, import_strategies.createStrategyAPI)({
6880
7122
  strategy: strategyName,
@@ -6901,39 +7143,45 @@ var createStrategyRuntime = ({
6901
7143
  indicatorsState
6902
7144
  });
6903
7145
  await invokeHook("onInit", strategyManifest?.hooks?.onInit, {
6904
- ...hookBase,
6905
- data,
6906
- btcData
7146
+ ctx: getHookCtx(),
7147
+ market: {
7148
+ data,
7149
+ btcData
7150
+ }
6907
7151
  });
6908
7152
  return async (candle, btcCandle) => {
6909
7153
  data.push(candle);
6910
7154
  btcData.push(btcCandle);
6911
7155
  indicatorsState.setCurrentBar(candle, btcCandle);
7156
+ const market = {
7157
+ candle,
7158
+ btcCandle
7159
+ };
6912
7160
  const decision = await core(candle, btcCandle);
6913
7161
  const decisionStrategyName = decision.kind === "entry" ? decision.entryContext.strategy : strategyName;
6914
7162
  const decisionManifest = resolveManifest(decisionStrategyName) ?? strategyManifest;
6915
- const decisionHookBase = {
6916
- ...hookBase,
6917
- strategyName: decisionStrategyName
6918
- };
7163
+ const decisionHookCtx = getHookCtx(decisionStrategyName);
6919
7164
  await invokeHook(
6920
7165
  "afterCoreDecision",
6921
7166
  decisionManifest?.hooks?.afterCoreDecision,
6922
7167
  {
6923
- ...decisionHookBase,
6924
- decision,
6925
- candle,
6926
- btcCandle
7168
+ ctx: decisionHookCtx,
7169
+ market,
7170
+ decision
6927
7171
  },
6928
- { decision }
7172
+ { decision, market }
6929
7173
  );
6930
7174
  if (decision.kind === "skip") {
6931
- await invokeHook("onSkip", decisionManifest?.hooks?.onSkip, {
6932
- ...decisionHookBase,
6933
- decision,
6934
- candle,
6935
- btcCandle
6936
- });
7175
+ await invokeHook(
7176
+ "onSkip",
7177
+ decisionManifest?.hooks?.onSkip,
7178
+ {
7179
+ ctx: decisionHookCtx,
7180
+ market,
7181
+ decision
7182
+ },
7183
+ { decision, market }
7184
+ );
6937
7185
  return decision.code;
6938
7186
  }
6939
7187
  const makeOrdersEnabled = typeof config.MAKE_ORDERS === "boolean" ? config.MAKE_ORDERS : true;
@@ -6945,10 +7193,11 @@ var createStrategyRuntime = ({
6945
7193
  "beforeClosePosition",
6946
7194
  decisionManifest?.hooks?.beforeClosePosition,
6947
7195
  {
6948
- ...decisionHookBase,
7196
+ ctx: decisionHookCtx,
7197
+ market,
6949
7198
  decision
6950
7199
  },
6951
- { decision }
7200
+ { decision, market }
6952
7201
  );
6953
7202
  if (closeGate?.allow === false) {
6954
7203
  return closeGate.reason ? `CLOSE_BLOCKED_BY_HOOK:${closeGate.reason}` : "CLOSE_BLOCKED_BY_HOOK";
@@ -6957,11 +7206,18 @@ var createStrategyRuntime = ({
6957
7206
  connector,
6958
7207
  symbol,
6959
7208
  decision,
6960
- onRuntimeError: async ({ stage, error, decision: exitDecision }) => {
7209
+ market,
7210
+ onRuntimeError: async ({
7211
+ stage,
7212
+ error,
7213
+ decision: exitDecision,
7214
+ market: errorMarket
7215
+ }) => {
6961
7216
  await notifyRuntimeError({
6962
7217
  stage,
6963
7218
  error,
6964
- decision: exitDecision
7219
+ decision: exitDecision,
7220
+ market: errorMarket
6965
7221
  });
6966
7222
  }
6967
7223
  });
@@ -6972,6 +7228,11 @@ var createStrategyRuntime = ({
6972
7228
  manifest: decisionManifest
6973
7229
  });
6974
7230
  const signal = decision.signal;
7231
+ const entry = buildHookEntry({
7232
+ decision,
7233
+ runtime
7234
+ });
7235
+ let ml;
6975
7236
  if (signal) {
6976
7237
  try {
6977
7238
  await enrichSignalWithMl({
@@ -6984,23 +7245,31 @@ var createStrategyRuntime = ({
6984
7245
  stage: "enrichSignalWithMl",
6985
7246
  error,
6986
7247
  decision,
6987
- signal
7248
+ entry,
7249
+ market
6988
7250
  });
6989
7251
  throw error;
6990
7252
  }
7253
+ ml = buildMlHookContext({
7254
+ signal,
7255
+ env,
7256
+ ml: runtime.ml
7257
+ });
6991
7258
  await invokeHook(
6992
7259
  "afterEnrichMl",
6993
7260
  decisionManifest?.hooks?.afterEnrichMl,
6994
7261
  {
6995
- ...decisionHookBase,
7262
+ ctx: decisionHookCtx,
7263
+ market,
6996
7264
  decision,
6997
- runtime,
6998
- signal
7265
+ entry,
7266
+ ml
6999
7267
  },
7000
- { decision, signal }
7268
+ { decision, entry, market }
7001
7269
  );
7002
7270
  }
7003
7271
  let quality;
7272
+ let ai;
7004
7273
  if (signal) {
7005
7274
  try {
7006
7275
  quality = await enrichSignalWithAi({
@@ -7015,24 +7284,36 @@ var createStrategyRuntime = ({
7015
7284
  stage: "enrichSignalWithAi",
7016
7285
  error,
7017
7286
  decision,
7018
- signal
7287
+ entry,
7288
+ market
7019
7289
  });
7020
7290
  throw error;
7021
7291
  }
7292
+ ai = buildAiHookContext({
7293
+ env,
7294
+ ai: runtime.ai,
7295
+ quality
7296
+ });
7022
7297
  await invokeHook(
7023
7298
  "afterEnrichAi",
7024
7299
  decisionManifest?.hooks?.afterEnrichAi,
7025
7300
  {
7026
- ...decisionHookBase,
7301
+ ctx: decisionHookCtx,
7302
+ market,
7027
7303
  decision,
7028
- runtime,
7029
- signal,
7030
- quality
7304
+ entry,
7305
+ ml: ml ?? buildMlHookContext({ signal, env, ml: runtime.ml }),
7306
+ ai
7031
7307
  },
7032
- { decision, signal }
7308
+ { decision, entry, market }
7033
7309
  );
7034
7310
  }
7035
7311
  const minAiQuality = runtime.ai?.minQuality ?? 4;
7312
+ const policy = buildHookPolicy({
7313
+ quality,
7314
+ makeOrdersEnabled,
7315
+ minAiQuality
7316
+ });
7036
7317
  const shouldMakeOrder = shouldExecuteEntryDecision({
7037
7318
  makeOrdersEnabled,
7038
7319
  env,
@@ -7056,15 +7337,15 @@ var createStrategyRuntime = ({
7056
7337
  "beforeEntryGate",
7057
7338
  decisionManifest?.hooks?.beforeEntryGate,
7058
7339
  {
7059
- ...decisionHookBase,
7340
+ ctx: decisionHookCtx,
7341
+ market,
7060
7342
  decision,
7061
- runtime,
7062
- signal,
7063
- quality,
7064
- makeOrdersEnabled,
7065
- minAiQuality
7343
+ entry,
7344
+ policy,
7345
+ ml,
7346
+ ai
7066
7347
  },
7067
- { decision, signal }
7348
+ { decision, entry, market }
7068
7349
  );
7069
7350
  if (entryGate?.allow === false) {
7070
7351
  const skipReason = entryGate.reason ? `HOOK_BEFORE_ENTRY_GATE:${entryGate.reason}` : "HOOK_BEFORE_ENTRY_GATE";
@@ -7080,7 +7361,12 @@ var createStrategyRuntime = ({
7080
7361
  decision,
7081
7362
  runtime,
7082
7363
  manifest: decisionManifest,
7083
- hookBase: decisionHookBase,
7364
+ hookCtx: decisionHookCtx,
7365
+ market,
7366
+ entry,
7367
+ policy,
7368
+ ml,
7369
+ ai,
7084
7370
  invokeHook,
7085
7371
  notifyRuntimeError
7086
7372
  });
@@ -7092,13 +7378,13 @@ var createStrategyRuntime = ({
7092
7378
  var createCloseOppositeBeforePlaceOrderHook = ({
7093
7379
  isEnabled
7094
7380
  }) => {
7095
- return async ({ connector, entryContext, config }) => {
7096
- if (!isEnabled(config)) {
7381
+ return async ({ ctx, entry }) => {
7382
+ if (!isEnabled(ctx.strategyConfig)) {
7097
7383
  return;
7098
7384
  }
7099
7385
  await closeOppositePositionsBeforeOpen({
7100
- connector,
7101
- entryContext
7386
+ connector: ctx.connector,
7387
+ entryContext: entry.context
7102
7388
  });
7103
7389
  };
7104
7390
  };