@tradejs/node 3.0.1 → 3.1.1
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/README.md +4 -0
- package/dist/ai.d.mts +26 -4
- package/dist/ai.d.ts +26 -4
- package/dist/ai.js +162 -165
- package/dist/ai.mjs +4 -1
- package/dist/backtest.js +515 -188
- package/dist/backtest.mjs +6 -4
- package/dist/chunk-3TWULKHV.mjs +595 -0
- package/dist/chunk-FB5NUEOQ.mjs +295 -0
- package/dist/chunk-LAJ7NA3Q.mjs +377 -0
- package/dist/{chunk-VRXU4E4O.mjs → chunk-SEQJ6V6B.mjs} +165 -446
- package/dist/{chunk-OGAWBO3Z.mjs → chunk-W26Y6IRP.mjs} +22 -3
- package/dist/chunk-XN7BC7XK.mjs +295 -0
- package/dist/cli.js +152 -161
- package/dist/cli.mjs +4 -2
- package/dist/connectors.d.mts +5 -2
- package/dist/connectors.d.ts +5 -2
- package/dist/connectors.js +329 -8
- package/dist/connectors.mjs +5 -1
- package/dist/registry-DHTLjQcr.d.mts +17 -0
- package/dist/registry-DHTLjQcr.d.ts +17 -0
- package/dist/registry.d.mts +2 -15
- package/dist/registry.d.ts +2 -15
- package/dist/registry.js +176 -161
- package/dist/registry.mjs +5 -2
- package/dist/runtimeDashboard.d.mts +12 -0
- package/dist/runtimeDashboard.d.ts +12 -0
- package/dist/runtimeDashboard.js +8502 -0
- package/dist/runtimeDashboard.mjs +1000 -0
- package/dist/runtimeStrategies.d.mts +38 -0
- package/dist/runtimeStrategies.d.ts +38 -0
- package/dist/runtimeStrategies.js +798 -0
- package/dist/runtimeStrategies.mjs +264 -0
- package/dist/runtimeTrades.d.mts +31 -0
- package/dist/runtimeTrades.d.ts +31 -0
- package/dist/runtimeTrades.js +321 -0
- package/dist/runtimeTrades.mjs +11 -0
- package/dist/strategies.d.mts +2 -2
- package/dist/strategies.d.ts +2 -2
- package/dist/strategies.js +194 -165
- package/dist/strategies.mjs +24 -379
- package/package.json +27 -6
- package/dist/chunk-V3YMKE4I.mjs +0 -271
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
loadTradejsConfig,
|
|
5
|
-
resolvePluginModuleSpecifier
|
|
6
|
-
} from "./chunk-WS5DYEVZ.mjs";
|
|
2
|
+
getStrategyManifest
|
|
3
|
+
} from "./chunk-XN7BC7XK.mjs";
|
|
7
4
|
|
|
8
5
|
// src/ai.ts
|
|
9
6
|
import {
|
|
10
7
|
DEFAULT_AI_RESPONSE_LANGUAGE,
|
|
11
|
-
getAiResponseLanguagePromptName
|
|
12
|
-
normalizeAiResponseLanguage
|
|
8
|
+
getAiResponseLanguagePromptName
|
|
13
9
|
} from "@tradejs/core/aiLanguages";
|
|
14
|
-
import { normalizeAiEndpoint } from "@tradejs/core/aiEndpoints";
|
|
15
|
-
import { normalizeAiModel } from "@tradejs/core/aiModels";
|
|
16
10
|
import { setData, redisKeys } from "@tradejs/infra/redis";
|
|
17
|
-
import {
|
|
18
|
-
getUserSettings
|
|
19
|
-
} from "@tradejs/infra/userSettings";
|
|
20
11
|
|
|
21
12
|
// src/aiShared.ts
|
|
22
13
|
var MAX_AI_SERIES_POINTS = 5;
|
|
@@ -590,266 +581,6 @@ var buildAiMarketContext = (signal) => ({
|
|
|
590
581
|
}
|
|
591
582
|
});
|
|
592
583
|
|
|
593
|
-
// src/strategy/manifests.ts
|
|
594
|
-
import {
|
|
595
|
-
registerIndicatorEntries,
|
|
596
|
-
resetIndicatorRegistryCache
|
|
597
|
-
} from "@tradejs/core/indicators";
|
|
598
|
-
import { logger } from "@tradejs/infra/logger";
|
|
599
|
-
var SHARED_STRATEGY_REGISTRY_KEY = "__tradejsNodeSharedStrategyRegistryV1__";
|
|
600
|
-
var sharedRegistryScope = globalThis;
|
|
601
|
-
var sharedStrategyRegistry = sharedRegistryScope[SHARED_STRATEGY_REGISTRY_KEY] ?? (sharedRegistryScope[SHARED_STRATEGY_REGISTRY_KEY] = {
|
|
602
|
-
registryStateByProjectRoot: /* @__PURE__ */ new Map()
|
|
603
|
-
});
|
|
604
|
-
var createStrategyRegistryState = () => ({
|
|
605
|
-
strategyCreators: /* @__PURE__ */ new Map(),
|
|
606
|
-
strategyManifestsMap: /* @__PURE__ */ new Map(),
|
|
607
|
-
strategyEntriesMap: /* @__PURE__ */ new Map(),
|
|
608
|
-
pluginsLoadPromise: null
|
|
609
|
-
});
|
|
610
|
-
var registryStateByProjectRoot = sharedStrategyRegistry.registryStateByProjectRoot;
|
|
611
|
-
var getStrategyRegistryState = (cwd = getTradejsProjectCwd()) => {
|
|
612
|
-
const projectRoot = getTradejsProjectCwd(cwd);
|
|
613
|
-
let state = registryStateByProjectRoot.get(projectRoot);
|
|
614
|
-
if (!state) {
|
|
615
|
-
state = createStrategyRegistryState();
|
|
616
|
-
registryStateByProjectRoot.set(projectRoot, state);
|
|
617
|
-
}
|
|
618
|
-
return {
|
|
619
|
-
projectRoot,
|
|
620
|
-
state
|
|
621
|
-
};
|
|
622
|
-
};
|
|
623
|
-
var toUniqueModules = (modules = []) => [
|
|
624
|
-
...new Set(modules.map((moduleName) => moduleName.trim()).filter(Boolean))
|
|
625
|
-
];
|
|
626
|
-
var getConfiguredPluginModuleNames = async (cwd = getTradejsProjectCwd()) => {
|
|
627
|
-
const config = await loadTradejsConfig(cwd);
|
|
628
|
-
return {
|
|
629
|
-
strategyModules: toUniqueModules(config.strategies),
|
|
630
|
-
indicatorModules: toUniqueModules(config.indicators)
|
|
631
|
-
};
|
|
632
|
-
};
|
|
633
|
-
var extractModuleEntries = (moduleExport, key) => {
|
|
634
|
-
if (!moduleExport || typeof moduleExport !== "object") {
|
|
635
|
-
return null;
|
|
636
|
-
}
|
|
637
|
-
const candidate = moduleExport;
|
|
638
|
-
if (Array.isArray(candidate[key])) {
|
|
639
|
-
return candidate[key];
|
|
640
|
-
}
|
|
641
|
-
const defaultExport = candidate.default;
|
|
642
|
-
if (defaultExport && Array.isArray(defaultExport[key])) {
|
|
643
|
-
return defaultExport[key];
|
|
644
|
-
}
|
|
645
|
-
return null;
|
|
646
|
-
};
|
|
647
|
-
var extractStrategyPluginDefinition = (moduleExport) => {
|
|
648
|
-
const strategyEntries = extractModuleEntries(
|
|
649
|
-
moduleExport,
|
|
650
|
-
"strategyEntries"
|
|
651
|
-
);
|
|
652
|
-
return strategyEntries ? { strategyEntries } : null;
|
|
653
|
-
};
|
|
654
|
-
var extractIndicatorPluginDefinition = (moduleExport) => {
|
|
655
|
-
const indicatorEntries = extractModuleEntries(
|
|
656
|
-
moduleExport,
|
|
657
|
-
"indicatorEntries"
|
|
658
|
-
);
|
|
659
|
-
return indicatorEntries ? { indicatorEntries } : null;
|
|
660
|
-
};
|
|
661
|
-
var registerEntries = (entries, source, state) => {
|
|
662
|
-
for (const entry of entries) {
|
|
663
|
-
const strategyName = entry.manifest?.name;
|
|
664
|
-
if (!strategyName) {
|
|
665
|
-
logger.warn("Skip strategy entry without name from %s", source);
|
|
666
|
-
continue;
|
|
667
|
-
}
|
|
668
|
-
if (state.strategyCreators.has(strategyName)) {
|
|
669
|
-
logger.warn(
|
|
670
|
-
'Skip duplicate strategy "%s" from %s: already registered',
|
|
671
|
-
strategyName,
|
|
672
|
-
source
|
|
673
|
-
);
|
|
674
|
-
continue;
|
|
675
|
-
}
|
|
676
|
-
state.strategyManifestsMap.set(strategyName, entry.manifest);
|
|
677
|
-
state.strategyEntriesMap.set(strategyName, entry);
|
|
678
|
-
materializeStrategyCreator(strategyName, state);
|
|
679
|
-
}
|
|
680
|
-
};
|
|
681
|
-
var materializeStrategyCreator = (strategyName, state) => {
|
|
682
|
-
if (state.strategyCreators.has(strategyName) || !sharedStrategyRegistry.strategyRuntimeFactory) {
|
|
683
|
-
return;
|
|
684
|
-
}
|
|
685
|
-
const entry = state.strategyEntriesMap.get(strategyName);
|
|
686
|
-
if (!entry) return;
|
|
687
|
-
state.strategyCreators.set(
|
|
688
|
-
strategyName,
|
|
689
|
-
sharedStrategyRegistry.strategyRuntimeFactory({
|
|
690
|
-
strategyName,
|
|
691
|
-
defaults: entry.defaults,
|
|
692
|
-
createCore: entry.createCore,
|
|
693
|
-
manifest: entry.manifest,
|
|
694
|
-
detectorKey: entry.detectorKey,
|
|
695
|
-
detectorNoSignalSkipReason: entry.detectorNoSignalSkipReason,
|
|
696
|
-
resolveRegisteredManifest: (name) => state.strategyManifestsMap.get(name)
|
|
697
|
-
})
|
|
698
|
-
);
|
|
699
|
-
};
|
|
700
|
-
var setStrategyRuntimeFactory = (factory) => {
|
|
701
|
-
sharedStrategyRegistry.strategyRuntimeFactory = factory;
|
|
702
|
-
for (const state of registryStateByProjectRoot.values()) {
|
|
703
|
-
for (const strategyName of state.strategyEntriesMap.keys()) {
|
|
704
|
-
materializeStrategyCreator(strategyName, state);
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
};
|
|
708
|
-
var importStrategyPluginModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
|
|
709
|
-
if (typeof importTradejsModule === "function") {
|
|
710
|
-
return importTradejsModule(moduleName, cwd);
|
|
711
|
-
}
|
|
712
|
-
return import(
|
|
713
|
-
/* webpackIgnore: true */
|
|
714
|
-
moduleName
|
|
715
|
-
);
|
|
716
|
-
};
|
|
717
|
-
var ensureStrategyPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
|
|
718
|
-
const { projectRoot, state } = getStrategyRegistryState(cwd);
|
|
719
|
-
if (!state.pluginsLoadPromise) {
|
|
720
|
-
resetIndicatorRegistryCache(projectRoot);
|
|
721
|
-
state.pluginsLoadPromise = (async () => {
|
|
722
|
-
const { strategyModules, indicatorModules } = await getConfiguredPluginModuleNames(projectRoot);
|
|
723
|
-
const strategySet = new Set(strategyModules);
|
|
724
|
-
const indicatorSet = new Set(indicatorModules);
|
|
725
|
-
const pluginModuleNames = [
|
|
726
|
-
.../* @__PURE__ */ new Set([...strategyModules, ...indicatorModules])
|
|
727
|
-
];
|
|
728
|
-
if (!pluginModuleNames.length) {
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
731
|
-
for (const moduleName of pluginModuleNames) {
|
|
732
|
-
try {
|
|
733
|
-
const resolvedModuleName = resolvePluginModuleSpecifier(
|
|
734
|
-
moduleName,
|
|
735
|
-
projectRoot
|
|
736
|
-
);
|
|
737
|
-
const moduleExport = await importStrategyPluginModule(
|
|
738
|
-
resolvedModuleName,
|
|
739
|
-
projectRoot
|
|
740
|
-
);
|
|
741
|
-
if (strategySet.has(moduleName)) {
|
|
742
|
-
const pluginDefinition = extractStrategyPluginDefinition(moduleExport);
|
|
743
|
-
if (!pluginDefinition) {
|
|
744
|
-
logger.warn(
|
|
745
|
-
'Skip strategy plugin "%s": export { strategyEntries } is missing',
|
|
746
|
-
moduleName
|
|
747
|
-
);
|
|
748
|
-
} else {
|
|
749
|
-
registerEntries(
|
|
750
|
-
pluginDefinition.strategyEntries,
|
|
751
|
-
moduleName,
|
|
752
|
-
state
|
|
753
|
-
);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
if (indicatorSet.has(moduleName)) {
|
|
757
|
-
const indicatorPluginDefinition = extractIndicatorPluginDefinition(moduleExport);
|
|
758
|
-
if (!indicatorPluginDefinition) {
|
|
759
|
-
logger.warn(
|
|
760
|
-
'Skip indicator plugin "%s": export { indicatorEntries } is missing',
|
|
761
|
-
moduleName
|
|
762
|
-
);
|
|
763
|
-
} else {
|
|
764
|
-
registerIndicatorEntries(
|
|
765
|
-
indicatorPluginDefinition.indicatorEntries,
|
|
766
|
-
moduleName,
|
|
767
|
-
projectRoot
|
|
768
|
-
);
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
if (!strategySet.has(moduleName) && !indicatorSet.has(moduleName)) {
|
|
772
|
-
logger.warn(
|
|
773
|
-
'Skip plugin "%s": no strategy/indicator sections requested in config',
|
|
774
|
-
moduleName
|
|
775
|
-
);
|
|
776
|
-
}
|
|
777
|
-
} catch (error) {
|
|
778
|
-
logger.warn(
|
|
779
|
-
'Failed to load plugin "%s": %s',
|
|
780
|
-
moduleName,
|
|
781
|
-
String(error)
|
|
782
|
-
);
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
})();
|
|
786
|
-
}
|
|
787
|
-
await state.pluginsLoadPromise;
|
|
788
|
-
};
|
|
789
|
-
var ensureIndicatorPluginsLoaded = async (cwd = getTradejsProjectCwd()) => ensureStrategyPluginsLoaded(cwd);
|
|
790
|
-
var getStrategyCreator = async (name, cwd = getTradejsProjectCwd()) => {
|
|
791
|
-
await ensureStrategyPluginsLoaded(cwd);
|
|
792
|
-
const { state } = getStrategyRegistryState(cwd);
|
|
793
|
-
return state.strategyCreators.get(name);
|
|
794
|
-
};
|
|
795
|
-
var getAvailableStrategyNames = async (cwd = getTradejsProjectCwd()) => {
|
|
796
|
-
await ensureStrategyPluginsLoaded(cwd);
|
|
797
|
-
const { state } = getStrategyRegistryState(cwd);
|
|
798
|
-
return [...state.strategyCreators.keys()].sort((a, b) => a.localeCompare(b));
|
|
799
|
-
};
|
|
800
|
-
var getRegisteredStrategies = (cwd = getTradejsProjectCwd()) => {
|
|
801
|
-
const { state } = getStrategyRegistryState(cwd);
|
|
802
|
-
return Object.fromEntries(state.strategyCreators.entries());
|
|
803
|
-
};
|
|
804
|
-
var getRegisteredManifests = (cwd = getTradejsProjectCwd()) => {
|
|
805
|
-
const { state } = getStrategyRegistryState(cwd);
|
|
806
|
-
return [...state.strategyManifestsMap.values()];
|
|
807
|
-
};
|
|
808
|
-
var getStrategyManifest = (name, cwd = getTradejsProjectCwd()) => {
|
|
809
|
-
if (!name) {
|
|
810
|
-
return void 0;
|
|
811
|
-
}
|
|
812
|
-
const { state } = getStrategyRegistryState(cwd);
|
|
813
|
-
return state.strategyManifestsMap.get(name);
|
|
814
|
-
};
|
|
815
|
-
var isKnownStrategy = (name, cwd = getTradejsProjectCwd()) => {
|
|
816
|
-
const { state } = getStrategyRegistryState(cwd);
|
|
817
|
-
return state.strategyCreators.has(name);
|
|
818
|
-
};
|
|
819
|
-
var registerStrategyEntries = (entries, cwd = getTradejsProjectCwd()) => {
|
|
820
|
-
const { state } = getStrategyRegistryState(cwd);
|
|
821
|
-
registerEntries(entries, "runtime", state);
|
|
822
|
-
};
|
|
823
|
-
var resetStrategyRegistryCache = (cwd) => {
|
|
824
|
-
const normalizedCwd = String(cwd ?? "").trim();
|
|
825
|
-
if (!normalizedCwd) {
|
|
826
|
-
registryStateByProjectRoot.clear();
|
|
827
|
-
resetIndicatorRegistryCache();
|
|
828
|
-
return;
|
|
829
|
-
}
|
|
830
|
-
const projectRoot = getTradejsProjectCwd(normalizedCwd);
|
|
831
|
-
registryStateByProjectRoot.delete(projectRoot);
|
|
832
|
-
resetIndicatorRegistryCache(projectRoot);
|
|
833
|
-
};
|
|
834
|
-
var strategies = new Proxy(
|
|
835
|
-
{},
|
|
836
|
-
{
|
|
837
|
-
get: (_target, property) => {
|
|
838
|
-
if (typeof property !== "string") {
|
|
839
|
-
return void 0;
|
|
840
|
-
}
|
|
841
|
-
return getStrategyRegistryState().state.strategyCreators.get(property);
|
|
842
|
-
},
|
|
843
|
-
ownKeys: () => {
|
|
844
|
-
return [...getStrategyRegistryState().state.strategyCreators.keys()];
|
|
845
|
-
},
|
|
846
|
-
getOwnPropertyDescriptor: () => ({
|
|
847
|
-
enumerable: true,
|
|
848
|
-
configurable: true
|
|
849
|
-
})
|
|
850
|
-
}
|
|
851
|
-
);
|
|
852
|
-
|
|
853
584
|
// src/strategy/policyProfiles.ts
|
|
854
585
|
var profileMatches = (profile, universe, assetClass) => {
|
|
855
586
|
const { appliesTo } = profile;
|
|
@@ -974,7 +705,151 @@ var postProcessLocalAiAnalysisByStrategy = (signal, analysis, payload = buildAiP
|
|
|
974
705
|
}) ?? strategyAnalysis;
|
|
975
706
|
};
|
|
976
707
|
|
|
708
|
+
// src/aiProvider.ts
|
|
709
|
+
import { normalizeAiEndpoint } from "@tradejs/core/aiEndpoints";
|
|
710
|
+
import { normalizeAiModel } from "@tradejs/core/aiModels";
|
|
711
|
+
import { normalizeAiResponseLanguage } from "@tradejs/core/aiLanguages";
|
|
712
|
+
import {
|
|
713
|
+
getUserSettings
|
|
714
|
+
} from "@tradejs/infra/userSettings";
|
|
715
|
+
var DEFAULT_AI_MODEL = "openai/gpt-5-mini";
|
|
716
|
+
var userSettingsCache = /* @__PURE__ */ new Map();
|
|
717
|
+
var aiModelCache = /* @__PURE__ */ new Map();
|
|
718
|
+
var normalizeResponseContent = (content) => {
|
|
719
|
+
if (typeof content === "string") return content;
|
|
720
|
+
if (content && typeof content === "object" && !Array.isArray(content)) {
|
|
721
|
+
return content;
|
|
722
|
+
}
|
|
723
|
+
if (Array.isArray(content)) {
|
|
724
|
+
return content.map(
|
|
725
|
+
(part) => typeof part?.text === "string" ? part.text : ""
|
|
726
|
+
).join("\n").trim();
|
|
727
|
+
}
|
|
728
|
+
return String(content ?? "");
|
|
729
|
+
};
|
|
730
|
+
var getAiInvocationError = (error) => {
|
|
731
|
+
const details = error instanceof Error && error.message.trim() ? error.message.trim() : String(error);
|
|
732
|
+
const isEmptyCompletion = error instanceof TypeError && /Cannot read properties of undefined \(reading ['"]message['"]\)/.test(
|
|
733
|
+
details
|
|
734
|
+
);
|
|
735
|
+
const wrapped = new Error(
|
|
736
|
+
isEmptyCompletion ? "AI provider returned an empty chat completion" : `AI model invocation failed: ${details}`
|
|
737
|
+
);
|
|
738
|
+
wrapped.cause = error;
|
|
739
|
+
return wrapped;
|
|
740
|
+
};
|
|
741
|
+
var isEmptyResponseContent = (content) => typeof content === "string" ? content.trim().length === 0 : Object.keys(content).length === 0;
|
|
742
|
+
var getAiModelCacheKey = (userName, modelName, temperature) => `${userName}::${modelName}::${temperature}`;
|
|
743
|
+
var resolveAiModelName = (settings, requestedModelName) => {
|
|
744
|
+
const explicitModelName = requestedModelName?.trim() ?? "";
|
|
745
|
+
if (explicitModelName) return explicitModelName;
|
|
746
|
+
return settings.AI_MODEL?.trim() || DEFAULT_AI_MODEL;
|
|
747
|
+
};
|
|
748
|
+
var getOpenRouterModelKwargs = (apiEndpoint) => {
|
|
749
|
+
const endpoint = String(apiEndpoint ?? "").trim();
|
|
750
|
+
if (!endpoint) return {};
|
|
751
|
+
let hostname = "";
|
|
752
|
+
try {
|
|
753
|
+
hostname = new URL(endpoint).hostname;
|
|
754
|
+
} catch {
|
|
755
|
+
hostname = endpoint;
|
|
756
|
+
}
|
|
757
|
+
return hostname.toLowerCase().includes("openrouter") ? { provider: { ignore: ["azure"] } } : {};
|
|
758
|
+
};
|
|
759
|
+
var getAiUserSettings = async (userName = "root") => {
|
|
760
|
+
let settingsPromise = userSettingsCache.get(userName);
|
|
761
|
+
if (!settingsPromise) {
|
|
762
|
+
settingsPromise = getUserSettings(userName).then((settings2) => {
|
|
763
|
+
const endpoint = normalizeAiEndpoint(settings2.AI_API_ENDPOINT);
|
|
764
|
+
return {
|
|
765
|
+
...settings2,
|
|
766
|
+
AI_API_ENDPOINT: endpoint,
|
|
767
|
+
AI_MODEL: normalizeAiModel(settings2.AI_MODEL, endpoint),
|
|
768
|
+
AI_RESPONSE_LANGUAGE: normalizeAiResponseLanguage(
|
|
769
|
+
settings2.AI_RESPONSE_LANGUAGE
|
|
770
|
+
)
|
|
771
|
+
};
|
|
772
|
+
});
|
|
773
|
+
settingsPromise.catch(() => userSettingsCache.delete(userName));
|
|
774
|
+
userSettingsCache.set(userName, settingsPromise);
|
|
775
|
+
}
|
|
776
|
+
const settings = await settingsPromise;
|
|
777
|
+
if (!settings.AI_API_KEY || !settings.AI_API_ENDPOINT) {
|
|
778
|
+
throw new Error(`AI settings are incomplete for user ${userName}`);
|
|
779
|
+
}
|
|
780
|
+
return settings;
|
|
781
|
+
};
|
|
782
|
+
var getAiModel = async (userName = "root", requestedModelName, temperature = 0.2) => {
|
|
783
|
+
const settings = await getAiUserSettings(userName);
|
|
784
|
+
const modelName = resolveAiModelName(settings, requestedModelName);
|
|
785
|
+
const cacheKey = getAiModelCacheKey(userName, modelName, temperature);
|
|
786
|
+
let modelPromise = aiModelCache.get(cacheKey);
|
|
787
|
+
if (!modelPromise) {
|
|
788
|
+
modelPromise = import("@langchain/openai").then(({ ChatOpenAI }) => {
|
|
789
|
+
const modelKwargs = getOpenRouterModelKwargs(settings.AI_API_ENDPOINT);
|
|
790
|
+
return new ChatOpenAI({
|
|
791
|
+
temperature,
|
|
792
|
+
modelName,
|
|
793
|
+
apiKey: settings.AI_API_KEY,
|
|
794
|
+
...Object.keys(modelKwargs).length ? { modelKwargs } : {},
|
|
795
|
+
configuration: {
|
|
796
|
+
baseURL: settings.AI_API_ENDPOINT,
|
|
797
|
+
defaultHeaders: {
|
|
798
|
+
"HTTP-Referer": "https://tradejs.dev",
|
|
799
|
+
"X-Title": "Inv"
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
});
|
|
804
|
+
modelPromise.catch(() => aiModelCache.delete(cacheKey));
|
|
805
|
+
aiModelCache.set(cacheKey, modelPromise);
|
|
806
|
+
}
|
|
807
|
+
try {
|
|
808
|
+
return await modelPromise;
|
|
809
|
+
} catch (error) {
|
|
810
|
+
aiModelCache.delete(cacheKey);
|
|
811
|
+
userSettingsCache.delete(userName);
|
|
812
|
+
throw error;
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
var resetAiRuntimeCache = () => {
|
|
816
|
+
aiModelCache.clear();
|
|
817
|
+
userSettingsCache.clear();
|
|
818
|
+
};
|
|
819
|
+
var invokeAiChatWithUserMessageEncoding = async ({
|
|
820
|
+
messages,
|
|
821
|
+
userName = "root",
|
|
822
|
+
model,
|
|
823
|
+
temperature = 0.2
|
|
824
|
+
}, resolveUserMessageEncoding) => {
|
|
825
|
+
const [{ HumanMessage, SystemMessage }, aiModel] = await Promise.all([
|
|
826
|
+
import("@langchain/core/messages"),
|
|
827
|
+
getAiModel(userName, model, temperature)
|
|
828
|
+
]);
|
|
829
|
+
const providerMessages = messages.map(
|
|
830
|
+
(message) => message.role === "system" ? new SystemMessage(message.content) : new HumanMessage(
|
|
831
|
+
resolveUserMessageEncoding(message) === "text-block" ? { content: [{ type: "text", text: message.content }] } : message.content
|
|
832
|
+
)
|
|
833
|
+
);
|
|
834
|
+
try {
|
|
835
|
+
const response = await aiModel.invoke(providerMessages);
|
|
836
|
+
const content = normalizeResponseContent(response?.content);
|
|
837
|
+
if (isEmptyResponseContent(content)) {
|
|
838
|
+
throw new Error("AI provider returned an empty chat completion");
|
|
839
|
+
}
|
|
840
|
+
return { content };
|
|
841
|
+
} catch (error) {
|
|
842
|
+
throw getAiInvocationError(error);
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
var invokeAiPromptChat = (options) => invokeAiChatWithUserMessageEncoding(options, () => "text-block");
|
|
846
|
+
var invokeCompatibleAiChat = (options) => invokeAiChatWithUserMessageEncoding(
|
|
847
|
+
options,
|
|
848
|
+
(message) => message.format ?? "plain"
|
|
849
|
+
);
|
|
850
|
+
|
|
977
851
|
// src/ai.ts
|
|
852
|
+
var invokeAiChat = (options) => invokeCompatibleAiChat(options);
|
|
978
853
|
var parseAIResponse = (input) => {
|
|
979
854
|
try {
|
|
980
855
|
if (typeof input === "object" && input !== null) return input;
|
|
@@ -987,18 +862,6 @@ var parseAIResponse = (input) => {
|
|
|
987
862
|
return {};
|
|
988
863
|
}
|
|
989
864
|
};
|
|
990
|
-
var normalizeResponseContent = (content) => {
|
|
991
|
-
if (typeof content === "string" || content && typeof content === "object") {
|
|
992
|
-
if (typeof content !== "object" || !Array.isArray(content)) {
|
|
993
|
-
return content;
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
if (Array.isArray(content)) {
|
|
997
|
-
const text = content.map((part) => typeof part?.text === "string" ? part.text : "").join("\n").trim();
|
|
998
|
-
return text;
|
|
999
|
-
}
|
|
1000
|
-
return String(content ?? "");
|
|
1001
|
-
};
|
|
1002
865
|
var normalizeAnalysis = (raw) => {
|
|
1003
866
|
const direction = raw?.direction === "LONG" || raw?.direction === "SHORT" ? raw.direction : null;
|
|
1004
867
|
const qualityNum = typeof raw?.quality === "number" ? Math.max(1, Math.min(5, Math.round(raw.quality))) : void 0;
|
|
@@ -1232,120 +1095,6 @@ Trade payload:
|
|
|
1232
1095
|
${JSON.stringify(payload)}
|
|
1233
1096
|
${buildAiHumanPromptAddonByStrategy(signal, payload)}
|
|
1234
1097
|
`;
|
|
1235
|
-
var getAiInvocationError = (error) => {
|
|
1236
|
-
const details = error instanceof Error && error.message.trim() ? error.message.trim() : String(error);
|
|
1237
|
-
const isEmptyCompletion = error instanceof TypeError && /Cannot read properties of undefined \(reading ['"]message['"]\)/.test(
|
|
1238
|
-
details
|
|
1239
|
-
);
|
|
1240
|
-
const wrapped = new Error(
|
|
1241
|
-
isEmptyCompletion ? "AI provider returned an empty chat completion" : `AI model invocation failed: ${details}`
|
|
1242
|
-
);
|
|
1243
|
-
wrapped.cause = error;
|
|
1244
|
-
return wrapped;
|
|
1245
|
-
};
|
|
1246
|
-
var isEmptyResponseContent = (content) => typeof content === "string" ? content.trim().length === 0 : Object.keys(content).length === 0;
|
|
1247
|
-
var DEFAULT_AI_MODEL = "openai/gpt-5-mini";
|
|
1248
|
-
var userSettingsCache = /* @__PURE__ */ new Map();
|
|
1249
|
-
var aiModelCache = /* @__PURE__ */ new Map();
|
|
1250
|
-
var getAiModelCacheKey = (userName, modelName) => `${userName}::${modelName}`;
|
|
1251
|
-
var resolveAiModelName = (settings, requestedModelName) => {
|
|
1252
|
-
const explicitModelName = typeof requestedModelName === "string" ? requestedModelName.trim() : "";
|
|
1253
|
-
if (explicitModelName) {
|
|
1254
|
-
return explicitModelName;
|
|
1255
|
-
}
|
|
1256
|
-
const settingsModelName = typeof settings.AI_MODEL === "string" ? settings.AI_MODEL.trim() : "";
|
|
1257
|
-
return settingsModelName || DEFAULT_AI_MODEL;
|
|
1258
|
-
};
|
|
1259
|
-
var getOpenRouterModelKwargs = (apiEndpoint) => {
|
|
1260
|
-
const endpoint = String(apiEndpoint ?? "").trim();
|
|
1261
|
-
if (!endpoint) {
|
|
1262
|
-
return {};
|
|
1263
|
-
}
|
|
1264
|
-
let hostname = "";
|
|
1265
|
-
try {
|
|
1266
|
-
hostname = new URL(endpoint).hostname;
|
|
1267
|
-
} catch {
|
|
1268
|
-
hostname = endpoint;
|
|
1269
|
-
}
|
|
1270
|
-
if (!hostname.toLowerCase().includes("openrouter")) {
|
|
1271
|
-
return {};
|
|
1272
|
-
}
|
|
1273
|
-
return {
|
|
1274
|
-
provider: {
|
|
1275
|
-
ignore: ["azure"]
|
|
1276
|
-
}
|
|
1277
|
-
};
|
|
1278
|
-
};
|
|
1279
|
-
var getAiSettings = async (userName = "root") => {
|
|
1280
|
-
let settingsPromise = userSettingsCache.get(userName);
|
|
1281
|
-
if (!settingsPromise) {
|
|
1282
|
-
settingsPromise = getUserSettings(userName).then((settings2) => {
|
|
1283
|
-
const endpoint = normalizeAiEndpoint(settings2.AI_API_ENDPOINT);
|
|
1284
|
-
return {
|
|
1285
|
-
...settings2,
|
|
1286
|
-
AI_API_ENDPOINT: endpoint,
|
|
1287
|
-
AI_MODEL: normalizeAiModel(settings2.AI_MODEL, endpoint),
|
|
1288
|
-
AI_RESPONSE_LANGUAGE: normalizeAiResponseLanguage(
|
|
1289
|
-
settings2.AI_RESPONSE_LANGUAGE
|
|
1290
|
-
)
|
|
1291
|
-
};
|
|
1292
|
-
});
|
|
1293
|
-
settingsPromise.catch(() => {
|
|
1294
|
-
userSettingsCache.delete(userName);
|
|
1295
|
-
});
|
|
1296
|
-
userSettingsCache.set(userName, settingsPromise);
|
|
1297
|
-
}
|
|
1298
|
-
const settings = await settingsPromise;
|
|
1299
|
-
if (!settings.AI_API_KEY || !settings.AI_API_ENDPOINT) {
|
|
1300
|
-
throw new Error(`AI settings are incomplete for user ${userName}`);
|
|
1301
|
-
}
|
|
1302
|
-
return settings;
|
|
1303
|
-
};
|
|
1304
|
-
var createAiModel = async (userName = "root", requestedModelName) => {
|
|
1305
|
-
const settings = await getAiSettings(userName);
|
|
1306
|
-
const modelName = resolveAiModelName(settings, requestedModelName);
|
|
1307
|
-
const cacheKey = getAiModelCacheKey(userName, modelName);
|
|
1308
|
-
let modelPromise = aiModelCache.get(cacheKey);
|
|
1309
|
-
if (!modelPromise) {
|
|
1310
|
-
modelPromise = (async () => {
|
|
1311
|
-
const { ChatOpenAI } = await import("@langchain/openai");
|
|
1312
|
-
const modelKwargs = getOpenRouterModelKwargs(settings.AI_API_ENDPOINT);
|
|
1313
|
-
return new ChatOpenAI({
|
|
1314
|
-
temperature: 0.2,
|
|
1315
|
-
modelName,
|
|
1316
|
-
apiKey: settings.AI_API_KEY,
|
|
1317
|
-
...Object.keys(modelKwargs).length ? { modelKwargs } : {},
|
|
1318
|
-
configuration: {
|
|
1319
|
-
baseURL: settings.AI_API_ENDPOINT,
|
|
1320
|
-
defaultHeaders: {
|
|
1321
|
-
"HTTP-Referer": "https://tradejs.dev",
|
|
1322
|
-
"X-Title": "Inv"
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
});
|
|
1326
|
-
})();
|
|
1327
|
-
modelPromise.catch(() => {
|
|
1328
|
-
aiModelCache.delete(cacheKey);
|
|
1329
|
-
});
|
|
1330
|
-
aiModelCache.set(cacheKey, modelPromise);
|
|
1331
|
-
}
|
|
1332
|
-
return modelPromise;
|
|
1333
|
-
};
|
|
1334
|
-
var getAiModel = async (userName = "root", requestedModelName) => {
|
|
1335
|
-
const settings = await getAiSettings(userName);
|
|
1336
|
-
const resolvedModelName = resolveAiModelName(settings, requestedModelName);
|
|
1337
|
-
try {
|
|
1338
|
-
return await createAiModel(userName, resolvedModelName);
|
|
1339
|
-
} catch (error) {
|
|
1340
|
-
aiModelCache.delete(getAiModelCacheKey(userName, resolvedModelName));
|
|
1341
|
-
userSettingsCache.delete(userName);
|
|
1342
|
-
throw error;
|
|
1343
|
-
}
|
|
1344
|
-
};
|
|
1345
|
-
var resetAiRuntimeCache = () => {
|
|
1346
|
-
aiModelCache.clear();
|
|
1347
|
-
userSettingsCache.clear();
|
|
1348
|
-
};
|
|
1349
1098
|
var buildAiPrompts = (signal) => {
|
|
1350
1099
|
const payload = buildAiPayload(signal);
|
|
1351
1100
|
return {
|
|
@@ -1354,42 +1103,23 @@ var buildAiPrompts = (signal) => {
|
|
|
1354
1103
|
};
|
|
1355
1104
|
};
|
|
1356
1105
|
var runAiPrompt = async ({ systemPrompt, humanPrompt }, options = {}) => {
|
|
1357
|
-
const
|
|
1358
|
-
import("@langchain/core/messages"),
|
|
1359
|
-
getAiModel(options.userName, options.model),
|
|
1360
|
-
getAiSettings(options.userName)
|
|
1361
|
-
]);
|
|
1362
|
-
const messages = [];
|
|
1106
|
+
const settings = await getAiUserSettings(options.userName);
|
|
1363
1107
|
const responseLanguage = getAiResponseLanguagePromptName(
|
|
1364
1108
|
settings.AI_RESPONSE_LANGUAGE || DEFAULT_AI_RESPONSE_LANGUAGE
|
|
1365
1109
|
);
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
]
|
|
1380
|
-
})
|
|
1381
|
-
);
|
|
1382
|
-
let response;
|
|
1383
|
-
try {
|
|
1384
|
-
response = await model.invoke(messages);
|
|
1385
|
-
} catch (error) {
|
|
1386
|
-
throw getAiInvocationError(error);
|
|
1387
|
-
}
|
|
1388
|
-
const responseContent = normalizeResponseContent(response?.content);
|
|
1389
|
-
if (isEmptyResponseContent(responseContent)) {
|
|
1390
|
-
throw new Error("AI provider returned an empty chat completion");
|
|
1391
|
-
}
|
|
1392
|
-
const parsed = parseAIResponse(responseContent);
|
|
1110
|
+
const response = await invokeAiPromptChat({
|
|
1111
|
+
userName: options.userName,
|
|
1112
|
+
model: options.model,
|
|
1113
|
+
messages: [
|
|
1114
|
+
{ role: "system", content: systemPrompt },
|
|
1115
|
+
{
|
|
1116
|
+
role: "system",
|
|
1117
|
+
content: `Write all user-visible text fields in ${responseLanguage}. Keep field names and JSON syntax unchanged.`
|
|
1118
|
+
},
|
|
1119
|
+
{ role: "user", content: humanPrompt }
|
|
1120
|
+
]
|
|
1121
|
+
});
|
|
1122
|
+
const parsed = parseAIResponse(response.content);
|
|
1393
1123
|
const normalized = normalizeAnalysis(parsed);
|
|
1394
1124
|
if (!options.signal) {
|
|
1395
1125
|
return normalized;
|
|
@@ -1441,27 +1171,16 @@ export {
|
|
|
1441
1171
|
MAX_AI_SERIES_POINTS,
|
|
1442
1172
|
trimSeriesDeep,
|
|
1443
1173
|
buildCompactAiIndicatorsSnapshot,
|
|
1444
|
-
setStrategyRuntimeFactory,
|
|
1445
|
-
ensureStrategyPluginsLoaded,
|
|
1446
|
-
ensureIndicatorPluginsLoaded,
|
|
1447
|
-
getStrategyCreator,
|
|
1448
|
-
getAvailableStrategyNames,
|
|
1449
|
-
getRegisteredStrategies,
|
|
1450
|
-
getRegisteredManifests,
|
|
1451
|
-
getStrategyManifest,
|
|
1452
|
-
isKnownStrategy,
|
|
1453
|
-
registerStrategyEntries,
|
|
1454
|
-
resetStrategyRegistryCache,
|
|
1455
|
-
strategies,
|
|
1456
1174
|
resolveStrategyPolicyProfile,
|
|
1457
1175
|
getStrategyProfileMlAdapter,
|
|
1176
|
+
DEFAULT_AI_MODEL,
|
|
1177
|
+
getOpenRouterModelKwargs,
|
|
1178
|
+
resetAiRuntimeCache,
|
|
1179
|
+
invokeAiChat,
|
|
1458
1180
|
buildAiSystemPrompt,
|
|
1459
1181
|
buildAiPayload,
|
|
1460
1182
|
getDeterministicAiGateContext,
|
|
1461
1183
|
buildAiHumanPrompt,
|
|
1462
|
-
DEFAULT_AI_MODEL,
|
|
1463
|
-
getOpenRouterModelKwargs,
|
|
1464
|
-
resetAiRuntimeCache,
|
|
1465
1184
|
buildAiPrompts,
|
|
1466
1185
|
runAiPrompt,
|
|
1467
1186
|
runAiPromptLocal,
|