@backtest-kit/ollama 0.2.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +263 -113
- package/build/index.cjs +500 -755
- package/build/index.mjs +492 -748
- package/package.json +1 -1
- package/types.d.ts +278 -396
package/build/index.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { event, validateToolArguments, RoundRobin, addCompletion } from 'agent-swarm-kit';
|
|
2
2
|
import { scoped } from 'di-scoped';
|
|
3
|
-
import { createActivator } from 'di-kit';
|
|
4
|
-
import { Markdown, getContext, getMode } from 'backtest-kit';
|
|
5
3
|
import path, { join } from 'path';
|
|
4
|
+
import { createRequire } from 'module';
|
|
5
|
+
import { createActivator } from 'di-kit';
|
|
6
|
+
import { memoize, ToolRegistry, Subject, trycatch, errorData, getErrorMessage, iterateDocuments, distinctDocuments, resolveDocuments, singleshot, randomString, fetchApi, str, timeout } from 'functools-kit';
|
|
7
|
+
import { Markdown, getSymbol, getContext, getMode } from 'backtest-kit';
|
|
6
8
|
import MarkdownIt from 'markdown-it';
|
|
7
9
|
import sanitizeHtml from 'sanitize-html';
|
|
8
10
|
import { lint } from 'markdownlint/promise';
|
|
9
11
|
import { applyFixes } from 'markdownlint';
|
|
10
|
-
import { ToolRegistry, memoize, singleshot, Subject, trycatch, errorData, getErrorMessage, iterateDocuments, distinctDocuments, resolveDocuments, randomString, fetchApi, str, timeout } from 'functools-kit';
|
|
11
|
-
import { createRequire } from 'module';
|
|
12
12
|
import fs, { mkdir, writeFile } from 'fs/promises';
|
|
13
13
|
import OpenAI from 'openai';
|
|
14
14
|
import { AIMessage, SystemMessage, HumanMessage, ToolMessage } from '@langchain/core/messages';
|
|
@@ -18,8 +18,6 @@ import { InferenceClient } from '@huggingface/inference';
|
|
|
18
18
|
import { ChatOpenAI } from '@langchain/openai';
|
|
19
19
|
import { get, set } from 'lodash-es';
|
|
20
20
|
import { Ollama } from 'ollama';
|
|
21
|
-
import { zodResponseFormat } from 'openai/helpers/zod';
|
|
22
|
-
import { z } from 'zod';
|
|
23
21
|
|
|
24
22
|
/**
|
|
25
23
|
* Enumeration of completion strategy types.
|
|
@@ -43,7 +41,6 @@ var CompletionName;
|
|
|
43
41
|
/** Outline completion mode (structured JSON with schema validation) */
|
|
44
42
|
CompletionName["RunnerOutlineCompletion"] = "runner_outline_completion";
|
|
45
43
|
})(CompletionName || (CompletionName = {}));
|
|
46
|
-
var CompletionName$1 = CompletionName;
|
|
47
44
|
|
|
48
45
|
/**
|
|
49
46
|
* Scoped context service for isolated execution contexts.
|
|
@@ -218,14 +215,6 @@ class LoggerService {
|
|
|
218
215
|
*/
|
|
219
216
|
const { provide, inject, init, override } = createActivator("ollama");
|
|
220
217
|
|
|
221
|
-
/**
|
|
222
|
-
* Common service type identifiers.
|
|
223
|
-
* Services used across the entire application.
|
|
224
|
-
*/
|
|
225
|
-
const commonServices$1 = {
|
|
226
|
-
/** Logger service for application-wide logging */
|
|
227
|
-
loggerService: Symbol("loggerService"),
|
|
228
|
-
};
|
|
229
218
|
/**
|
|
230
219
|
* Base service type identifiers.
|
|
231
220
|
* Core foundational services.
|
|
@@ -233,6 +222,7 @@ const commonServices$1 = {
|
|
|
233
222
|
const baseServices$1 = {
|
|
234
223
|
/** Context service for scoped execution contexts */
|
|
235
224
|
contextService: Symbol('contextService'),
|
|
225
|
+
loggerService: Symbol("loggerService"),
|
|
236
226
|
};
|
|
237
227
|
/**
|
|
238
228
|
* Private service type identifiers.
|
|
@@ -241,8 +231,6 @@ const baseServices$1 = {
|
|
|
241
231
|
const privateServices$1 = {
|
|
242
232
|
/** Runner private service for AI provider operations */
|
|
243
233
|
runnerPrivateService: Symbol('runnerPrivateService'),
|
|
244
|
-
/** Outline private service for structured completions */
|
|
245
|
-
outlinePrivateService: Symbol('outlinePrivateService'),
|
|
246
234
|
};
|
|
247
235
|
/**
|
|
248
236
|
* Public service type identifiers.
|
|
@@ -251,20 +239,29 @@ const privateServices$1 = {
|
|
|
251
239
|
const publicServices$1 = {
|
|
252
240
|
/** Runner public service for context-managed AI operations */
|
|
253
241
|
runnerPublicService: Symbol('runnerPublicService'),
|
|
254
|
-
/** Outline public service for simplified structured completions */
|
|
255
|
-
outlinePublicService: Symbol('outlinePublicService'),
|
|
256
242
|
};
|
|
257
243
|
const promptServices$1 = {
|
|
258
|
-
|
|
244
|
+
resolvePromptService: Symbol('resolvePromptService'),
|
|
245
|
+
};
|
|
246
|
+
const cacheServices$1 = {
|
|
247
|
+
promptCacheService: Symbol('promptCacheService'),
|
|
259
248
|
};
|
|
260
249
|
const markdownServices$1 = {
|
|
261
250
|
outlineMarkdownService: Symbol('outlineMarkdownService'),
|
|
262
251
|
};
|
|
263
|
-
const
|
|
252
|
+
const templateServices$1 = {
|
|
264
253
|
optimizerTemplateService: Symbol('optimizerTemplateService'),
|
|
254
|
+
};
|
|
255
|
+
const schemaServices$1 = {
|
|
265
256
|
optimizerSchemaService: Symbol('optimizerSchemaService'),
|
|
257
|
+
};
|
|
258
|
+
const validationServices$1 = {
|
|
266
259
|
optimizerValidationService: Symbol('optimizerValidationService'),
|
|
260
|
+
};
|
|
261
|
+
const connectionServices$1 = {
|
|
267
262
|
optimizerConnectionService: Symbol('optimizerConnectionService'),
|
|
263
|
+
};
|
|
264
|
+
const globalServices$1 = {
|
|
268
265
|
optimizerGlobalService: Symbol('optimizerGlobalService'),
|
|
269
266
|
};
|
|
270
267
|
/**
|
|
@@ -283,15 +280,66 @@ const optimizerServices$1 = {
|
|
|
283
280
|
* ```
|
|
284
281
|
*/
|
|
285
282
|
const TYPES = {
|
|
286
|
-
...commonServices$1,
|
|
287
283
|
...baseServices$1,
|
|
288
284
|
...promptServices$1,
|
|
285
|
+
...cacheServices$1,
|
|
289
286
|
...markdownServices$1,
|
|
290
|
-
...
|
|
287
|
+
...templateServices$1,
|
|
288
|
+
...schemaServices$1,
|
|
289
|
+
...validationServices$1,
|
|
290
|
+
...connectionServices$1,
|
|
291
|
+
...globalServices$1,
|
|
291
292
|
...privateServices$1,
|
|
292
293
|
...publicServices$1,
|
|
293
294
|
};
|
|
294
295
|
|
|
296
|
+
const PROMPT_TYPE_SYMBOL = Symbol("prompt-type");
|
|
297
|
+
class Prompt {
|
|
298
|
+
constructor(source) {
|
|
299
|
+
this.source = source;
|
|
300
|
+
this.__type__ = PROMPT_TYPE_SYMBOL;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
Prompt.fromPrompt = (source) => {
|
|
304
|
+
if (!source || typeof source !== "object") {
|
|
305
|
+
throw new Error("Source must be a valid PromptModel object");
|
|
306
|
+
}
|
|
307
|
+
return new Prompt(source);
|
|
308
|
+
};
|
|
309
|
+
Prompt.isPrompt = (value) => {
|
|
310
|
+
return (value !== null &&
|
|
311
|
+
typeof value === "object" &&
|
|
312
|
+
value.__type__ === PROMPT_TYPE_SYMBOL);
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const require = createRequire(import.meta.url);
|
|
316
|
+
const REQUIRE_MODULE_FN = memoize(([module]) => module, (module) => {
|
|
317
|
+
const modulePath = require.resolve(join(module.baseDir, module.path));
|
|
318
|
+
return require(modulePath);
|
|
319
|
+
});
|
|
320
|
+
class PromptCacheService {
|
|
321
|
+
constructor() {
|
|
322
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
323
|
+
this.readModule = (module) => {
|
|
324
|
+
this.loggerService.log("promptCacheService readModule", {
|
|
325
|
+
path: module.path,
|
|
326
|
+
baseDir: module.baseDir,
|
|
327
|
+
});
|
|
328
|
+
const source = REQUIRE_MODULE_FN(module);
|
|
329
|
+
return Prompt.fromPrompt(source);
|
|
330
|
+
};
|
|
331
|
+
this.clear = (module) => {
|
|
332
|
+
this.loggerService.log("promptCacheService clear", {
|
|
333
|
+
module,
|
|
334
|
+
});
|
|
335
|
+
if (module) {
|
|
336
|
+
REQUIRE_MODULE_FN.clear(module);
|
|
337
|
+
}
|
|
338
|
+
REQUIRE_MODULE_FN.clear();
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
295
343
|
/**
|
|
296
344
|
* Warning threshold for message size in kilobytes.
|
|
297
345
|
* Messages exceeding this size trigger console warnings.
|
|
@@ -448,203 +496,6 @@ class OutlineMarkdownService {
|
|
|
448
496
|
}
|
|
449
497
|
}
|
|
450
498
|
|
|
451
|
-
/**
|
|
452
|
-
* Enumeration of supported JSON schema outlines.
|
|
453
|
-
*
|
|
454
|
-
* Defines unique identifiers for structured output schemas used with
|
|
455
|
-
* LLM providers. Outlines enforce JSON schema validation for critical
|
|
456
|
-
* data structures like trading signals.
|
|
457
|
-
*
|
|
458
|
-
* @example
|
|
459
|
-
* ```typescript
|
|
460
|
-
* import { OutlineName } from '@backtest-kit/ollama';
|
|
461
|
-
*
|
|
462
|
-
* const outlineName = OutlineName.SignalOutline;
|
|
463
|
-
* ```
|
|
464
|
-
*/
|
|
465
|
-
var OutlineName;
|
|
466
|
-
(function (OutlineName) {
|
|
467
|
-
/** Trading signal JSON schema for position, TP/SL, and risk parameters */
|
|
468
|
-
OutlineName["SignalOutline"] = "signal_outline";
|
|
469
|
-
})(OutlineName || (OutlineName = {}));
|
|
470
|
-
var OutlineName$1 = OutlineName;
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* Lints and auto-fixes markdown content using markdownlint rules.
|
|
474
|
-
*
|
|
475
|
-
* Validates markdown syntax and applies automatic fixes for common issues
|
|
476
|
-
* like inconsistent list markers, trailing spaces, and heading styles.
|
|
477
|
-
* Returns the original content if no errors found or fixes cannot be applied.
|
|
478
|
-
*
|
|
479
|
-
* @param content - Raw markdown content to lint
|
|
480
|
-
* @returns Promise resolving to linted markdown content
|
|
481
|
-
*
|
|
482
|
-
* @example
|
|
483
|
-
* ```typescript
|
|
484
|
-
* const markdown = "# Title\n\n\n## Subtitle"; // Multiple blank lines
|
|
485
|
-
* const linted = await toLintMarkdown(markdown);
|
|
486
|
-
* // Returns: "# Title\n\n## Subtitle" (extra blank line removed)
|
|
487
|
-
* ```
|
|
488
|
-
*/
|
|
489
|
-
const toLintMarkdown = async (content) => {
|
|
490
|
-
if (!content) {
|
|
491
|
-
return "";
|
|
492
|
-
}
|
|
493
|
-
const { content: errors } = await lint({ strings: { content } });
|
|
494
|
-
if (!errors.length) {
|
|
495
|
-
return content;
|
|
496
|
-
}
|
|
497
|
-
const value = applyFixes(content, errors);
|
|
498
|
-
return value ? value : content;
|
|
499
|
-
};
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* Converts markdown content to plain text with Telegram-compatible HTML formatting.
|
|
503
|
-
*
|
|
504
|
-
* Processes markdown through three stages:
|
|
505
|
-
* 1. Lints and fixes markdown using markdownlint
|
|
506
|
-
* 2. Renders markdown to HTML using markdown-it
|
|
507
|
-
* 3. Sanitizes HTML to Telegram-compatible subset
|
|
508
|
-
*
|
|
509
|
-
* Supported tags: b, i, a, code, pre, s, u, tg-spoiler, blockquote, br
|
|
510
|
-
* Transforms: headings removed, lists to bullets, multiple newlines collapsed
|
|
511
|
-
*
|
|
512
|
-
* @param content - Raw markdown content
|
|
513
|
-
* @returns Promise resolving to sanitized plain text with HTML formatting
|
|
514
|
-
*
|
|
515
|
-
* @example
|
|
516
|
-
* ```typescript
|
|
517
|
-
* const markdown = "# Title\n**Bold** and *italic*\n- Item 1\n- Item 2";
|
|
518
|
-
* const plain = await toPlainString(markdown);
|
|
519
|
-
* // Returns: "Bold and italic\n• Item 1\n• Item 2"
|
|
520
|
-
* ```
|
|
521
|
-
*/
|
|
522
|
-
const toPlainString = async (content) => {
|
|
523
|
-
if (!content) {
|
|
524
|
-
return "";
|
|
525
|
-
}
|
|
526
|
-
const markdown = await toLintMarkdown(content);
|
|
527
|
-
const md = new MarkdownIt({
|
|
528
|
-
html: false,
|
|
529
|
-
breaks: true,
|
|
530
|
-
linkify: true,
|
|
531
|
-
typographer: true,
|
|
532
|
-
});
|
|
533
|
-
let telegramHtml = md.render(markdown);
|
|
534
|
-
telegramHtml = sanitizeHtml(telegramHtml, {
|
|
535
|
-
allowedTags: [
|
|
536
|
-
"b",
|
|
537
|
-
"i",
|
|
538
|
-
"a",
|
|
539
|
-
"code",
|
|
540
|
-
"pre",
|
|
541
|
-
"s",
|
|
542
|
-
"u",
|
|
543
|
-
"tg-spoiler",
|
|
544
|
-
"blockquote",
|
|
545
|
-
"br",
|
|
546
|
-
],
|
|
547
|
-
allowedAttributes: {
|
|
548
|
-
a: ["href"],
|
|
549
|
-
},
|
|
550
|
-
transformTags: {
|
|
551
|
-
h1: "",
|
|
552
|
-
h2: "",
|
|
553
|
-
h3: "",
|
|
554
|
-
h4: "",
|
|
555
|
-
h5: "",
|
|
556
|
-
h6: "",
|
|
557
|
-
a: "",
|
|
558
|
-
strong: "",
|
|
559
|
-
em: "",
|
|
560
|
-
p: () => "",
|
|
561
|
-
ul: () => "",
|
|
562
|
-
li: () => "• ",
|
|
563
|
-
ol: () => "",
|
|
564
|
-
hr: () => "\n",
|
|
565
|
-
br: () => "\n",
|
|
566
|
-
div: () => "",
|
|
567
|
-
},
|
|
568
|
-
});
|
|
569
|
-
return telegramHtml.replaceAll(/\n[\s\n]*\n/g, "\n").trim();
|
|
570
|
-
};
|
|
571
|
-
|
|
572
|
-
/**
|
|
573
|
-
* Private service for processing structured outline completions.
|
|
574
|
-
*
|
|
575
|
-
* Handles the core logic for executing outline-based AI completions with schema validation.
|
|
576
|
-
* Processes AI responses through the agent-swarm-kit json function to extract and validate
|
|
577
|
-
* structured trading signal data.
|
|
578
|
-
*
|
|
579
|
-
* Key features:
|
|
580
|
-
* - JSON schema validation using agent-swarm-kit
|
|
581
|
-
* - Trading signal extraction and transformation
|
|
582
|
-
* - Type conversion for numeric fields
|
|
583
|
-
* - Markdown formatting cleanup for notes
|
|
584
|
-
* - Error handling for validation failures
|
|
585
|
-
*
|
|
586
|
-
* @example
|
|
587
|
-
* ```typescript
|
|
588
|
-
* const outlinePrivate = inject<OutlinePrivateService>(TYPES.outlinePrivateService);
|
|
589
|
-
* const signal = await outlinePrivate.getCompletion([
|
|
590
|
-
* { role: "user", content: "Analyze market" }
|
|
591
|
-
* ]);
|
|
592
|
-
* ```
|
|
593
|
-
*/
|
|
594
|
-
class OutlinePrivateService {
|
|
595
|
-
constructor() {
|
|
596
|
-
/** Logger service for operation tracking */
|
|
597
|
-
this.loggerService = inject(TYPES.loggerService);
|
|
598
|
-
/**
|
|
599
|
-
* Processes outline completion messages and extracts structured signal data.
|
|
600
|
-
*
|
|
601
|
-
* Sends messages to the AI provider, validates the response against the signal schema,
|
|
602
|
-
* and transforms the data into a structured format. Returns null if the AI decides
|
|
603
|
-
* to wait (no position).
|
|
604
|
-
*
|
|
605
|
-
* @param messages - Array of conversation messages for the AI
|
|
606
|
-
* @returns Promise resolving to structured signal data or null if position is "wait"
|
|
607
|
-
* @throws Error if validation fails or AI returns an error
|
|
608
|
-
*
|
|
609
|
-
* @example
|
|
610
|
-
* ```typescript
|
|
611
|
-
* const signal = await outlinePrivateService.getCompletion([
|
|
612
|
-
* { role: "system", content: "Trading analyst role" },
|
|
613
|
-
* { role: "user", content: "Market analysis data..." }
|
|
614
|
-
* ]);
|
|
615
|
-
*
|
|
616
|
-
* if (signal) {
|
|
617
|
-
* console.log(`Position: ${signal.position}`);
|
|
618
|
-
* console.log(`Entry: ${signal.priceOpen}`);
|
|
619
|
-
* console.log(`SL: ${signal.priceStopLoss}`);
|
|
620
|
-
* console.log(`TP: ${signal.priceTakeProfit}`);
|
|
621
|
-
* }
|
|
622
|
-
* ```
|
|
623
|
-
*/
|
|
624
|
-
this.getCompletion = async (messages) => {
|
|
625
|
-
this.loggerService.log("outlinePrivateService getCompletion", {
|
|
626
|
-
messages,
|
|
627
|
-
});
|
|
628
|
-
const { data, resultId, error } = await json(OutlineName$1.SignalOutline, messages);
|
|
629
|
-
if (error) {
|
|
630
|
-
throw new Error(error);
|
|
631
|
-
}
|
|
632
|
-
if (data.position === "wait") {
|
|
633
|
-
return null;
|
|
634
|
-
}
|
|
635
|
-
return {
|
|
636
|
-
id: resultId,
|
|
637
|
-
position: data.position,
|
|
638
|
-
minuteEstimatedTime: +data.minute_estimated_time,
|
|
639
|
-
priceStopLoss: +data.price_stop_loss,
|
|
640
|
-
priceTakeProfit: +data.price_take_profit,
|
|
641
|
-
note: await toPlainString(data.risk_note),
|
|
642
|
-
priceOpen: data.price_open ? +data.price_open : undefined,
|
|
643
|
-
};
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
|
|
648
499
|
/**
|
|
649
500
|
* Private service managing AI inference provider registry and execution.
|
|
650
501
|
*
|
|
@@ -765,47 +616,19 @@ class RunnerPrivateService {
|
|
|
765
616
|
}
|
|
766
617
|
}
|
|
767
618
|
|
|
768
|
-
const require = createRequire(import.meta.url);
|
|
769
|
-
/**
|
|
770
|
-
* Default fallback prompt configuration.
|
|
771
|
-
* Used when signal.prompt.cjs file is not found.
|
|
772
|
-
*/
|
|
773
|
-
const DEFAULT_PROMPT = {
|
|
774
|
-
user: "",
|
|
775
|
-
system: [],
|
|
776
|
-
};
|
|
777
|
-
/**
|
|
778
|
-
* Lazy-loads and caches signal prompt configuration.
|
|
779
|
-
* Attempts to load from config/prompt/signal.prompt.cjs, falls back to DEFAULT_PROMPT if not found.
|
|
780
|
-
* Uses singleshot pattern to ensure configuration is loaded only once.
|
|
781
|
-
* @returns Prompt configuration with system and user prompts
|
|
782
|
-
*/
|
|
783
|
-
const GET_PROMPT_FN = singleshot(() => {
|
|
784
|
-
try {
|
|
785
|
-
const modulePath = require.resolve(path.join(process.cwd(), `./config/prompt/signal.prompt.cjs`));
|
|
786
|
-
console.log(`Using ${modulePath} implementation as signal.prompt.cjs`);
|
|
787
|
-
return require(modulePath);
|
|
788
|
-
}
|
|
789
|
-
catch (error) {
|
|
790
|
-
console.log(`Using empty fallback for signal.prompt.cjs`, error);
|
|
791
|
-
return DEFAULT_PROMPT;
|
|
792
|
-
}
|
|
793
|
-
});
|
|
794
619
|
/**
|
|
795
620
|
* Service for managing signal prompts for AI/LLM integrations.
|
|
796
621
|
*
|
|
797
|
-
* Provides access to system and user prompts
|
|
622
|
+
* Provides access to system and user prompts from Prompt.
|
|
798
623
|
* Supports both static prompt arrays and dynamic prompt functions.
|
|
799
624
|
*
|
|
800
625
|
* Key responsibilities:
|
|
801
|
-
* - Lazy-loads prompt configuration from config/prompt/signal.prompt.cjs
|
|
802
626
|
* - Resolves system prompts (static arrays or async functions)
|
|
803
627
|
* - Provides user prompt strings
|
|
804
|
-
* - Falls back to empty prompts if configuration is missing
|
|
805
628
|
*
|
|
806
629
|
* Used for AI-powered signal analysis and strategy recommendations.
|
|
807
630
|
*/
|
|
808
|
-
class
|
|
631
|
+
class ResolvePromptService {
|
|
809
632
|
constructor() {
|
|
810
633
|
this.loggerService = inject(TYPES.loggerService);
|
|
811
634
|
/**
|
|
@@ -816,6 +639,7 @@ class SignalPromptService {
|
|
|
816
639
|
* - Async/sync function returning string array (executed and awaited)
|
|
817
640
|
* - Undefined (returns empty array)
|
|
818
641
|
*
|
|
642
|
+
* @param prompt - Prompt containing the loaded module
|
|
819
643
|
* @param symbol - Trading symbol (e.g., "BTCUSDT")
|
|
820
644
|
* @param strategyName - Strategy identifier
|
|
821
645
|
* @param exchangeName - Exchange identifier
|
|
@@ -823,15 +647,15 @@ class SignalPromptService {
|
|
|
823
647
|
* @param backtest - Whether running in backtest mode
|
|
824
648
|
* @returns Promise resolving to array of system prompt strings
|
|
825
649
|
*/
|
|
826
|
-
this.getSystemPrompt = async (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
827
|
-
this.loggerService.log("
|
|
650
|
+
this.getSystemPrompt = async (prompt, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
651
|
+
this.loggerService.log("resolvePromptService getSystemPrompt", {
|
|
828
652
|
symbol,
|
|
829
653
|
strategyName,
|
|
830
654
|
exchangeName,
|
|
831
655
|
frameName,
|
|
832
656
|
backtest,
|
|
833
657
|
});
|
|
834
|
-
const { system } =
|
|
658
|
+
const { system } = prompt.source;
|
|
835
659
|
if (Array.isArray(system)) {
|
|
836
660
|
return system;
|
|
837
661
|
}
|
|
@@ -843,6 +667,7 @@ class SignalPromptService {
|
|
|
843
667
|
/**
|
|
844
668
|
* Retrieves user prompt string for AI input.
|
|
845
669
|
*
|
|
670
|
+
* @param prompt - Prompt containing the loaded module
|
|
846
671
|
* @param symbol - Trading symbol (e.g., "BTCUSDT")
|
|
847
672
|
* @param strategyName - Strategy identifier
|
|
848
673
|
* @param exchangeName - Exchange identifier
|
|
@@ -850,15 +675,15 @@ class SignalPromptService {
|
|
|
850
675
|
* @param backtest - Whether running in backtest mode
|
|
851
676
|
* @returns Promise resolving to user prompt string
|
|
852
677
|
*/
|
|
853
|
-
this.getUserPrompt = async (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
854
|
-
this.loggerService.log("
|
|
678
|
+
this.getUserPrompt = async (prompt, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
679
|
+
this.loggerService.log("resolvePromptService getUserPrompt", {
|
|
855
680
|
symbol,
|
|
856
681
|
strategyName,
|
|
857
682
|
exchangeName,
|
|
858
683
|
frameName,
|
|
859
684
|
backtest,
|
|
860
685
|
});
|
|
861
|
-
const { user } =
|
|
686
|
+
const { user } = prompt.source;
|
|
862
687
|
if (typeof user === "string") {
|
|
863
688
|
return user;
|
|
864
689
|
}
|
|
@@ -870,92 +695,6 @@ class SignalPromptService {
|
|
|
870
695
|
}
|
|
871
696
|
}
|
|
872
697
|
|
|
873
|
-
/**
|
|
874
|
-
* Public-facing service for structured AI outline completions.
|
|
875
|
-
*
|
|
876
|
-
* Provides a simplified interface for executing structured AI completions with schema validation.
|
|
877
|
-
* Handles context creation and isolation for outline-based operations.
|
|
878
|
-
* Used for extracting structured data from AI responses (e.g., trading signals).
|
|
879
|
-
*
|
|
880
|
-
* Key features:
|
|
881
|
-
* - Simplified API with automatic context management
|
|
882
|
-
* - JSON schema validation for structured outputs
|
|
883
|
-
* - Support for multiple AI providers
|
|
884
|
-
* - Optional API key parameter with fallback
|
|
885
|
-
* - Logging integration
|
|
886
|
-
*
|
|
887
|
-
* @example
|
|
888
|
-
* ```typescript
|
|
889
|
-
* import { engine } from "./lib";
|
|
890
|
-
* import { InferenceName } from "./enum/InferenceName";
|
|
891
|
-
*
|
|
892
|
-
* const signal = await engine.outlinePublicService.getCompletion(
|
|
893
|
-
* [{ role: "user", content: "Analyze BTC/USDT and decide position" }],
|
|
894
|
-
* InferenceName.ClaudeInference,
|
|
895
|
-
* "claude-3-5-sonnet-20240620",
|
|
896
|
-
* "sk-ant-..."
|
|
897
|
-
* );
|
|
898
|
-
*
|
|
899
|
-
* // Returns structured signal:
|
|
900
|
-
* // {
|
|
901
|
-
* // position: "long",
|
|
902
|
-
* // priceOpen: 50000,
|
|
903
|
-
* // priceStopLoss: 48000,
|
|
904
|
-
* // priceTakeProfit: 52000,
|
|
905
|
-
* // minuteEstimatedTime: 120,
|
|
906
|
-
* // note: "Strong bullish momentum..."
|
|
907
|
-
* // }
|
|
908
|
-
* ```
|
|
909
|
-
*/
|
|
910
|
-
class OutlinePublicService {
|
|
911
|
-
constructor() {
|
|
912
|
-
/** Logger service for operation tracking */
|
|
913
|
-
this.loggerService = inject(TYPES.loggerService);
|
|
914
|
-
/** Private service handling outline completion logic */
|
|
915
|
-
this.outlinePrivateService = inject(TYPES.outlinePrivateService);
|
|
916
|
-
/**
|
|
917
|
-
* Executes a structured outline completion with schema validation.
|
|
918
|
-
*
|
|
919
|
-
* Creates an isolated execution context and processes messages through the AI provider
|
|
920
|
-
* to generate a structured response conforming to a predefined schema.
|
|
921
|
-
*
|
|
922
|
-
* @param messages - Array of conversation messages for the AI
|
|
923
|
-
* @param inference - AI provider identifier
|
|
924
|
-
* @param model - Model name/identifier
|
|
925
|
-
* @param apiKey - Optional API key(s), required for most providers
|
|
926
|
-
* @returns Promise resolving to structured signal data or null if position is "wait"
|
|
927
|
-
*
|
|
928
|
-
* @example
|
|
929
|
-
* ```typescript
|
|
930
|
-
* const result = await outlinePublicService.getCompletion(
|
|
931
|
-
* [
|
|
932
|
-
* { role: "system", content: "You are a trading analyst" },
|
|
933
|
-
* { role: "user", content: "Analyze current BTC market" }
|
|
934
|
-
* ],
|
|
935
|
-
* InferenceName.DeepseekInference,
|
|
936
|
-
* "deepseek-chat",
|
|
937
|
-
* "sk-..."
|
|
938
|
-
* );
|
|
939
|
-
* ```
|
|
940
|
-
*/
|
|
941
|
-
this.getCompletion = async (messages, inference, model, apiKey) => {
|
|
942
|
-
this.loggerService.log("outlinePublicService getCompletion", {
|
|
943
|
-
messages,
|
|
944
|
-
model,
|
|
945
|
-
apiKey,
|
|
946
|
-
inference,
|
|
947
|
-
});
|
|
948
|
-
return await ContextService.runInContext(async () => {
|
|
949
|
-
return await this.outlinePrivateService.getCompletion(messages);
|
|
950
|
-
}, {
|
|
951
|
-
apiKey: apiKey,
|
|
952
|
-
inference,
|
|
953
|
-
model,
|
|
954
|
-
});
|
|
955
|
-
};
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
|
|
959
698
|
/**
|
|
960
699
|
* Public-facing service for AI inference operations with context management.
|
|
961
700
|
*
|
|
@@ -1093,6 +832,106 @@ class RunnerPublicService {
|
|
|
1093
832
|
}
|
|
1094
833
|
}
|
|
1095
834
|
|
|
835
|
+
/**
|
|
836
|
+
* Lints and auto-fixes markdown content using markdownlint rules.
|
|
837
|
+
*
|
|
838
|
+
* Validates markdown syntax and applies automatic fixes for common issues
|
|
839
|
+
* like inconsistent list markers, trailing spaces, and heading styles.
|
|
840
|
+
* Returns the original content if no errors found or fixes cannot be applied.
|
|
841
|
+
*
|
|
842
|
+
* @param content - Raw markdown content to lint
|
|
843
|
+
* @returns Promise resolving to linted markdown content
|
|
844
|
+
*
|
|
845
|
+
* @example
|
|
846
|
+
* ```typescript
|
|
847
|
+
* const markdown = "# Title\n\n\n## Subtitle"; // Multiple blank lines
|
|
848
|
+
* const linted = await toLintMarkdown(markdown);
|
|
849
|
+
* // Returns: "# Title\n\n## Subtitle" (extra blank line removed)
|
|
850
|
+
* ```
|
|
851
|
+
*/
|
|
852
|
+
const toLintMarkdown = async (content) => {
|
|
853
|
+
if (!content) {
|
|
854
|
+
return "";
|
|
855
|
+
}
|
|
856
|
+
const { content: errors } = await lint({ strings: { content } });
|
|
857
|
+
if (!errors.length) {
|
|
858
|
+
return content;
|
|
859
|
+
}
|
|
860
|
+
const value = applyFixes(content, errors);
|
|
861
|
+
return value ? value : content;
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* Converts markdown content to plain text with Telegram-compatible HTML formatting.
|
|
866
|
+
*
|
|
867
|
+
* Processes markdown through three stages:
|
|
868
|
+
* 1. Lints and fixes markdown using markdownlint
|
|
869
|
+
* 2. Renders markdown to HTML using markdown-it
|
|
870
|
+
* 3. Sanitizes HTML to Telegram-compatible subset
|
|
871
|
+
*
|
|
872
|
+
* Supported tags: b, i, a, code, pre, s, u, tg-spoiler, blockquote, br
|
|
873
|
+
* Transforms: headings removed, lists to bullets, multiple newlines collapsed
|
|
874
|
+
*
|
|
875
|
+
* @param content - Raw markdown content
|
|
876
|
+
* @returns Promise resolving to sanitized plain text with HTML formatting
|
|
877
|
+
*
|
|
878
|
+
* @example
|
|
879
|
+
* ```typescript
|
|
880
|
+
* const markdown = "# Title\n**Bold** and *italic*\n- Item 1\n- Item 2";
|
|
881
|
+
* const plain = await toPlainString(markdown);
|
|
882
|
+
* // Returns: "Bold and italic\n• Item 1\n• Item 2"
|
|
883
|
+
* ```
|
|
884
|
+
*/
|
|
885
|
+
const toPlainString = async (content) => {
|
|
886
|
+
if (!content) {
|
|
887
|
+
return "";
|
|
888
|
+
}
|
|
889
|
+
const markdown = await toLintMarkdown(content);
|
|
890
|
+
const md = new MarkdownIt({
|
|
891
|
+
html: false,
|
|
892
|
+
breaks: true,
|
|
893
|
+
linkify: true,
|
|
894
|
+
typographer: true,
|
|
895
|
+
});
|
|
896
|
+
let telegramHtml = md.render(markdown);
|
|
897
|
+
telegramHtml = sanitizeHtml(telegramHtml, {
|
|
898
|
+
allowedTags: [
|
|
899
|
+
"b",
|
|
900
|
+
"i",
|
|
901
|
+
"a",
|
|
902
|
+
"code",
|
|
903
|
+
"pre",
|
|
904
|
+
"s",
|
|
905
|
+
"u",
|
|
906
|
+
"tg-spoiler",
|
|
907
|
+
"blockquote",
|
|
908
|
+
"br",
|
|
909
|
+
],
|
|
910
|
+
allowedAttributes: {
|
|
911
|
+
a: ["href"],
|
|
912
|
+
},
|
|
913
|
+
transformTags: {
|
|
914
|
+
h1: "",
|
|
915
|
+
h2: "",
|
|
916
|
+
h3: "",
|
|
917
|
+
h4: "",
|
|
918
|
+
h5: "",
|
|
919
|
+
h6: "",
|
|
920
|
+
a: "",
|
|
921
|
+
strong: "",
|
|
922
|
+
em: "",
|
|
923
|
+
p: () => "",
|
|
924
|
+
ul: () => "",
|
|
925
|
+
li: () => "• ",
|
|
926
|
+
ol: () => "",
|
|
927
|
+
hr: () => "\n",
|
|
928
|
+
br: () => "\n",
|
|
929
|
+
div: () => "",
|
|
930
|
+
},
|
|
931
|
+
});
|
|
932
|
+
return telegramHtml.replaceAll(/\n[\s\n]*\n/g, "\n").trim();
|
|
933
|
+
};
|
|
934
|
+
|
|
1096
935
|
/**
|
|
1097
936
|
* Default template service for generating optimizer code snippets.
|
|
1098
937
|
* Implements all IOptimizerTemplate methods with Ollama LLM integration.
|
|
@@ -2467,46 +2306,62 @@ class OptimizerGlobalService {
|
|
|
2467
2306
|
* This file is imported by lib/index.ts to ensure services are registered
|
|
2468
2307
|
* before the DI container is initialized.
|
|
2469
2308
|
*/
|
|
2470
|
-
/**
|
|
2471
|
-
* Register common services.
|
|
2472
|
-
*/
|
|
2473
|
-
{
|
|
2474
|
-
provide(TYPES.loggerService, () => new LoggerService());
|
|
2475
|
-
}
|
|
2476
2309
|
/**
|
|
2477
2310
|
* Register base services.
|
|
2478
2311
|
*/
|
|
2479
2312
|
{
|
|
2480
2313
|
provide(TYPES.contextService, () => new ContextService());
|
|
2314
|
+
provide(TYPES.loggerService, () => new LoggerService());
|
|
2481
2315
|
}
|
|
2482
2316
|
/**
|
|
2483
2317
|
* Register private services.
|
|
2484
2318
|
*/
|
|
2485
2319
|
{
|
|
2486
2320
|
provide(TYPES.runnerPrivateService, () => new RunnerPrivateService());
|
|
2487
|
-
provide(TYPES.outlinePrivateService, () => new OutlinePrivateService());
|
|
2488
2321
|
}
|
|
2489
2322
|
/**
|
|
2490
2323
|
* Register public services.
|
|
2491
2324
|
*/
|
|
2492
2325
|
{
|
|
2493
2326
|
provide(TYPES.runnerPublicService, () => new RunnerPublicService());
|
|
2494
|
-
provide(TYPES.outlinePublicService, () => new OutlinePublicService());
|
|
2495
2327
|
}
|
|
2496
2328
|
{
|
|
2497
|
-
provide(TYPES.
|
|
2329
|
+
provide(TYPES.resolvePromptService, () => new ResolvePromptService());
|
|
2330
|
+
}
|
|
2331
|
+
{
|
|
2332
|
+
provide(TYPES.promptCacheService, () => new PromptCacheService());
|
|
2498
2333
|
}
|
|
2499
2334
|
{
|
|
2500
2335
|
provide(TYPES.outlineMarkdownService, () => new OutlineMarkdownService());
|
|
2501
2336
|
}
|
|
2502
2337
|
/**
|
|
2503
|
-
* Register
|
|
2338
|
+
* Register template services.
|
|
2504
2339
|
*/
|
|
2505
2340
|
{
|
|
2506
2341
|
provide(TYPES.optimizerTemplateService, () => new OptimizerTemplateService());
|
|
2342
|
+
}
|
|
2343
|
+
/**
|
|
2344
|
+
* Register schema services.
|
|
2345
|
+
*/
|
|
2346
|
+
{
|
|
2507
2347
|
provide(TYPES.optimizerSchemaService, () => new OptimizerSchemaService());
|
|
2348
|
+
}
|
|
2349
|
+
/**
|
|
2350
|
+
* Register validation services.
|
|
2351
|
+
*/
|
|
2352
|
+
{
|
|
2508
2353
|
provide(TYPES.optimizerValidationService, () => new OptimizerValidationService());
|
|
2354
|
+
}
|
|
2355
|
+
/**
|
|
2356
|
+
* Register connection services.
|
|
2357
|
+
*/
|
|
2358
|
+
{
|
|
2509
2359
|
provide(TYPES.optimizerConnectionService, () => new OptimizerConnectionService());
|
|
2360
|
+
}
|
|
2361
|
+
/**
|
|
2362
|
+
* Register global services.
|
|
2363
|
+
*/
|
|
2364
|
+
{
|
|
2510
2365
|
provide(TYPES.optimizerGlobalService, () => new OptimizerGlobalService());
|
|
2511
2366
|
}
|
|
2512
2367
|
|
|
@@ -6483,43 +6338,47 @@ class GLM4Provider {
|
|
|
6483
6338
|
* );
|
|
6484
6339
|
* ```
|
|
6485
6340
|
*/
|
|
6486
|
-
/**
|
|
6487
|
-
* Common service instances.
|
|
6488
|
-
*/
|
|
6489
|
-
const commonServices = {
|
|
6490
|
-
loggerService: inject(TYPES.loggerService),
|
|
6491
|
-
};
|
|
6492
6341
|
/**
|
|
6493
6342
|
* Base service instances.
|
|
6494
6343
|
*/
|
|
6495
6344
|
const baseServices = {
|
|
6496
6345
|
contextService: inject(TYPES.contextService),
|
|
6346
|
+
loggerService: inject(TYPES.loggerService),
|
|
6497
6347
|
};
|
|
6498
6348
|
/**
|
|
6499
6349
|
* Private service instances.
|
|
6500
6350
|
*/
|
|
6501
6351
|
const privateServices = {
|
|
6502
6352
|
runnerPrivateService: inject(TYPES.runnerPrivateService),
|
|
6503
|
-
outlinePrivateService: inject(TYPES.outlinePrivateService),
|
|
6504
6353
|
};
|
|
6505
6354
|
/**
|
|
6506
6355
|
* Public service instances.
|
|
6507
6356
|
*/
|
|
6508
6357
|
const publicServices = {
|
|
6509
6358
|
runnerPublicService: inject(TYPES.runnerPublicService),
|
|
6510
|
-
outlinePublicService: inject(TYPES.outlinePublicService),
|
|
6511
6359
|
};
|
|
6512
6360
|
const promptServices = {
|
|
6513
|
-
|
|
6361
|
+
resolvePromptService: inject(TYPES.resolvePromptService),
|
|
6362
|
+
};
|
|
6363
|
+
const cacheServices = {
|
|
6364
|
+
promptCacheService: inject(TYPES.promptCacheService),
|
|
6514
6365
|
};
|
|
6515
6366
|
const markdownServices = {
|
|
6516
6367
|
outlineMarkdownService: inject(TYPES.outlineMarkdownService),
|
|
6517
6368
|
};
|
|
6518
|
-
const
|
|
6369
|
+
const templateServices = {
|
|
6519
6370
|
optimizerTemplateService: inject(TYPES.optimizerTemplateService),
|
|
6371
|
+
};
|
|
6372
|
+
const schemaServices = {
|
|
6520
6373
|
optimizerSchemaService: inject(TYPES.optimizerSchemaService),
|
|
6374
|
+
};
|
|
6375
|
+
const validationServices = {
|
|
6521
6376
|
optimizerValidationService: inject(TYPES.optimizerValidationService),
|
|
6377
|
+
};
|
|
6378
|
+
const connectionServices = {
|
|
6522
6379
|
optimizerConnectionService: inject(TYPES.optimizerConnectionService),
|
|
6380
|
+
};
|
|
6381
|
+
const globalServices = {
|
|
6523
6382
|
optimizerGlobalService: inject(TYPES.optimizerGlobalService),
|
|
6524
6383
|
};
|
|
6525
6384
|
/**
|
|
@@ -6527,13 +6386,17 @@ const optimizerServices = {
|
|
|
6527
6386
|
* Provides unified access to the entire service layer.
|
|
6528
6387
|
*/
|
|
6529
6388
|
const engine = {
|
|
6530
|
-
...commonServices,
|
|
6531
6389
|
...baseServices,
|
|
6532
6390
|
...privateServices,
|
|
6533
6391
|
...publicServices,
|
|
6534
6392
|
...promptServices,
|
|
6393
|
+
...cacheServices,
|
|
6535
6394
|
...markdownServices,
|
|
6536
|
-
...
|
|
6395
|
+
...templateServices,
|
|
6396
|
+
...schemaServices,
|
|
6397
|
+
...validationServices,
|
|
6398
|
+
...connectionServices,
|
|
6399
|
+
...globalServices,
|
|
6537
6400
|
};
|
|
6538
6401
|
// Initialize DI container
|
|
6539
6402
|
init();
|
|
@@ -6685,446 +6548,356 @@ addCompletion({
|
|
|
6685
6548
|
});
|
|
6686
6549
|
|
|
6687
6550
|
/**
|
|
6688
|
-
*
|
|
6551
|
+
* Wrap async function with Ollama inference context.
|
|
6689
6552
|
*
|
|
6690
|
-
*
|
|
6691
|
-
*
|
|
6692
|
-
* completion to enforce structured output from language models.
|
|
6553
|
+
* Creates a higher-order function that executes the provided async function
|
|
6554
|
+
* within an Ollama inference context. Supports token rotation by passing multiple API keys.
|
|
6693
6555
|
*
|
|
6694
|
-
*
|
|
6695
|
-
* -
|
|
6696
|
-
* - price_open: Entry price in USD
|
|
6697
|
-
* - price_stop_loss: Stop-loss price in USD
|
|
6698
|
-
* - price_take_profit: Take-profit price in USD
|
|
6699
|
-
* - minute_estimated_time: Estimated hold duration in minutes
|
|
6700
|
-
* - risk_note: Detailed risk assessment with specific metrics
|
|
6701
|
-
*
|
|
6702
|
-
* @example
|
|
6703
|
-
* ```typescript
|
|
6704
|
-
* import { SignalSchema } from './schema/Signal.schema';
|
|
6705
|
-
*
|
|
6706
|
-
* const signal = SignalSchema.parse({
|
|
6707
|
-
* position: 'long',
|
|
6708
|
-
* price_open: 50000,
|
|
6709
|
-
* price_stop_loss: 49000,
|
|
6710
|
-
* price_take_profit: 52000,
|
|
6711
|
-
* minute_estimated_time: 120,
|
|
6712
|
-
* risk_note: 'RSI oversold at 32%, volume spike +45%'
|
|
6713
|
-
* });
|
|
6714
|
-
* ```
|
|
6715
|
-
*/
|
|
6716
|
-
const SignalSchema = z.object({
|
|
6717
|
-
position: z
|
|
6718
|
-
.enum(["long", "short", "wait"])
|
|
6719
|
-
.describe(str.newline("Position direction (ALWAYS required):", "long: market shows consistent bullish signals, uptrend or growth potential", "short: market shows consistent bearish signals, downtrend or decline potential", "wait: conflicting signals between timeframes OR unfavorable trading conditions")),
|
|
6720
|
-
price_open: z
|
|
6721
|
-
.number()
|
|
6722
|
-
.describe(str.newline("Position entry price in USD", "Can be either:", "- Current market price for immediate entry (market order)", "- Price above/below market to open a limit order and wait for its resolve before entering")),
|
|
6723
|
-
price_stop_loss: z
|
|
6724
|
-
.number()
|
|
6725
|
-
.describe(str.newline("Stop-loss price in USD", "For LONG: price below price_open (protection against decline)", "For SHORT: price above price_open (protection against rise)", "NEVER set SL in 'empty space' without technical justification")),
|
|
6726
|
-
price_take_profit: z
|
|
6727
|
-
.number()
|
|
6728
|
-
.describe(str.newline("Take-profit price in USD", "For LONG: price above price_open (growth target)", "For SHORT: price below price_open (decline target)", "NEVER set TP based on trend without technical justification")),
|
|
6729
|
-
minute_estimated_time: z
|
|
6730
|
-
.number()
|
|
6731
|
-
.describe(str.newline("Estimated time to reach Take Profit in minutes", "Calculated based on HONEST technical analysis, using:", "ATR, ADX, MACD, Momentum, Slope and other metrics")),
|
|
6732
|
-
risk_note: z
|
|
6733
|
-
.string()
|
|
6734
|
-
.describe(str.newline("Description of current market situation risks:", "", "Analyze and specify applicable risks:", "1. Whale manipulations (volume spikes, long shadows, pin bars, candle engulfing, false breakouts)", "2. Order book (order book walls, spoofing, bid/ask imbalance, low liquidity)", "3. P&L history (recurring mistakes on similar patterns)", "4. Time factors (trading session, low liquidity, upcoming events)", "5. Correlations (overall market trend, conflicting trends across timeframes)", "6. Technical risks (indicator divergences, weak volumes, critical levels)", "7. Gaps and anomalies (price gaps, unfilled gaps, movements without volume)", "", "Provide SPECIFIC numbers, percentages and probabilities.")),
|
|
6735
|
-
});
|
|
6736
|
-
|
|
6737
|
-
/**
|
|
6738
|
-
* Trading signal outline schema registration.
|
|
6739
|
-
*
|
|
6740
|
-
* Registers a structured outline for trading signal generation with comprehensive
|
|
6741
|
-
* validation rules. This outline enforces a strict schema for AI-generated trading
|
|
6742
|
-
* signals, ensuring all required fields are present and correctly formatted.
|
|
6743
|
-
*
|
|
6744
|
-
* Schema fields:
|
|
6745
|
-
* - position: Trading direction ("long", "short", or "wait")
|
|
6746
|
-
* - price_open: Entry price for the position
|
|
6747
|
-
* - price_stop_loss: Stop-loss price level
|
|
6748
|
-
* - price_take_profit: Take-profit price level
|
|
6749
|
-
* - minute_estimated_time: Estimated time to reach TP (in minutes)
|
|
6750
|
-
* - risk_note: Risk assessment and reasoning (markdown format)
|
|
6751
|
-
*
|
|
6752
|
-
* Validation rules:
|
|
6753
|
-
* 1. All required fields must be present
|
|
6754
|
-
* 2. Prices must be positive numbers
|
|
6755
|
-
* 3. For LONG: SL < entry < TP
|
|
6756
|
-
* 4. For SHORT: TP < entry < SL
|
|
6757
|
-
* 5. Estimated time must be <= 360 minutes (6 hours)
|
|
6758
|
-
* 6. Wait position skips price validations
|
|
6759
|
-
*
|
|
6760
|
-
* @example
|
|
6761
|
-
* ```typescript
|
|
6762
|
-
* import { json } from "agent-swarm-kit";
|
|
6763
|
-
* import { OutlineName } from "./enum/OutlineName";
|
|
6764
|
-
*
|
|
6765
|
-
* const { data } = await json(OutlineName.SignalOutline, [
|
|
6766
|
-
* { role: "user", content: "Analyze BTC/USDT and decide position" }
|
|
6767
|
-
* ]);
|
|
6768
|
-
*
|
|
6769
|
-
* if (data.position !== "wait") {
|
|
6770
|
-
* console.log(`Position: ${data.position}`);
|
|
6771
|
-
* console.log(`Entry: ${data.price_open}`);
|
|
6772
|
-
* console.log(`SL: ${data.price_stop_loss}`);
|
|
6773
|
-
* console.log(`TP: ${data.price_take_profit}`);
|
|
6774
|
-
* }
|
|
6775
|
-
* ```
|
|
6776
|
-
*/
|
|
6777
|
-
addOutline({
|
|
6778
|
-
outlineName: OutlineName.SignalOutline,
|
|
6779
|
-
completion: CompletionName.RunnerOutlineCompletion,
|
|
6780
|
-
format: zodResponseFormat(SignalSchema, "position_open_decision"),
|
|
6781
|
-
getOutlineHistory: async ({ history, param: messages = [] }) => {
|
|
6782
|
-
await history.push(messages);
|
|
6783
|
-
},
|
|
6784
|
-
validations: [
|
|
6785
|
-
{
|
|
6786
|
-
validate: ({ data }) => {
|
|
6787
|
-
if (!data.position) {
|
|
6788
|
-
throw new Error("The position field is not filled");
|
|
6789
|
-
}
|
|
6790
|
-
},
|
|
6791
|
-
docDescription: "Validates that position direction (long/short/wait) is specified.",
|
|
6792
|
-
},
|
|
6793
|
-
{
|
|
6794
|
-
validate: ({ data }) => {
|
|
6795
|
-
if (!data.risk_note) {
|
|
6796
|
-
throw new Error("The risk_note field is not filled");
|
|
6797
|
-
}
|
|
6798
|
-
},
|
|
6799
|
-
docDescription: "Validates that risk description is provided.",
|
|
6800
|
-
},
|
|
6801
|
-
{
|
|
6802
|
-
validate: ({ data }) => {
|
|
6803
|
-
if (data.position !== "wait" &&
|
|
6804
|
-
(!data.price_open || data.price_open <= 0)) {
|
|
6805
|
-
throw new Error("When position='long' or 'short', the price_open field is required and must be positive");
|
|
6806
|
-
}
|
|
6807
|
-
},
|
|
6808
|
-
docDescription: "Validates that opening price is specified and positive when opening a position.",
|
|
6809
|
-
},
|
|
6810
|
-
{
|
|
6811
|
-
validate: ({ data }) => {
|
|
6812
|
-
if (data.position !== "wait" &&
|
|
6813
|
-
(!data.price_stop_loss || data.price_stop_loss <= 0)) {
|
|
6814
|
-
throw new Error("When position='long' or 'short', the price_stop_loss field is required and must be positive");
|
|
6815
|
-
}
|
|
6816
|
-
},
|
|
6817
|
-
docDescription: "Validates that stop-loss is specified when opening a position.",
|
|
6818
|
-
},
|
|
6819
|
-
{
|
|
6820
|
-
validate: ({ data }) => {
|
|
6821
|
-
if (data.position !== "wait" &&
|
|
6822
|
-
(!data.price_take_profit || data.price_take_profit <= 0)) {
|
|
6823
|
-
throw new Error("When position='long' or 'short', the price_take_profit field is required and must be positive");
|
|
6824
|
-
}
|
|
6825
|
-
},
|
|
6826
|
-
docDescription: "Validates that take-profit is specified when opening a position.",
|
|
6827
|
-
},
|
|
6828
|
-
{
|
|
6829
|
-
validate: ({ data }) => {
|
|
6830
|
-
if (data.position === "long" && data.price_open && data.price_stop_loss && data.price_take_profit) {
|
|
6831
|
-
if (data.price_stop_loss >= data.price_open) {
|
|
6832
|
-
throw new Error("For LONG position, price_stop_loss must be below price_open");
|
|
6833
|
-
}
|
|
6834
|
-
if (data.price_take_profit <= data.price_open) {
|
|
6835
|
-
throw new Error("For LONG position, price_take_profit must be above price_open");
|
|
6836
|
-
}
|
|
6837
|
-
}
|
|
6838
|
-
},
|
|
6839
|
-
docDescription: "Validates price correctness for LONG position.",
|
|
6840
|
-
},
|
|
6841
|
-
{
|
|
6842
|
-
validate: ({ data }) => {
|
|
6843
|
-
if (data.position === "short" && data.price_open && data.price_stop_loss && data.price_take_profit) {
|
|
6844
|
-
if (data.price_stop_loss <= data.price_open) {
|
|
6845
|
-
throw new Error("For SHORT position, price_stop_loss must be above price_open");
|
|
6846
|
-
}
|
|
6847
|
-
if (data.price_take_profit >= data.price_open) {
|
|
6848
|
-
throw new Error("For SHORT position, price_take_profit must be below price_open");
|
|
6849
|
-
}
|
|
6850
|
-
}
|
|
6851
|
-
},
|
|
6852
|
-
docDescription: "Validates price correctness for SHORT position.",
|
|
6853
|
-
},
|
|
6854
|
-
{
|
|
6855
|
-
validate: ({ data }) => {
|
|
6856
|
-
if (data.position !== "wait" &&
|
|
6857
|
-
(!data.minute_estimated_time || data.minute_estimated_time <= 0)) {
|
|
6858
|
-
throw new Error("When position='long' or 'short', the minute_estimated_time field is required and must be positive");
|
|
6859
|
-
}
|
|
6860
|
-
},
|
|
6861
|
-
docDescription: "Validates that estimated time to TP is specified when opening a position.",
|
|
6862
|
-
},
|
|
6863
|
-
{
|
|
6864
|
-
validate: ({ data }) => {
|
|
6865
|
-
if (data.position !== "wait" && data.minute_estimated_time > 360) {
|
|
6866
|
-
throw new Error("Estimated time to reach TP exceeds 6 hours (360 minutes). Use position='wait' for low volatility conditions");
|
|
6867
|
-
}
|
|
6868
|
-
},
|
|
6869
|
-
docDescription: "Validates that estimated time to reach TP does not exceed 6 hours.",
|
|
6870
|
-
},
|
|
6871
|
-
],
|
|
6872
|
-
});
|
|
6873
|
-
|
|
6874
|
-
/**
|
|
6875
|
-
* Bootstrap module for agent-swarm-kit validation.
|
|
6876
|
-
*
|
|
6877
|
-
* Validates that all completion and outline names are properly registered
|
|
6878
|
-
* with agent-swarm-kit before the application starts. This ensures that
|
|
6879
|
-
* all referenced completions and outlines exist and are correctly configured.
|
|
6880
|
-
*
|
|
6881
|
-
* Validation checks:
|
|
6882
|
-
* - All CompletionName enum values have corresponding registered handlers
|
|
6883
|
-
* - All OutlineName enum values have corresponding registered schemas
|
|
6884
|
-
* - No duplicate registrations exist
|
|
6885
|
-
*
|
|
6886
|
-
* This file is imported by index.ts to run validation at startup.
|
|
6887
|
-
*
|
|
6888
|
-
* @throws Error if validation fails (missing or duplicate registrations)
|
|
6889
|
-
*/
|
|
6890
|
-
validate$1({
|
|
6891
|
-
CompletionName: CompletionName$1,
|
|
6892
|
-
OutlineName: OutlineName$1,
|
|
6893
|
-
});
|
|
6894
|
-
|
|
6895
|
-
/**
|
|
6896
|
-
* Generate structured trading signal from Ollama models.
|
|
6897
|
-
*
|
|
6898
|
-
* Supports token rotation by passing multiple API keys. Automatically enforces
|
|
6899
|
-
* the signal JSON schema defined in Signal.schema.ts.
|
|
6900
|
-
*
|
|
6901
|
-
* @param messages - Array of outline messages (user/assistant/system)
|
|
6556
|
+
* @template T - Async function type
|
|
6557
|
+
* @param fn - Async function to wrap
|
|
6902
6558
|
* @param model - Ollama model name (e.g., "llama3.3:70b")
|
|
6903
6559
|
* @param apiKey - Single API key or array of keys for rotation
|
|
6904
|
-
* @returns
|
|
6560
|
+
* @returns Wrapped function with same signature as input
|
|
6905
6561
|
*
|
|
6906
6562
|
* @example
|
|
6907
6563
|
* ```typescript
|
|
6908
6564
|
* import { ollama } from '@backtest-kit/ollama';
|
|
6909
6565
|
*
|
|
6910
|
-
* const
|
|
6911
|
-
*
|
|
6566
|
+
* const wrappedFn = ollama(myAsyncFn, 'llama3.3:70b', ['key1', 'key2']);
|
|
6567
|
+
* const result = await wrappedFn(args);
|
|
6912
6568
|
* ```
|
|
6913
6569
|
*/
|
|
6914
|
-
const ollama =
|
|
6915
|
-
|
|
6570
|
+
const ollama = (fn, model, apiKey) => {
|
|
6571
|
+
const wrappedFn = async (args) => {
|
|
6572
|
+
return await ContextService.runInContext(async () => {
|
|
6573
|
+
return await fn(...args);
|
|
6574
|
+
}, {
|
|
6575
|
+
apiKey,
|
|
6576
|
+
inference: InferenceName$1.OllamaInference,
|
|
6577
|
+
model,
|
|
6578
|
+
});
|
|
6579
|
+
};
|
|
6580
|
+
return wrappedFn;
|
|
6916
6581
|
};
|
|
6917
6582
|
/**
|
|
6918
|
-
*
|
|
6583
|
+
* Wrap async function with Grok inference context.
|
|
6919
6584
|
*
|
|
6920
|
-
*
|
|
6585
|
+
* Creates a higher-order function that executes the provided async function
|
|
6586
|
+
* within a Grok (xAI) inference context.
|
|
6921
6587
|
*
|
|
6922
|
-
* @
|
|
6588
|
+
* @template T - Async function type
|
|
6589
|
+
* @param fn - Async function to wrap
|
|
6923
6590
|
* @param model - Grok model name (e.g., "grok-beta")
|
|
6924
|
-
* @param apiKey - Single API key
|
|
6925
|
-
* @returns
|
|
6926
|
-
* @throws Error if apiKey is an array (token rotation not supported)
|
|
6591
|
+
* @param apiKey - Single API key or array of keys
|
|
6592
|
+
* @returns Wrapped function with same signature as input
|
|
6927
6593
|
*
|
|
6928
6594
|
* @example
|
|
6929
6595
|
* ```typescript
|
|
6930
6596
|
* import { grok } from '@backtest-kit/ollama';
|
|
6931
6597
|
*
|
|
6932
|
-
* const
|
|
6598
|
+
* const wrappedFn = grok(myAsyncFn, 'grok-beta', process.env.GROK_API_KEY);
|
|
6599
|
+
* const result = await wrappedFn(args);
|
|
6933
6600
|
* ```
|
|
6934
6601
|
*/
|
|
6935
|
-
const grok =
|
|
6936
|
-
|
|
6602
|
+
const grok = (fn, model, apiKey) => {
|
|
6603
|
+
const wrappedFn = async (args) => {
|
|
6604
|
+
return await ContextService.runInContext(async () => {
|
|
6605
|
+
return await fn(...args);
|
|
6606
|
+
}, {
|
|
6607
|
+
apiKey,
|
|
6608
|
+
inference: InferenceName$1.GrokInference,
|
|
6609
|
+
model,
|
|
6610
|
+
});
|
|
6611
|
+
};
|
|
6612
|
+
return wrappedFn;
|
|
6937
6613
|
};
|
|
6938
6614
|
/**
|
|
6939
|
-
*
|
|
6615
|
+
* Wrap async function with HuggingFace inference context.
|
|
6940
6616
|
*
|
|
6941
|
-
*
|
|
6617
|
+
* Creates a higher-order function that executes the provided async function
|
|
6618
|
+
* within a HuggingFace Router API inference context.
|
|
6942
6619
|
*
|
|
6943
|
-
* @
|
|
6944
|
-
* @param
|
|
6945
|
-
* @param
|
|
6946
|
-
* @
|
|
6620
|
+
* @template T - Async function type
|
|
6621
|
+
* @param fn - Async function to wrap
|
|
6622
|
+
* @param model - HuggingFace model name (e.g., "meta-llama/Llama-3-70b")
|
|
6623
|
+
* @param apiKey - Single API key or array of keys
|
|
6624
|
+
* @returns Wrapped function with same signature as input
|
|
6947
6625
|
*
|
|
6948
6626
|
* @example
|
|
6949
6627
|
* ```typescript
|
|
6950
6628
|
* import { hf } from '@backtest-kit/ollama';
|
|
6951
6629
|
*
|
|
6952
|
-
* const
|
|
6630
|
+
* const wrappedFn = hf(myAsyncFn, 'meta-llama/Llama-3-70b', process.env.HF_API_KEY);
|
|
6631
|
+
* const result = await wrappedFn(args);
|
|
6953
6632
|
* ```
|
|
6954
6633
|
*/
|
|
6955
|
-
const hf =
|
|
6956
|
-
|
|
6634
|
+
const hf = (fn, model, apiKey) => {
|
|
6635
|
+
const wrappedFn = async (args) => {
|
|
6636
|
+
return await ContextService.runInContext(async () => {
|
|
6637
|
+
return await fn(...args);
|
|
6638
|
+
}, {
|
|
6639
|
+
apiKey,
|
|
6640
|
+
inference: InferenceName$1.HfInference,
|
|
6641
|
+
model,
|
|
6642
|
+
});
|
|
6643
|
+
};
|
|
6644
|
+
return wrappedFn;
|
|
6957
6645
|
};
|
|
6958
6646
|
/**
|
|
6959
|
-
*
|
|
6647
|
+
* Wrap async function with Claude inference context.
|
|
6960
6648
|
*
|
|
6961
|
-
*
|
|
6649
|
+
* Creates a higher-order function that executes the provided async function
|
|
6650
|
+
* within an Anthropic Claude inference context.
|
|
6962
6651
|
*
|
|
6963
|
-
* @
|
|
6652
|
+
* @template T - Async function type
|
|
6653
|
+
* @param fn - Async function to wrap
|
|
6964
6654
|
* @param model - Claude model name (e.g., "claude-3-5-sonnet-20241022")
|
|
6965
|
-
* @param apiKey - Single API key
|
|
6966
|
-
* @returns
|
|
6967
|
-
* @throws Error if apiKey is an array (token rotation not supported)
|
|
6655
|
+
* @param apiKey - Single API key or array of keys
|
|
6656
|
+
* @returns Wrapped function with same signature as input
|
|
6968
6657
|
*
|
|
6969
6658
|
* @example
|
|
6970
6659
|
* ```typescript
|
|
6971
6660
|
* import { claude } from '@backtest-kit/ollama';
|
|
6972
6661
|
*
|
|
6973
|
-
* const
|
|
6662
|
+
* const wrappedFn = claude(myAsyncFn, 'claude-3-5-sonnet-20241022', process.env.ANTHROPIC_API_KEY);
|
|
6663
|
+
* const result = await wrappedFn(args);
|
|
6974
6664
|
* ```
|
|
6975
6665
|
*/
|
|
6976
|
-
const claude =
|
|
6977
|
-
|
|
6666
|
+
const claude = (fn, model, apiKey) => {
|
|
6667
|
+
const wrappedFn = async (args) => {
|
|
6668
|
+
return await ContextService.runInContext(async () => {
|
|
6669
|
+
return await fn(...args);
|
|
6670
|
+
}, {
|
|
6671
|
+
apiKey,
|
|
6672
|
+
inference: InferenceName$1.ClaudeInference,
|
|
6673
|
+
model,
|
|
6674
|
+
});
|
|
6675
|
+
};
|
|
6676
|
+
return wrappedFn;
|
|
6978
6677
|
};
|
|
6979
6678
|
/**
|
|
6980
|
-
*
|
|
6679
|
+
* Wrap async function with OpenAI GPT inference context.
|
|
6981
6680
|
*
|
|
6982
|
-
*
|
|
6681
|
+
* Creates a higher-order function that executes the provided async function
|
|
6682
|
+
* within an OpenAI GPT inference context.
|
|
6983
6683
|
*
|
|
6984
|
-
* @
|
|
6684
|
+
* @template T - Async function type
|
|
6685
|
+
* @param fn - Async function to wrap
|
|
6985
6686
|
* @param model - OpenAI model name (e.g., "gpt-4o", "gpt-4-turbo")
|
|
6986
|
-
* @param apiKey - Single API key
|
|
6987
|
-
* @returns
|
|
6988
|
-
* @throws Error if apiKey is an array (token rotation not supported)
|
|
6687
|
+
* @param apiKey - Single API key or array of keys
|
|
6688
|
+
* @returns Wrapped function with same signature as input
|
|
6989
6689
|
*
|
|
6990
6690
|
* @example
|
|
6991
6691
|
* ```typescript
|
|
6992
6692
|
* import { gpt5 } from '@backtest-kit/ollama';
|
|
6993
6693
|
*
|
|
6994
|
-
* const
|
|
6694
|
+
* const wrappedFn = gpt5(myAsyncFn, 'gpt-4o', process.env.OPENAI_API_KEY);
|
|
6695
|
+
* const result = await wrappedFn(args);
|
|
6995
6696
|
* ```
|
|
6996
6697
|
*/
|
|
6997
|
-
const gpt5 =
|
|
6998
|
-
|
|
6698
|
+
const gpt5 = (fn, model, apiKey) => {
|
|
6699
|
+
const wrappedFn = async (args) => {
|
|
6700
|
+
return await ContextService.runInContext(async () => {
|
|
6701
|
+
return await fn(...args);
|
|
6702
|
+
}, {
|
|
6703
|
+
apiKey,
|
|
6704
|
+
inference: InferenceName$1.GPT5Inference,
|
|
6705
|
+
model,
|
|
6706
|
+
});
|
|
6707
|
+
};
|
|
6708
|
+
return wrappedFn;
|
|
6999
6709
|
};
|
|
7000
6710
|
/**
|
|
7001
|
-
*
|
|
6711
|
+
* Wrap async function with DeepSeek inference context.
|
|
7002
6712
|
*
|
|
7003
|
-
*
|
|
6713
|
+
* Creates a higher-order function that executes the provided async function
|
|
6714
|
+
* within a DeepSeek AI inference context.
|
|
7004
6715
|
*
|
|
7005
|
-
* @
|
|
6716
|
+
* @template T - Async function type
|
|
6717
|
+
* @param fn - Async function to wrap
|
|
7006
6718
|
* @param model - DeepSeek model name (e.g., "deepseek-chat")
|
|
7007
|
-
* @param apiKey - Single API key
|
|
7008
|
-
* @returns
|
|
7009
|
-
* @throws Error if apiKey is an array (token rotation not supported)
|
|
6719
|
+
* @param apiKey - Single API key or array of keys
|
|
6720
|
+
* @returns Wrapped function with same signature as input
|
|
7010
6721
|
*
|
|
7011
6722
|
* @example
|
|
7012
6723
|
* ```typescript
|
|
7013
6724
|
* import { deepseek } from '@backtest-kit/ollama';
|
|
7014
6725
|
*
|
|
7015
|
-
* const
|
|
6726
|
+
* const wrappedFn = deepseek(myAsyncFn, 'deepseek-chat', process.env.DEEPSEEK_API_KEY);
|
|
6727
|
+
* const result = await wrappedFn(args);
|
|
7016
6728
|
* ```
|
|
7017
6729
|
*/
|
|
7018
|
-
const deepseek =
|
|
7019
|
-
|
|
6730
|
+
const deepseek = (fn, model, apiKey) => {
|
|
6731
|
+
const wrappedFn = async (args) => {
|
|
6732
|
+
return await ContextService.runInContext(async () => {
|
|
6733
|
+
return await fn(...args);
|
|
6734
|
+
}, {
|
|
6735
|
+
apiKey,
|
|
6736
|
+
inference: InferenceName$1.DeepseekInference,
|
|
6737
|
+
model,
|
|
6738
|
+
});
|
|
6739
|
+
};
|
|
6740
|
+
return wrappedFn;
|
|
7020
6741
|
};
|
|
7021
6742
|
/**
|
|
7022
|
-
*
|
|
6743
|
+
* Wrap async function with Mistral AI inference context.
|
|
7023
6744
|
*
|
|
7024
|
-
*
|
|
6745
|
+
* Creates a higher-order function that executes the provided async function
|
|
6746
|
+
* within a Mistral AI inference context.
|
|
7025
6747
|
*
|
|
7026
|
-
* @
|
|
6748
|
+
* @template T - Async function type
|
|
6749
|
+
* @param fn - Async function to wrap
|
|
7027
6750
|
* @param model - Mistral model name (e.g., "mistral-large-latest")
|
|
7028
|
-
* @param apiKey - Single API key
|
|
7029
|
-
* @returns
|
|
7030
|
-
* @throws Error if apiKey is an array (token rotation not supported)
|
|
6751
|
+
* @param apiKey - Single API key or array of keys
|
|
6752
|
+
* @returns Wrapped function with same signature as input
|
|
7031
6753
|
*
|
|
7032
6754
|
* @example
|
|
7033
6755
|
* ```typescript
|
|
7034
6756
|
* import { mistral } from '@backtest-kit/ollama';
|
|
7035
6757
|
*
|
|
7036
|
-
* const
|
|
6758
|
+
* const wrappedFn = mistral(myAsyncFn, 'mistral-large-latest', process.env.MISTRAL_API_KEY);
|
|
6759
|
+
* const result = await wrappedFn(args);
|
|
7037
6760
|
* ```
|
|
7038
6761
|
*/
|
|
7039
|
-
const mistral =
|
|
7040
|
-
|
|
6762
|
+
const mistral = (fn, model, apiKey) => {
|
|
6763
|
+
const wrappedFn = async (args) => {
|
|
6764
|
+
return await ContextService.runInContext(async () => {
|
|
6765
|
+
return await fn(...args);
|
|
6766
|
+
}, {
|
|
6767
|
+
apiKey,
|
|
6768
|
+
inference: InferenceName$1.MistralInference,
|
|
6769
|
+
model,
|
|
6770
|
+
});
|
|
6771
|
+
};
|
|
6772
|
+
return wrappedFn;
|
|
7041
6773
|
};
|
|
7042
6774
|
/**
|
|
7043
|
-
*
|
|
6775
|
+
* Wrap async function with Perplexity AI inference context.
|
|
7044
6776
|
*
|
|
7045
|
-
*
|
|
6777
|
+
* Creates a higher-order function that executes the provided async function
|
|
6778
|
+
* within a Perplexity AI inference context.
|
|
7046
6779
|
*
|
|
7047
|
-
* @
|
|
6780
|
+
* @template T - Async function type
|
|
6781
|
+
* @param fn - Async function to wrap
|
|
7048
6782
|
* @param model - Perplexity model name (e.g., "llama-3.1-sonar-huge-128k-online")
|
|
7049
|
-
* @param apiKey - Single API key
|
|
7050
|
-
* @returns
|
|
7051
|
-
* @throws Error if apiKey is an array (token rotation not supported)
|
|
6783
|
+
* @param apiKey - Single API key or array of keys
|
|
6784
|
+
* @returns Wrapped function with same signature as input
|
|
7052
6785
|
*
|
|
7053
6786
|
* @example
|
|
7054
6787
|
* ```typescript
|
|
7055
6788
|
* import { perplexity } from '@backtest-kit/ollama';
|
|
7056
6789
|
*
|
|
7057
|
-
* const
|
|
6790
|
+
* const wrappedFn = perplexity(myAsyncFn, 'llama-3.1-sonar-huge-128k-online', process.env.PERPLEXITY_API_KEY);
|
|
6791
|
+
* const result = await wrappedFn(args);
|
|
7058
6792
|
* ```
|
|
7059
6793
|
*/
|
|
7060
|
-
const perplexity =
|
|
7061
|
-
|
|
6794
|
+
const perplexity = (fn, model, apiKey) => {
|
|
6795
|
+
const wrappedFn = async (args) => {
|
|
6796
|
+
return await ContextService.runInContext(async () => {
|
|
6797
|
+
return await fn(...args);
|
|
6798
|
+
}, {
|
|
6799
|
+
apiKey,
|
|
6800
|
+
inference: InferenceName$1.PerplexityInference,
|
|
6801
|
+
model,
|
|
6802
|
+
});
|
|
6803
|
+
};
|
|
6804
|
+
return wrappedFn;
|
|
7062
6805
|
};
|
|
7063
6806
|
/**
|
|
7064
|
-
*
|
|
6807
|
+
* Wrap async function with Cohere inference context.
|
|
7065
6808
|
*
|
|
7066
|
-
*
|
|
6809
|
+
* Creates a higher-order function that executes the provided async function
|
|
6810
|
+
* within a Cohere AI inference context.
|
|
7067
6811
|
*
|
|
7068
|
-
* @
|
|
6812
|
+
* @template T - Async function type
|
|
6813
|
+
* @param fn - Async function to wrap
|
|
7069
6814
|
* @param model - Cohere model name (e.g., "command-r-plus")
|
|
7070
|
-
* @param apiKey - Single API key
|
|
7071
|
-
* @returns
|
|
7072
|
-
* @throws Error if apiKey is an array (token rotation not supported)
|
|
6815
|
+
* @param apiKey - Single API key or array of keys
|
|
6816
|
+
* @returns Wrapped function with same signature as input
|
|
7073
6817
|
*
|
|
7074
6818
|
* @example
|
|
7075
6819
|
* ```typescript
|
|
7076
6820
|
* import { cohere } from '@backtest-kit/ollama';
|
|
7077
6821
|
*
|
|
7078
|
-
* const
|
|
6822
|
+
* const wrappedFn = cohere(myAsyncFn, 'command-r-plus', process.env.COHERE_API_KEY);
|
|
6823
|
+
* const result = await wrappedFn(args);
|
|
7079
6824
|
* ```
|
|
7080
6825
|
*/
|
|
7081
|
-
const cohere =
|
|
7082
|
-
|
|
6826
|
+
const cohere = (fn, model, apiKey) => {
|
|
6827
|
+
const wrappedFn = async (args) => {
|
|
6828
|
+
return await ContextService.runInContext(async () => {
|
|
6829
|
+
return await fn(...args);
|
|
6830
|
+
}, {
|
|
6831
|
+
apiKey,
|
|
6832
|
+
inference: InferenceName$1.CohereInference,
|
|
6833
|
+
model,
|
|
6834
|
+
});
|
|
6835
|
+
};
|
|
6836
|
+
return wrappedFn;
|
|
7083
6837
|
};
|
|
7084
6838
|
/**
|
|
7085
|
-
*
|
|
6839
|
+
* Wrap async function with Alibaba Qwen inference context.
|
|
7086
6840
|
*
|
|
7087
|
-
*
|
|
6841
|
+
* Creates a higher-order function that executes the provided async function
|
|
6842
|
+
* within an Alibaba DashScope API inference context.
|
|
7088
6843
|
*
|
|
7089
|
-
* @
|
|
6844
|
+
* @template T - Async function type
|
|
6845
|
+
* @param fn - Async function to wrap
|
|
7090
6846
|
* @param model - Qwen model name (e.g., "qwen-max")
|
|
7091
|
-
* @param apiKey - Single API key
|
|
7092
|
-
* @returns
|
|
7093
|
-
* @throws Error if apiKey is an array (token rotation not supported)
|
|
6847
|
+
* @param apiKey - Single API key or array of keys
|
|
6848
|
+
* @returns Wrapped function with same signature as input
|
|
7094
6849
|
*
|
|
7095
6850
|
* @example
|
|
7096
6851
|
* ```typescript
|
|
7097
6852
|
* import { alibaba } from '@backtest-kit/ollama';
|
|
7098
6853
|
*
|
|
7099
|
-
* const
|
|
6854
|
+
* const wrappedFn = alibaba(myAsyncFn, 'qwen-max', process.env.ALIBABA_API_KEY);
|
|
6855
|
+
* const result = await wrappedFn(args);
|
|
7100
6856
|
* ```
|
|
7101
6857
|
*/
|
|
7102
|
-
const alibaba =
|
|
7103
|
-
|
|
6858
|
+
const alibaba = (fn, model, apiKey) => {
|
|
6859
|
+
const wrappedFn = async (args) => {
|
|
6860
|
+
return await ContextService.runInContext(async () => {
|
|
6861
|
+
return await fn(...args);
|
|
6862
|
+
}, {
|
|
6863
|
+
apiKey,
|
|
6864
|
+
inference: InferenceName$1.AlibabaInference,
|
|
6865
|
+
model,
|
|
6866
|
+
});
|
|
6867
|
+
};
|
|
6868
|
+
return wrappedFn;
|
|
7104
6869
|
};
|
|
7105
6870
|
/**
|
|
7106
|
-
*
|
|
6871
|
+
* Wrap async function with Zhipu AI GLM-4 inference context.
|
|
7107
6872
|
*
|
|
7108
|
-
*
|
|
7109
|
-
*
|
|
6873
|
+
* Creates a higher-order function that executes the provided async function
|
|
6874
|
+
* within a Zhipu AI GLM-4 inference context via OpenAI-compatible Z.ai API.
|
|
7110
6875
|
*
|
|
7111
|
-
* @
|
|
6876
|
+
* @template T - Async function type
|
|
6877
|
+
* @param fn - Async function to wrap
|
|
7112
6878
|
* @param model - GLM-4 model name (e.g., "glm-4-plus", "glm-4-air")
|
|
7113
|
-
* @param apiKey - Single API key
|
|
7114
|
-
* @returns
|
|
7115
|
-
* @throws Error if apiKey is an array (token rotation not supported)
|
|
6879
|
+
* @param apiKey - Single API key or array of keys
|
|
6880
|
+
* @returns Wrapped function with same signature as input
|
|
7116
6881
|
*
|
|
7117
6882
|
* @example
|
|
7118
6883
|
* ```typescript
|
|
7119
6884
|
* import { glm4 } from '@backtest-kit/ollama';
|
|
7120
6885
|
*
|
|
7121
|
-
* const
|
|
7122
|
-
*
|
|
7123
|
-
* console.log(`Entry: ${signal.priceOpen}`);
|
|
6886
|
+
* const wrappedFn = glm4(myAsyncFn, 'glm-4-plus', process.env.ZAI_API_KEY);
|
|
6887
|
+
* const result = await wrappedFn(args);
|
|
7124
6888
|
* ```
|
|
7125
6889
|
*/
|
|
7126
|
-
const glm4 =
|
|
7127
|
-
|
|
6890
|
+
const glm4 = (fn, model, apiKey) => {
|
|
6891
|
+
const wrappedFn = async (args) => {
|
|
6892
|
+
return await ContextService.runInContext(async () => {
|
|
6893
|
+
return await fn(...args);
|
|
6894
|
+
}, {
|
|
6895
|
+
apiKey,
|
|
6896
|
+
inference: InferenceName$1.GLM4Inference,
|
|
6897
|
+
model,
|
|
6898
|
+
});
|
|
6899
|
+
};
|
|
6900
|
+
return wrappedFn;
|
|
7128
6901
|
};
|
|
7129
6902
|
|
|
7130
6903
|
/**
|
|
@@ -7148,64 +6921,6 @@ const setLogger = (logger) => {
|
|
|
7148
6921
|
engine$1.loggerService.setLogger(logger);
|
|
7149
6922
|
};
|
|
7150
6923
|
|
|
7151
|
-
/**
|
|
7152
|
-
* Overrides the default signal format schema for LLM-generated trading signals.
|
|
7153
|
-
*
|
|
7154
|
-
* This function allows customization of the structured output format used by the
|
|
7155
|
-
* SignalOutline. It replaces the default signal schema with a custom Zod schema,
|
|
7156
|
-
* enabling flexible signal structure definitions while maintaining type safety.
|
|
7157
|
-
*
|
|
7158
|
-
* The override affects all subsequent signal generation calls using SignalOutline
|
|
7159
|
-
* until the application restarts or the schema is overridden again.
|
|
7160
|
-
*
|
|
7161
|
-
* @template ZodInput - The Zod schema type used for validation and type inference
|
|
7162
|
-
*
|
|
7163
|
-
* @param {ZodInput} format - Custom Zod schema defining the signal structure.
|
|
7164
|
-
* Must be a valid Zod type (z.object, z.string, etc.)
|
|
7165
|
-
*
|
|
7166
|
-
* @example
|
|
7167
|
-
* ```typescript
|
|
7168
|
-
* import { z } from 'zod';
|
|
7169
|
-
* import { overrideSignalFormat } from '@backtest-kit/ollama';
|
|
7170
|
-
*
|
|
7171
|
-
* // Override with custom signal schema
|
|
7172
|
-
* const CustomSignalSchema = z.object({
|
|
7173
|
-
* position: z.enum(['long', 'short', 'wait']),
|
|
7174
|
-
* price_open: z.number(),
|
|
7175
|
-
* confidence: z.number().min(0).max(100),
|
|
7176
|
-
* custom_field: z.string()
|
|
7177
|
-
* });
|
|
7178
|
-
*
|
|
7179
|
-
* overrideSignalFormat(CustomSignalSchema);
|
|
7180
|
-
* ```
|
|
7181
|
-
*
|
|
7182
|
-
* @example
|
|
7183
|
-
* ```typescript
|
|
7184
|
-
* // Override with simplified schema
|
|
7185
|
-
* const SimpleSignalSchema = z.object({
|
|
7186
|
-
* action: z.enum(['buy', 'sell', 'hold']),
|
|
7187
|
-
* price: z.number()
|
|
7188
|
-
* });
|
|
7189
|
-
*
|
|
7190
|
-
* overrideSignalFormat(SimpleSignalSchema);
|
|
7191
|
-
* ```
|
|
7192
|
-
*
|
|
7193
|
-
* @remarks
|
|
7194
|
-
* - The custom schema replaces the default SignalSchema completely
|
|
7195
|
-
* - Schema name in OpenAI format is always "position_open_decision"
|
|
7196
|
-
* - Changes persist until application restart or next override
|
|
7197
|
-
* - Ensure the custom schema matches your signal processing logic
|
|
7198
|
-
*
|
|
7199
|
-
* @see {@link SignalSchema} - Default signal schema structure
|
|
7200
|
-
* @see {@link OutlineName.SignalOutline} - Outline being overridden
|
|
7201
|
-
*/
|
|
7202
|
-
function overrideSignalFormat(format) {
|
|
7203
|
-
overrideOutline({
|
|
7204
|
-
outlineName: OutlineName$1.SignalOutline,
|
|
7205
|
-
format: zodResponseFormat(format, "position_open_decision"),
|
|
7206
|
-
});
|
|
7207
|
-
}
|
|
7208
|
-
|
|
7209
6924
|
const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
|
|
7210
6925
|
/**
|
|
7211
6926
|
* Dumps signal data and LLM conversation history to markdown files.
|
|
@@ -7373,7 +7088,36 @@ async function validate(args = {}) {
|
|
|
7373
7088
|
return await validateInternal(args);
|
|
7374
7089
|
}
|
|
7375
7090
|
|
|
7091
|
+
const MODULE_TYPE_SYMBOL = Symbol("module-type");
|
|
7092
|
+
class Module {
|
|
7093
|
+
constructor(path, baseDir) {
|
|
7094
|
+
this.path = path;
|
|
7095
|
+
this.baseDir = baseDir;
|
|
7096
|
+
this.__type__ = MODULE_TYPE_SYMBOL;
|
|
7097
|
+
}
|
|
7098
|
+
}
|
|
7099
|
+
Module.fromPath = (path, baseDir = join(process.cwd(), "config/prompt")) => {
|
|
7100
|
+
if (!path || typeof path !== "string" || !path.trim()) {
|
|
7101
|
+
throw new Error("Path must be a non-empty string");
|
|
7102
|
+
}
|
|
7103
|
+
return new Module(path, baseDir);
|
|
7104
|
+
};
|
|
7105
|
+
Module.isModule = (value) => {
|
|
7106
|
+
return (value !== null &&
|
|
7107
|
+
typeof value === "object" &&
|
|
7108
|
+
value.__type__ === MODULE_TYPE_SYMBOL);
|
|
7109
|
+
};
|
|
7110
|
+
|
|
7376
7111
|
const METHOD_NAME_SIGNAL = "history.commitSignalPromptHistory";
|
|
7112
|
+
const GET_PROMPT_FN = async (source) => {
|
|
7113
|
+
if (Prompt.isPrompt(source)) {
|
|
7114
|
+
return source;
|
|
7115
|
+
}
|
|
7116
|
+
if (Module.isModule(source)) {
|
|
7117
|
+
return await engine$1.promptCacheService.readModule(source);
|
|
7118
|
+
}
|
|
7119
|
+
throw new Error("Source must be a Prompt or Module instance");
|
|
7120
|
+
};
|
|
7377
7121
|
/**
|
|
7378
7122
|
* Commits signal prompt history to the message array.
|
|
7379
7123
|
*
|
|
@@ -7382,40 +7126,40 @@ const METHOD_NAME_SIGNAL = "history.commitSignalPromptHistory";
|
|
|
7382
7126
|
* at the end of the history array if they are not empty.
|
|
7383
7127
|
*
|
|
7384
7128
|
* Context extraction:
|
|
7385
|
-
* - symbol:
|
|
7386
|
-
* - backtest mode: From
|
|
7387
|
-
* - strategyName, exchangeName, frameName: From
|
|
7129
|
+
* - symbol: From getSymbol()
|
|
7130
|
+
* - backtest mode: From getMode()
|
|
7131
|
+
* - strategyName, exchangeName, frameName: From getContext()
|
|
7388
7132
|
*
|
|
7389
|
-
* @param
|
|
7133
|
+
* @param source - Module object containing path to .cjs module
|
|
7390
7134
|
* @param history - Message array to append prompts to
|
|
7391
7135
|
* @returns Promise that resolves when prompts are added
|
|
7392
7136
|
* @throws Error if ExecutionContext or MethodContext is not active
|
|
7393
7137
|
*
|
|
7394
|
-
* @example
|
|
7395
|
-
* ```typescript
|
|
7396
|
-
* const messages: MessageModel[] = [];
|
|
7397
|
-
* await commitSignalPromptHistory("BTCUSDT", messages);
|
|
7398
|
-
* // messages now contains system prompts at start and user prompt at end
|
|
7399
7138
|
* ```
|
|
7400
7139
|
*/
|
|
7401
|
-
async function
|
|
7140
|
+
async function commitPrompt(source, history) {
|
|
7402
7141
|
engine$1.loggerService.log(METHOD_NAME_SIGNAL, {
|
|
7403
|
-
|
|
7142
|
+
source,
|
|
7404
7143
|
});
|
|
7144
|
+
const symbol = await getSymbol();
|
|
7405
7145
|
const { strategyName, exchangeName, frameName } = await getContext();
|
|
7406
7146
|
const mode = await getMode();
|
|
7407
7147
|
const isBacktest = mode === "backtest";
|
|
7408
|
-
const
|
|
7409
|
-
const
|
|
7148
|
+
const prompt = await GET_PROMPT_FN(source);
|
|
7149
|
+
const systemPrompts = await engine$1.resolvePromptService.getSystemPrompt(prompt, symbol, strategyName, exchangeName, frameName, isBacktest);
|
|
7150
|
+
const userPrompt = await engine$1.resolvePromptService.getUserPrompt(prompt, symbol, strategyName, exchangeName, frameName, isBacktest);
|
|
7410
7151
|
if (systemPrompts.length > 0) {
|
|
7411
7152
|
for (const content of systemPrompts) {
|
|
7153
|
+
if (!content.trim()) {
|
|
7154
|
+
continue;
|
|
7155
|
+
}
|
|
7412
7156
|
history.unshift({
|
|
7413
7157
|
role: "system",
|
|
7414
7158
|
content,
|
|
7415
7159
|
});
|
|
7416
7160
|
}
|
|
7417
7161
|
}
|
|
7418
|
-
if (userPrompt && userPrompt.trim()
|
|
7162
|
+
if (userPrompt && userPrompt.trim()) {
|
|
7419
7163
|
history.push({
|
|
7420
7164
|
role: "user",
|
|
7421
7165
|
content: userPrompt,
|
|
@@ -7722,4 +7466,4 @@ class OptimizerUtils {
|
|
|
7722
7466
|
*/
|
|
7723
7467
|
const Optimizer = new OptimizerUtils();
|
|
7724
7468
|
|
|
7725
|
-
export { Optimizer, addOptimizerSchema, alibaba, claude, cohere,
|
|
7469
|
+
export { CompletionName, Module, Optimizer, Prompt, addOptimizerSchema, alibaba, claude, cohere, commitPrompt, deepseek, dumpSignalData, getOptimizerSchema, glm4, gpt5, grok, hf, engine as lib, listOptimizerSchema, listenError, listenOptimizerProgress, mistral, ollama, perplexity, setLogger, validate };
|