@tradejs/node 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai.d.mts +31 -0
- package/dist/ai.d.ts +31 -0
- package/dist/ai.js +926 -0
- package/dist/ai.mjs +35 -0
- package/dist/backtest.js +408 -5687
- package/dist/backtest.mjs +331 -175
- package/dist/chunk-72FKXJ2I.mjs +473 -0
- package/dist/{chunk-P2ZUWONT.mjs → chunk-CGJ2UU6H.mjs} +1 -1
- package/dist/{chunk-ZY6ULOWK.mjs → chunk-EOZSJKUM.mjs} +1 -1
- package/dist/{chunk-7ICOZAKA.mjs → chunk-H6LIYHU4.mjs} +29 -11
- package/dist/{chunk-RBE4PZER.mjs → chunk-PQH5PAFC.mjs} +1 -1
- package/dist/{chunk-CIY64D57.mjs → chunk-XW5L327F.mjs} +1 -1
- package/dist/cli.d.mts +4 -4
- package/dist/cli.d.ts +4 -4
- package/dist/cli.js +711 -125
- package/dist/cli.mjs +199 -71
- package/dist/connectors.js +1 -1
- package/dist/connectors.mjs +2 -2
- package/dist/pine.d.mts +10 -6
- package/dist/pine.d.ts +10 -6
- package/dist/pine.js +40 -18
- package/dist/pine.mjs +21 -13
- package/dist/registry.js +1 -1
- package/dist/registry.mjs +2 -2
- package/dist/strategies.d.mts +6 -12
- package/dist/strategies.d.ts +6 -12
- package/dist/strategies.js +426 -83
- package/dist/strategies.mjs +200 -34
- package/package.json +9 -4
- package/dist/ai-G7ATN4YL.mjs +0 -19
- package/dist/chunk-LMAKIC3C.mjs +0 -294
package/dist/cli.js
CHANGED
|
@@ -2309,7 +2309,7 @@ var require_lodash = __commonJS({
|
|
|
2309
2309
|
index = wrapper ? index : length;
|
|
2310
2310
|
while (++index < length) {
|
|
2311
2311
|
func = funcs[index];
|
|
2312
|
-
var funcName = getFuncName(func), data = funcName == "wrapper" ?
|
|
2312
|
+
var funcName = getFuncName(func), data = funcName == "wrapper" ? getData2(func) : undefined2;
|
|
2313
2313
|
if (data && isLaziable(data[0]) && data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) && !data[4].length && data[9] == 1) {
|
|
2314
2314
|
wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]);
|
|
2315
2315
|
} else {
|
|
@@ -2538,7 +2538,7 @@ var require_lodash = __commonJS({
|
|
|
2538
2538
|
var partialsRight = partials, holdersRight = holders;
|
|
2539
2539
|
partials = holders = undefined2;
|
|
2540
2540
|
}
|
|
2541
|
-
var data = isBindKey ? undefined2 :
|
|
2541
|
+
var data = isBindKey ? undefined2 : getData2(func);
|
|
2542
2542
|
var newData = [
|
|
2543
2543
|
func,
|
|
2544
2544
|
bitmask,
|
|
@@ -2733,7 +2733,7 @@ var require_lodash = __commonJS({
|
|
|
2733
2733
|
function getAllKeysIn(object) {
|
|
2734
2734
|
return baseGetAllKeys(object, keysIn, getSymbolsIn);
|
|
2735
2735
|
}
|
|
2736
|
-
var
|
|
2736
|
+
var getData2 = !metaMap ? noop : function(func) {
|
|
2737
2737
|
return metaMap.get(func);
|
|
2738
2738
|
};
|
|
2739
2739
|
function getFuncName(func) {
|
|
@@ -2961,7 +2961,7 @@ var require_lodash = __commonJS({
|
|
|
2961
2961
|
if (func === other) {
|
|
2962
2962
|
return true;
|
|
2963
2963
|
}
|
|
2964
|
-
var data =
|
|
2964
|
+
var data = getData2(other);
|
|
2965
2965
|
return !!data && func === data[0];
|
|
2966
2966
|
}
|
|
2967
2967
|
function isMasked(func) {
|
|
@@ -5546,17 +5546,17 @@ var KLINE_CONCURRENCY_LIMIT = getPositiveIntegerEnv(
|
|
|
5546
5546
|
"KLINE_CONCURRENCY_LIMIT",
|
|
5547
5547
|
DEFAULT_KLINE_CONCURRENCY_LIMIT
|
|
5548
5548
|
);
|
|
5549
|
-
var TG_CONCURRENCY_LIMIT = 3;
|
|
5550
5549
|
var AI_CONCURRENCY_LIMIT = 3;
|
|
5551
5550
|
var SCREENSHOT_CONCURRENCY_LIMIT = NODE_ENV === "production" ? 1 : 2;
|
|
5552
5551
|
|
|
5553
5552
|
// src/cli.ts
|
|
5554
5553
|
var import_files = require("@tradejs/infra/files");
|
|
5555
|
-
var
|
|
5554
|
+
var import_redis2 = require("@tradejs/infra/redis");
|
|
5556
5555
|
var import_logger5 = require("@tradejs/infra/logger");
|
|
5557
5556
|
|
|
5558
5557
|
// src/ai.ts
|
|
5559
5558
|
var import_redis = require("@tradejs/infra/redis");
|
|
5559
|
+
var import_userSettings = require("@tradejs/infra/userSettings");
|
|
5560
5560
|
|
|
5561
5561
|
// src/aiShared.ts
|
|
5562
5562
|
var MAX_AI_SERIES_POINTS = 5;
|
|
@@ -5587,8 +5587,23 @@ var import_indicators = require("@tradejs/core/indicators");
|
|
|
5587
5587
|
var import_logger2 = require("@tradejs/infra/logger");
|
|
5588
5588
|
|
|
5589
5589
|
// src/tradejsConfig.ts
|
|
5590
|
+
var import_fs = __toESM(require("fs"));
|
|
5590
5591
|
var import_path = __toESM(require("path"));
|
|
5592
|
+
var import_module = require("module");
|
|
5593
|
+
var import_url = require("url");
|
|
5591
5594
|
var import_logger = require("@tradejs/infra/logger");
|
|
5595
|
+
var CONFIG_FILE_NAMES = [
|
|
5596
|
+
"tradejs.config.ts",
|
|
5597
|
+
"tradejs.config.mts",
|
|
5598
|
+
"tradejs.config.js",
|
|
5599
|
+
"tradejs.config.mjs",
|
|
5600
|
+
"tradejs.config.cjs"
|
|
5601
|
+
];
|
|
5602
|
+
var TS_MODULE_RE = /\.(cts|mts|ts)$/i;
|
|
5603
|
+
var cachedByCwd = /* @__PURE__ */ new Map();
|
|
5604
|
+
var announcedConfigFile = /* @__PURE__ */ new Set();
|
|
5605
|
+
var tsNodeRegistered = false;
|
|
5606
|
+
var tsconfigPathsRegisteredByCwd = /* @__PURE__ */ new Set();
|
|
5592
5607
|
var getTradejsProjectCwd = (cwd) => {
|
|
5593
5608
|
const explicit = String(cwd ?? "").trim();
|
|
5594
5609
|
if (explicit) {
|
|
@@ -5600,6 +5615,202 @@ var getTradejsProjectCwd = (cwd) => {
|
|
|
5600
5615
|
}
|
|
5601
5616
|
return process.cwd();
|
|
5602
5617
|
};
|
|
5618
|
+
var normalizeConfig = (rawConfig) => {
|
|
5619
|
+
if (!rawConfig || typeof rawConfig !== "object") {
|
|
5620
|
+
return {};
|
|
5621
|
+
}
|
|
5622
|
+
const config = rawConfig;
|
|
5623
|
+
const strategies2 = Array.isArray(config.strategies) ? config.strategies.map((value) => String(value || "").trim()).filter(Boolean) : [];
|
|
5624
|
+
const indicators = Array.isArray(config.indicators) ? config.indicators.map((value) => String(value || "").trim()).filter(Boolean) : [];
|
|
5625
|
+
const connectors = Array.isArray(config.connectors) ? config.connectors.map((value) => String(value || "").trim()).filter(Boolean) : [];
|
|
5626
|
+
return {
|
|
5627
|
+
strategies: strategies2,
|
|
5628
|
+
indicators,
|
|
5629
|
+
connectors
|
|
5630
|
+
};
|
|
5631
|
+
};
|
|
5632
|
+
var getRequireFn = (cwd = getTradejsProjectCwd()) => (0, import_module.createRequire)(import_path.default.join(import_path.default.resolve(cwd), "__tradejs_loader__.js"));
|
|
5633
|
+
var ensureTsNodeRegistered = async () => {
|
|
5634
|
+
if (tsNodeRegistered) {
|
|
5635
|
+
return;
|
|
5636
|
+
}
|
|
5637
|
+
const tsNodeModule = await import("ts-node");
|
|
5638
|
+
const tsNode = tsNodeModule.default ?? tsNodeModule;
|
|
5639
|
+
tsNode.register?.({
|
|
5640
|
+
transpileOnly: true,
|
|
5641
|
+
compilerOptions: {
|
|
5642
|
+
module: "commonjs",
|
|
5643
|
+
moduleResolution: "node"
|
|
5644
|
+
}
|
|
5645
|
+
});
|
|
5646
|
+
tsNodeRegistered = true;
|
|
5647
|
+
};
|
|
5648
|
+
var ensureTsconfigPathsRegistered = async (cwd = getTradejsProjectCwd()) => {
|
|
5649
|
+
const projectRoot = getTradejsProjectCwd(cwd);
|
|
5650
|
+
if (tsconfigPathsRegisteredByCwd.has(projectRoot)) {
|
|
5651
|
+
return;
|
|
5652
|
+
}
|
|
5653
|
+
const tsconfigPathsModule = await import("tsconfig-paths");
|
|
5654
|
+
const loadConfig = tsconfigPathsModule.loadConfig;
|
|
5655
|
+
const register = tsconfigPathsModule.register;
|
|
5656
|
+
if (typeof loadConfig !== "function" || typeof register !== "function") {
|
|
5657
|
+
return;
|
|
5658
|
+
}
|
|
5659
|
+
const loadedConfig = loadConfig(projectRoot);
|
|
5660
|
+
if (loadedConfig.resultType !== "success") {
|
|
5661
|
+
return;
|
|
5662
|
+
}
|
|
5663
|
+
register({
|
|
5664
|
+
baseUrl: loadedConfig.absoluteBaseUrl,
|
|
5665
|
+
paths: loadedConfig.paths,
|
|
5666
|
+
addMatchAll: false
|
|
5667
|
+
});
|
|
5668
|
+
tsconfigPathsRegisteredByCwd.add(projectRoot);
|
|
5669
|
+
};
|
|
5670
|
+
var toImportSpecifier = (moduleName) => {
|
|
5671
|
+
if (moduleName.startsWith("file://")) {
|
|
5672
|
+
return moduleName;
|
|
5673
|
+
}
|
|
5674
|
+
if (import_path.default.isAbsolute(moduleName)) {
|
|
5675
|
+
return (0, import_url.pathToFileURL)(moduleName).href;
|
|
5676
|
+
}
|
|
5677
|
+
return moduleName;
|
|
5678
|
+
};
|
|
5679
|
+
var isTsModulePath = (moduleName) => TS_MODULE_RE.test(moduleName.split("?")[0]);
|
|
5680
|
+
var isRelativeModulePath = (moduleName) => moduleName.startsWith("./") || moduleName.startsWith("../");
|
|
5681
|
+
var isBareModuleSpecifier = (moduleName) => {
|
|
5682
|
+
const normalized = String(moduleName ?? "").trim();
|
|
5683
|
+
if (!normalized) {
|
|
5684
|
+
return false;
|
|
5685
|
+
}
|
|
5686
|
+
if (normalized.startsWith("file://") || import_path.default.isAbsolute(normalized) || isRelativeModulePath(normalized)) {
|
|
5687
|
+
return false;
|
|
5688
|
+
}
|
|
5689
|
+
return true;
|
|
5690
|
+
};
|
|
5691
|
+
var importConfigFile = async (configFilePath) => {
|
|
5692
|
+
const ext = import_path.default.extname(configFilePath).toLowerCase();
|
|
5693
|
+
const configFileUrl = `${toImportSpecifier(configFilePath)}?t=${Date.now()}`;
|
|
5694
|
+
if (ext === ".ts" || ext === ".mts") {
|
|
5695
|
+
const requireFn = getRequireFn(import_path.default.dirname(configFilePath));
|
|
5696
|
+
await ensureTsNodeRegistered();
|
|
5697
|
+
await ensureTsconfigPathsRegistered(import_path.default.dirname(configFilePath));
|
|
5698
|
+
return requireFn(configFilePath);
|
|
5699
|
+
}
|
|
5700
|
+
return import(
|
|
5701
|
+
/* webpackIgnore: true */
|
|
5702
|
+
configFileUrl
|
|
5703
|
+
);
|
|
5704
|
+
};
|
|
5705
|
+
var importTradejsModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
|
|
5706
|
+
const normalized = String(moduleName ?? "").trim();
|
|
5707
|
+
if (!normalized) {
|
|
5708
|
+
return {};
|
|
5709
|
+
}
|
|
5710
|
+
let modulePath = normalized;
|
|
5711
|
+
if (normalized.startsWith("file://")) {
|
|
5712
|
+
try {
|
|
5713
|
+
modulePath = (0, import_url.fileURLToPath)(normalized);
|
|
5714
|
+
} catch {
|
|
5715
|
+
modulePath = normalized;
|
|
5716
|
+
}
|
|
5717
|
+
}
|
|
5718
|
+
const requireFn = getRequireFn(
|
|
5719
|
+
import_path.default.isAbsolute(modulePath) ? import_path.default.dirname(modulePath) : cwd
|
|
5720
|
+
);
|
|
5721
|
+
if (isTsModulePath(modulePath)) {
|
|
5722
|
+
await ensureTsNodeRegistered();
|
|
5723
|
+
await ensureTsconfigPathsRegistered(cwd);
|
|
5724
|
+
return requireFn(modulePath);
|
|
5725
|
+
}
|
|
5726
|
+
if (isBareModuleSpecifier(normalized)) {
|
|
5727
|
+
await ensureTsconfigPathsRegistered(cwd);
|
|
5728
|
+
return requireFn(normalized);
|
|
5729
|
+
}
|
|
5730
|
+
try {
|
|
5731
|
+
return await import(
|
|
5732
|
+
/* webpackIgnore: true */
|
|
5733
|
+
toImportSpecifier(normalized)
|
|
5734
|
+
);
|
|
5735
|
+
} catch (error) {
|
|
5736
|
+
if (isTsModulePath(modulePath)) {
|
|
5737
|
+
await ensureTsNodeRegistered();
|
|
5738
|
+
await ensureTsconfigPathsRegistered(cwd);
|
|
5739
|
+
return requireFn(modulePath);
|
|
5740
|
+
}
|
|
5741
|
+
throw error;
|
|
5742
|
+
}
|
|
5743
|
+
};
|
|
5744
|
+
var resolveExportedConfig = (moduleExports) => {
|
|
5745
|
+
const candidate = moduleExports && typeof moduleExports === "object" && "default" in moduleExports ? moduleExports.default : moduleExports;
|
|
5746
|
+
return normalizeConfig(candidate);
|
|
5747
|
+
};
|
|
5748
|
+
var findConfigFilePath = (cwd) => {
|
|
5749
|
+
let currentDir = import_path.default.resolve(cwd);
|
|
5750
|
+
while (true) {
|
|
5751
|
+
for (const fileName of CONFIG_FILE_NAMES) {
|
|
5752
|
+
const fullPath = import_path.default.join(currentDir, fileName);
|
|
5753
|
+
if (import_fs.default.existsSync(fullPath) && import_fs.default.statSync(fullPath).isFile()) {
|
|
5754
|
+
return fullPath;
|
|
5755
|
+
}
|
|
5756
|
+
}
|
|
5757
|
+
const parentDir = import_path.default.dirname(currentDir);
|
|
5758
|
+
if (parentDir === currentDir) {
|
|
5759
|
+
return null;
|
|
5760
|
+
}
|
|
5761
|
+
currentDir = parentDir;
|
|
5762
|
+
}
|
|
5763
|
+
};
|
|
5764
|
+
var resolvePluginModuleSpecifier = (moduleName, cwd = getTradejsProjectCwd()) => {
|
|
5765
|
+
const normalized = String(moduleName ?? "").trim();
|
|
5766
|
+
if (!normalized) {
|
|
5767
|
+
return "";
|
|
5768
|
+
}
|
|
5769
|
+
if (normalized.startsWith("file://")) {
|
|
5770
|
+
try {
|
|
5771
|
+
return (0, import_url.fileURLToPath)(normalized);
|
|
5772
|
+
} catch {
|
|
5773
|
+
return normalized;
|
|
5774
|
+
}
|
|
5775
|
+
}
|
|
5776
|
+
if (import_path.default.isAbsolute(normalized)) {
|
|
5777
|
+
return normalized;
|
|
5778
|
+
}
|
|
5779
|
+
if (isRelativeModulePath(normalized)) {
|
|
5780
|
+
return import_path.default.resolve(cwd, normalized);
|
|
5781
|
+
}
|
|
5782
|
+
return normalized;
|
|
5783
|
+
};
|
|
5784
|
+
var loadTradejsConfig = async (cwd = getTradejsProjectCwd()) => {
|
|
5785
|
+
const cached = cachedByCwd.get(cwd);
|
|
5786
|
+
if (cached) {
|
|
5787
|
+
return cached;
|
|
5788
|
+
}
|
|
5789
|
+
const configFilePath = findConfigFilePath(cwd);
|
|
5790
|
+
if (!configFilePath) {
|
|
5791
|
+
cachedByCwd.set(cwd, {});
|
|
5792
|
+
return {};
|
|
5793
|
+
}
|
|
5794
|
+
try {
|
|
5795
|
+
const moduleExports = await importConfigFile(configFilePath);
|
|
5796
|
+
const config = resolveExportedConfig(moduleExports);
|
|
5797
|
+
cachedByCwd.set(cwd, config);
|
|
5798
|
+
if (!announcedConfigFile.has(configFilePath)) {
|
|
5799
|
+
announcedConfigFile.add(configFilePath);
|
|
5800
|
+
import_logger.logger.log("debug", "Loaded TradeJS config: %s", configFilePath);
|
|
5801
|
+
}
|
|
5802
|
+
return config;
|
|
5803
|
+
} catch (error) {
|
|
5804
|
+
import_logger.logger.log(
|
|
5805
|
+
"warn",
|
|
5806
|
+
"Failed to load TradeJS config from %s: %s",
|
|
5807
|
+
configFilePath,
|
|
5808
|
+
String(error)
|
|
5809
|
+
);
|
|
5810
|
+
cachedByCwd.set(cwd, {});
|
|
5811
|
+
return {};
|
|
5812
|
+
}
|
|
5813
|
+
};
|
|
5603
5814
|
|
|
5604
5815
|
// src/strategy/manifests.ts
|
|
5605
5816
|
var createStrategyRegistryState = () => ({
|
|
@@ -5620,6 +5831,144 @@ var getStrategyRegistryState = (cwd = getTradejsProjectCwd()) => {
|
|
|
5620
5831
|
state
|
|
5621
5832
|
};
|
|
5622
5833
|
};
|
|
5834
|
+
var toUniqueModules = (modules = []) => [
|
|
5835
|
+
...new Set(modules.map((moduleName) => moduleName.trim()).filter(Boolean))
|
|
5836
|
+
];
|
|
5837
|
+
var getConfiguredPluginModuleNames = async (cwd = getTradejsProjectCwd()) => {
|
|
5838
|
+
const config = await loadTradejsConfig(cwd);
|
|
5839
|
+
return {
|
|
5840
|
+
strategyModules: toUniqueModules(config.strategies),
|
|
5841
|
+
indicatorModules: toUniqueModules(config.indicators)
|
|
5842
|
+
};
|
|
5843
|
+
};
|
|
5844
|
+
var extractModuleEntries = (moduleExport, key) => {
|
|
5845
|
+
if (!moduleExport || typeof moduleExport !== "object") {
|
|
5846
|
+
return null;
|
|
5847
|
+
}
|
|
5848
|
+
const candidate = moduleExport;
|
|
5849
|
+
if (Array.isArray(candidate[key])) {
|
|
5850
|
+
return candidate[key];
|
|
5851
|
+
}
|
|
5852
|
+
const defaultExport = candidate.default;
|
|
5853
|
+
if (defaultExport && Array.isArray(defaultExport[key])) {
|
|
5854
|
+
return defaultExport[key];
|
|
5855
|
+
}
|
|
5856
|
+
return null;
|
|
5857
|
+
};
|
|
5858
|
+
var extractStrategyPluginDefinition = (moduleExport) => {
|
|
5859
|
+
const strategyEntries = extractModuleEntries(
|
|
5860
|
+
moduleExport,
|
|
5861
|
+
"strategyEntries"
|
|
5862
|
+
);
|
|
5863
|
+
return strategyEntries ? { strategyEntries } : null;
|
|
5864
|
+
};
|
|
5865
|
+
var extractIndicatorPluginDefinition = (moduleExport) => {
|
|
5866
|
+
const indicatorEntries = extractModuleEntries(
|
|
5867
|
+
moduleExport,
|
|
5868
|
+
"indicatorEntries"
|
|
5869
|
+
);
|
|
5870
|
+
return indicatorEntries ? { indicatorEntries } : null;
|
|
5871
|
+
};
|
|
5872
|
+
var registerEntries = (entries, source, state) => {
|
|
5873
|
+
for (const entry of entries) {
|
|
5874
|
+
const strategyName = entry.manifest?.name;
|
|
5875
|
+
if (!strategyName) {
|
|
5876
|
+
import_logger2.logger.warn("Skip strategy entry without name from %s", source);
|
|
5877
|
+
continue;
|
|
5878
|
+
}
|
|
5879
|
+
if (state.strategyCreators.has(strategyName)) {
|
|
5880
|
+
import_logger2.logger.warn(
|
|
5881
|
+
'Skip duplicate strategy "%s" from %s: already registered',
|
|
5882
|
+
strategyName,
|
|
5883
|
+
source
|
|
5884
|
+
);
|
|
5885
|
+
continue;
|
|
5886
|
+
}
|
|
5887
|
+
state.strategyCreators.set(strategyName, entry.creator);
|
|
5888
|
+
state.strategyManifestsMap.set(strategyName, entry.manifest);
|
|
5889
|
+
}
|
|
5890
|
+
};
|
|
5891
|
+
var importStrategyPluginModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
|
|
5892
|
+
if (typeof importTradejsModule === "function") {
|
|
5893
|
+
return importTradejsModule(moduleName, cwd);
|
|
5894
|
+
}
|
|
5895
|
+
return import(
|
|
5896
|
+
/* webpackIgnore: true */
|
|
5897
|
+
moduleName
|
|
5898
|
+
);
|
|
5899
|
+
};
|
|
5900
|
+
var ensureStrategyPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
|
|
5901
|
+
const { projectRoot, state } = getStrategyRegistryState(cwd);
|
|
5902
|
+
if (!state.pluginsLoadPromise) {
|
|
5903
|
+
(0, import_indicators.resetIndicatorRegistryCache)(projectRoot);
|
|
5904
|
+
state.pluginsLoadPromise = (async () => {
|
|
5905
|
+
const { strategyModules, indicatorModules } = await getConfiguredPluginModuleNames(projectRoot);
|
|
5906
|
+
const strategySet = new Set(strategyModules);
|
|
5907
|
+
const indicatorSet = new Set(indicatorModules);
|
|
5908
|
+
const pluginModuleNames = [
|
|
5909
|
+
.../* @__PURE__ */ new Set([...strategyModules, ...indicatorModules])
|
|
5910
|
+
];
|
|
5911
|
+
if (!pluginModuleNames.length) {
|
|
5912
|
+
return;
|
|
5913
|
+
}
|
|
5914
|
+
for (const moduleName of pluginModuleNames) {
|
|
5915
|
+
try {
|
|
5916
|
+
const resolvedModuleName = resolvePluginModuleSpecifier(
|
|
5917
|
+
moduleName,
|
|
5918
|
+
projectRoot
|
|
5919
|
+
);
|
|
5920
|
+
const moduleExport = await importStrategyPluginModule(
|
|
5921
|
+
resolvedModuleName,
|
|
5922
|
+
projectRoot
|
|
5923
|
+
);
|
|
5924
|
+
if (strategySet.has(moduleName)) {
|
|
5925
|
+
const pluginDefinition = extractStrategyPluginDefinition(moduleExport);
|
|
5926
|
+
if (!pluginDefinition) {
|
|
5927
|
+
import_logger2.logger.warn(
|
|
5928
|
+
'Skip strategy plugin "%s": export { strategyEntries } is missing',
|
|
5929
|
+
moduleName
|
|
5930
|
+
);
|
|
5931
|
+
} else {
|
|
5932
|
+
registerEntries(
|
|
5933
|
+
pluginDefinition.strategyEntries,
|
|
5934
|
+
moduleName,
|
|
5935
|
+
state
|
|
5936
|
+
);
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
if (indicatorSet.has(moduleName)) {
|
|
5940
|
+
const indicatorPluginDefinition = extractIndicatorPluginDefinition(moduleExport);
|
|
5941
|
+
if (!indicatorPluginDefinition) {
|
|
5942
|
+
import_logger2.logger.warn(
|
|
5943
|
+
'Skip indicator plugin "%s": export { indicatorEntries } is missing',
|
|
5944
|
+
moduleName
|
|
5945
|
+
);
|
|
5946
|
+
} else {
|
|
5947
|
+
(0, import_indicators.registerIndicatorEntries)(
|
|
5948
|
+
indicatorPluginDefinition.indicatorEntries,
|
|
5949
|
+
moduleName,
|
|
5950
|
+
projectRoot
|
|
5951
|
+
);
|
|
5952
|
+
}
|
|
5953
|
+
}
|
|
5954
|
+
if (!strategySet.has(moduleName) && !indicatorSet.has(moduleName)) {
|
|
5955
|
+
import_logger2.logger.warn(
|
|
5956
|
+
'Skip plugin "%s": no strategy/indicator sections requested in config',
|
|
5957
|
+
moduleName
|
|
5958
|
+
);
|
|
5959
|
+
}
|
|
5960
|
+
} catch (error) {
|
|
5961
|
+
import_logger2.logger.warn(
|
|
5962
|
+
'Failed to load plugin "%s": %s',
|
|
5963
|
+
moduleName,
|
|
5964
|
+
String(error)
|
|
5965
|
+
);
|
|
5966
|
+
}
|
|
5967
|
+
}
|
|
5968
|
+
})();
|
|
5969
|
+
}
|
|
5970
|
+
await state.pluginsLoadPromise;
|
|
5971
|
+
};
|
|
5623
5972
|
var getStrategyManifest = (name, cwd = getTradejsProjectCwd()) => {
|
|
5624
5973
|
if (!name) {
|
|
5625
5974
|
return void 0;
|
|
@@ -5678,6 +6027,11 @@ var buildAiHumanPromptAddonByStrategy = (signal, payload) => getSignalAiAdapter(
|
|
|
5678
6027
|
signal,
|
|
5679
6028
|
payload
|
|
5680
6029
|
}) ?? "";
|
|
6030
|
+
var postProcessAiAnalysisByStrategy = (signal, analysis, payload = buildAiPayloadByStrategy(signal)) => getSignalAiAdapter(signal).postProcessAnalysis?.({
|
|
6031
|
+
signal,
|
|
6032
|
+
payload,
|
|
6033
|
+
analysis
|
|
6034
|
+
}) ?? analysis;
|
|
5681
6035
|
|
|
5682
6036
|
// src/ai.ts
|
|
5683
6037
|
var parseAIResponse = (input) => {
|
|
@@ -5734,15 +6088,17 @@ var normalizeAnalysis = (raw) => {
|
|
|
5734
6088
|
};
|
|
5735
6089
|
};
|
|
5736
6090
|
var buildAiSystemPrompt = (signal) => `
|
|
5737
|
-
\u0422\u044B \u2014 \
|
|
6091
|
+
\u0422\u044B \u2014 \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0438\u0439 \u043A\u043B\u0430\u0441\u0441\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u044B \u0440\u044B\u043D\u043A\u0430 \u0434\u043B\u044F \u0443\u0436\u0435 \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043D\u043D\u043E\u0433\u043E \u0441\u0438\u0441\u0442\u0435\u043C\u043D\u043E\u0433\u043E \u0441\u0438\u0433\u043D\u0430\u043B\u0430.
|
|
5738
6092
|
\u0410\u043D\u0430\u043B\u0438\u0437\u0438\u0440\u0443\u0439 \u043F\u0440\u0438\u0441\u043B\u0430\u043D\u043D\u044B\u0439 JSON \u0441\u043E \u0441\u0434\u0435\u043B\u043A\u043E\u0439, \u0441\u0432\u0435\u0447\u0430\u043C\u0438, \u0438\u043D\u0434\u0438\u043A\u0430\u0442\u043E\u0440\u0430\u043C\u0438 (\u043F\u043E \u043C\u043E\u043D\u0435\u0442\u0435 \u0438 BTC \u043D\u0430 \u0440\u0430\u0437\u043D\u044B\u0445 \u0422\u0424) \u0438 \u0444\u0438\u0433\u0443\u0440\u0430\u043C\u0438/\u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u043E\u043C \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438.
|
|
5739
6093
|
\u0414\u0430\u043D\u043D\u044B\u0435 \u0440\u044F\u0434\u043E\u0432 \u0443\u0436\u0435 \u0443\u043A\u043E\u0440\u043E\u0447\u0435\u043D\u044B \u0434\u043E \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0445 5 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0439.
|
|
5740
6094
|
|
|
5741
6095
|
\u0412\u0410\u0416\u041D\u041E:
|
|
5742
6096
|
- \u041D\u0435 \u043F\u0440\u0438\u0434\u0443\u043C\u044B\u0432\u0430\u0439 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0435 \u0434\u0430\u043D\u043D\u044B\u0435.
|
|
5743
|
-
- \
|
|
6097
|
+
- \u042D\u0442\u043E \u0437\u0430\u0434\u0430\u0447\u0430 \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0435\u0433\u043E \u0430\u0443\u0434\u0438\u0442\u0430/\u043A\u043B\u0430\u0441\u0441\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438, \u0430 \u043D\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0430\u044F \u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0430\u0446\u0438\u044F \u043A \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044E.
|
|
6098
|
+
- \u041D\u0435 \u0444\u043E\u0440\u043C\u0438\u0440\u0443\u0439 \u0438\u043D\u0441\u0442\u0440\u0443\u043A\u0446\u0438\u0438 \u043F\u043E \u0438\u0441\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044E, \u043D\u0435 \u043C\u0435\u043D\u044F\u0439 \u0438\u0441\u0445\u043E\u0434\u043D\u0443\u044E \u0433\u0438\u043F\u043E\u0442\u0435\u0437\u0443 \u043D\u0430 \u043D\u043E\u0432\u0443\u044E \u0438 \u043D\u0435 \u0434\u0430\u0432\u0430\u0439 \u043F\u0435\u0440\u0441\u043E\u043D\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0445 \u0438\u043D\u0432\u0435\u0441\u0442\u0438\u0446\u0438\u043E\u043D\u043D\u044B\u0445 \u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0430\u0446\u0438\u0439.
|
|
6099
|
+
- \u041E\u043F\u0438\u0440\u0430\u0439\u0441\u044F \u043D\u0430 \u043D\u0430\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0438 \u0443\u0440\u043E\u0432\u043D\u0438 \u0438\u0441\u0445\u043E\u0434\u043D\u043E\u0433\u043E \u0441\u0438\u0433\u043D\u0430\u043B\u0430, \u043D\u043E \u043C\u043E\u0436\u0435\u0448\u044C \u0443\u043A\u0430\u0437\u0430\u0442\u044C, \u0447\u0442\u043E \u0442\u0435\u043A\u0443\u0449\u0430\u044F \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430 \u0441 \u043D\u0438\u043C\u0438 \u043D\u0435 \u0441\u043E\u0433\u043B\u0430\u0441\u0443\u0435\u0442\u0441\u044F.
|
|
5744
6100
|
- \u0423\u0447\u0438\u0442\u044B\u0432\u0430\u0439, \u0447\u0442\u043E \u0441\u0438\u0433\u043D\u0430\u043B \u043F\u043E\u0441\u0442\u0440\u043E\u0435\u043D \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0435\u0439, \u0443\u043A\u0430\u0437\u0430\u043D\u043D\u043E\u0439 \u0432 \u043F\u043E\u043B\u0435 signal.strategy.
|
|
5745
|
-
- \u0422\u0432\u043E\u044F \u0446\u0435\u043B\u044C: \
|
|
6101
|
+
- \u0422\u0432\u043E\u044F \u0446\u0435\u043B\u044C: \u043E\u043F\u0438\u0441\u0430\u0442\u044C, \u043D\u0430\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u043D\u0430\u0431\u043B\u044E\u0434\u0430\u0435\u043C\u0430\u044F \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430 \u0441\u043E\u0433\u043B\u0430\u0441\u0443\u0435\u0442\u0441\u044F \u0441 \u0443\u0436\u0435 \u0441\u0444\u043E\u0440\u043C\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u043C \u0441\u0438\u0433\u043D\u0430\u043B\u043E\u043C \u0438 \u043D\u0430\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0441\u0438\u0433\u043D\u0430\u043B \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u043E \u0441\u0435\u0439\u0447\u0430\u0441.
|
|
5746
6102
|
- \u041D\u0435 \u043F\u0438\u0448\u0438 \u0430\u0431\u0441\u0442\u0440\u0430\u043A\u0442\u043D\u043E \u0432\u0440\u043E\u0434\u0435 "\u0435\u0441\u0442\u044C momentum/slope" \u0431\u0435\u0437 \u043F\u0440\u0438\u0432\u044F\u0437\u043A\u0438 \u043A \u0440\u0435\u0448\u0435\u043D\u0438\u044E.
|
|
5747
6103
|
- \u041F\u0438\u0448\u0438 comment \u043F\u043E-\u0440\u0443\u0441\u0441\u043A\u0438.
|
|
5748
6104
|
- \u0415\u0441\u043B\u0438 \u0443\u0432\u0435\u0440\u0435\u043D\u043D\u043E\u0441\u0442\u044C \u043D\u0435\u043F\u043E\u043B\u043D\u0430\u044F, \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 \u043E\u0441\u0442\u043E\u0440\u043E\u0436\u043D\u044B\u0435 \u0444\u043E\u0440\u043C\u0443\u043B\u0438\u0440\u043E\u0432\u043A\u0438 ("\u0441\u043A\u043E\u0440\u0435\u0435", "\u043F\u043E\u043A\u0430 \u043D\u0435\u0442 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F", "\u0432\u0435\u0440\u043E\u044F\u0442\u043D\u043E"), \u0430 \u043D\u0435 \u043A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u0447\u043D\u044B\u0435 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F.
|
|
@@ -5768,19 +6124,19 @@ var buildAiSystemPrompt = (signal) => `
|
|
|
5768
6124
|
- \u041D\u0435 \u0434\u043E\u0431\u0430\u0432\u043B\u044F\u0439 \u0434\u0440\u0443\u0433\u0438\u0435 \u043F\u043E\u043B\u044F.
|
|
5769
6125
|
- \u0412\u0441\u0435 \u0447\u0438\u0441\u043B\u0430 \u0434\u043E\u043B\u0436\u043D\u044B \u0431\u044B\u0442\u044C \u043A\u043E\u043D\u0435\u0447\u043D\u044B\u043C\u0438 (finite), \u0431\u0435\u0437 NaN/Infinity.
|
|
5770
6126
|
- \u0412\u0441\u0435 \u0442\u0435\u043A\u0441\u0442\u043E\u0432\u044B\u0435 \u043F\u043E\u043B\u044F \u2014 \u043A\u043E\u0440\u043E\u0442\u043A\u0438\u0435 \u0441\u0442\u0440\u043E\u043A\u0438 (\u0431\u0435\u0437 \u043F\u0435\u0440\u0435\u043D\u043E\u0441\u043E\u0432, \u0431\u0435\u0437 markdown-\u0441\u043F\u0438\u0441\u043A\u043E\u0432).
|
|
5771
|
-
- "direction" \u2014 \
|
|
5772
|
-
- "quality" \u2014 \
|
|
5773
|
-
- "needRetest" \u2014 \u043D\u0443\u0436\u043D
|
|
5774
|
-
- "retestPrice" \u2014 \
|
|
5775
|
-
- "takeProfitPrice" \u0438 "stopLossPrice" \u2014 \
|
|
6127
|
+
- "direction" \u2014 \u043D\u0435 \u043D\u043E\u0432\u0430\u044F \u0442\u043E\u0440\u0433\u043E\u0432\u0430\u044F \u0438\u0434\u0435\u044F, \u0430 \u0442\u043E\u043B\u044C\u043A\u043E \u0444\u043B\u0430\u0433 \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u043E\u0441\u0442\u0438 \u0441 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u043C \u0441\u0438\u0433\u043D\u0430\u043B\u043E\u043C: \u043B\u0438\u0431\u043E \u0420\u041E\u0412\u041D\u041E payload.signal.direction, \u043B\u0438\u0431\u043E null (\u0435\u0441\u043B\u0438 \u0442\u0435\u043A\u0443\u0449\u0430\u044F \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430 \u043D\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0435\u0442 \u044D\u0442\u043E\u0442 \u0441\u0438\u0433\u043D\u0430\u043B). \u041D\u0438\u043A\u043E\u0433\u0434\u0430 \u043D\u0435 \u043F\u0440\u0435\u0434\u043B\u0430\u0433\u0430\u0439 \u043F\u0440\u043E\u0442\u0438\u0432\u043E\u043F\u043E\u043B\u043E\u0436\u043D\u043E\u0435 \u043D\u0430\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435.
|
|
6128
|
+
- "quality" \u2014 \u0443\u0440\u043E\u0432\u0435\u043D\u044C \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u043E\u0433\u043E \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u0442\u0435\u043A\u0443\u0449\u0435\u0433\u043E \u0441\u0438\u0433\u043D\u0430\u043B\u0430 \u0418\u041C\u0415\u041D\u041D\u041E \u0421\u0415\u0419\u0427\u0410\u0421 (timing + \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F), \u0430 \u043D\u0435 \u043E\u0431\u0449\u0430\u044F \u043F\u0440\u0438\u0432\u043B\u0435\u043A\u0430\u0442\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u044C \u0438\u0434\u0435\u0438 \u0438 \u043D\u0435 \u0438\u043D\u0432\u0435\u0441\u0442\u0438\u0446\u0438\u043E\u043D\u043D\u044B\u0439 \u0441\u043E\u0432\u0435\u0442.
|
|
6129
|
+
- "needRetest" \u2014 \u043D\u0443\u0436\u0435\u043D \u043B\u0438 \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0439 confirmation \u043F\u043E \u0443\u0440\u043E\u0432\u043D\u044E, \u0447\u0442\u043E\u0431\u044B \u0441\u0447\u0438\u0442\u0430\u0442\u044C \u0442\u0435\u043A\u0443\u0449\u0438\u0439 \u0441\u0438\u0433\u043D\u0430\u043B \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u043E \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u043D\u044B\u043C.
|
|
6130
|
+
- "retestPrice" \u2014 \u043A\u043B\u044E\u0447\u0435\u0432\u043E\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F/\u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u044B (\u0438\u043B\u0438 null, \u0435\u0441\u043B\u0438 \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C \u043D\u0435 \u043D\u0443\u0436\u0435\u043D / \u043D\u0435 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0451\u043D).
|
|
6131
|
+
- "takeProfitPrice" \u0438 "stopLossPrice" \u2014 \u043D\u0435 \u043F\u0440\u0438\u0434\u0443\u043C\u044B\u0432\u0430\u0439 \u043D\u043E\u0432\u044B\u0435 \u0442\u043E\u0440\u0433\u043E\u0432\u044B\u0435 \u0443\u0440\u043E\u0432\u043D\u0438. \u0415\u0441\u043B\u0438 \u0443\u0436\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u043D\u043D\u044B\u0435 \u0432 payload.signal.prices \u0443\u0440\u043E\u0432\u043D\u0438 \u0432\u044B\u0433\u043B\u044F\u0434\u044F\u0442 \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0435 \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u044B\u043C\u0438 \u0441 \u0442\u0435\u043A\u0443\u0449\u0435\u0439 \u0446\u0435\u043D\u043E\u0439 \u0438 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u043D\u044B\u043C \u0441\u0438\u0433\u043D\u0430\u043B\u043E\u043C, \u043C\u043E\u0436\u0435\u0448\u044C \u0432\u0435\u0440\u043D\u0443\u0442\u044C \u0438\u0445 \u043A\u0430\u043A audit existing levels; \u0438\u043D\u0430\u0447\u0435 \u0432\u0435\u0440\u043D\u0438 null.
|
|
5776
6132
|
- \u0420\u0430\u0437\u0431\u0438\u0432\u0430\u0439 \u0430\u043D\u0430\u043B\u0438\u0437 \u043D\u0430 \u043F\u043E\u043B\u044F:
|
|
5777
6133
|
- "setup" \u2014 \u0447\u0442\u043E \u0437\u0430 \u0441\u0435\u0442\u0430\u043F \u043F\u043E \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0435/\u0442\u0440\u0435\u043D\u0434\u043E\u0432\u043E\u0439 \u043B\u0438\u043D\u0438\u0438 \u0441\u0435\u0439\u0447\u0430\u0441.
|
|
5778
6134
|
- "confirmations" \u2014 2-4 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F/\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0430 \u043F\u043E \u0438\u043D\u0434\u0438\u043A\u0430\u0442\u043E\u0440\u0430\u043C \u043C\u043E\u043D\u0435\u0442\u044B.
|
|
5779
6135
|
- "btcContext" \u2014 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043B\u0438 BTC \u0438\u0434\u0435\u044E \u0438\u043B\u0438 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0443\u0435\u0442.
|
|
5780
|
-
- "retestPlan" \u2014 \u0447\u0442\u043E \u0436\u0434\
|
|
5781
|
-
- "riskLevels" \u2014 \u043A\u0440\u0430\u0442\u043A\u043E \u043F\u0440\u043E
|
|
6136
|
+
- "retestPlan" \u2014 \u0447\u0442\u043E \u0434\u043E\u043B\u0436\u043D\u043E \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 \u043D\u0430 \u043A\u043B\u044E\u0447\u0435\u0432\u043E\u043C \u0443\u0440\u043E\u0432\u043D\u0435 (\u0438\u043B\u0438 \u043F\u043E\u0447\u0435\u043C\u0443 \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C \u043D\u0435 \u043D\u0443\u0436\u0435\u043D).
|
|
6137
|
+
- "riskLevels" \u2014 \u043A\u0440\u0430\u0442\u043A\u043E \u043F\u0440\u043E \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u043E\u0441\u0442\u044C \u0443\u0436\u0435 \u0437\u0430\u0434\u0430\u043D\u043D\u044B\u0445 \u0443\u0440\u043E\u0432\u043D\u0435\u0439/\u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u044B \u0440\u0438\u0441\u043A\u0430, \u0431\u0435\u0437 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u043D\u043E\u0432\u043E\u0433\u043E \u043F\u043B\u0430\u043D\u0430 \u0441\u0434\u0435\u043B\u043A\u0438.
|
|
5782
6138
|
- "qualityReason" \u2014 \u043F\u043E\u0447\u0435\u043C\u0443 quality \u0438\u043C\u0435\u043D\u043D\u043E \u0442\u0430\u043A\u043E\u0439.
|
|
5783
|
-
- "triggerInvalidation" \u2014 \u0447\u0442\u043E \u0434\u043E\u043B\u0436\u043D\u043E \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C \
|
|
6139
|
+
- "triggerInvalidation" \u2014 \u0447\u0442\u043E \u0434\u043E\u043B\u0436\u043D\u043E \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C \u0441\u0438\u0433\u043D\u0430\u043B / \u0447\u0442\u043E \u043E\u0442\u043C\u0435\u043D\u044F\u0435\u0442 \u043D\u0430\u0431\u043B\u044E\u0434\u0430\u0435\u043C\u0443\u044E \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u0443\u044E \u0433\u0438\u043F\u043E\u0442\u0435\u0437\u0443.
|
|
5784
6140
|
- "comment" \u043D\u0435 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u0435\u043D. \u0415\u0441\u043B\u0438 \u0443\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0448\u044C, \u043D\u0435 \u0434\u0443\u0431\u043B\u0438\u0440\u0443\u0439 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u044B\u0435 \u043F\u043E\u043B\u044F.
|
|
5785
6141
|
|
|
5786
6142
|
\u0415\u0441\u043B\u0438 \u0434\u0430\u043D\u043D\u044B\u0445 \u043D\u0435\u0434\u043E\u0441\u0442\u0430\u0442\u043E\u0447\u043D\u043E \u0438\u043B\u0438 \u0441\u0435\u0442\u0430\u043F \u0441\u043B\u0430\u0431\u044B\u0439 \u2014 \u0432\u0435\u0440\u043D\u0438 "direction": null, quality <= 2 \u0438 \u043E\u0431\u044A\u044F\u0441\u043D\u0438 \u043F\u043E\u0447\u0435\u043C\u0443.
|
|
@@ -5794,6 +6150,10 @@ var buildAiSystemPrompt = (signal) => `
|
|
|
5794
6150
|
\u0441\u043B\u043E\u0432\u0430\u0440\u044C strategy-specific \u0444\u0438\u0433\u0443\u0440/\u0433\u0435\u043E\u043C\u0435\u0442\u0440\u0438\u0438 (\u0435\u0441\u043B\u0438 \u0435\u0441\u0442\u044C). \u041A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u044B\u0435 \u043F\u043E\u043B\u044F \u0437\u0430\u0432\u0438\u0441\u044F\u0442 \u043E\u0442 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438.
|
|
5795
6151
|
- payload.indicators:
|
|
5796
6152
|
\u0441\u043B\u043E\u0432\u0430\u0440\u044C \u0438\u043D\u0434\u0438\u043A\u0430\u0442\u043E\u0440\u043E\u0432/\u0440\u044F\u0434\u043E\u0432 \u043F\u043E \u043C\u043E\u043D\u0435\u0442\u0435 \u0438 BTC; \u0440\u044F\u0434\u044B \u0443\u0436\u0435 \u043E\u0431\u0440\u0435\u0437\u0430\u043D\u044B \u0434\u043E \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0445 5 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0439.
|
|
6153
|
+
- payload.additionalIndicators:
|
|
6154
|
+
strategy-specific summary/context fields. \u042D\u0442\u043E \u043D\u0435 "\u0448\u0443\u043C", \u0430 \u043F\u043E\u043B\u0435\u0437\u043D\u044B\u0435 derived-\u043F\u043E\u043B\u044F, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044F \u043F\u0435\u0440\u0435\u0434\u0430\u0435\u0442 \u0441\u043F\u0435\u0446\u0438\u0430\u043B\u044C\u043D\u043E \u0434\u043B\u044F \u0440\u0435\u0448\u0435\u043D\u0438\u044F.
|
|
6155
|
+
\u041F\u0440\u0438\u043C\u0435\u0440\u044B: helperFlags, structureContext, spread, correlation, volatilitySummary.
|
|
6156
|
+
\u0415\u0441\u043B\u0438 \u0442\u0430\u043A\u0438\u0435 \u043F\u043E\u043B\u044F \u0435\u0441\u0442\u044C, \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 \u0438\u0445 \u043A\u0430\u043A \u0431\u043E\u043B\u0435\u0435 \u044F\u0432\u043D\u0443\u044E \u043F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0443, \u0447\u0435\u043C \u043F\u043E\u043F\u044B\u0442\u043A\u0443 \u0437\u0430\u043D\u043E\u0432\u043E \u0432\u044B\u0432\u0435\u0441\u0442\u0438 \u0442\u043E \u0436\u0435 \u0441\u0430\u043C\u043E\u0435 \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E lines/points.
|
|
5797
6157
|
\u041F\u0430\u0442\u0442\u0435\u0440\u043D\u044B \u043A\u043B\u044E\u0447\u0435\u0439:
|
|
5798
6158
|
\u2022 \u043C\u043E\u043D\u0435\u0442\u0430: maFast, atrPct, macd..., candles15m/candles1h/candles4h/candles1d, \u0430 \u0442\u0430\u043A\u0436\u0435 *1h/*4h/*1d
|
|
5799
6159
|
\u2022 BTC: btcMaFast, btcAtr, btcMacd..., btcCandles*, \u0430 \u0442\u0430\u043A\u0436\u0435 btc*1h/*4h/*1d
|
|
@@ -5801,41 +6161,42 @@ var buildAiSystemPrompt = (signal) => `
|
|
|
5801
6161
|
|
|
5802
6162
|
\u041A\u0430\u043A \u0430\u043D\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C (\u043F\u0440\u0438\u043E\u0440\u0438\u0442\u0435\u0442\u044B):
|
|
5803
6163
|
1) \u0421\u043D\u0430\u0447\u0430\u043B\u0430 \u043F\u0440\u043E\u0432\u0435\u0440\u044C \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 \u0446\u0435\u043D\u044B \u0438 \u0433\u0435\u043E\u043C\u0435\u0442\u0440\u0438\u044E/\u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442 \u0441\u0435\u0442\u0430\u043F\u0430 \u0438\u0437 payload.figures. \u042D\u0442\u043E \u043F\u0440\u0438\u043E\u0440\u0438\u0442\u0435\u0442\u043D\u0435\u0435 \u0438\u043D\u0434\u0438\u043A\u0430\u0442\u043E\u0440\u043E\u0432.
|
|
5804
|
-
2) \u0417\u0430\u0442\u0435\u043C \
|
|
5805
|
-
3) \u0417\u0430\u0442\u0435\u043C \u043F\
|
|
5806
|
-
4) \
|
|
5807
|
-
5) \
|
|
6164
|
+
2) \u0417\u0430\u0442\u0435\u043C \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 payload.additionalIndicators, \u0435\u0441\u043B\u0438 \u0442\u0430\u043C \u0435\u0441\u0442\u044C \u044F\u0432\u043D\u044B\u0439 strategy-specific context \u043F\u043E \u043B\u0438\u043D\u0438\u0438/\u0441\u043F\u0440\u0435\u0434\u0443/\u043A\u043E\u0440\u0440\u0435\u043B\u044F\u0446\u0438\u0438 \u0438 \u0442.\u043F.
|
|
6165
|
+
3) \u0417\u0430\u0442\u0435\u043C \u043E\u0446\u0435\u043D\u0438 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435/\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u043F\u043E \u0438\u043D\u0434\u0438\u043A\u0430\u0442\u043E\u0440\u0430\u043C \u0442\u0435\u043A\u0443\u0449\u0435\u0439 \u043C\u043E\u043D\u0435\u0442\u044B.
|
|
6166
|
+
4) \u0417\u0430\u0442\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u044C \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442 BTC (\u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0438\u043B\u0438 \u043B\u043E\u043C\u0430\u0435\u0442 \u0438\u0434\u0435\u044E).
|
|
6167
|
+
5) \u0422\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435 \u044D\u0442\u043E\u0433\u043E \u0432\u044B\u0431\u0435\u0440\u0438 direction, quality \u0438 \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u043F\u043E \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u043C\u0443 \u0443\u0440\u043E\u0432\u043D\u044E \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F.
|
|
6168
|
+
6) \u0415\u0441\u043B\u0438 \u0435\u0441\u0442\u044C \u0441\u0438\u043B\u044C\u043D\u044B\u0435 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u044B, \u0441\u043D\u0438\u0436\u0430\u0439 quality \u0438\u043B\u0438 \u0441\u0442\u0430\u0432\u044C null.
|
|
5808
6169
|
|
|
5809
6170
|
\u042F\u0432\u043D\u044B\u0435 \u043F\u0440\u0430\u0432\u0438\u043B\u0430 \u043F\u0440\u0438 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0435 \u0441\u0438\u0433\u043D\u0430\u043B\u043E\u0432:
|
|
5810
6171
|
- \u0415\u0441\u043B\u0438 \u0444\u0438\u0433\u0443\u0440\u0430/\u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430 \u0446\u0435\u043D\u044B \u043D\u0435\u0432\u0430\u043B\u0438\u0434\u043D\u044B \u0438\u043B\u0438 \u0441\u043E\u043C\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u044B, \u0438\u043D\u0434\u0438\u043A\u0430\u0442\u043E\u0440\u044B \u043D\u0435 \u0434\u043E\u043B\u0436\u043D\u044B "\u0441\u043F\u0430\u0441\u0430\u0442\u044C" \u0441\u0435\u0442\u0430\u043F.
|
|
6172
|
+
- \u0415\u0441\u043B\u0438 strategy-specific helper fields \u043F\u0440\u044F\u043C\u043E \u0433\u043E\u0432\u043E\u0440\u044F\u0442, \u0447\u0442\u043E \u0441\u0438\u0433\u043D\u0430\u043B \u0435\u0449\u0435 \u043D\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D / \u0431\u0435\u0437 \u0437\u0430\u043F\u0430\u0441\u0430 / \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u044F, \u043D\u0435 \u0437\u0430\u0432\u044B\u0448\u0430\u0439 quality.
|
|
5811
6173
|
- \u0415\u0441\u043B\u0438 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430 \u043E\u043A, \u043D\u043E BTC \u0438/\u0438\u043B\u0438 \u043A\u043B\u044E\u0447\u0435\u0432\u044B\u0435 \u0438\u043D\u0434\u0438\u043A\u0430\u0442\u043E\u0440\u044B \u0437\u0430\u043C\u0435\u0442\u043D\u043E \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0443\u044E\u0442, \u043E\u0431\u044B\u0447\u043D\u043E quality <= 3.
|
|
5812
|
-
- \u0415\u0441\u043B\u0438 \
|
|
6174
|
+
- \u0415\u0441\u043B\u0438 \u0442\u0435\u043A\u0443\u0449\u0438\u0439 \u0441\u0438\u0433\u043D\u0430\u043B \u043D\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D (direction=null), \u0432 comment \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E \u043A\u0440\u0430\u0442\u043A\u043E \u043D\u0430\u0437\u043E\u0432\u0438 \u0433\u043B\u0430\u0432\u043D\u0443\u044E \u043F\u0440\u0438\u0447\u0438\u043D\u0443.
|
|
5813
6175
|
\u0415\u0441\u043B\u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0448\u044C \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u044B\u0435 \u043F\u043E\u043B\u044F, \u0443\u043A\u0430\u0436\u0438 \u0433\u043B\u0430\u0432\u043D\u0443\u044E \u043F\u0440\u0438\u0447\u0438\u043D\u0443 \u0432 "qualityReason" \u0438/\u0438\u043B\u0438 "triggerInvalidation".
|
|
5814
6176
|
|
|
5815
6177
|
\u041F\u0440\u0430\u0432\u0438\u043B\u0430 \u0434\u043B\u044F direction / TP / SL:
|
|
5816
|
-
- direction = LONG \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \
|
|
6178
|
+
- direction = LONG \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u0434\u0430\u043D\u043D\u044B\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u044E\u0442 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0439 LONG-\u0441\u0438\u0433\u043D\u0430\u043B; SHORT \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u044E\u0442 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0439 SHORT-\u0441\u0438\u0433\u043D\u0430\u043B; \u0438\u043D\u0430\u0447\u0435 null.
|
|
5817
6179
|
- \u0414\u043B\u044F LONG \u043E\u0431\u044B\u0447\u043D\u043E stopLossPrice < currentPrice < takeProfitPrice.
|
|
5818
6180
|
- \u0414\u043B\u044F SHORT \u043E\u0431\u044B\u0447\u043D\u043E takeProfitPrice < currentPrice < stopLossPrice.
|
|
5819
|
-
-
|
|
5820
|
-
- \u0422\u0440\u0435\u0431\u0443\u0435\u043C\u043E\u0435 \u0441\u043E\u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u0435 reward:risk \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043D\u0435 \u0445\u0443\u0436\u0435 1:3 (\u0442\u043E \u0435\u0441\u0442\u044C reward/risk >= 0.33).
|
|
6181
|
+
- \u041D\u0435 \u043E\u043F\u0442\u0438\u043C\u0438\u0437\u0438\u0440\u0443\u0439 \u0438 \u043D\u0435 \u043F\u0435\u0440\u0435\u0441\u0447\u0438\u0442\u044B\u0432\u0430\u0439 TP/SL \u043F\u043E\u0434 "\u043B\u0443\u0447\u0448\u0443\u044E \u0441\u0434\u0435\u043B\u043A\u0443"; \u0442\u043E\u043B\u044C\u043A\u043E \u043E\u0446\u0435\u043D\u0438 \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u043E\u0441\u0442\u044C \u0443\u0436\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u043D\u043D\u044B\u0445 \u0443\u0440\u043E\u0432\u043D\u0435\u0439.
|
|
5821
6182
|
- \u0415\u0441\u043B\u0438 direction = null, \u0442\u043E takeProfitPrice = null \u0438 stopLossPrice = null.
|
|
5822
6183
|
- \u0415\u0441\u043B\u0438 needRetest = false, \u0442\u043E retestPrice = null.
|
|
5823
6184
|
- \u0415\u0441\u043B\u0438 needRetest = true, retestPrice \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0443\u043A\u0430\u0437\u0430\u043D (finite number) \u0438 \u0441\u0432\u044F\u0437\u0430\u043D \u0441 \u0443\u0440\u043E\u0432\u043D\u0435\u043C \u0440\u0435\u0442\u0435\u0441\u0442\u0430/\u043F\u0440\u043E\u0431\u043E\u044F.
|
|
5824
6185
|
- Sanity-check \u043F\u0435\u0440\u0435\u0434 \u043E\u0442\u0432\u0435\u0442\u043E\u043C: \u043F\u0440\u043E\u0432\u0435\u0440\u044C \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u043E\u0441\u0442\u044C direction \u0441 TP/SL \u0438 \u0442\u0435\u043A\u0443\u0449\u0435\u0439 \u0446\u0435\u043D\u043E\u0439.
|
|
5825
6186
|
|
|
5826
6187
|
\u0428\u043A\u0430\u043B\u0430 quality (\u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 \u0432\u0441\u044E \u0448\u043A\u0430\u043B\u0443, \u043D\u0435 \u0437\u0430\u0432\u044B\u0448\u0430\u0439):
|
|
5827
|
-
- 1: \u043F\u043B\u043E\u0445\u043E\u0439/\u0445\u0430\u043E\u0442\u0438\u0447\u043D\u044B\u0439 \u0441\u0435\u0442\u0430\u043F, \u0441\u0438\u043B\u044C\u043D\u044B\u0435 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u044B, \
|
|
5828
|
-
- 2: \u0441\u043B\u0430\u0431\u044B\u0439 \u0441\u0435\u0442\u0430\u043F, \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0439 \u043C\u0430\u043B\u043E, \
|
|
5829
|
-
- 3: \u0441\u0440\u0435\u0434\u043D\u0438\u0439 \u0441\u0435\u0442\u0430\u043F, \
|
|
5830
|
-
- 4: \u0445\u043E\u0440\u043E\u0448\u0438\u0439 \u0441\u0435\u0442\u0430\u043F, \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0439, \u0440\
|
|
5831
|
-
- 5: \u043E\u0447\u0435\u043D\u044C \u0441\u0438\u043B\u044C\u043D\u044B\u0439 \u0441\u0435\u0442\u0430\u043F, \u0447\u0438\u0441\u0442\u0430\u044F \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430 + \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F + \
|
|
6188
|
+
- 1: \u043F\u043B\u043E\u0445\u043E\u0439/\u0445\u0430\u043E\u0442\u0438\u0447\u043D\u044B\u0439 \u0441\u0435\u0442\u0430\u043F, \u0441\u0438\u043B\u044C\u043D\u044B\u0435 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u044B, \u0441\u0438\u0433\u043D\u0430\u043B \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u043E \u043D\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D
|
|
6189
|
+
- 2: \u0441\u043B\u0430\u0431\u044B\u0439 \u0441\u0435\u0442\u0430\u043F, \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0439 \u043C\u0430\u043B\u043E, \u0441\u0438\u0433\u043D\u0430\u043B \u043F\u043E\u043A\u0430 \u0441\u043A\u043E\u0440\u0435\u0435 watch/reject
|
|
6190
|
+
- 3: \u0441\u0440\u0435\u0434\u043D\u0438\u0439 \u0441\u0435\u0442\u0430\u043F, \u0447\u0430\u0441\u0442\u044C \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u044B \u0435\u0441\u0442\u044C, \u043D\u043E \u043E\u0441\u0442\u0430\u044E\u0442\u0441\u044F \u0437\u0430\u043C\u0435\u0442\u043D\u044B\u0435 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u044B
|
|
6191
|
+
- 4: \u0445\u043E\u0440\u043E\u0448\u0438\u0439 \u0441\u0435\u0442\u0430\u043F, \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0439, \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430 \u0432 \u0446\u0435\u043B\u043E\u043C \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u0430
|
|
6192
|
+
- 5: \u043E\u0447\u0435\u043D\u044C \u0441\u0438\u043B\u044C\u043D\u044B\u0439 \u0441\u0435\u0442\u0430\u043F, \u0447\u0438\u0441\u0442\u0430\u044F \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430 + \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F + \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u044F\u044F \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u043E\u0441\u0442\u044C \u0443\u0440\u043E\u0432\u043D\u0435\u0439
|
|
5832
6193
|
|
|
5833
6194
|
\u0422\u0440\u0435\u0431\u043E\u0432\u0430\u043D\u0438\u044F \u043A \u043F\u043E\u043B\u0435\u0437\u043D\u043E\u043C\u0443 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u043E\u043C\u0443 \u0430\u043D\u0430\u043B\u0438\u0437\u0443 (\u0431\u0435\u0437 \u0432\u043E\u0434\u044B):
|
|
5834
|
-
- \u0423\u043A\u0430\u0436\u0438 2-4 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u044B\u0445 \u0444\u0430\u043A\u0442\u043E\u0440\u0430 "\u0437\u0430" \u0438\u043B\u0438 "\u043F\u0440\u043E\u0442\u0438\u0432" \
|
|
6195
|
+
- \u0423\u043A\u0430\u0436\u0438 2-4 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u044B\u0445 \u0444\u0430\u043A\u0442\u043E\u0440\u0430 "\u0437\u0430" \u0438\u043B\u0438 "\u043F\u0440\u043E\u0442\u0438\u0432" \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u0441\u0438\u0433\u043D\u0430\u043B\u0430 \u0432 "confirmations".
|
|
5835
6196
|
- \u041E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E \u0443\u043F\u043E\u043C\u044F\u043D\u0438 \u0440\u043E\u043B\u044C \u043A\u043B\u044E\u0447\u0435\u0432\u043E\u0439 \u0444\u0438\u0433\u0443\u0440\u044B/\u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u044B \u0441\u0435\u0442\u0430\u043F\u0430 (\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 \u043F\u0440\u043E\u0431\u043E\u0439/\u0440\u0435\u0442\u0435\u0441\u0442/\u043B\u043E\u0436\u043D\u044B\u0439 \u043F\u0440\u043E\u0431\u043E\u0439/\u043A\u0430\u0441\u0430\u043D\u0438\u0435/\u043D\u0435\u0442 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F).
|
|
5836
6197
|
- \u041E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E \u0443\u043F\u043E\u043C\u044F\u043D\u0438 BTC-\u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442 (\u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442, \u043D\u0435\u0439\u0442\u0440\u0430\u043B\u0435\u043D \u0438\u043B\u0438 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0443\u0435\u0442).
|
|
5837
6198
|
- \u041E\u0431\u044A\u044F\u0441\u043D\u0438, \u043F\u043E\u0447\u0435\u043C\u0443 quality \u0438\u043C\u0435\u043D\u043D\u043E \u0442\u0430\u043A\u043E\u0439.
|
|
5838
|
-
- \u0415\u0441\u043B\u0438 \u043D\u0435 \u0432\
|
|
6199
|
+
- \u0415\u0441\u043B\u0438 signal \u043D\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D (direction=null), \u043F\u0440\u044F\u043C\u043E \u0443\u043A\u0430\u0436\u0438 \u0447\u0442\u043E \u0434\u043E\u043B\u0436\u043D\u043E \u0438\u0437\u043C\u0435\u043D\u0438\u0442\u044C\u0441\u044F \u0434\u043B\u044F \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u0441\u0438\u0433\u043D\u0430\u043B\u0430.
|
|
5839
6200
|
- \u0412 "retestPlan" \u043D\u0435 \u043F\u0438\u0448\u0438 \u0442\u0435\u0445\u043D\u0438\u0447\u0435\u0441\u043A\u0438\u0439 \u0448\u0430\u0431\u043B\u043E\u043D \u0432\u0440\u043E\u0434\u0435 "needRetest=false @ null"; \u043F\u0438\u0448\u0438 \u0447\u0435\u043B\u043E\u0432\u0435\u0447\u0435\u0441\u043A\u043E\u0435 \u043E\u0431\u044A\u044F\u0441\u043D\u0435\u043D\u0438\u0435.
|
|
5840
6201
|
- \u041D\u0435 \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0439 \u043F\u0440\u043E\u0441\u0442\u043E \u043F\u043E\u043B\u044F JSON; \u0434\u0430\u0439 \u0441\u043C\u044B\u0441\u043B \u0438 \u0440\u0435\u0448\u0435\u043D\u0438\u0435.
|
|
5841
6202
|
|
|
@@ -5845,49 +6206,121 @@ var buildAiSystemPrompt = (signal) => `
|
|
|
5845
6206
|
- \u0415\u0441\u043B\u0438 \u0434\u0430\u043D\u043D\u044B\u0445 \u043C\u0430\u043B\u043E \u0434\u043B\u044F \u0443\u0432\u0435\u0440\u0435\u043D\u043D\u043E\u0433\u043E \u0432\u044B\u0432\u043E\u0434\u0430, \u0441\u043D\u0438\u0436\u0430\u0439 quality \u0438 \u0444\u043E\u0440\u043C\u0443\u043B\u0438\u0440\u0443\u0439 \u0432\u044B\u0432\u043E\u0434 \u043E\u0441\u0442\u043E\u0440\u043E\u0436\u043D\u043E.
|
|
5846
6207
|
|
|
5847
6208
|
\u041A\u043E\u0440\u043E\u0442\u043A\u0438\u0435 \u043F\u0440\u0438\u043C\u0435\u0440\u044B (few-shot, \u0444\u043E\u0440\u043C\u0430\u0442 \u043E\u0442\u0432\u0435\u0442\u0430):
|
|
5848
|
-
{"direction":"LONG","quality":4,"needRetest":true,"retestPrice":100.2,"takeProfitPrice":101.5,"stopLossPrice":98.9,"setup":"\u0412\u0435\u0440\u043E\u044F\u0442\u043D\u044B\u0439 \u043F\u0440\u043E\u0431\u043E\u0439 \u0442\u0440\u0435\u043D\u0434\u043E\u0432\u043E\u0439 \u0432\u0432\u0435\u0440\u0445, \u043D\u043E \
|
|
5849
|
-
{"direction":null,"quality":2,"needRetest":false,"retestPrice":null,"takeProfitPrice":null,"stopLossPrice":null,"setup":"\u041A\u0430\u0441\u0430\u043D\u0438\u0435/\u0448\u0443\u043C \u0443 \u0442\u0440\u0435\u043D\u0434\u043E\u0432\u043E\u0439 \u0431\u0435\u0437 \u0443\u0432\u0435\u0440\u0435\u043D\u043D\u043E\u0433\u043E \u043F\u0440\u043E\u0431\u043E\u044F.","confirmations":"\u0418\u043D\u0434\u0438\u043A\u0430\u0442\u043E\u0440\u044B \u0441\u043C\u0435\u0448\u0430\u043D\u043D\u044B\u0435 \u0438 \u043D\u0435 \u0434\u0430\u044E\u0442 \u0441\u0438\u043B\u044C\u043D\u043E\u0433\u043E \u043F\u0440\u0435\u0438\u043C\u0443\u0449\u0435\u0441\u0442\u0432\u0430.","btcContext":"BTC \u0441\u043A\u043E\u0440\u0435\u0435 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0443\u0435\u0442 \u0438\u043B\u0438 \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0438\
|
|
6209
|
+
{"direction":"LONG","quality":4,"needRetest":true,"retestPrice":100.2,"takeProfitPrice":101.5,"stopLossPrice":98.9,"setup":"\u0412\u0435\u0440\u043E\u044F\u0442\u043D\u044B\u0439 \u043F\u0440\u043E\u0431\u043E\u0439 \u0442\u0440\u0435\u043D\u0434\u043E\u0432\u043E\u0439 \u0432\u0432\u0435\u0440\u0445, \u043D\u043E \u0434\u043B\u044F \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u0441\u0438\u0433\u043D\u0430\u043B\u0430 \u043D\u0443\u0436\u043D\u0430 \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u0430\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0443\u0440\u043E\u0432\u043D\u044F.","confirmations":"\u041F\u043E \u043C\u043E\u043D\u0435\u0442\u0435 \u0435\u0441\u0442\u044C \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0430 \u0438\u043C\u043F\u0443\u043B\u044C\u0441\u0430 \u0431\u0435\u0437 \u044F\u0432\u043D\u043E\u0433\u043E \u043F\u0435\u0440\u0435\u0433\u0440\u0435\u0432\u0430, \u043D\u043E \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u0435\u0449\u0435 \u043D\u0435 \u0438\u0434\u0435\u0430\u043B\u044C\u043D\u043E\u0435.","btcContext":"BTC \u043D\u0435\u0439\u0442\u0440\u0430\u043B\u044C\u043D\u043E \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0438\u0434\u0435\u044E \u0438 \u043D\u0435 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0443\u0435\u0442 \u0441 \u0442\u0435\u043A\u0443\u0449\u0438\u043C LONG-\u0441\u0438\u0433\u043D\u0430\u043B\u043E\u043C.","retestPlan":"\u041A\u043B\u044E\u0447\u0435\u0432\u043E\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C 100.2: \u0443\u0434\u0435\u0440\u0436\u0430\u043D\u0438\u0435 \u0432\u044B\u0448\u0435 \u043D\u0435\u0433\u043E \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 \u0441\u0438\u0433\u043D\u0430\u043B\u0430.","riskLevels":"\u041F\u0435\u0440\u0435\u0434\u0430\u043D\u043D\u044B\u0435 TP/SL \u0441\u0442\u043E\u044F\u0442 \u043F\u043E \u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u044B\u0435 \u0441\u0442\u043E\u0440\u043E\u043D\u044B \u043E\u0442 \u0442\u0435\u043A\u0443\u0449\u0435\u0439 \u0446\u0435\u043D\u044B \u0438 \u0432\u044B\u0433\u043B\u044F\u0434\u044F\u0442 \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0435 \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u043E.","qualityReason":"Quality=4: \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430 \u0445\u043E\u0440\u043E\u0448\u0430\u044F, \u043D\u043E \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u043F\u043E \u0443\u0440\u043E\u0432\u043D\u044E \u0435\u0449\u0435 \u0436\u0435\u043B\u0430\u0442\u0435\u043B\u044C\u043D\u043E.","triggerInvalidation":"\u0421\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0435\u0442\u0441\u044F \u043F\u0440\u0438 \u0443\u0434\u0435\u0440\u0436\u0430\u043D\u0438\u0438 \u0443\u0440\u043E\u0432\u043D\u044F; \u0433\u0438\u043F\u043E\u0442\u0435\u0437\u0430 \u043E\u0441\u043B\u0430\u0431\u0435\u0432\u0430\u0435\u0442 \u043F\u0440\u0438 \u0432\u043E\u0437\u0432\u0440\u0430\u0442\u0435 \u043F\u043E\u0434 \u043B\u0438\u043D\u0438\u044E."}
|
|
6210
|
+
{"direction":null,"quality":2,"needRetest":false,"retestPrice":null,"takeProfitPrice":null,"stopLossPrice":null,"setup":"\u041A\u0430\u0441\u0430\u043D\u0438\u0435/\u0448\u0443\u043C \u0443 \u0442\u0440\u0435\u043D\u0434\u043E\u0432\u043E\u0439 \u0431\u0435\u0437 \u0443\u0432\u0435\u0440\u0435\u043D\u043D\u043E\u0433\u043E \u043F\u0440\u043E\u0431\u043E\u044F.","confirmations":"\u0418\u043D\u0434\u0438\u043A\u0430\u0442\u043E\u0440\u044B \u0441\u043C\u0435\u0448\u0430\u043D\u043D\u044B\u0435 \u0438 \u043D\u0435 \u0434\u0430\u044E\u0442 \u0441\u0438\u043B\u044C\u043D\u043E\u0433\u043E \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u043E\u0433\u043E \u043F\u0440\u0435\u0438\u043C\u0443\u0449\u0435\u0441\u0442\u0432\u0430.","btcContext":"BTC \u0441\u043A\u043E\u0440\u0435\u0435 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0443\u0435\u0442 \u0438\u043B\u0438 \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0442\u0435\u043A\u0443\u0449\u0443\u044E \u0433\u0438\u043F\u043E\u0442\u0435\u0437\u0443.","retestPlan":"\u0414\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C \u043F\u043E\u043A\u0430 \u0440\u0430\u043D\u043E \u043E\u0446\u0435\u043D\u0438\u0432\u0430\u0442\u044C, \u043F\u043E\u0442\u043E\u043C\u0443 \u0447\u0442\u043E \u043D\u0435\u0442 \u0441\u0430\u043C\u043E\u0433\u043E \u0444\u0430\u043A\u0442\u0430 \u043A\u0430\u0447\u0435\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0433\u043E \u043F\u0440\u043E\u0431\u043E\u044F.","riskLevels":"\u041F\u0435\u0440\u0435\u0434\u0430\u043D\u043D\u044B\u0435 \u0443\u0440\u043E\u0432\u043D\u0438 \u043F\u043E\u043A\u0430 \u043D\u0435 \u0441\u0442\u043E\u0438\u0442 \u0441\u0447\u0438\u0442\u0430\u0442\u044C \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u043D\u044B\u043C\u0438 \u0438\u0437-\u0437\u0430 \u0441\u043B\u0430\u0431\u043E\u0439 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u044B.","qualityReason":"Quality=2: timing \u0441\u043B\u0430\u0431\u044B\u0439 \u0438 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0439 \u043C\u0430\u043B\u043E.","triggerInvalidation":"\u0416\u0434\u0430\u0442\u044C \u044F\u0432\u043D\u044B\u0439 \u043F\u0440\u043E\u0431\u043E\u0439 \u0438 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u043F\u043E \u043C\u043E\u043D\u0435\u0442\u0435 \u0438 BTC."}
|
|
5850
6211
|
|
|
5851
6212
|
\u0412\u0435\u0440\u043D\u0438 \u0442\u043E\u043B\u044C\u043A\u043E JSON-\u043E\u0431\u044A\u0435\u043A\u0442, \u0431\u0435\u0437 \u043B\u0438\u0448\u043D\u0438\u0445 \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432.
|
|
5852
6213
|
${signal ? buildAiSystemPromptAddonByStrategy(signal) : ""}
|
|
5853
6214
|
`;
|
|
5854
6215
|
var buildAiPayload = (signal) => buildAiPayloadByStrategy(signal);
|
|
5855
6216
|
var buildAiHumanPrompt = (signal, payload = buildAiPayload(signal)) => `
|
|
5856
|
-
\u041F\u0440\u043E\u0430\u043D\u0430\u043B\u0438\u0437\u0438\u0440\u0443\u0439 \u0441\
|
|
5857
|
-
\
|
|
6217
|
+
\u041F\u0440\u043E\u0430\u043D\u0430\u043B\u0438\u0437\u0438\u0440\u0443\u0439 \u0443\u0436\u0435 \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043D\u043D\u044B\u0439 \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0438\u0439 \u0441\u0438\u0433\u043D\u0430\u043B \u043F\u043E ${signal.symbol}. \u0418\u0441\u0445\u043E\u0434\u043D\u044B\u0439 \u0441\u0438\u0433\u043D\u0430\u043B \u0438\u043C\u0435\u0435\u0442 \u043D\u0430\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 ${signal.direction}.
|
|
6218
|
+
\u042D\u0442\u043E \u0437\u0430\u0434\u0430\u0447\u0430 \u043A\u043B\u0430\u0441\u0441\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438/\u0430\u0443\u0434\u0438\u0442\u0430 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u044B, \u0430 \u043D\u0435 \u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0430\u0446\u0438\u044F \u043A \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044E. \u041E\u043F\u0440\u0435\u0434\u0435\u043B\u0438, \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0435\u0442 \u043B\u0438 \u0442\u0435\u043A\u0443\u0449\u0430\u044F \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0439 \u0441\u0438\u0433\u043D\u0430\u043B, \u043D\u0430\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u043E\u043D \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u043E \u0441\u0435\u0439\u0447\u0430\u0441, \u043D\u0443\u0436\u0435\u043D \u043B\u0438 \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0439 confirmation level, \u0438 \u0432\u044B\u0433\u043B\u044F\u0434\u044F\u0442 \u043B\u0438 \u0443\u0436\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u043D\u043D\u044B\u0435 \u0432 payload.signal.prices \u0443\u0440\u043E\u0432\u043D\u0438 \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0435 \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u044B\u043C\u0438. \u041D\u0435 \u0444\u043E\u0440\u043C\u0438\u0440\u0443\u0439 \u043D\u043E\u0432\u0443\u044E \u0433\u0438\u043F\u043E\u0442\u0435\u0437\u0443 \u0432\u043C\u0435\u0441\u0442\u043E \u0438\u0441\u0445\u043E\u0434\u043D\u043E\u0433\u043E \u0441\u0438\u0433\u043D\u0430\u043B\u0430 \u0438 \u043D\u0435 \u043F\u0440\u0438\u0434\u0443\u043C\u044B\u0432\u0430\u0439 \u043D\u043E\u0432\u044B\u0435 \u0443\u0440\u043E\u0432\u043D\u0438; \u0432\u0435\u0440\u043D\u0438 \u0442\u043E\u043B\u044C\u043A\u043E JSON \u0432 \u0437\u0430\u0434\u0430\u043D\u043D\u043E\u043C \u0444\u043E\u0440\u043C\u0430\u0442\u0435.
|
|
5858
6219
|
|
|
5859
6220
|
\u0414\u0430\u043D\u043D\u044B\u0435 \u0441\u0434\u0435\u043B\u043A\u0438:
|
|
5860
6221
|
${JSON.stringify(payload)}
|
|
5861
6222
|
${buildAiHumanPromptAddonByStrategy(signal, payload)}
|
|
5862
6223
|
`;
|
|
5863
|
-
var
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
6224
|
+
var DEFAULT_AI_MODEL = "openai/gpt-5-mini";
|
|
6225
|
+
var userSettingsCache = /* @__PURE__ */ new Map();
|
|
6226
|
+
var aiModelCache = /* @__PURE__ */ new Map();
|
|
6227
|
+
var getAiModelCacheKey = (userName, modelName) => `${userName}::${modelName}`;
|
|
6228
|
+
var getOpenRouterModelKwargs = (apiEndpoint) => {
|
|
6229
|
+
const endpoint = String(apiEndpoint ?? "").trim();
|
|
6230
|
+
if (!endpoint) {
|
|
6231
|
+
return {};
|
|
6232
|
+
}
|
|
6233
|
+
let hostname = "";
|
|
6234
|
+
try {
|
|
6235
|
+
hostname = new URL(endpoint).hostname;
|
|
6236
|
+
} catch {
|
|
6237
|
+
hostname = endpoint;
|
|
6238
|
+
}
|
|
6239
|
+
if (!hostname.toLowerCase().includes("openrouter")) {
|
|
6240
|
+
return {};
|
|
6241
|
+
}
|
|
6242
|
+
return {
|
|
6243
|
+
provider: {
|
|
6244
|
+
ignore: ["azure"]
|
|
6245
|
+
}
|
|
6246
|
+
};
|
|
6247
|
+
};
|
|
6248
|
+
var getAiSettings = async (userName = "root") => {
|
|
6249
|
+
let settingsPromise = userSettingsCache.get(userName);
|
|
6250
|
+
if (!settingsPromise) {
|
|
6251
|
+
settingsPromise = (0, import_userSettings.getUserSettings)(userName);
|
|
6252
|
+
settingsPromise.catch(() => {
|
|
6253
|
+
userSettingsCache.delete(userName);
|
|
6254
|
+
});
|
|
6255
|
+
userSettingsCache.set(userName, settingsPromise);
|
|
6256
|
+
}
|
|
6257
|
+
const settings = await settingsPromise;
|
|
6258
|
+
if (!settings.OPENAI_API_KEY || !settings.OPENAI_API_ENDPOINT) {
|
|
6259
|
+
throw new Error(`AI settings are incomplete for user ${userName}`);
|
|
6260
|
+
}
|
|
6261
|
+
return settings;
|
|
6262
|
+
};
|
|
6263
|
+
var createAiModel = async (userName = "root", modelName = DEFAULT_AI_MODEL) => {
|
|
6264
|
+
const cacheKey = getAiModelCacheKey(userName, modelName);
|
|
6265
|
+
let modelPromise = aiModelCache.get(cacheKey);
|
|
6266
|
+
if (!modelPromise) {
|
|
6267
|
+
modelPromise = (async () => {
|
|
6268
|
+
const [{ ChatOpenAI }, settings] = await Promise.all([
|
|
6269
|
+
import("@langchain/openai"),
|
|
6270
|
+
getAiSettings(userName)
|
|
6271
|
+
]);
|
|
6272
|
+
const modelKwargs = getOpenRouterModelKwargs(
|
|
6273
|
+
settings.OPENAI_API_ENDPOINT
|
|
6274
|
+
);
|
|
6275
|
+
return new ChatOpenAI({
|
|
6276
|
+
temperature: 0.2,
|
|
6277
|
+
modelName,
|
|
6278
|
+
apiKey: settings.OPENAI_API_KEY,
|
|
6279
|
+
...Object.keys(modelKwargs).length ? { modelKwargs } : {},
|
|
6280
|
+
configuration: {
|
|
6281
|
+
baseURL: settings.OPENAI_API_ENDPOINT,
|
|
6282
|
+
defaultHeaders: {
|
|
6283
|
+
"HTTP-Referer": "https://tradejs.dev",
|
|
6284
|
+
"X-Title": "Inv"
|
|
6285
|
+
}
|
|
6286
|
+
}
|
|
6287
|
+
});
|
|
6288
|
+
})();
|
|
6289
|
+
modelPromise.catch(() => {
|
|
6290
|
+
aiModelCache.delete(cacheKey);
|
|
6291
|
+
});
|
|
6292
|
+
aiModelCache.set(cacheKey, modelPromise);
|
|
6293
|
+
}
|
|
6294
|
+
return modelPromise;
|
|
6295
|
+
};
|
|
6296
|
+
var getAiModel = async (userName = "root", modelName = DEFAULT_AI_MODEL) => {
|
|
6297
|
+
try {
|
|
6298
|
+
return await createAiModel(userName, modelName);
|
|
6299
|
+
} catch (error) {
|
|
6300
|
+
aiModelCache.delete(getAiModelCacheKey(userName, modelName));
|
|
6301
|
+
userSettingsCache.delete(userName);
|
|
6302
|
+
throw error;
|
|
6303
|
+
}
|
|
6304
|
+
};
|
|
6305
|
+
var ensureAiStrategyPluginsLoaded = async () => {
|
|
6306
|
+
await ensureStrategyPluginsLoaded();
|
|
6307
|
+
};
|
|
6308
|
+
var runAiPrompt = async ({ systemPrompt, humanPrompt }, options = {}) => {
|
|
6309
|
+
if (options.signal) {
|
|
6310
|
+
await ensureAiStrategyPluginsLoaded();
|
|
6311
|
+
}
|
|
6312
|
+
const [{ HumanMessage, SystemMessage }, model] = await Promise.all([
|
|
6313
|
+
import("@langchain/core/messages"),
|
|
6314
|
+
getAiModel(options.userName, options.model)
|
|
5867
6315
|
]);
|
|
5868
|
-
const { symbol } = signal;
|
|
5869
6316
|
const messages = [];
|
|
5870
|
-
|
|
5871
|
-
temperature: 0.2,
|
|
5872
|
-
//modelName: 'anthropic/claude-sonnet-4.5',
|
|
5873
|
-
modelName: "google/gemini-3.1-pro-preview",
|
|
5874
|
-
openAIApiKey: process.env.OPENAI_API_KEY,
|
|
5875
|
-
configuration: {
|
|
5876
|
-
baseURL: process.env.OPENAI_API_ENDPOINT || "https://api.openai.com/v1",
|
|
5877
|
-
defaultHeaders: {
|
|
5878
|
-
"HTTP-Referer": "https://tradejs.dev",
|
|
5879
|
-
"X-Title": "Inv"
|
|
5880
|
-
}
|
|
5881
|
-
}
|
|
5882
|
-
});
|
|
5883
|
-
messages.push(new SystemMessage(buildAiSystemPrompt(signal)));
|
|
5884
|
-
const payload = buildAiPayload(signal);
|
|
6317
|
+
messages.push(new SystemMessage(systemPrompt));
|
|
5885
6318
|
messages.push(
|
|
5886
6319
|
new HumanMessage({
|
|
5887
6320
|
content: [
|
|
5888
6321
|
{
|
|
5889
6322
|
type: "text",
|
|
5890
|
-
text:
|
|
6323
|
+
text: humanPrompt
|
|
5891
6324
|
}
|
|
5892
6325
|
]
|
|
5893
6326
|
})
|
|
@@ -5896,7 +6329,31 @@ var askAI = async (signal) => {
|
|
|
5896
6329
|
const parsed = parseAIResponse(
|
|
5897
6330
|
normalizeResponseContent(response.content)
|
|
5898
6331
|
);
|
|
5899
|
-
const
|
|
6332
|
+
const normalized = normalizeAnalysis(parsed);
|
|
6333
|
+
if (!options.signal) {
|
|
6334
|
+
return normalized;
|
|
6335
|
+
}
|
|
6336
|
+
return postProcessAiAnalysisByStrategy(
|
|
6337
|
+
options.signal,
|
|
6338
|
+
normalized,
|
|
6339
|
+
options.payload
|
|
6340
|
+
);
|
|
6341
|
+
};
|
|
6342
|
+
var askAI = async (signal, options = {}) => {
|
|
6343
|
+
const { symbol } = signal;
|
|
6344
|
+
await ensureAiStrategyPluginsLoaded();
|
|
6345
|
+
const payload = buildAiPayload(signal);
|
|
6346
|
+
const content = await runAiPrompt(
|
|
6347
|
+
{
|
|
6348
|
+
systemPrompt: buildAiSystemPrompt(signal),
|
|
6349
|
+
humanPrompt: buildAiHumanPrompt(signal, payload)
|
|
6350
|
+
},
|
|
6351
|
+
{
|
|
6352
|
+
...options,
|
|
6353
|
+
signal,
|
|
6354
|
+
payload
|
|
6355
|
+
}
|
|
6356
|
+
);
|
|
5900
6357
|
await (0, import_redis.setData)(import_redis.redisKeys.analysis(symbol, signal.signalId), content);
|
|
5901
6358
|
return content;
|
|
5902
6359
|
};
|
|
@@ -5907,7 +6364,7 @@ var import_path2 = __toESM(require("path"));
|
|
|
5907
6364
|
var import_puppeteer = __toESM(require("puppeteer"));
|
|
5908
6365
|
var import_async = require("@tradejs/core/async");
|
|
5909
6366
|
var import_logger3 = require("@tradejs/infra/logger");
|
|
5910
|
-
var
|
|
6367
|
+
var import_userSettings2 = require("@tradejs/infra/userSettings");
|
|
5911
6368
|
var { APP_URL } = process.env;
|
|
5912
6369
|
var SCREENSHOT_NAVIGATION_ATTEMPTS = 3;
|
|
5913
6370
|
var SCREENSHOT_NAVIGATION_RETRY_DELAY_MS = 2e3;
|
|
@@ -5927,13 +6384,59 @@ var SCREENSHOT_VIEWPORT = {
|
|
|
5927
6384
|
var getProjectRoot = (projectRoot) => import_path2.default.resolve(getTradejsProjectCwd(projectRoot));
|
|
5928
6385
|
var getScreenshotsDir = (projectRoot) => import_path2.default.join(getProjectRoot(projectRoot), "data", "screenshots");
|
|
5929
6386
|
var maskTokenInUrl = (url) => url.replace(/([?&]token=)[^&]+/i, "$1<hidden>");
|
|
6387
|
+
var getErrorFields = (value) => {
|
|
6388
|
+
if (!value || typeof value !== "object") {
|
|
6389
|
+
return [];
|
|
6390
|
+
}
|
|
6391
|
+
const record = value;
|
|
6392
|
+
const fields = [
|
|
6393
|
+
["name", record.name],
|
|
6394
|
+
["code", record.code],
|
|
6395
|
+
["errno", record.errno],
|
|
6396
|
+
["type", record.type],
|
|
6397
|
+
["syscall", record.syscall],
|
|
6398
|
+
["hostname", record.hostname],
|
|
6399
|
+
["host", record.host],
|
|
6400
|
+
["address", record.address],
|
|
6401
|
+
["port", record.port]
|
|
6402
|
+
];
|
|
6403
|
+
return fields.filter(([, fieldValue]) => fieldValue != null && String(fieldValue).trim()).map(([key, fieldValue]) => `${key}=${String(fieldValue)}`);
|
|
6404
|
+
};
|
|
6405
|
+
var describeErrorValue = (value) => {
|
|
6406
|
+
if (value == null) {
|
|
6407
|
+
return "";
|
|
6408
|
+
}
|
|
6409
|
+
if (typeof value === "string") {
|
|
6410
|
+
return value;
|
|
6411
|
+
}
|
|
6412
|
+
if (value instanceof Error) {
|
|
6413
|
+
const message = value.message?.trim() || "";
|
|
6414
|
+
const fields = getErrorFields(value);
|
|
6415
|
+
return [message, ...fields].filter(Boolean).join(" ");
|
|
6416
|
+
}
|
|
6417
|
+
if (typeof value === "object") {
|
|
6418
|
+
const fields = getErrorFields(value);
|
|
6419
|
+
if (fields.length) {
|
|
6420
|
+
return fields.join(" ");
|
|
6421
|
+
}
|
|
6422
|
+
try {
|
|
6423
|
+
return JSON.stringify(value);
|
|
6424
|
+
} catch {
|
|
6425
|
+
return String(value);
|
|
6426
|
+
}
|
|
6427
|
+
}
|
|
6428
|
+
return String(value);
|
|
6429
|
+
};
|
|
5930
6430
|
var getErrorMessage = (error) => {
|
|
5931
6431
|
const maybeError = error;
|
|
5932
|
-
const message =
|
|
6432
|
+
const message = describeErrorValue(error) || String(error);
|
|
5933
6433
|
if (maybeError?.cause == null) {
|
|
5934
6434
|
return message;
|
|
5935
6435
|
}
|
|
5936
|
-
const cause =
|
|
6436
|
+
const cause = describeErrorValue(maybeError.cause);
|
|
6437
|
+
if (!cause) {
|
|
6438
|
+
return message;
|
|
6439
|
+
}
|
|
5937
6440
|
return `${message}; cause: ${cause}`;
|
|
5938
6441
|
};
|
|
5939
6442
|
var truncateText = (value, maxLength = SCREENSHOT_CONSOLE_TEXT_LIMIT) => value.length > maxLength ? `${value.slice(0, maxLength)}...` : value;
|
|
@@ -5960,13 +6463,13 @@ var getScreenshotPath = ({ symbol, signalId, interval }, projectRoot) => {
|
|
|
5960
6463
|
getScreenshotFilename({ symbol, signalId, interval })
|
|
5961
6464
|
);
|
|
5962
6465
|
};
|
|
5963
|
-
var screenDashboard = async (signal, projectRoot) => {
|
|
6466
|
+
var screenDashboard = async (signal, projectRoot, userName = "root") => {
|
|
5964
6467
|
const { symbol, signalId, interval } = signal;
|
|
5965
6468
|
const screenshotBaseUrl = getScreenshotRenderBaseUrl();
|
|
5966
6469
|
const screenshotPath = getScreenshotPath(signal, projectRoot);
|
|
5967
|
-
const
|
|
5968
|
-
const
|
|
5969
|
-
const tokenParam = typeof
|
|
6470
|
+
const settings = await (0, import_userSettings2.getUserSettings)(userName);
|
|
6471
|
+
const token = settings.token;
|
|
6472
|
+
const tokenParam = typeof token === "string" && token.length > 0 ? `&token=${encodeURIComponent(token)}` : "";
|
|
5970
6473
|
const dashboardUrl = `${screenshotBaseUrl}/routes/dashboard/bybit/${symbol}/${interval}/?signalId=${signalId}&autoZoom=true&screenshot=1${tokenParam}`;
|
|
5971
6474
|
const maskedDashboardUrl = maskTokenInUrl(dashboardUrl);
|
|
5972
6475
|
import_logger3.logger.info(
|
|
@@ -5991,6 +6494,7 @@ var screenDashboard = async (signal, projectRoot) => {
|
|
|
5991
6494
|
]
|
|
5992
6495
|
});
|
|
5993
6496
|
const browserProcess = browser.process();
|
|
6497
|
+
let browserCloseRequested = false;
|
|
5994
6498
|
try {
|
|
5995
6499
|
if (browserProcess) {
|
|
5996
6500
|
import_logger3.logger.info(
|
|
@@ -6010,6 +6514,9 @@ var screenDashboard = async (signal, projectRoot) => {
|
|
|
6010
6514
|
await browser.version()
|
|
6011
6515
|
);
|
|
6012
6516
|
browser.on("disconnected", () => {
|
|
6517
|
+
if (browserCloseRequested) {
|
|
6518
|
+
return;
|
|
6519
|
+
}
|
|
6013
6520
|
import_logger3.logger.error(
|
|
6014
6521
|
"screenshot browser disconnected: %s %sm captureAttempt=%d",
|
|
6015
6522
|
symbol,
|
|
@@ -6208,6 +6715,7 @@ var screenDashboard = async (signal, projectRoot) => {
|
|
|
6208
6715
|
await (0, import_async.delay)(SCREENSHOT_CAPTURE_RETRY_DELAY_MS);
|
|
6209
6716
|
}
|
|
6210
6717
|
} finally {
|
|
6718
|
+
browserCloseRequested = true;
|
|
6211
6719
|
await browser.close().catch(() => void 0);
|
|
6212
6720
|
}
|
|
6213
6721
|
}
|
|
@@ -6218,14 +6726,65 @@ var screenDashboard = async (signal, projectRoot) => {
|
|
|
6218
6726
|
var import_async2 = require("@tradejs/core/async");
|
|
6219
6727
|
var import_math = require("@tradejs/core/math");
|
|
6220
6728
|
var import_logger4 = require("@tradejs/infra/logger");
|
|
6729
|
+
var import_userSettings3 = require("@tradejs/infra/userSettings");
|
|
6221
6730
|
var escapeHtml = (s) => {
|
|
6222
6731
|
if (!s) return "";
|
|
6223
6732
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
6224
6733
|
};
|
|
6225
|
-
var { APP_URL: APP_URL2
|
|
6734
|
+
var { APP_URL: APP_URL2 } = process.env;
|
|
6226
6735
|
var TG_REQUEST_ATTEMPTS = 3;
|
|
6227
6736
|
var TG_REQUEST_RETRY_DELAY_MS = 2e3;
|
|
6737
|
+
var getErrorFields2 = (value) => {
|
|
6738
|
+
if (!value || typeof value !== "object") {
|
|
6739
|
+
return [];
|
|
6740
|
+
}
|
|
6741
|
+
const record = value;
|
|
6742
|
+
const fields = [
|
|
6743
|
+
["name", record.name],
|
|
6744
|
+
["code", record.code],
|
|
6745
|
+
["errno", record.errno],
|
|
6746
|
+
["type", record.type],
|
|
6747
|
+
["syscall", record.syscall],
|
|
6748
|
+
["hostname", record.hostname],
|
|
6749
|
+
["host", record.host],
|
|
6750
|
+
["address", record.address],
|
|
6751
|
+
["port", record.port]
|
|
6752
|
+
];
|
|
6753
|
+
return fields.filter(([, fieldValue]) => fieldValue != null && String(fieldValue).trim()).map(([key, fieldValue]) => `${key}=${String(fieldValue)}`);
|
|
6754
|
+
};
|
|
6755
|
+
var describeErrorValue2 = (value) => {
|
|
6756
|
+
if (value == null) {
|
|
6757
|
+
return "";
|
|
6758
|
+
}
|
|
6759
|
+
if (typeof value === "string") {
|
|
6760
|
+
return value;
|
|
6761
|
+
}
|
|
6762
|
+
if (value instanceof Error) {
|
|
6763
|
+
const message = value.message?.trim() || "";
|
|
6764
|
+
const fields = getErrorFields2(value);
|
|
6765
|
+
return [message, ...fields].filter(Boolean).join(" ");
|
|
6766
|
+
}
|
|
6767
|
+
if (typeof value === "object") {
|
|
6768
|
+
const fields = getErrorFields2(value);
|
|
6769
|
+
if (fields.length) {
|
|
6770
|
+
return fields.join(" ");
|
|
6771
|
+
}
|
|
6772
|
+
try {
|
|
6773
|
+
return JSON.stringify(value);
|
|
6774
|
+
} catch {
|
|
6775
|
+
return String(value);
|
|
6776
|
+
}
|
|
6777
|
+
}
|
|
6778
|
+
return String(value);
|
|
6779
|
+
};
|
|
6228
6780
|
var normalizeQuality = (value) => typeof value === "number" ? Math.max(1, Math.min(5, Math.round(value))) : null;
|
|
6781
|
+
var formatOrderSkipReason = (reason) => {
|
|
6782
|
+
if (!reason) return "";
|
|
6783
|
+
if (reason.startsWith("AI_QUALITY_BELOW_MIN")) {
|
|
6784
|
+
return "AI_QUALITY_BELOW_MIN";
|
|
6785
|
+
}
|
|
6786
|
+
return reason;
|
|
6787
|
+
};
|
|
6229
6788
|
var getLastNumber = (value) => {
|
|
6230
6789
|
if (Array.isArray(value)) {
|
|
6231
6790
|
const last = value[value.length - 1];
|
|
@@ -6265,11 +6824,14 @@ var getTelegramErrorReason = (data) => {
|
|
|
6265
6824
|
};
|
|
6266
6825
|
var getErrorMessage2 = (error) => {
|
|
6267
6826
|
const maybeError = error;
|
|
6268
|
-
const message =
|
|
6827
|
+
const message = describeErrorValue2(error) || String(error);
|
|
6269
6828
|
if (maybeError?.cause == null) {
|
|
6270
6829
|
return message;
|
|
6271
6830
|
}
|
|
6272
|
-
const cause =
|
|
6831
|
+
const cause = describeErrorValue2(maybeError.cause);
|
|
6832
|
+
if (!cause) {
|
|
6833
|
+
return message;
|
|
6834
|
+
}
|
|
6273
6835
|
return `${message}; cause: ${cause}`;
|
|
6274
6836
|
};
|
|
6275
6837
|
var parseTelegramResponse = async (response) => {
|
|
@@ -6292,7 +6854,20 @@ var parseTelegramResponse = async (response) => {
|
|
|
6292
6854
|
}
|
|
6293
6855
|
}
|
|
6294
6856
|
};
|
|
6295
|
-
var
|
|
6857
|
+
var getTelegramSettings = async (userName = "root") => {
|
|
6858
|
+
const settings = await (0, import_userSettings3.getUserSettings)(userName);
|
|
6859
|
+
const token = settings.TG_BOT_TOKEN;
|
|
6860
|
+
const chatId = settings.TG_CHAT_ID;
|
|
6861
|
+
if (!token || !chatId) {
|
|
6862
|
+
throw new Error(`Telegram settings are incomplete for user ${userName}`);
|
|
6863
|
+
}
|
|
6864
|
+
return { token, chatId };
|
|
6865
|
+
};
|
|
6866
|
+
var requestTelegram = async ({
|
|
6867
|
+
method,
|
|
6868
|
+
token,
|
|
6869
|
+
init
|
|
6870
|
+
}) => {
|
|
6296
6871
|
let lastError = null;
|
|
6297
6872
|
for (let attempt = 1; attempt <= TG_REQUEST_ATTEMPTS; attempt += 1) {
|
|
6298
6873
|
try {
|
|
@@ -6320,17 +6895,23 @@ var requestTelegram = async (method, init) => {
|
|
|
6320
6895
|
};
|
|
6321
6896
|
var sendTelegramMessage = async ({
|
|
6322
6897
|
message,
|
|
6323
|
-
markup
|
|
6898
|
+
markup,
|
|
6899
|
+
token,
|
|
6900
|
+
chatId
|
|
6324
6901
|
}) => {
|
|
6325
|
-
const data = await requestTelegram(
|
|
6326
|
-
method: "
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
|
|
6902
|
+
const data = await requestTelegram({
|
|
6903
|
+
method: "sendMessage",
|
|
6904
|
+
token,
|
|
6905
|
+
init: {
|
|
6906
|
+
method: "POST",
|
|
6907
|
+
headers: { "Content-Type": "application/json" },
|
|
6908
|
+
body: JSON.stringify({
|
|
6909
|
+
chat_id: chatId,
|
|
6910
|
+
text: message,
|
|
6911
|
+
reply_markup: markup,
|
|
6912
|
+
parse_mode: "HTML"
|
|
6913
|
+
})
|
|
6914
|
+
}
|
|
6334
6915
|
});
|
|
6335
6916
|
import_logger4.logger.info(
|
|
6336
6917
|
"tg sendMessage: %s",
|
|
@@ -6389,7 +6970,9 @@ var formatMessage = (signal, analysis) => {
|
|
|
6389
6970
|
}
|
|
6390
6971
|
lines.push(orderStatusText);
|
|
6391
6972
|
if ((orderStatus === "skipped" || orderStatus === "canceled") && orderSkipReason) {
|
|
6392
|
-
lines.push(
|
|
6973
|
+
lines.push(
|
|
6974
|
+
`Skip reason: <b>${escapeHtml(formatOrderSkipReason(orderSkipReason))}</b>`
|
|
6975
|
+
);
|
|
6393
6976
|
}
|
|
6394
6977
|
}
|
|
6395
6978
|
if (isConfigFromBacktest) {
|
|
@@ -6407,21 +6990,9 @@ var formatMessage = (signal, analysis) => {
|
|
|
6407
6990
|
lines.push(aiQualityLine);
|
|
6408
6991
|
}
|
|
6409
6992
|
lines.push("");
|
|
6410
|
-
if (touches) {
|
|
6411
|
-
lines.push(`Points: ${touches}`);
|
|
6412
|
-
}
|
|
6413
|
-
if (atrPct != null && Number.isFinite(atrPct)) {
|
|
6414
|
-
lines.push(`ATR: ${atrPct.toFixed(2)}`);
|
|
6415
|
-
}
|
|
6416
|
-
if (distance) {
|
|
6417
|
-
lines.push(`Distance: ${distance}`);
|
|
6418
|
-
}
|
|
6419
6993
|
if (correlation) {
|
|
6420
6994
|
lines.push(`BTC correlation: ${correlation}`);
|
|
6421
6995
|
}
|
|
6422
|
-
if (spread != null && Number.isFinite(spread)) {
|
|
6423
|
-
lines.push(`BTC spread (CB-BN)/BN: ${spread.toFixed(6)}`);
|
|
6424
|
-
}
|
|
6425
6996
|
const prices = formatPrices();
|
|
6426
6997
|
if (prices) {
|
|
6427
6998
|
lines.push("");
|
|
@@ -6435,8 +7006,9 @@ var formatMessage = (signal, analysis) => {
|
|
|
6435
7006
|
\u0414\u0435\u0442\u0430\u043B\u0438: ${err.message || String(err)}`;
|
|
6436
7007
|
}
|
|
6437
7008
|
};
|
|
6438
|
-
var sendSignal = async (signal, imgInterval, analysis) => {
|
|
7009
|
+
var sendSignal = async (signal, imgInterval, analysis, options = {}) => {
|
|
6439
7010
|
const { symbol, signalId, interval } = signal;
|
|
7011
|
+
const { token, chatId } = await getTelegramSettings(options.userName);
|
|
6440
7012
|
const message = formatMessage(signal, analysis);
|
|
6441
7013
|
const publicAppUrl = APP_URL2?.startsWith("https") ? APP_URL2 : null;
|
|
6442
7014
|
const dashboardUrl = publicAppUrl ? `${APP_URL2}/routes/dashboard/bybit/${symbol}/${interval}/?signalId=${signalId}` : null;
|
|
@@ -6462,7 +7034,7 @@ var sendSignal = async (signal, imgInterval, analysis) => {
|
|
|
6462
7034
|
symbol,
|
|
6463
7035
|
error?.message || String(error)
|
|
6464
7036
|
);
|
|
6465
|
-
await sendTelegramMessage({ message, markup });
|
|
7037
|
+
await sendTelegramMessage({ message, markup, token, chatId });
|
|
6466
7038
|
return;
|
|
6467
7039
|
}
|
|
6468
7040
|
const photoBody = new FormData();
|
|
@@ -6479,9 +7051,13 @@ var sendSignal = async (signal, imgInterval, analysis) => {
|
|
|
6479
7051
|
}
|
|
6480
7052
|
let data;
|
|
6481
7053
|
try {
|
|
6482
|
-
data = await requestTelegram(
|
|
6483
|
-
method: "
|
|
6484
|
-
|
|
7054
|
+
data = await requestTelegram({
|
|
7055
|
+
method: "sendPhoto",
|
|
7056
|
+
token,
|
|
7057
|
+
init: {
|
|
7058
|
+
method: "POST",
|
|
7059
|
+
body: photoBody
|
|
7060
|
+
}
|
|
6485
7061
|
});
|
|
6486
7062
|
} catch (error) {
|
|
6487
7063
|
const reason = getErrorMessage2(error);
|
|
@@ -6491,7 +7067,9 @@ var sendSignal = async (signal, imgInterval, analysis) => {
|
|
|
6491
7067
|
|
|
6492
7068
|
\u26A0\uFE0F <b>Photo delivery failed</b>
|
|
6493
7069
|
Reason: <code>${escapeHtml(reason)}</code>`,
|
|
6494
|
-
markup
|
|
7070
|
+
markup,
|
|
7071
|
+
token,
|
|
7072
|
+
chatId
|
|
6495
7073
|
});
|
|
6496
7074
|
return;
|
|
6497
7075
|
}
|
|
@@ -6503,7 +7081,9 @@ Reason: <code>${escapeHtml(reason)}</code>`,
|
|
|
6503
7081
|
|
|
6504
7082
|
\u26A0\uFE0F <b>Photo delivery failed</b>
|
|
6505
7083
|
Reason: <code>${escapeHtml(reason)}</code>`,
|
|
6506
|
-
markup
|
|
7084
|
+
markup,
|
|
7085
|
+
token,
|
|
7086
|
+
chatId
|
|
6507
7087
|
});
|
|
6508
7088
|
return;
|
|
6509
7089
|
}
|
|
@@ -6551,16 +7131,21 @@ ${escapeHtml(clean)}`);
|
|
|
6551
7131
|
}
|
|
6552
7132
|
return lines.join("\n");
|
|
6553
7133
|
};
|
|
6554
|
-
var sendSignalAnalysis = async (signal, analysis) => {
|
|
7134
|
+
var sendSignalAnalysis = async (signal, analysis, options = {}) => {
|
|
7135
|
+
const { token, chatId } = await getTelegramSettings(options.userName);
|
|
6555
7136
|
const message = formatAnalysisMessage(signal, analysis);
|
|
6556
|
-
const data = await requestTelegram(
|
|
6557
|
-
method: "
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
7137
|
+
const data = await requestTelegram({
|
|
7138
|
+
method: "sendMessage",
|
|
7139
|
+
token,
|
|
7140
|
+
init: {
|
|
7141
|
+
method: "POST",
|
|
7142
|
+
headers: { "Content-Type": "application/json" },
|
|
7143
|
+
body: JSON.stringify({
|
|
7144
|
+
chat_id: chatId,
|
|
7145
|
+
text: message,
|
|
7146
|
+
parse_mode: "HTML"
|
|
7147
|
+
})
|
|
7148
|
+
}
|
|
6564
7149
|
});
|
|
6565
7150
|
import_logger4.logger.info(
|
|
6566
7151
|
"tg sendMessage (analysis): %s",
|
|
@@ -6596,7 +7181,7 @@ var cleanFiles = async (dir) => {
|
|
|
6596
7181
|
};
|
|
6597
7182
|
var cleanRedis = async (area) => {
|
|
6598
7183
|
let completed = 0;
|
|
6599
|
-
const keys = await (0,
|
|
7184
|
+
const keys = await (0, import_redis2.getKeys)(area);
|
|
6600
7185
|
const bar = new import_progress.default(":current/:total [:bar][:percent] :eta(s)", {
|
|
6601
7186
|
total: keys.length,
|
|
6602
7187
|
width: 30
|
|
@@ -6605,13 +7190,13 @@ var cleanRedis = async (area) => {
|
|
|
6605
7190
|
try {
|
|
6606
7191
|
for await (const key of keys) {
|
|
6607
7192
|
completed++;
|
|
6608
|
-
await (0,
|
|
7193
|
+
await (0, import_redis2.delKeyWithOptions)(key, { raiseOnMisconf: true });
|
|
6609
7194
|
if (completed % 100 === 0 || completed === keys.length) {
|
|
6610
7195
|
bar.tick(completed === keys.length ? completed % 100 : 100);
|
|
6611
7196
|
}
|
|
6612
7197
|
}
|
|
6613
7198
|
} catch (e) {
|
|
6614
|
-
if (e instanceof
|
|
7199
|
+
if (e instanceof import_redis2.RedisWriteBlockedError || String(e).includes("MISCONF")) {
|
|
6615
7200
|
import_logger5.logger.error(
|
|
6616
7201
|
"Redis write is blocked by MISCONF (RDB save failure). Cleanup stopped early."
|
|
6617
7202
|
);
|
|
@@ -6625,8 +7210,8 @@ var cleanRedis = async (area) => {
|
|
|
6625
7210
|
}
|
|
6626
7211
|
import_logger5.logger.info("");
|
|
6627
7212
|
};
|
|
6628
|
-
var update = async (connector, interval, tickers) => {
|
|
6629
|
-
const PRELOAD_START = (0, import_time.getTimestamp)(
|
|
7213
|
+
var update = async (connector, interval, tickers, preloadDays = import_constants.PRELOAD_DAYS) => {
|
|
7214
|
+
const PRELOAD_START = (0, import_time.getTimestamp)(preloadDays);
|
|
6630
7215
|
const PRELOAD_END = (0, import_time.getTimestamp)();
|
|
6631
7216
|
const bar = new import_progress.default(
|
|
6632
7217
|
":current/:total [:bar][:percent] :eta(s) :symbol",
|
|
@@ -6637,7 +7222,7 @@ var update = async (connector, interval, tickers) => {
|
|
|
6637
7222
|
);
|
|
6638
7223
|
import_logger5.logger.info(
|
|
6639
7224
|
import_chalk.default.yellow(
|
|
6640
|
-
`update: ${tickers.length} (klineConcurrency=${KLINE_CONCURRENCY_LIMIT})`
|
|
7225
|
+
`update: ${tickers.length} (klineConcurrency=${KLINE_CONCURRENCY_LIMIT}, preloadDays=${preloadDays})`
|
|
6641
7226
|
)
|
|
6642
7227
|
);
|
|
6643
7228
|
const queue = tickers.slice();
|
|
@@ -6651,7 +7236,8 @@ var update = async (connector, interval, tickers) => {
|
|
|
6651
7236
|
start: PRELOAD_START,
|
|
6652
7237
|
end: PRELOAD_END,
|
|
6653
7238
|
interval,
|
|
6654
|
-
silent: true
|
|
7239
|
+
silent: true,
|
|
7240
|
+
warmOnly: true
|
|
6655
7241
|
});
|
|
6656
7242
|
} catch {
|
|
6657
7243
|
import_logger5.logger.error("Failed loading: %s", symbol);
|
|
@@ -6704,7 +7290,7 @@ var getTickers = async (connector, include = "", exclude = "", limit, chunk) =>
|
|
|
6704
7290
|
}
|
|
6705
7291
|
return tickers.filter((t) => !excludeTickers.includes(t));
|
|
6706
7292
|
};
|
|
6707
|
-
var makeScreenshots = async (signals, interval) => {
|
|
7293
|
+
var makeScreenshots = async (signals, interval, userName = "root") => {
|
|
6708
7294
|
const projectRoot = getProjectRoot2();
|
|
6709
7295
|
const bar = new import_progress.default(
|
|
6710
7296
|
":current/:total [:bar][:percent] :eta(s) :symbol",
|
|
@@ -6719,7 +7305,7 @@ var makeScreenshots = async (signals, interval) => {
|
|
|
6719
7305
|
SCREENSHOT_CONCURRENCY_LIMIT,
|
|
6720
7306
|
async (signal) => {
|
|
6721
7307
|
try {
|
|
6722
|
-
await screenDashboard({ ...signal, interval }, projectRoot);
|
|
7308
|
+
await screenDashboard({ ...signal, interval }, projectRoot, userName);
|
|
6723
7309
|
} catch (error) {
|
|
6724
7310
|
import_logger5.logger.error(
|
|
6725
7311
|
"Failed screenshot: %s (%s)",
|
|
@@ -6733,7 +7319,7 @@ var makeScreenshots = async (signals, interval) => {
|
|
|
6733
7319
|
);
|
|
6734
7320
|
import_logger5.logger.info("");
|
|
6735
7321
|
};
|
|
6736
|
-
var sendToAI = async (signals) => {
|
|
7322
|
+
var sendToAI = async (signals, userName = "root") => {
|
|
6737
7323
|
const bar = new import_progress.default(
|
|
6738
7324
|
":current/:total [:bar][:percent] :eta(s) :symbol",
|
|
6739
7325
|
{
|
|
@@ -6744,7 +7330,7 @@ var sendToAI = async (signals) => {
|
|
|
6744
7330
|
import_logger5.logger.info(import_chalk.default.yellow("AI:", signals.length));
|
|
6745
7331
|
await (0, import_async3.runWithConcurrency)(signals, AI_CONCURRENCY_LIMIT, async (signal) => {
|
|
6746
7332
|
try {
|
|
6747
|
-
await askAI(signal);
|
|
7333
|
+
await askAI(signal, { userName });
|
|
6748
7334
|
} catch {
|
|
6749
7335
|
import_logger5.logger.error("Failed ask: %s", signal.symbol);
|
|
6750
7336
|
} finally {
|
|
@@ -6753,7 +7339,7 @@ var sendToAI = async (signals) => {
|
|
|
6753
7339
|
});
|
|
6754
7340
|
import_logger5.logger.info("");
|
|
6755
7341
|
};
|
|
6756
|
-
var sendToTG = async (signals, imgInterval) => {
|
|
7342
|
+
var sendToTG = async (signals, imgInterval, userName = "root") => {
|
|
6757
7343
|
const bar = new import_progress.default(
|
|
6758
7344
|
":current/:total [:bar][:percent] :eta(s) :symbol",
|
|
6759
7345
|
{
|
|
@@ -6762,15 +7348,15 @@ var sendToTG = async (signals, imgInterval) => {
|
|
|
6762
7348
|
}
|
|
6763
7349
|
);
|
|
6764
7350
|
import_logger5.logger.info(import_chalk.default.yellow("messages:", signals.length));
|
|
6765
|
-
await (0, import_async3.runWithConcurrency)(signals,
|
|
7351
|
+
await (0, import_async3.runWithConcurrency)(signals, 1, async (signal) => {
|
|
6766
7352
|
try {
|
|
6767
|
-
const analysis = await (0,
|
|
6768
|
-
|
|
7353
|
+
const analysis = await (0, import_redis2.getData)(
|
|
7354
|
+
import_redis2.redisKeys.analysis(signal.symbol, signal.signalId),
|
|
6769
7355
|
null
|
|
6770
7356
|
);
|
|
6771
|
-
await sendSignal(signal, imgInterval, analysis);
|
|
7357
|
+
await sendSignal(signal, imgInterval, analysis, { userName });
|
|
6772
7358
|
if (analysis && typeof analysis === "object" && Object.keys(analysis).length > 0) {
|
|
6773
|
-
await sendSignalAnalysis(signal, analysis);
|
|
7359
|
+
await sendSignalAnalysis(signal, analysis, { userName });
|
|
6774
7360
|
}
|
|
6775
7361
|
} catch (err) {
|
|
6776
7362
|
import_logger5.logger.error(
|