@backtest-kit/ollama 13.5.0 โ†’ 14.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.
Files changed (2) hide show
  1. package/README.md +164 -237
  2. package/package.json +6 -6
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # ๐Ÿค– @backtest-kit/ollama
4
4
 
5
- > Multi-provider LLM context wrapper for trading strategies. Supports 10+ providers with unified HOF API.
5
+ > Universal LLM adapter for [backtest-kit](https://www.npmjs.com/package/backtest-kit) trading strategies. One higher-order-function API across **12 providers**, schema-enforced structured output, userspace prompt modules, token rotation โ€” plus an **LLM strategy optimizer** that generates runnable strategy code.
6
6
 
7
7
  ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot16.png)
8
8
 
@@ -10,84 +10,92 @@
10
10
  [![npm](https://img.shields.io/npm/v/@backtest-kit/ollama.svg?style=flat-square)](https://npmjs.org/package/@backtest-kit/ollama)
11
11
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue)]()
12
12
 
13
- Transform technical analysis into trading decisions with multi-provider LLM support, structured output, and built-in risk management.
13
+ ๐Ÿ“š **[Docs](https://backtest-kit.github.io/documents/article_07_ai_news_trading_signals.html)** ยท ๐ŸŒŸ **[Reference implementation](https://github.com/tripolskypetr/backtest-kit/tree/master/example)** ยท ๐Ÿ™ **[GitHub](https://github.com/tripolskypetr/backtest-kit)**
14
14
 
15
- ๐Ÿ“š **[Backtest Kit Docs](https://backtest-kit.github.io/documents/article_07_ai_news_trading_signals.html)** | ๐ŸŒŸ **[GitHub](https://github.com/tripolskypetr/backtest-kit)**
15
+ ```bash
16
+ npm install @backtest-kit/ollama backtest-kit agent-swarm-kit
17
+ ```
16
18
 
17
- > **New to backtest-kit?** The fastest way to get a real, production-ready setup is to clone the [reference implementation](https://github.com/tripolskypetr/backtest-kit/tree/master/example) โ€” a fully working news-sentiment AI trading system with LLM forecasting, multi-timeframe data, and a documented February 2026 backtest. Start there instead of from scratch.
19
+ ---
18
20
 
19
- ## โœจ Features
21
+ ## Why
20
22
 
21
- - ๐Ÿ”Œ 10+ LLM Providers: OpenAI, Claude, DeepSeek, Grok, Mistral, Perplexity, Cohere, Alibaba, Hugging Face, Ollama, GLM-4
22
- - โšก Higher-Order Functions: Wrap async functions with inference context via `di-scoped`
23
- - ๐Ÿ“ Userspace Prompts: Load prompts from `.cjs` modules in `config/prompt/`
24
- - ๐ŸŽฏ Userspace Schema: Define your own Zod or JSON schema with `addOutline`
25
- - ๐Ÿ”„ Token Rotation: Pass array of API keys for automatic rotation
26
- - ๐Ÿ—„๏ธ Memoized Cache: Prompt modules cached with `memoize` from `functools-kit`
23
+ AI strategies normally mean per-provider SDK boilerplate and JSON you can't trust. This package collapses all of it: wrap any async function with a provider HOF and it runs inside that provider's inference context โ€” swap `deepseek()` โ†’ `claude()` โ†’ `gpt5()` with no other change. Structured output is schema-enforced (Zod or JSON schema via `agent-swarm-kit`'s `addOutline`), prompts live as memoized userspace modules
27
24
 
28
- ## ๐Ÿ“ฆ Installation
25
+ - ๐Ÿ”Œ **12 providers** โ€” OpenAI, Claude, DeepSeek, Grok, Groq, Mistral, Perplexity, Cohere, Alibaba, Hugging Face, Ollama (local), GLM-4 (Z.ai).
26
+ - โšก **Higher-order functions** โ€” wrap an async fn with inference context via `di-scoped`; same signature in, same out.
27
+ - ๐ŸŽฏ **Userspace schema** โ€” define your own Zod or JSON schema; structured output enforced with auto-retry + custom validations.
28
+ - ๐Ÿ“ **Userspace prompts** โ€” load from `.cjs` modules in `config/prompt/`, or inline; memoized via `functools-kit`.
29
+ - ๐Ÿ”„ **Token rotation** โ€” pass an array of API keys for automatic rotation.
30
+ - ๐Ÿงฌ **Strategy optimizer** โ€” `Optimizer` generates *complete executable strategy code* from LLM analysis across training ranges.
29
31
 
30
- ```bash
31
- npm install @backtest-kit/ollama backtest-kit agent-swarm-kit
32
+ ---
33
+
34
+ ## The provider HOF
35
+
36
+ The whole adapter is one shape, repeated for 12 providers: `provider(fn, model, apiKey?) => fn`. It returns a function with the **same signature** as `fn`, executed inside the provider's inference context (so any `agent-swarm-kit` completion inside resolves to that provider).
37
+
38
+ ```typescript
39
+ import { deepseek } from '@backtest-kit/ollama';
40
+ import { addStrategy } from 'backtest-kit';
41
+
42
+ addStrategy({
43
+ strategyName: 'llm-signal', interval: '5m',
44
+ // swap deepseek() โ†’ claude() / gpt5() / ollama() / groq() with no other change
45
+ getSignal: deepseek(getSignal, 'deepseek-chat', process.env.DEEPSEEK_API_KEY),
46
+ });
32
47
  ```
33
48
 
34
- ## ๐Ÿš€ Usage
49
+ <details>
50
+ <summary>All 12 providers, base URLs & token rotation</summary>
51
+
52
+ | Provider | Function | Inference | Base URL |
53
+ |----------|----------|-----------|----------|
54
+ | OpenAI | `gpt5()` | `gpt5_inference` | `https://api.openai.com/v1/` |
55
+ | Claude | `claude()` | `claude_inference` | `https://api.anthropic.com/v1/` |
56
+ | DeepSeek | `deepseek()` | `deepseek_inference` | `https://api.deepseek.com/` |
57
+ | Grok (xAI) | `grok()` | `grok_inference` | `https://api.x.ai/v1/` |
58
+ | Groq | `groq()` | `groq_inference` | `https://api.groq.com/` |
59
+ | Mistral | `mistral()` | `mistral_inference` | `https://api.mistral.ai/v1/` |
60
+ | Perplexity | `perplexity()` | `perplexity_inference` | `https://api.perplexity.ai/` |
61
+ | Cohere | `cohere()` | `cohere_inference` | `https://api.cohere.ai/compatibility/v1/` |
62
+ | Alibaba (Qwen) | `alibaba()` | `alibaba_inference` | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1/` |
63
+ | Hugging Face | `hf()` | `hf_inference` | `https://router.huggingface.co/v1/` |
64
+ | Ollama (local) | `ollama()` | `ollama_inference` | `http://localhost:11434/` |
65
+ | GLM-4 (Z.ai) | `glm4()` | `glm4_inference` | `https://open.bigmodel.cn/api/paas/v4/` |
66
+
67
+ ```typescript
68
+ // apiKey accepts a single key OR an array โ†’ automatic rotation across calls
69
+ const wrappedFn = ollama(myFn, 'llama3.3:70b', ['key1', 'key2', 'key3']);
70
+ ```
71
+
72
+ All twelve share one signature โ€” `<T>(fn: T, model: string, apiKey?: string | string[]) => T` โ€” and run `fn` inside `ContextService.runInContext({ apiKey, inference, model })`. The matching `InferenceName` enum + per-provider `client/*Provider.client.ts` + `config/*.ts` resolve the actual SDK call.
73
+
74
+ </details>
75
+
76
+ ---
77
+
78
+ ## Structured output
35
79
 
36
- ### Signal Schema (userspace)
80
+ Define a schema (Zod or raw JSON), register it as an outline against this package's `CompletionName`, and the LLM is forced to return valid JSON โ€” with custom validations that reject bad signals (e.g. "SL must be below entry for LONG").
81
+
82
+ <details>
83
+ <summary>Zod outline</summary>
37
84
 
38
85
  ```typescript
39
86
  // schema/Signal.schema.ts
40
87
  import { z } from 'zod';
41
- import { str } from 'functools-kit';
42
-
43
88
  export const SignalSchema = z.object({
44
- position: z.enum(['long', 'short', 'wait']).describe(
45
- str.newline(
46
- 'Position direction:',
47
- 'long: bullish signals, uptrend potential',
48
- 'short: bearish signals, downtrend potential',
49
- 'wait: conflicting signals or unfavorable conditions',
50
- )
51
- ),
52
- price_open: z.number().describe(
53
- str.newline(
54
- 'Entry price in USD',
55
- 'Current market price or limit order price',
56
- )
57
- ),
58
- price_stop_loss: z.number().describe(
59
- str.newline(
60
- 'Stop-loss price in USD',
61
- 'LONG: below price_open',
62
- 'SHORT: above price_open',
63
- )
64
- ),
65
- price_take_profit: z.number().describe(
66
- str.newline(
67
- 'Take-profit price in USD',
68
- 'LONG: above price_open',
69
- 'SHORT: below price_open',
70
- )
71
- ),
72
- minute_estimated_time: z.number().describe(
73
- 'Estimated time to reach TP in minutes'
74
- ),
75
- risk_note: z.string().describe(
76
- str.newline(
77
- 'Risk assessment:',
78
- '- Whale manipulations',
79
- '- Order book imbalance',
80
- '- Technical divergences',
81
- 'Provide specific numbers and percentages',
82
- )
83
- ),
89
+ position: z.enum(['long', 'short', 'wait']).describe('long: bullish ยท short: bearish ยท wait: unclear'),
90
+ price_open: z.number().describe('Entry price in USD'),
91
+ price_stop_loss: z.number().describe('LONG: below entry ยท SHORT: above entry'),
92
+ price_take_profit: z.number().describe('LONG: above entry ยท SHORT: below entry'),
93
+ minute_estimated_time: z.number().describe('Estimated minutes to reach TP'),
94
+ risk_note: z.string().describe('Whale manipulation, order-book imbalance, divergences โ€” with numbers'),
84
95
  });
85
-
86
96
  export type TSignalSchema = z.infer<typeof SignalSchema>;
87
97
  ```
88
98
 
89
- ### Signal Outline with Zod (userspace)
90
-
91
99
  ```typescript
92
100
  // outline/signal.outline.ts
93
101
  import { addOutline } from 'agent-swarm-kit';
@@ -99,28 +107,22 @@ addOutline<TSignalSchema>({
99
107
  outlineName: 'SignalOutline',
100
108
  completion: CompletionName.RunnerOutlineCompletion,
101
109
  format: zodResponseFormat(SignalSchema, 'position_decision'),
102
- getOutlineHistory: async ({ history, param: messages = [] }) => {
103
- await history.push(messages);
104
- },
105
- validations: [
106
- {
107
- validate: ({ data }) => {
108
- if (data.position === 'long' && data.price_stop_loss >= data.price_open) {
109
- throw new Error('For LONG, stop_loss must be below price_open');
110
- }
111
- if (data.position === 'short' && data.price_stop_loss <= data.price_open) {
112
- throw new Error('For SHORT, stop_loss must be above price_open');
113
- }
114
- },
110
+ getOutlineHistory: async ({ history, param: messages = [] }) => { await history.push(messages); },
111
+ validations: [{
112
+ validate: ({ data }) => {
113
+ if (data.position === 'long' && data.price_stop_loss >= data.price_open) throw new Error('LONG: SL must be below entry');
114
+ if (data.position === 'short' && data.price_stop_loss <= data.price_open) throw new Error('SHORT: SL must be above entry');
115
115
  },
116
- ],
116
+ }],
117
117
  });
118
118
  ```
119
119
 
120
- ### Signal Outline without Zod (userspace)
120
+ </details>
121
+
122
+ <details>
123
+ <summary>Raw JSON-schema outline (no Zod)</summary>
121
124
 
122
125
  ```typescript
123
- // outline/signal.outline.ts
124
126
  import { addOutline, IOutlineFormat } from 'agent-swarm-kit';
125
127
  import { CompletionName } from '@backtest-kit/ollama';
126
128
 
@@ -128,51 +130,40 @@ const format: IOutlineFormat = {
128
130
  type: 'object',
129
131
  properties: {
130
132
  take_profit_price: { type: 'number', description: 'Take profit price in USD' },
131
- stop_loss_price: { type: 'number', description: 'Stop-loss price in USD' },
132
- description: { type: 'string', description: 'User-friendly explanation of risks, min 10 sentences' },
133
- reasoning: { type: 'string', description: 'Technical analysis, min 15 sentences' },
133
+ stop_loss_price: { type: 'number', description: 'Stop-loss price in USD' },
134
+ description: { type: 'string', description: 'User-friendly risk explanation, min 10 sentences' },
135
+ reasoning: { type: 'string', description: 'Technical analysis, min 15 sentences' },
134
136
  },
135
137
  required: ['take_profit_price', 'stop_loss_price', 'description', 'reasoning'],
136
138
  };
137
139
 
138
140
  addOutline({
139
- outlineName: 'SignalOutline',
140
- format,
141
- prompt: 'Generate crypto trading signals based on price and volume indicators in JSON format.',
142
- completion: CompletionName.RunnerOutlineCompletion,
141
+ outlineName: 'SignalOutline', format, completion: CompletionName.RunnerOutlineCompletion,
142
+ prompt: 'Generate crypto trading signals from price & volume indicators in JSON.',
143
143
  getOutlineHistory: async ({ history, param }) => {
144
- const signalReport = await ioc.signalReportService.getSignalReport(param);
145
- await commitReports(history, signalReport);
144
+ const report = await ioc.signalReportService.getSignalReport(param);
145
+ await commitReports(history, report);
146
146
  await history.push({ role: 'user', content: 'Generate JSON based on reports.' });
147
147
  },
148
148
  validations: [
149
- {
150
- validate: ({ data }) => {
151
- if (data.action !== 'buy') return;
152
- const stopLossChange = percentDiff(data.current_price, data.stop_loss_price);
153
- if (stopLossChange > CC_LADDER_STOP_LOSS) {
154
- throw new Error(`Stop loss must not exceed -${CC_LADDER_STOP_LOSS}%`);
155
- }
156
- },
157
- docDescription: 'Checks stop-loss price against max loss percentage.',
158
- },
159
- {
160
- validate: ({ data }) => {
161
- if (data.action !== 'buy') return;
162
- const sellChange = percentDiff(data.current_price, data.take_profit_price);
163
- if (sellChange > CC_LADDER_TAKE_PROFIT) {
164
- throw new Error(`Take profit must not exceed +${CC_LADDER_TAKE_PROFIT}%`);
165
- }
166
- },
167
- docDescription: 'Checks take-profit price against max profit percentage.',
168
- },
149
+ { docDescription: 'Stop-loss vs max loss %', validate: ({ data }) => { if (data.action === 'buy' && percentDiff(data.current_price, data.stop_loss_price) > CC_LADDER_STOP_LOSS) throw new Error(`SL must not exceed -${CC_LADDER_STOP_LOSS}%`); } },
150
+ { docDescription: 'Take-profit vs max profit %', validate: ({ data }) => { if (data.action === 'buy' && percentDiff(data.current_price, data.take_profit_price) > CC_LADDER_TAKE_PROFIT) throw new Error(`TP must not exceed +${CC_LADDER_TAKE_PROFIT}%`); } },
169
151
  ],
170
152
  });
171
153
  ```
172
154
 
173
- ### Prompt Module (userspace)
155
+ </details>
174
156
 
175
- ```typescript
157
+ ---
158
+
159
+ ## Prompts
160
+
161
+ Prompt modules receive trading context automatically. `system` may be a string array or a function of `(symbol, strategyName, exchangeName, frameName, backtest)`; `user` likewise.
162
+
163
+ <details>
164
+ <summary>Module file, inline prompt & commitPrompt</summary>
165
+
166
+ ```javascript
176
167
  // config/prompt/signal.prompt.cjs
177
168
  module.exports = {
178
169
  system: (symbol, strategyName, exchangeName, frameName, backtest) => [
@@ -180,180 +171,116 @@ module.exports = {
180
171
  `Strategy: ${strategyName}, Timeframe: ${frameName}`,
181
172
  backtest ? 'Backtest mode' : 'Live mode',
182
173
  ],
183
- user: (symbol) => `Analyze ${symbol} and return trading decision`,
174
+ user: (symbol) => `Analyze ${symbol} and return a trading decision`,
184
175
  };
185
176
  ```
186
177
 
187
- ### Strategy
188
-
189
178
  ```typescript
190
- // strategy.ts
191
- import './outline/signal.outline'; // register outline
179
+ import { Module, Prompt, commitPrompt, MessageModel } from '@backtest-kit/ollama';
180
+
181
+ // from a .cjs module (default baseDir: {cwd}/config/prompt/), memoized
182
+ const signalModule = Module.fromPath('./signal.prompt.cjs');
183
+ // or inline
184
+ const inline = Prompt.fromPrompt({ system: ['You are a trading bot'], user: (symbol) => `Trend for ${symbol}?` });
192
185
 
186
+ const messages: MessageModel[] = [];
187
+ await commitPrompt(signalModule, messages); // pushes rendered system + user messages with context
188
+ ```
189
+
190
+ Full strategy: register the outline, build messages from a prompt, request structured JSON, wrap with a provider HOF:
191
+
192
+ ```typescript
193
+ import './outline/signal.outline';
193
194
  import { deepseek, Module, commitPrompt, MessageModel } from '@backtest-kit/ollama';
194
195
  import { addStrategy } from 'backtest-kit';
195
196
  import { json } from 'agent-swarm-kit';
196
197
 
197
198
  const signalModule = Module.fromPath('./signal.prompt.cjs');
198
-
199
199
  const getSignal = async () => {
200
200
  const messages: MessageModel[] = [];
201
201
  await commitPrompt(signalModule, messages);
202
-
203
202
  const { data } = await json('SignalOutline', messages);
204
203
  return data;
205
204
  };
206
-
207
- addStrategy({
208
- strategyName: 'llm-signal',
209
- interval: '5m',
210
- getSignal: deepseek(getSignal, 'deepseek-chat', process.env.DEEPSEEK_API_KEY),
211
- });
205
+ addStrategy({ strategyName: 'llm-signal', interval: '5m',
206
+ getSignal: deepseek(getSignal, 'deepseek-chat', process.env.DEEPSEEK_API_KEY) });
212
207
  ```
213
208
 
214
- ### Dynamic Prompt
209
+ </details>
215
210
 
216
- ```typescript
217
- // config/prompt/risk.prompt.cjs
218
- module.exports = {
219
- system: ['You are a risk analyst', 'Be conservative'],
220
- user: (symbol, strategyName, exchangeName, frameName, backtest) =>
221
- `Evaluate risk for ${symbol} position on ${frameName} timeframe`,
222
- };
223
- ```
211
+ ---
224
212
 
225
- ### Inline Prompt
213
+ ## Debugging โ€” dump the conversation
226
214
 
227
- ```typescript
228
- import { Prompt, commitPrompt, MessageModel } from '@backtest-kit/ollama';
215
+ `dumpSignalData(signalId, history, signal, outputDir?)` archives the full LLM conversation attached to a signal, so an opaque model decision becomes a readable record. Skips if the directory already exists (never overwrites prior runs).
229
216
 
230
- const prompt = Prompt.fromPrompt({
231
- system: ['You are a trading bot'],
232
- user: (symbol) => `What is the trend for ${symbol}?`,
233
- });
217
+ <details>
218
+ <summary>What it writes</summary>
234
219
 
235
- const messages: MessageModel[] = [];
236
- await commitPrompt(prompt, messages);
237
- ```
220
+ Into `{outputDir}/{signalId}/` (default `./dump/strategy`): `00_system_prompt.md` (system messages + output summary), numbered `XX_user_message.md` / `XX_assistant_message.md` per turn, and a final `XX_llm_output.md` with the signal DTO. Call it from `getSignal` right before returning the signal.
238
221
 
239
- ### Token Rotation
222
+ </details>
240
223
 
241
- ```typescript
242
- import { ollama } from '@backtest-kit/ollama';
224
+ ---
243
225
 
244
- const wrappedFn = ollama(myFn, 'llama3.3:70b', ['key1', 'key2', 'key3']);
245
- ```
226
+ ## Strategy optimizer โ€” generate runnable strategy code
246
227
 
247
- ## ๐Ÿ”Œ Providers
228
+ The most powerful piece, and the one the rest of the package feeds: `Optimizer` uses an LLM to analyze a symbol across training ranges and emit a **complete, executable strategy file** โ€” imports, helpers, strategies, walker, and launcher โ€” that you can run with backtest-kit directly.
248
229
 
249
- | Provider | Function | Base URL |
250
- |----------|----------|----------|
251
- | OpenAI | `gpt5()` | `https://api.openai.com/v1/` |
252
- | Claude | `claude()` | `https://api.anthropic.com/v1/` |
253
- | DeepSeek | `deepseek()` | `https://api.deepseek.com/` |
254
- | Grok | `grok()` | `https://api.x.ai/v1/` |
255
- | Mistral | `mistral()` | `https://api.mistral.ai/v1/` |
256
- | Perplexity | `perplexity()` | `https://api.perplexity.ai/` |
257
- | Cohere | `cohere()` | `https://api.cohere.ai/compatibility/v1/` |
258
- | Alibaba | `alibaba()` | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1/` |
259
- | Hugging Face | `hf()` | `https://router.huggingface.co/v1/` |
260
- | Ollama | `ollama()` | `http://localhost:11434/` |
261
- | Zhipu AI | `glm4()` | `https://open.bigmodel.cn/api/paas/v4/` |
262
-
263
- ## ๐Ÿ“– API
264
-
265
- ### Provider HOF
230
+ <details>
231
+ <summary>Optimizer API + addOptimizerSchema + progress events</summary>
266
232
 
267
233
  ```typescript
268
- ollama | gpt5 | claude | deepseek | grok | mistral | perplexity | cohere | alibaba | hf | glm4
269
- (fn, model, apiKey?) => fn
270
- ```
234
+ import { Optimizer, addOptimizerSchema, listenOptimizerProgress } from '@backtest-kit/ollama';
271
235
 
272
- ### Module
273
-
274
- ```typescript
275
- Module.fromPath(path: string, baseDir?: string): Module
276
- ```
236
+ // describe sources, training ranges, strategy/template generation (see IOptimizer* interfaces)
237
+ addOptimizerSchema({ optimizerName: 'my-optimizer', /* sources, ranges, strategy, template */ });
277
238
 
278
- Default baseDir: `{cwd}/config/prompt/`
239
+ listenOptimizerProgress((p) => console.log(p)); // ProgressOptimizerContract
279
240
 
280
- ### Prompt
281
-
282
- ```typescript
283
- Prompt.fromPrompt(source: PromptModel): Prompt
241
+ const strategies = await Optimizer.getData('BTCUSDT', { optimizerName: 'my-optimizer' }); // metadata + LLM context per range
242
+ const code = await Optimizer.getCode('BTCUSDT', { optimizerName: 'my-optimizer' }); // full TS/JS source as string
243
+ await Optimizer.dump('BTCUSDT', { optimizerName: 'my-optimizer' }, './output'); // writes {optimizerName}_{symbol}.mjs
284
244
  ```
285
245
 
286
- ### commitPrompt
246
+ `getData` fetches from all sources and builds the LLM conversation per training range; `getCode` assembles the executable strategy; `dump` writes it to `{optimizerName}_{symbol}.mjs`. Companion registry functions: `getOptimizerSchema`, `listOptimizerSchema`, and `listenError`. The engine behind it is `common/ClientOptimizer.ts` driven by the `IOptimizer*` interfaces (`IOptimizerSchema`, `IOptimizerSource`, `IOptimizerStrategy`, `IOptimizerTemplate`, `IOptimizerRange`, `IOptimizerData`, `IOptimizerFetchArgs`, `IOptimizerFilterArgs`, `IOptimizerCallbacks`).
287
247
 
288
- ```typescript
289
- async function commitPrompt(source: Module | Prompt, history: MessageModel[]): Promise<void>
290
- ```
248
+ </details>
291
249
 
292
- ### PromptModel
250
+ ---
293
251
 
294
- ```typescript
295
- interface PromptModel {
296
- system?: string[] | SystemPromptFn;
297
- user: string | UserPromptFn;
298
- }
299
-
300
- type SystemPromptFn = (
301
- symbol: string,
302
- strategyName: string,
303
- exchangeName: string,
304
- frameName: string,
305
- backtest: boolean
306
- ) => Promise<string[]> | string[];
307
-
308
- type UserPromptFn = (
309
- symbol: string,
310
- strategyName: string,
311
- exchangeName: string,
312
- frameName: string,
313
- backtest: boolean
314
- ) => Promise<string> | string;
315
- ```
316
-
317
- ## ๐Ÿ’ก Why Use @backtest-kit/ollama?
318
-
319
- Instead of manually integrating LLM SDKs:
320
-
321
- **โŒ Without ollama (manual work)**
322
-
323
- ```typescript
324
- import OpenAI from 'openai';
325
- import Anthropic from '@anthropic-ai/sdk';
326
-
327
- const openai = new OpenAI({ apiKey: process.env.OPENAI_KEY });
328
- const response = await openai.chat.completions.create({
329
- model: 'gpt-4o',
330
- messages,
331
- response_format: { type: 'json_object' }
332
- });
333
- const signal = JSON.parse(response.choices[0].message.content);
334
- // ... manual schema validation
335
- // ... manual error handling
336
- // ... no fallback
337
- ```
252
+ ## API reference
338
253
 
339
- **โœ… With ollama (one line)**
254
+ | Export | Description |
255
+ |--------|-------------|
256
+ | `ollama` `gpt5` `claude` `deepseek` `grok` `groq` `mistral` `perplexity` `cohere` `alibaba` `hf` `glm4` | Provider HOFs โ€” `(fn, model, apiKey?) => fn` |
257
+ | `CompletionName` | Completion-name enum for `agent-swarm-kit` outlines (`RunnerOutlineCompletion`, โ€ฆ) |
258
+ | `Module.fromPath(path, baseDir?)` | Load a prompt `.cjs` module (default baseDir `{cwd}/config/prompt/`) |
259
+ | `Prompt.fromPrompt(source)` | Build a prompt from an inline `PromptModel` |
260
+ | `commitPrompt(source, history)` | Render a Module/Prompt's system+user messages into `history` |
261
+ | `dumpSignalData(id, history, signal, dir?)` | Archive the LLM conversation for one signal |
262
+ | `validate(...)` | Validate an outline result |
263
+ | `Optimizer` | `.getData` / `.getCode` / `.dump` โ€” LLM strategy-code generation |
264
+ | `addOptimizerSchema` ยท `getOptimizerSchema` ยท `listOptimizerSchema` | Optimizer schema registry |
265
+ | `listenOptimizerProgress` ยท `listenError` | Optimizer progress / error events |
266
+ | `MessageModel` `MessageRole` `PromptModel` | Message & prompt models |
267
+ | `IOptimizer*` ยท `ProgressOptimizerContract` | Optimizer interfaces & progress contract |
268
+ | `lib` | The internal engine (IoC container) for advanced use |
340
269
 
341
- ```typescript
342
- const signal = await gpt5(messages, 'gpt-4o');
343
- ```
270
+ <details>
271
+ <summary>Complete source map</summary>
344
272
 
345
- **๐Ÿ”ฅ Benefits:**
273
+ - `function/signal.function.ts` โ€” the 12 provider HOFs. `function/{add,get,list,event,setup,validate,history,dump,signal}.ts` โ€” registry, events, `setLogger`, `commitPrompt`, `dumpSignalData`.
274
+ - `client/*Provider.client.ts` (12) โ€” per-provider SDK adapters. `config/*.ts` โ€” per-provider base URLs/params, `ollama.rotate.ts` (token rotation), `params.ts`, `emitters.ts`.
275
+ - `classes/` โ€” `Module`, `Prompt`, `Optimizer`. `common/ClientOptimizer.ts` โ€” the optimizer engine.
276
+ - `enum/` โ€” `InferenceName` (12), `CompletionName`. `interface/Optimizer.interface.ts`, `contract/ProgressOptimizer.contract.ts`, `model/{Message,Prompt}.model.ts`.
277
+ - `helpers/{toLintMarkdown,toPlainString}.ts`, `lib/` (IoC: `core/{di,provide,types}`, services). Nothing in `src/` is undocumented.
346
278
 
347
- - โšก Unified API across 10+ providers
348
- - ๐ŸŽฏ Enforced JSON schema (no parsing errors)
349
- - ๐Ÿ”„ Built-in token rotation (Ollama)
350
- - ๐Ÿ”‘ Context-based API keys
351
- - ๐Ÿ›ก๏ธ Type-safe TypeScript interfaces
352
- - ๐Ÿ“Š Trading-specific output format
279
+ </details>
353
280
 
354
281
  ## ๐Ÿค Contribute
355
282
 
356
- Fork/PR on [GitHub](https://github.com/tripolskypetr/backtest-kit).
283
+ Fork / PR on [GitHub](https://github.com/tripolskypetr/backtest-kit).
357
284
 
358
285
  ## ๐Ÿ“œ License
359
286
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backtest-kit/ollama",
3
- "version": "13.5.0",
3
+ "version": "14.0.0",
4
4
  "description": "Multi-provider LLM inference library for AI-powered trading strategies. Supports 10+ providers including OpenAI, Claude, DeepSeek, Grok, Mistral with unified API and automatic token rotation.",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",
@@ -81,24 +81,24 @@
81
81
  "ts-morph": "27.0.2",
82
82
  "tslib": "2.7.0",
83
83
  "typedoc": "0.27.9",
84
- "backtest-kit": "13.5.0",
85
- "worker-testbed": "2.0.0"
84
+ "backtest-kit": "14.0.0",
85
+ "worker-testbed": "2.1.0"
86
86
  },
87
87
  "peerDependencies": {
88
88
  "@huggingface/inference": "^4.7.1",
89
89
  "@langchain/core": "^0.3.57",
90
90
  "@langchain/xai": "^0.0.2",
91
- "backtest-kit": "^13.5.0",
91
+ "backtest-kit": "^14.0.0",
92
92
  "groq-sdk": "^0.37.0",
93
93
  "ollama": "^0.6.0",
94
94
  "openai": "^4.97.0",
95
95
  "typescript": "^5.0.0"
96
96
  },
97
97
  "dependencies": {
98
- "agent-swarm-kit": "^2.7.0",
98
+ "agent-swarm-kit": "^3.2.0",
99
99
  "di-kit": "^1.1.1",
100
100
  "di-scoped": "^1.0.21",
101
- "functools-kit": "^2.3.0",
101
+ "functools-kit": "^3.0.0",
102
102
  "get-moment-stamp": "^2.0.0",
103
103
  "jsonrepair": "^3.12.0",
104
104
  "markdown-it": "^14.1.0",