@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/ai.js
ADDED
|
@@ -0,0 +1,926 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/ai.ts
|
|
31
|
+
var ai_exports = {};
|
|
32
|
+
__export(ai_exports, {
|
|
33
|
+
DEFAULT_AI_MODEL: () => DEFAULT_AI_MODEL,
|
|
34
|
+
MAX_AI_SERIES_POINTS: () => MAX_AI_SERIES_POINTS,
|
|
35
|
+
askAI: () => askAI,
|
|
36
|
+
buildAiHumanPrompt: () => buildAiHumanPrompt,
|
|
37
|
+
buildAiPayload: () => buildAiPayload,
|
|
38
|
+
buildAiPrompts: () => buildAiPrompts,
|
|
39
|
+
buildAiSystemPrompt: () => buildAiSystemPrompt,
|
|
40
|
+
ensureAiStrategyPluginsLoaded: () => ensureAiStrategyPluginsLoaded,
|
|
41
|
+
getDeterministicAiGateContext: () => getDeterministicAiGateContext,
|
|
42
|
+
getOpenRouterModelKwargs: () => getOpenRouterModelKwargs,
|
|
43
|
+
resetAiRuntimeCache: () => resetAiRuntimeCache,
|
|
44
|
+
runAiPrompt: () => runAiPrompt,
|
|
45
|
+
runAiPromptLocal: () => runAiPromptLocal,
|
|
46
|
+
trimSeriesDeep: () => trimSeriesDeep
|
|
47
|
+
});
|
|
48
|
+
module.exports = __toCommonJS(ai_exports);
|
|
49
|
+
var import_redis = require("@tradejs/infra/redis");
|
|
50
|
+
var import_userSettings = require("@tradejs/infra/userSettings");
|
|
51
|
+
|
|
52
|
+
// src/aiShared.ts
|
|
53
|
+
var MAX_AI_SERIES_POINTS = 5;
|
|
54
|
+
var trimSeriesDeep = (value) => {
|
|
55
|
+
if (Array.isArray(value)) {
|
|
56
|
+
const trimmed = value.slice(-MAX_AI_SERIES_POINTS);
|
|
57
|
+
const isMatrix = trimmed.every((item) => Array.isArray(item));
|
|
58
|
+
if (isMatrix) {
|
|
59
|
+
return trimmed;
|
|
60
|
+
}
|
|
61
|
+
return trimmed.map(
|
|
62
|
+
(item) => item && typeof item === "object" ? trimSeriesDeep(item) : item
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
if (value && typeof value === "object") {
|
|
66
|
+
return Object.fromEntries(
|
|
67
|
+
Object.entries(value).map(([key, nested]) => [
|
|
68
|
+
key,
|
|
69
|
+
trimSeriesDeep(nested)
|
|
70
|
+
])
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return value;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// src/strategy/manifests.ts
|
|
77
|
+
var import_indicators = require("@tradejs/core/indicators");
|
|
78
|
+
var import_logger2 = require("@tradejs/infra/logger");
|
|
79
|
+
|
|
80
|
+
// src/tradejsConfig.ts
|
|
81
|
+
var import_fs = __toESM(require("fs"));
|
|
82
|
+
var import_path = __toESM(require("path"));
|
|
83
|
+
var import_module = require("module");
|
|
84
|
+
var import_url = require("url");
|
|
85
|
+
var import_logger = require("@tradejs/infra/logger");
|
|
86
|
+
var CONFIG_FILE_NAMES = [
|
|
87
|
+
"tradejs.config.ts",
|
|
88
|
+
"tradejs.config.mts",
|
|
89
|
+
"tradejs.config.js",
|
|
90
|
+
"tradejs.config.mjs",
|
|
91
|
+
"tradejs.config.cjs"
|
|
92
|
+
];
|
|
93
|
+
var TS_MODULE_RE = /\.(cts|mts|ts)$/i;
|
|
94
|
+
var cachedByCwd = /* @__PURE__ */ new Map();
|
|
95
|
+
var announcedConfigFile = /* @__PURE__ */ new Set();
|
|
96
|
+
var tsNodeRegistered = false;
|
|
97
|
+
var tsconfigPathsRegisteredByCwd = /* @__PURE__ */ new Set();
|
|
98
|
+
var getTradejsProjectCwd = (cwd) => {
|
|
99
|
+
const explicit = String(cwd ?? "").trim();
|
|
100
|
+
if (explicit) {
|
|
101
|
+
return import_path.default.resolve(explicit);
|
|
102
|
+
}
|
|
103
|
+
const fromEnv = String(process.env.PROJECT_CWD || "").trim();
|
|
104
|
+
if (fromEnv) {
|
|
105
|
+
return import_path.default.resolve(fromEnv);
|
|
106
|
+
}
|
|
107
|
+
return process.cwd();
|
|
108
|
+
};
|
|
109
|
+
var normalizeConfig = (rawConfig) => {
|
|
110
|
+
if (!rawConfig || typeof rawConfig !== "object") {
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
const config = rawConfig;
|
|
114
|
+
const strategies2 = Array.isArray(config.strategies) ? config.strategies.map((value) => String(value || "").trim()).filter(Boolean) : [];
|
|
115
|
+
const indicators = Array.isArray(config.indicators) ? config.indicators.map((value) => String(value || "").trim()).filter(Boolean) : [];
|
|
116
|
+
const connectors = Array.isArray(config.connectors) ? config.connectors.map((value) => String(value || "").trim()).filter(Boolean) : [];
|
|
117
|
+
return {
|
|
118
|
+
strategies: strategies2,
|
|
119
|
+
indicators,
|
|
120
|
+
connectors
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
var getRequireFn = (cwd = getTradejsProjectCwd()) => (0, import_module.createRequire)(import_path.default.join(import_path.default.resolve(cwd), "__tradejs_loader__.js"));
|
|
124
|
+
var ensureTsNodeRegistered = async () => {
|
|
125
|
+
if (tsNodeRegistered) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const tsNodeModule = await import("ts-node");
|
|
129
|
+
const tsNode = tsNodeModule.default ?? tsNodeModule;
|
|
130
|
+
tsNode.register?.({
|
|
131
|
+
transpileOnly: true,
|
|
132
|
+
compilerOptions: {
|
|
133
|
+
module: "commonjs",
|
|
134
|
+
moduleResolution: "node"
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
tsNodeRegistered = true;
|
|
138
|
+
};
|
|
139
|
+
var ensureTsconfigPathsRegistered = async (cwd = getTradejsProjectCwd()) => {
|
|
140
|
+
const projectRoot = getTradejsProjectCwd(cwd);
|
|
141
|
+
if (tsconfigPathsRegisteredByCwd.has(projectRoot)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const tsconfigPathsModule = await import("tsconfig-paths");
|
|
145
|
+
const loadConfig = tsconfigPathsModule.loadConfig;
|
|
146
|
+
const register = tsconfigPathsModule.register;
|
|
147
|
+
if (typeof loadConfig !== "function" || typeof register !== "function") {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const loadedConfig = loadConfig(projectRoot);
|
|
151
|
+
if (loadedConfig.resultType !== "success") {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
register({
|
|
155
|
+
baseUrl: loadedConfig.absoluteBaseUrl,
|
|
156
|
+
paths: loadedConfig.paths,
|
|
157
|
+
addMatchAll: false
|
|
158
|
+
});
|
|
159
|
+
tsconfigPathsRegisteredByCwd.add(projectRoot);
|
|
160
|
+
};
|
|
161
|
+
var toImportSpecifier = (moduleName) => {
|
|
162
|
+
if (moduleName.startsWith("file://")) {
|
|
163
|
+
return moduleName;
|
|
164
|
+
}
|
|
165
|
+
if (import_path.default.isAbsolute(moduleName)) {
|
|
166
|
+
return (0, import_url.pathToFileURL)(moduleName).href;
|
|
167
|
+
}
|
|
168
|
+
return moduleName;
|
|
169
|
+
};
|
|
170
|
+
var isTsModulePath = (moduleName) => TS_MODULE_RE.test(moduleName.split("?")[0]);
|
|
171
|
+
var isRelativeModulePath = (moduleName) => moduleName.startsWith("./") || moduleName.startsWith("../");
|
|
172
|
+
var isBareModuleSpecifier = (moduleName) => {
|
|
173
|
+
const normalized = String(moduleName ?? "").trim();
|
|
174
|
+
if (!normalized) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
if (normalized.startsWith("file://") || import_path.default.isAbsolute(normalized) || isRelativeModulePath(normalized)) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
};
|
|
182
|
+
var importConfigFile = async (configFilePath) => {
|
|
183
|
+
const ext = import_path.default.extname(configFilePath).toLowerCase();
|
|
184
|
+
const configFileUrl = `${toImportSpecifier(configFilePath)}?t=${Date.now()}`;
|
|
185
|
+
if (ext === ".ts" || ext === ".mts") {
|
|
186
|
+
const requireFn = getRequireFn(import_path.default.dirname(configFilePath));
|
|
187
|
+
await ensureTsNodeRegistered();
|
|
188
|
+
await ensureTsconfigPathsRegistered(import_path.default.dirname(configFilePath));
|
|
189
|
+
return requireFn(configFilePath);
|
|
190
|
+
}
|
|
191
|
+
return import(
|
|
192
|
+
/* webpackIgnore: true */
|
|
193
|
+
configFileUrl
|
|
194
|
+
);
|
|
195
|
+
};
|
|
196
|
+
var importTradejsModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
|
|
197
|
+
const normalized = String(moduleName ?? "").trim();
|
|
198
|
+
if (!normalized) {
|
|
199
|
+
return {};
|
|
200
|
+
}
|
|
201
|
+
let modulePath = normalized;
|
|
202
|
+
if (normalized.startsWith("file://")) {
|
|
203
|
+
try {
|
|
204
|
+
modulePath = (0, import_url.fileURLToPath)(normalized);
|
|
205
|
+
} catch {
|
|
206
|
+
modulePath = normalized;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const requireFn = getRequireFn(
|
|
210
|
+
import_path.default.isAbsolute(modulePath) ? import_path.default.dirname(modulePath) : cwd
|
|
211
|
+
);
|
|
212
|
+
if (isTsModulePath(modulePath)) {
|
|
213
|
+
await ensureTsNodeRegistered();
|
|
214
|
+
await ensureTsconfigPathsRegistered(cwd);
|
|
215
|
+
return requireFn(modulePath);
|
|
216
|
+
}
|
|
217
|
+
if (isBareModuleSpecifier(normalized)) {
|
|
218
|
+
await ensureTsconfigPathsRegistered(cwd);
|
|
219
|
+
return requireFn(normalized);
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
return await import(
|
|
223
|
+
/* webpackIgnore: true */
|
|
224
|
+
toImportSpecifier(normalized)
|
|
225
|
+
);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
if (isTsModulePath(modulePath)) {
|
|
228
|
+
await ensureTsNodeRegistered();
|
|
229
|
+
await ensureTsconfigPathsRegistered(cwd);
|
|
230
|
+
return requireFn(modulePath);
|
|
231
|
+
}
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
var resolveExportedConfig = (moduleExports) => {
|
|
236
|
+
const candidate = moduleExports && typeof moduleExports === "object" && "default" in moduleExports ? moduleExports.default : moduleExports;
|
|
237
|
+
return normalizeConfig(candidate);
|
|
238
|
+
};
|
|
239
|
+
var findConfigFilePath = (cwd) => {
|
|
240
|
+
let currentDir = import_path.default.resolve(cwd);
|
|
241
|
+
while (true) {
|
|
242
|
+
for (const fileName of CONFIG_FILE_NAMES) {
|
|
243
|
+
const fullPath = import_path.default.join(currentDir, fileName);
|
|
244
|
+
if (import_fs.default.existsSync(fullPath) && import_fs.default.statSync(fullPath).isFile()) {
|
|
245
|
+
return fullPath;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const parentDir = import_path.default.dirname(currentDir);
|
|
249
|
+
if (parentDir === currentDir) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
currentDir = parentDir;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
var resolvePluginModuleSpecifier = (moduleName, cwd = getTradejsProjectCwd()) => {
|
|
256
|
+
const normalized = String(moduleName ?? "").trim();
|
|
257
|
+
if (!normalized) {
|
|
258
|
+
return "";
|
|
259
|
+
}
|
|
260
|
+
if (normalized.startsWith("file://")) {
|
|
261
|
+
try {
|
|
262
|
+
return (0, import_url.fileURLToPath)(normalized);
|
|
263
|
+
} catch {
|
|
264
|
+
return normalized;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (import_path.default.isAbsolute(normalized)) {
|
|
268
|
+
return normalized;
|
|
269
|
+
}
|
|
270
|
+
if (isRelativeModulePath(normalized)) {
|
|
271
|
+
return import_path.default.resolve(cwd, normalized);
|
|
272
|
+
}
|
|
273
|
+
return normalized;
|
|
274
|
+
};
|
|
275
|
+
var loadTradejsConfig = async (cwd = getTradejsProjectCwd()) => {
|
|
276
|
+
const cached = cachedByCwd.get(cwd);
|
|
277
|
+
if (cached) {
|
|
278
|
+
return cached;
|
|
279
|
+
}
|
|
280
|
+
const configFilePath = findConfigFilePath(cwd);
|
|
281
|
+
if (!configFilePath) {
|
|
282
|
+
cachedByCwd.set(cwd, {});
|
|
283
|
+
return {};
|
|
284
|
+
}
|
|
285
|
+
try {
|
|
286
|
+
const moduleExports = await importConfigFile(configFilePath);
|
|
287
|
+
const config = resolveExportedConfig(moduleExports);
|
|
288
|
+
cachedByCwd.set(cwd, config);
|
|
289
|
+
if (!announcedConfigFile.has(configFilePath)) {
|
|
290
|
+
announcedConfigFile.add(configFilePath);
|
|
291
|
+
import_logger.logger.log("debug", "Loaded TradeJS config: %s", configFilePath);
|
|
292
|
+
}
|
|
293
|
+
return config;
|
|
294
|
+
} catch (error) {
|
|
295
|
+
import_logger.logger.log(
|
|
296
|
+
"warn",
|
|
297
|
+
"Failed to load TradeJS config from %s: %s",
|
|
298
|
+
configFilePath,
|
|
299
|
+
String(error)
|
|
300
|
+
);
|
|
301
|
+
cachedByCwd.set(cwd, {});
|
|
302
|
+
return {};
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
// src/strategy/manifests.ts
|
|
307
|
+
var createStrategyRegistryState = () => ({
|
|
308
|
+
strategyCreators: /* @__PURE__ */ new Map(),
|
|
309
|
+
strategyManifestsMap: /* @__PURE__ */ new Map(),
|
|
310
|
+
pluginsLoadPromise: null
|
|
311
|
+
});
|
|
312
|
+
var registryStateByProjectRoot = /* @__PURE__ */ new Map();
|
|
313
|
+
var getStrategyRegistryState = (cwd = getTradejsProjectCwd()) => {
|
|
314
|
+
const projectRoot = getTradejsProjectCwd(cwd);
|
|
315
|
+
let state = registryStateByProjectRoot.get(projectRoot);
|
|
316
|
+
if (!state) {
|
|
317
|
+
state = createStrategyRegistryState();
|
|
318
|
+
registryStateByProjectRoot.set(projectRoot, state);
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
projectRoot,
|
|
322
|
+
state
|
|
323
|
+
};
|
|
324
|
+
};
|
|
325
|
+
var toUniqueModules = (modules = []) => [
|
|
326
|
+
...new Set(modules.map((moduleName) => moduleName.trim()).filter(Boolean))
|
|
327
|
+
];
|
|
328
|
+
var getConfiguredPluginModuleNames = async (cwd = getTradejsProjectCwd()) => {
|
|
329
|
+
const config = await loadTradejsConfig(cwd);
|
|
330
|
+
return {
|
|
331
|
+
strategyModules: toUniqueModules(config.strategies),
|
|
332
|
+
indicatorModules: toUniqueModules(config.indicators)
|
|
333
|
+
};
|
|
334
|
+
};
|
|
335
|
+
var extractModuleEntries = (moduleExport, key) => {
|
|
336
|
+
if (!moduleExport || typeof moduleExport !== "object") {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
const candidate = moduleExport;
|
|
340
|
+
if (Array.isArray(candidate[key])) {
|
|
341
|
+
return candidate[key];
|
|
342
|
+
}
|
|
343
|
+
const defaultExport = candidate.default;
|
|
344
|
+
if (defaultExport && Array.isArray(defaultExport[key])) {
|
|
345
|
+
return defaultExport[key];
|
|
346
|
+
}
|
|
347
|
+
return null;
|
|
348
|
+
};
|
|
349
|
+
var extractStrategyPluginDefinition = (moduleExport) => {
|
|
350
|
+
const strategyEntries = extractModuleEntries(
|
|
351
|
+
moduleExport,
|
|
352
|
+
"strategyEntries"
|
|
353
|
+
);
|
|
354
|
+
return strategyEntries ? { strategyEntries } : null;
|
|
355
|
+
};
|
|
356
|
+
var extractIndicatorPluginDefinition = (moduleExport) => {
|
|
357
|
+
const indicatorEntries = extractModuleEntries(
|
|
358
|
+
moduleExport,
|
|
359
|
+
"indicatorEntries"
|
|
360
|
+
);
|
|
361
|
+
return indicatorEntries ? { indicatorEntries } : null;
|
|
362
|
+
};
|
|
363
|
+
var registerEntries = (entries, source, state) => {
|
|
364
|
+
for (const entry of entries) {
|
|
365
|
+
const strategyName = entry.manifest?.name;
|
|
366
|
+
if (!strategyName) {
|
|
367
|
+
import_logger2.logger.warn("Skip strategy entry without name from %s", source);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (state.strategyCreators.has(strategyName)) {
|
|
371
|
+
import_logger2.logger.warn(
|
|
372
|
+
'Skip duplicate strategy "%s" from %s: already registered',
|
|
373
|
+
strategyName,
|
|
374
|
+
source
|
|
375
|
+
);
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
state.strategyCreators.set(strategyName, entry.creator);
|
|
379
|
+
state.strategyManifestsMap.set(strategyName, entry.manifest);
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
var importStrategyPluginModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
|
|
383
|
+
if (typeof importTradejsModule === "function") {
|
|
384
|
+
return importTradejsModule(moduleName, cwd);
|
|
385
|
+
}
|
|
386
|
+
return import(
|
|
387
|
+
/* webpackIgnore: true */
|
|
388
|
+
moduleName
|
|
389
|
+
);
|
|
390
|
+
};
|
|
391
|
+
var ensureStrategyPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
|
|
392
|
+
const { projectRoot, state } = getStrategyRegistryState(cwd);
|
|
393
|
+
if (!state.pluginsLoadPromise) {
|
|
394
|
+
(0, import_indicators.resetIndicatorRegistryCache)(projectRoot);
|
|
395
|
+
state.pluginsLoadPromise = (async () => {
|
|
396
|
+
const { strategyModules, indicatorModules } = await getConfiguredPluginModuleNames(projectRoot);
|
|
397
|
+
const strategySet = new Set(strategyModules);
|
|
398
|
+
const indicatorSet = new Set(indicatorModules);
|
|
399
|
+
const pluginModuleNames = [
|
|
400
|
+
.../* @__PURE__ */ new Set([...strategyModules, ...indicatorModules])
|
|
401
|
+
];
|
|
402
|
+
if (!pluginModuleNames.length) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
for (const moduleName of pluginModuleNames) {
|
|
406
|
+
try {
|
|
407
|
+
const resolvedModuleName = resolvePluginModuleSpecifier(
|
|
408
|
+
moduleName,
|
|
409
|
+
projectRoot
|
|
410
|
+
);
|
|
411
|
+
const moduleExport = await importStrategyPluginModule(
|
|
412
|
+
resolvedModuleName,
|
|
413
|
+
projectRoot
|
|
414
|
+
);
|
|
415
|
+
if (strategySet.has(moduleName)) {
|
|
416
|
+
const pluginDefinition = extractStrategyPluginDefinition(moduleExport);
|
|
417
|
+
if (!pluginDefinition) {
|
|
418
|
+
import_logger2.logger.warn(
|
|
419
|
+
'Skip strategy plugin "%s": export { strategyEntries } is missing',
|
|
420
|
+
moduleName
|
|
421
|
+
);
|
|
422
|
+
} else {
|
|
423
|
+
registerEntries(
|
|
424
|
+
pluginDefinition.strategyEntries,
|
|
425
|
+
moduleName,
|
|
426
|
+
state
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
if (indicatorSet.has(moduleName)) {
|
|
431
|
+
const indicatorPluginDefinition = extractIndicatorPluginDefinition(moduleExport);
|
|
432
|
+
if (!indicatorPluginDefinition) {
|
|
433
|
+
import_logger2.logger.warn(
|
|
434
|
+
'Skip indicator plugin "%s": export { indicatorEntries } is missing',
|
|
435
|
+
moduleName
|
|
436
|
+
);
|
|
437
|
+
} else {
|
|
438
|
+
(0, import_indicators.registerIndicatorEntries)(
|
|
439
|
+
indicatorPluginDefinition.indicatorEntries,
|
|
440
|
+
moduleName,
|
|
441
|
+
projectRoot
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
if (!strategySet.has(moduleName) && !indicatorSet.has(moduleName)) {
|
|
446
|
+
import_logger2.logger.warn(
|
|
447
|
+
'Skip plugin "%s": no strategy/indicator sections requested in config',
|
|
448
|
+
moduleName
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
} catch (error) {
|
|
452
|
+
import_logger2.logger.warn(
|
|
453
|
+
'Failed to load plugin "%s": %s',
|
|
454
|
+
moduleName,
|
|
455
|
+
String(error)
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
})();
|
|
460
|
+
}
|
|
461
|
+
await state.pluginsLoadPromise;
|
|
462
|
+
};
|
|
463
|
+
var getStrategyManifest = (name, cwd = getTradejsProjectCwd()) => {
|
|
464
|
+
if (!name) {
|
|
465
|
+
return void 0;
|
|
466
|
+
}
|
|
467
|
+
const { state } = getStrategyRegistryState(cwd);
|
|
468
|
+
return state.strategyManifestsMap.get(name);
|
|
469
|
+
};
|
|
470
|
+
var strategies = new Proxy(
|
|
471
|
+
{},
|
|
472
|
+
{
|
|
473
|
+
get: (_target, property) => {
|
|
474
|
+
if (typeof property !== "string") {
|
|
475
|
+
return void 0;
|
|
476
|
+
}
|
|
477
|
+
return getStrategyRegistryState().state.strategyCreators.get(property);
|
|
478
|
+
},
|
|
479
|
+
ownKeys: () => {
|
|
480
|
+
return [...getStrategyRegistryState().state.strategyCreators.keys()];
|
|
481
|
+
},
|
|
482
|
+
getOwnPropertyDescriptor: () => ({
|
|
483
|
+
enumerable: true,
|
|
484
|
+
configurable: true
|
|
485
|
+
})
|
|
486
|
+
}
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
// src/strategyAdapters/ai.ts
|
|
490
|
+
var buildBaseAiPayload = (signal) => ({
|
|
491
|
+
signal: {
|
|
492
|
+
symbol: signal.symbol,
|
|
493
|
+
signalId: signal.signalId,
|
|
494
|
+
interval: signal.interval,
|
|
495
|
+
direction: signal.direction,
|
|
496
|
+
timestamp: signal.timestamp,
|
|
497
|
+
strategy: signal.strategy,
|
|
498
|
+
prices: {
|
|
499
|
+
currentPrice: signal.prices.currentPrice,
|
|
500
|
+
takeProfitPrice: signal.prices.takeProfitPrice,
|
|
501
|
+
stopLossPrice: signal.prices.stopLossPrice
|
|
502
|
+
}
|
|
503
|
+
},
|
|
504
|
+
figures: trimSeriesDeep(signal.figures ?? {}),
|
|
505
|
+
indicators: trimSeriesDeep(signal.indicators),
|
|
506
|
+
additionalIndicators: trimSeriesDeep(signal.additionalIndicators ?? {})
|
|
507
|
+
});
|
|
508
|
+
var defaultAiAdapter = {};
|
|
509
|
+
var getStrategyAiAdapter = (strategy) => getStrategyManifest(strategy)?.aiAdapter ?? defaultAiAdapter;
|
|
510
|
+
var getSignalAiAdapter = (signal) => getStrategyAiAdapter(signal.strategy);
|
|
511
|
+
var buildAiPayloadByStrategy = (signal) => {
|
|
512
|
+
const basePayload = buildBaseAiPayload(signal);
|
|
513
|
+
const adapter = getSignalAiAdapter(signal);
|
|
514
|
+
return adapter.buildPayload?.({ signal, basePayload }) ?? basePayload;
|
|
515
|
+
};
|
|
516
|
+
var buildAiSystemPromptAddonByStrategy = (signal) => getSignalAiAdapter(signal).buildSystemPromptAddon?.({ signal }) ?? "";
|
|
517
|
+
var buildAiHumanPromptAddonByStrategy = (signal, payload) => getSignalAiAdapter(signal).buildHumanPromptAddon?.({
|
|
518
|
+
signal,
|
|
519
|
+
payload
|
|
520
|
+
}) ?? "";
|
|
521
|
+
var postProcessAiAnalysisByStrategy = (signal, analysis, payload = buildAiPayloadByStrategy(signal)) => getSignalAiAdapter(signal).postProcessAnalysis?.({
|
|
522
|
+
signal,
|
|
523
|
+
payload,
|
|
524
|
+
analysis
|
|
525
|
+
}) ?? analysis;
|
|
526
|
+
|
|
527
|
+
// src/ai.ts
|
|
528
|
+
var parseAIResponse = (input) => {
|
|
529
|
+
try {
|
|
530
|
+
if (typeof input === "object" && input !== null) return input;
|
|
531
|
+
const match = input.match(/\{[\s\S]*\}/);
|
|
532
|
+
if (!match) throw new Error("JSON block not found");
|
|
533
|
+
return JSON.parse(match[0]);
|
|
534
|
+
} catch (err) {
|
|
535
|
+
console.error("\u274C \u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0430\u0440\u0441\u0438\u043D\u0433\u0430 AI-\u043E\u0442\u0432\u0435\u0442\u0430:", err);
|
|
536
|
+
console.log("\u{1F50D} \u0418\u0441\u0445\u043E\u0434\u043D\u044B\u0439 \u0442\u0435\u043A\u0441\u0442:", input);
|
|
537
|
+
return {};
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
var normalizeResponseContent = (content) => {
|
|
541
|
+
if (typeof content === "string" || content && typeof content === "object") {
|
|
542
|
+
if (typeof content !== "object" || !Array.isArray(content)) {
|
|
543
|
+
return content;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
if (Array.isArray(content)) {
|
|
547
|
+
const text = content.map((part) => typeof part?.text === "string" ? part.text : "").join("\n").trim();
|
|
548
|
+
return text;
|
|
549
|
+
}
|
|
550
|
+
return String(content ?? "");
|
|
551
|
+
};
|
|
552
|
+
var normalizeAnalysis = (raw) => {
|
|
553
|
+
const direction = raw?.direction === "LONG" || raw?.direction === "SHORT" ? raw.direction : null;
|
|
554
|
+
const qualityNum = typeof raw?.quality === "number" ? Math.max(1, Math.min(5, Math.round(raw.quality))) : void 0;
|
|
555
|
+
const toNumberOrNull = (value) => {
|
|
556
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
557
|
+
if (typeof value === "string" && value.trim()) {
|
|
558
|
+
const parsed = Number(value);
|
|
559
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
560
|
+
}
|
|
561
|
+
return null;
|
|
562
|
+
};
|
|
563
|
+
const toText = (value) => typeof value === "string" ? value.slice(0, 400) : void 0;
|
|
564
|
+
return {
|
|
565
|
+
direction,
|
|
566
|
+
quality: qualityNum,
|
|
567
|
+
needRetest: Boolean(raw?.needRetest),
|
|
568
|
+
retestPrice: toNumberOrNull(raw?.retestPrice),
|
|
569
|
+
takeProfitPrice: toNumberOrNull(raw?.takeProfitPrice),
|
|
570
|
+
stopLossPrice: toNumberOrNull(raw?.stopLossPrice),
|
|
571
|
+
setup: toText(raw?.setup),
|
|
572
|
+
confirmations: toText(raw?.confirmations),
|
|
573
|
+
btcContext: toText(raw?.btcContext),
|
|
574
|
+
retestPlan: toText(raw?.retestPlan),
|
|
575
|
+
riskLevels: toText(raw?.riskLevels),
|
|
576
|
+
qualityReason: toText(raw?.qualityReason),
|
|
577
|
+
triggerInvalidation: toText(raw?.triggerInvalidation),
|
|
578
|
+
comment: typeof raw?.comment === "string" ? raw.comment.slice(0, 1024) : ""
|
|
579
|
+
};
|
|
580
|
+
};
|
|
581
|
+
var asRecord = (value) => {
|
|
582
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
return value;
|
|
586
|
+
};
|
|
587
|
+
var getSignalDirection = (signal) => signal.direction === "LONG" || signal.direction === "SHORT" ? signal.direction : null;
|
|
588
|
+
var getDeterministicQuality = (gateContext) => {
|
|
589
|
+
const deterministicQuality = Number(gateContext?.deterministicQuality);
|
|
590
|
+
if (Number.isFinite(deterministicQuality)) {
|
|
591
|
+
return Math.max(1, Math.min(5, Math.round(deterministicQuality)));
|
|
592
|
+
}
|
|
593
|
+
const maxAllowedQuality = Number(gateContext?.maxAllowedQuality);
|
|
594
|
+
if (Number.isFinite(maxAllowedQuality)) {
|
|
595
|
+
return Math.max(1, Math.min(5, Math.round(maxAllowedQuality)));
|
|
596
|
+
}
|
|
597
|
+
return Array.isArray(gateContext?.structuralHardBlockReasons) && gateContext.structuralHardBlockReasons.length > 0 ? 2 : 3;
|
|
598
|
+
};
|
|
599
|
+
var buildAiSystemPrompt = (signal) => `
|
|
600
|
+
\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.
|
|
601
|
+
\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.
|
|
602
|
+
\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.
|
|
603
|
+
|
|
604
|
+
\u0412\u0410\u0416\u041D\u041E:
|
|
605
|
+
- \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.
|
|
606
|
+
- \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.
|
|
607
|
+
- \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.
|
|
608
|
+
- \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.
|
|
609
|
+
- \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.
|
|
610
|
+
- \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.
|
|
611
|
+
- \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.
|
|
612
|
+
- \u041F\u0438\u0448\u0438 comment \u043F\u043E-\u0440\u0443\u0441\u0441\u043A\u0438.
|
|
613
|
+
- \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.
|
|
614
|
+
|
|
615
|
+
\u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u0441\u0442\u0440\u043E\u0433\u043E \u041E\u0414\u041D\u0418\u041C JSON-\u043E\u0431\u044A\u0435\u043A\u0442\u043E\u043C \u0431\u0435\u0437 \u0442\u0435\u043A\u0441\u0442\u0430 \u0432\u043E\u043A\u0440\u0443\u0433:
|
|
616
|
+
|
|
617
|
+
{
|
|
618
|
+
"direction": payload.signal.direction | null,
|
|
619
|
+
"quality": 1 | 2 | 3 | 4 | 5,
|
|
620
|
+
"needRetest": boolean,
|
|
621
|
+
"retestPrice": number | null,
|
|
622
|
+
"takeProfitPrice": number | null,
|
|
623
|
+
"stopLossPrice": number | null,
|
|
624
|
+
"setup": string,
|
|
625
|
+
"confirmations": string,
|
|
626
|
+
"btcContext": string,
|
|
627
|
+
"retestPlan": string,
|
|
628
|
+
"riskLevels": string,
|
|
629
|
+
"qualityReason": string,
|
|
630
|
+
"triggerInvalidation": string
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
- \u041D\u0435 \u0434\u043E\u0431\u0430\u0432\u043B\u044F\u0439 \u0434\u0440\u0443\u0433\u0438\u0435 \u043F\u043E\u043B\u044F.
|
|
634
|
+
- \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.
|
|
635
|
+
- \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).
|
|
636
|
+
- "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.
|
|
637
|
+
- "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.
|
|
638
|
+
- "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.
|
|
639
|
+
- "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).
|
|
640
|
+
- "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.
|
|
641
|
+
- \u0420\u0430\u0437\u0431\u0438\u0432\u0430\u0439 \u0430\u043D\u0430\u043B\u0438\u0437 \u043D\u0430 \u043F\u043E\u043B\u044F:
|
|
642
|
+
- "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.
|
|
643
|
+
- "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.
|
|
644
|
+
- "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.
|
|
645
|
+
- "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).
|
|
646
|
+
- "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.
|
|
647
|
+
- "qualityReason" \u2014 \u043F\u043E\u0447\u0435\u043C\u0443 quality \u0438\u043C\u0435\u043D\u043D\u043E \u0442\u0430\u043A\u043E\u0439.
|
|
648
|
+
- "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.
|
|
649
|
+
- "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.
|
|
650
|
+
|
|
651
|
+
\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.
|
|
652
|
+
|
|
653
|
+
\u0421\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0430 \u0432\u0445\u043E\u0434\u043D\u043E\u0433\u043E payload (JSON \u0432 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F):
|
|
654
|
+
- payload.signal:
|
|
655
|
+
symbol, signalId, interval, direction, timestamp, strategy, prices
|
|
656
|
+
- payload.signal.prices:
|
|
657
|
+
currentPrice, takeProfitPrice, stopLossPrice
|
|
658
|
+
- payload.figures:
|
|
659
|
+
\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.
|
|
660
|
+
- payload.indicators:
|
|
661
|
+
\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.
|
|
662
|
+
- payload.additionalIndicators:
|
|
663
|
+
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.
|
|
664
|
+
\u041F\u0440\u0438\u043C\u0435\u0440\u044B: helperFlags, structureContext, spread, correlation, volatilitySummary.
|
|
665
|
+
\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.
|
|
666
|
+
\u041F\u0430\u0442\u0442\u0435\u0440\u043D\u044B \u043A\u043B\u044E\u0447\u0435\u0439:
|
|
667
|
+
\u2022 \u043C\u043E\u043D\u0435\u0442\u0430: maFast, atrPct, macd..., candles15m/candles1h/candles4h/candles1d, \u0430 \u0442\u0430\u043A\u0436\u0435 *1h/*4h/*1d
|
|
668
|
+
\u2022 BTC: btcMaFast, btcAtr, btcMacd..., btcCandles*, \u0430 \u0442\u0430\u043A\u0436\u0435 btc*1h/*4h/*1d
|
|
669
|
+
\u2022 \u0441\u043B\u0443\u0436\u0435\u0431\u043D\u044B\u0435 \u043A\u043B\u044E\u0447\u0438 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438 \u0432\u043E\u0437\u043C\u043E\u0436\u043D\u044B (\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 correlation, spread, touches, distance)
|
|
670
|
+
|
|
671
|
+
\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):
|
|
672
|
+
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.
|
|
673
|
+
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.
|
|
674
|
+
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.
|
|
675
|
+
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).
|
|
676
|
+
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.
|
|
677
|
+
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.
|
|
678
|
+
|
|
679
|
+
\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:
|
|
680
|
+
- \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.
|
|
681
|
+
- \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.
|
|
682
|
+
- \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.
|
|
683
|
+
- \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.
|
|
684
|
+
\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".
|
|
685
|
+
|
|
686
|
+
\u041F\u0440\u0430\u0432\u0438\u043B\u0430 \u0434\u043B\u044F direction / TP / SL:
|
|
687
|
+
- 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.
|
|
688
|
+
- \u0414\u043B\u044F LONG \u043E\u0431\u044B\u0447\u043D\u043E stopLossPrice < currentPrice < takeProfitPrice.
|
|
689
|
+
- \u0414\u043B\u044F SHORT \u043E\u0431\u044B\u0447\u043D\u043E takeProfitPrice < currentPrice < stopLossPrice.
|
|
690
|
+
- \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.
|
|
691
|
+
- \u0415\u0441\u043B\u0438 direction = null, \u0442\u043E takeProfitPrice = null \u0438 stopLossPrice = null.
|
|
692
|
+
- \u0415\u0441\u043B\u0438 needRetest = false, \u0442\u043E retestPrice = null.
|
|
693
|
+
- \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.
|
|
694
|
+
- 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.
|
|
695
|
+
|
|
696
|
+
\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):
|
|
697
|
+
- 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
|
|
698
|
+
- 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
|
|
699
|
+
- 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
|
|
700
|
+
- 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
|
|
701
|
+
- 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
|
|
702
|
+
|
|
703
|
+
\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):
|
|
704
|
+
- \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".
|
|
705
|
+
- \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).
|
|
706
|
+
- \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).
|
|
707
|
+
- \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.
|
|
708
|
+
- \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.
|
|
709
|
+
- \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.
|
|
710
|
+
- \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.
|
|
711
|
+
|
|
712
|
+
\u041F\u0440\u0430\u0432\u0438\u043B\u0430 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u044F \u043E\u0431\u0440\u0435\u0437\u0430\u043D\u043D\u044B\u0445 \u0440\u044F\u0434\u043E\u0432 (last 5 values):
|
|
713
|
+
- \u041D\u0435 \u0434\u0435\u043B\u0430\u0439 \u0441\u0438\u043B\u044C\u043D\u044B\u0445 \u0432\u044B\u0432\u043E\u0434\u043E\u0432 \u043E \u0434\u043E\u043B\u0433\u043E\u0441\u0440\u043E\u0447\u043D\u043E\u0439 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0435 \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E 5 \u0442\u043E\u0447\u043A\u0430\u043C.
|
|
714
|
+
- \u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 4h/1d \u0440\u044F\u0434\u044B \u043A\u0430\u043A \u043A\u0440\u0430\u0442\u043A\u0438\u0439 \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442, \u0430 \u043D\u0435 \u043A\u0430\u043A \u043F\u043E\u043B\u043D\u0443\u044E \u0438\u0441\u0442\u043E\u0440\u0438\u044E.
|
|
715
|
+
- \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.
|
|
716
|
+
|
|
717
|
+
\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):
|
|
718
|
+
{"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."}
|
|
719
|
+
{"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."}
|
|
720
|
+
|
|
721
|
+
\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.
|
|
722
|
+
${signal ? buildAiSystemPromptAddonByStrategy(signal) : ""}
|
|
723
|
+
`;
|
|
724
|
+
var buildAiPayload = (signal) => buildAiPayloadByStrategy(signal);
|
|
725
|
+
var getDeterministicAiGateContext = (payload) => {
|
|
726
|
+
const additionalIndicators = asRecord(payload.additionalIndicators);
|
|
727
|
+
const candidates = [
|
|
728
|
+
additionalIndicators,
|
|
729
|
+
...Object.values(additionalIndicators ?? {}).map(asRecord)
|
|
730
|
+
].filter((value) => Boolean(value));
|
|
731
|
+
return candidates.find(
|
|
732
|
+
(candidate) => Array.isArray(candidate.structuralHardBlockReasons) || typeof candidate.approvalAllowedNow === "boolean"
|
|
733
|
+
) ?? null;
|
|
734
|
+
};
|
|
735
|
+
var buildAiHumanPrompt = (signal, payload = buildAiPayload(signal)) => `
|
|
736
|
+
\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}.
|
|
737
|
+
\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.
|
|
738
|
+
|
|
739
|
+
\u0414\u0430\u043D\u043D\u044B\u0435 \u0441\u0434\u0435\u043B\u043A\u0438:
|
|
740
|
+
${JSON.stringify(payload)}
|
|
741
|
+
${buildAiHumanPromptAddonByStrategy(signal, payload)}
|
|
742
|
+
`;
|
|
743
|
+
var DEFAULT_AI_MODEL = "openai/gpt-5-mini";
|
|
744
|
+
var userSettingsCache = /* @__PURE__ */ new Map();
|
|
745
|
+
var aiModelCache = /* @__PURE__ */ new Map();
|
|
746
|
+
var getAiModelCacheKey = (userName, modelName) => `${userName}::${modelName}`;
|
|
747
|
+
var getOpenRouterModelKwargs = (apiEndpoint) => {
|
|
748
|
+
const endpoint = String(apiEndpoint ?? "").trim();
|
|
749
|
+
if (!endpoint) {
|
|
750
|
+
return {};
|
|
751
|
+
}
|
|
752
|
+
let hostname = "";
|
|
753
|
+
try {
|
|
754
|
+
hostname = new URL(endpoint).hostname;
|
|
755
|
+
} catch {
|
|
756
|
+
hostname = endpoint;
|
|
757
|
+
}
|
|
758
|
+
if (!hostname.toLowerCase().includes("openrouter")) {
|
|
759
|
+
return {};
|
|
760
|
+
}
|
|
761
|
+
return {
|
|
762
|
+
provider: {
|
|
763
|
+
ignore: ["azure"]
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
};
|
|
767
|
+
var getAiSettings = async (userName = "root") => {
|
|
768
|
+
let settingsPromise = userSettingsCache.get(userName);
|
|
769
|
+
if (!settingsPromise) {
|
|
770
|
+
settingsPromise = (0, import_userSettings.getUserSettings)(userName);
|
|
771
|
+
settingsPromise.catch(() => {
|
|
772
|
+
userSettingsCache.delete(userName);
|
|
773
|
+
});
|
|
774
|
+
userSettingsCache.set(userName, settingsPromise);
|
|
775
|
+
}
|
|
776
|
+
const settings = await settingsPromise;
|
|
777
|
+
if (!settings.OPENAI_API_KEY || !settings.OPENAI_API_ENDPOINT) {
|
|
778
|
+
throw new Error(`AI settings are incomplete for user ${userName}`);
|
|
779
|
+
}
|
|
780
|
+
return settings;
|
|
781
|
+
};
|
|
782
|
+
var createAiModel = async (userName = "root", modelName = DEFAULT_AI_MODEL) => {
|
|
783
|
+
const cacheKey = getAiModelCacheKey(userName, modelName);
|
|
784
|
+
let modelPromise = aiModelCache.get(cacheKey);
|
|
785
|
+
if (!modelPromise) {
|
|
786
|
+
modelPromise = (async () => {
|
|
787
|
+
const [{ ChatOpenAI }, settings] = await Promise.all([
|
|
788
|
+
import("@langchain/openai"),
|
|
789
|
+
getAiSettings(userName)
|
|
790
|
+
]);
|
|
791
|
+
const modelKwargs = getOpenRouterModelKwargs(
|
|
792
|
+
settings.OPENAI_API_ENDPOINT
|
|
793
|
+
);
|
|
794
|
+
return new ChatOpenAI({
|
|
795
|
+
temperature: 0.2,
|
|
796
|
+
modelName,
|
|
797
|
+
apiKey: settings.OPENAI_API_KEY,
|
|
798
|
+
...Object.keys(modelKwargs).length ? { modelKwargs } : {},
|
|
799
|
+
configuration: {
|
|
800
|
+
baseURL: settings.OPENAI_API_ENDPOINT,
|
|
801
|
+
defaultHeaders: {
|
|
802
|
+
"HTTP-Referer": "https://tradejs.dev",
|
|
803
|
+
"X-Title": "Inv"
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
})();
|
|
808
|
+
modelPromise.catch(() => {
|
|
809
|
+
aiModelCache.delete(cacheKey);
|
|
810
|
+
});
|
|
811
|
+
aiModelCache.set(cacheKey, modelPromise);
|
|
812
|
+
}
|
|
813
|
+
return modelPromise;
|
|
814
|
+
};
|
|
815
|
+
var getAiModel = async (userName = "root", modelName = DEFAULT_AI_MODEL) => {
|
|
816
|
+
try {
|
|
817
|
+
return await createAiModel(userName, modelName);
|
|
818
|
+
} catch (error) {
|
|
819
|
+
aiModelCache.delete(getAiModelCacheKey(userName, modelName));
|
|
820
|
+
userSettingsCache.delete(userName);
|
|
821
|
+
throw error;
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
var resetAiRuntimeCache = () => {
|
|
825
|
+
aiModelCache.clear();
|
|
826
|
+
userSettingsCache.clear();
|
|
827
|
+
};
|
|
828
|
+
var ensureAiStrategyPluginsLoaded = async () => {
|
|
829
|
+
await ensureStrategyPluginsLoaded();
|
|
830
|
+
};
|
|
831
|
+
var buildAiPrompts = (signal) => {
|
|
832
|
+
const payload = buildAiPayload(signal);
|
|
833
|
+
return {
|
|
834
|
+
systemPrompt: buildAiSystemPrompt(signal),
|
|
835
|
+
humanPrompt: buildAiHumanPrompt(signal, payload)
|
|
836
|
+
};
|
|
837
|
+
};
|
|
838
|
+
var runAiPrompt = async ({ systemPrompt, humanPrompt }, options = {}) => {
|
|
839
|
+
if (options.signal) {
|
|
840
|
+
await ensureAiStrategyPluginsLoaded();
|
|
841
|
+
}
|
|
842
|
+
const [{ HumanMessage, SystemMessage }, model] = await Promise.all([
|
|
843
|
+
import("@langchain/core/messages"),
|
|
844
|
+
getAiModel(options.userName, options.model)
|
|
845
|
+
]);
|
|
846
|
+
const messages = [];
|
|
847
|
+
messages.push(new SystemMessage(systemPrompt));
|
|
848
|
+
messages.push(
|
|
849
|
+
new HumanMessage({
|
|
850
|
+
content: [
|
|
851
|
+
{
|
|
852
|
+
type: "text",
|
|
853
|
+
text: humanPrompt
|
|
854
|
+
}
|
|
855
|
+
]
|
|
856
|
+
})
|
|
857
|
+
);
|
|
858
|
+
const response = await model.invoke(messages);
|
|
859
|
+
const parsed = parseAIResponse(
|
|
860
|
+
normalizeResponseContent(response.content)
|
|
861
|
+
);
|
|
862
|
+
const normalized = normalizeAnalysis(parsed);
|
|
863
|
+
if (!options.signal) {
|
|
864
|
+
return normalized;
|
|
865
|
+
}
|
|
866
|
+
return postProcessAiAnalysisByStrategy(
|
|
867
|
+
options.signal,
|
|
868
|
+
normalized,
|
|
869
|
+
options.payload
|
|
870
|
+
);
|
|
871
|
+
};
|
|
872
|
+
var runAiPromptLocal = async (signal, options = {}) => {
|
|
873
|
+
await ensureAiStrategyPluginsLoaded();
|
|
874
|
+
const payload = options.payload ?? buildAiPayload(signal);
|
|
875
|
+
const gateContext = getDeterministicAiGateContext(payload);
|
|
876
|
+
const signalDirection = getSignalDirection(signal);
|
|
877
|
+
const deterministicQuality = getDeterministicQuality(gateContext);
|
|
878
|
+
const approvalAllowedNow = typeof gateContext?.approvalAllowedNow === "boolean" ? gateContext.approvalAllowedNow : deterministicQuality >= 4;
|
|
879
|
+
return postProcessAiAnalysisByStrategy(
|
|
880
|
+
signal,
|
|
881
|
+
{
|
|
882
|
+
direction: approvalAllowedNow ? signalDirection : null,
|
|
883
|
+
quality: deterministicQuality,
|
|
884
|
+
needRetest: !approvalAllowedNow,
|
|
885
|
+
retestPrice: null,
|
|
886
|
+
takeProfitPrice: approvalAllowedNow ? signal.prices?.takeProfitPrice ?? null : null,
|
|
887
|
+
stopLossPrice: approvalAllowedNow ? signal.prices?.stopLossPrice ?? null : null
|
|
888
|
+
},
|
|
889
|
+
payload
|
|
890
|
+
);
|
|
891
|
+
};
|
|
892
|
+
var askAI = async (signal, options = {}) => {
|
|
893
|
+
const { symbol } = signal;
|
|
894
|
+
await ensureAiStrategyPluginsLoaded();
|
|
895
|
+
const payload = buildAiPayload(signal);
|
|
896
|
+
const content = await runAiPrompt(
|
|
897
|
+
{
|
|
898
|
+
systemPrompt: buildAiSystemPrompt(signal),
|
|
899
|
+
humanPrompt: buildAiHumanPrompt(signal, payload)
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
...options,
|
|
903
|
+
signal,
|
|
904
|
+
payload
|
|
905
|
+
}
|
|
906
|
+
);
|
|
907
|
+
await (0, import_redis.setData)(import_redis.redisKeys.analysis(symbol, signal.signalId), content);
|
|
908
|
+
return content;
|
|
909
|
+
};
|
|
910
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
911
|
+
0 && (module.exports = {
|
|
912
|
+
DEFAULT_AI_MODEL,
|
|
913
|
+
MAX_AI_SERIES_POINTS,
|
|
914
|
+
askAI,
|
|
915
|
+
buildAiHumanPrompt,
|
|
916
|
+
buildAiPayload,
|
|
917
|
+
buildAiPrompts,
|
|
918
|
+
buildAiSystemPrompt,
|
|
919
|
+
ensureAiStrategyPluginsLoaded,
|
|
920
|
+
getDeterministicAiGateContext,
|
|
921
|
+
getOpenRouterModelKwargs,
|
|
922
|
+
resetAiRuntimeCache,
|
|
923
|
+
runAiPrompt,
|
|
924
|
+
runAiPromptLocal,
|
|
925
|
+
trimSeriesDeep
|
|
926
|
+
});
|