@backtest-kit/sidekick 0.0.7 → 0.0.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backtest-kit/sidekick",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "The easiest way to create a new Backtest Kit trading bot project. Like create-react-app, but for algorithmic trading with LLM integration and technical analysis.",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",
package/scripts/init.mjs CHANGED
@@ -110,6 +110,7 @@ async function main() {
110
110
 
111
111
  const projectPath = path.resolve(process.cwd(), projectName);
112
112
  const srcTemplatePath = path.resolve(__dirname, '..', 'src');
113
+ const typesTemplatePath = path.resolve(__dirname, '..', 'types');
113
114
  const templateDir = path.resolve(__dirname, '..', 'template');
114
115
 
115
116
  // Template data for Mustache
@@ -145,6 +146,13 @@ async function main() {
145
146
  log.success('Copied template files');
146
147
  }
147
148
 
149
+ // Copy types template files
150
+ {
151
+ log.info('Copying types files...');
152
+ await copyFiles(typesTemplatePath, path.join(projectPath, 'types'));
153
+ log.success('Copied types files');
154
+ }
155
+
148
156
  // Create package.json from template
149
157
  {
150
158
  log.info('Creating package.json...');
@@ -211,6 +219,21 @@ async function main() {
211
219
  console.log();
212
220
  }
213
221
 
222
+ // Create jsconfig.json from template
223
+ {
224
+ log.info('Creating jsconfig.json...');
225
+ const jsconfigContent = await renderTemplate(
226
+ path.join(templateDir, 'jsconfig.json.mustache'),
227
+ templateData
228
+ );
229
+ await fs.writeFile(
230
+ path.join(projectPath, 'jsconfig.json'),
231
+ jsconfigContent,
232
+ 'utf-8'
233
+ );
234
+ log.success('Created jsconfig.json');
235
+ }
236
+
214
237
  // Install dependencies
215
238
  {
216
239
  await runNpmInstall(projectPath);
@@ -0,0 +1,15 @@
1
+ import { singleshot } from "functools-kit";
2
+ import ccxt from "ccxt";
3
+
4
+ export const getExchange = singleshot(async () => {
5
+ const exchange = new ccxt.binance({
6
+ options: {
7
+ defaultType: "spot",
8
+ adjustForTimeDifference: true,
9
+ recvWindow: 60000,
10
+ },
11
+ enableRateLimit: true,
12
+ });
13
+ await exchange.loadMarkets();
14
+ return exchange;
15
+ });
@@ -0,0 +1,19 @@
1
+ import { Markdown, Report, setLogger } from "backtest-kit"
2
+ import { createLogger } from "pinolog";
3
+
4
+ {
5
+ const logger = createLogger(`backtest-kit.log`);
6
+ setLogger({
7
+ log: (...args) => logger.log(...args),
8
+ debug: (...args) => logger.info(...args),
9
+ info: (...args) => logger.info(...args),
10
+ warn: (...args) => logger.warn(...args),
11
+ });
12
+ }
13
+
14
+ {
15
+ Markdown.disable();
16
+ Report.enable();
17
+ }
18
+
19
+
@@ -0,0 +1,12 @@
1
+ import { validate } from "backtest-kit";
2
+ import ExchangeName from "../enum/ExchangeName.mjs";
3
+ import FrameName from "../enum/FrameName.mjs";
4
+ import RiskName from "../enum/RiskName.mjs";
5
+ import StrategyName from "../enum/StrategyName.mjs";
6
+
7
+ validate({
8
+ ExchangeName,
9
+ FrameName,
10
+ RiskName,
11
+ StrategyName,
12
+ })
@@ -0,0 +1,3 @@
1
+ export default {
2
+ BinanceExchange: "binance_exchange",
3
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ October2025: "oct_2025_frame",
3
+ November2025: "nov_2025_frame",
4
+ December2025: "dec_2025_frame",
5
+ }
@@ -0,0 +1,4 @@
1
+ export default {
2
+ TakeProfitDistanceRisk: "tp_distance_risk",
3
+ RiskRewardRatioRisk: "rr_ratio_risk",
4
+ }
@@ -0,0 +1,3 @@
1
+ export default {
2
+ MainStrategy: "main_strategy",
3
+ }
@@ -0,0 +1,46 @@
1
+ import {
2
+ commitFifteenMinuteHistory,
3
+ commitHourHistory,
4
+ commitLongTermMath,
5
+ commitMicroTermMath,
6
+ commitOneMinuteHistory,
7
+ commitShortTermMath,
8
+ commitSwingTermMath,
9
+ commitThirtyMinuteHistory,
10
+ } from "@backtest-kit/signals";
11
+ import { formatPrice, getAveragePrice, getDate } from "backtest-kit";
12
+ import { str } from "functools-kit";
13
+
14
+ const commitHistorySetup = async (symbol, history) => {
15
+ // Candle histories across timeframes
16
+ {
17
+ await commitOneMinuteHistory(symbol, history);
18
+ await commitFifteenMinuteHistory(symbol, history);
19
+ await commitThirtyMinuteHistory(symbol, history);
20
+ await commitHourHistory(symbol, history);
21
+ }
22
+
23
+ // Technical indicators across timeframes
24
+ {
25
+ await commitMicroTermMath(symbol, history);
26
+ await commitShortTermMath(symbol, history);
27
+ await commitSwingTermMath(symbol, history);
28
+ await commitLongTermMath(symbol, history);
29
+ }
30
+
31
+ const displayName = await String(symbol).toUpperCase();
32
+
33
+ const currentPrice = await getAveragePrice(symbol);
34
+ const currentData = await getDate();
35
+
36
+ await history.push({
37
+ role: "system",
38
+ content: str.newline(
39
+ `Trading symbol: ${displayName}`,
40
+ `Current price: ${await formatPrice(symbol, currentPrice)} USD`,
41
+ `Current time: ${currentData.toISOString()}`
42
+ ),
43
+ });
44
+ };
45
+
46
+ export { commitHistorySetup };
package/src/index.mjs CHANGED
@@ -1 +1,6 @@
1
- export const foo = "bar";
1
+ import "./config/setup.mjs";
2
+ import "./logic/index.mjs";
3
+
4
+ import "./config/validate.mjs";
5
+
6
+ import "./main/bootstrap.mjs";
@@ -0,0 +1,59 @@
1
+ import { addExchange, roundTicks } from "backtest-kit";
2
+ import { getExchange } from "../../config/ccxt.mjs";
3
+ import ExchangeName from "../../enum/ExchangeName.mjs";
4
+
5
+ const MAX_DEPTH_LEVELS = 1_000;
6
+
7
+ addExchange({
8
+ exchangeName: ExchangeName.BinanceExchange,
9
+ getCandles: async (symbol, interval, since, limit) => {
10
+ const exchange = await getExchange();
11
+ const candles = await exchange.fetchOHLCV(
12
+ symbol,
13
+ interval,
14
+ since.getTime(),
15
+ limit
16
+ );
17
+ return candles.map(([timestamp, open, high, low, close, volume]) => ({
18
+ timestamp,
19
+ open,
20
+ high,
21
+ low,
22
+ close,
23
+ volume,
24
+ }));
25
+ },
26
+ formatPrice: async (symbol, price) => {
27
+ const exchange = await getExchange();
28
+ const market = exchange.market(symbol);
29
+ const tickSize = market.limits?.price?.min || market.precision?.price;
30
+ if (tickSize !== undefined) {
31
+ return roundTicks(price, tickSize);
32
+ }
33
+ return exchange.priceToPrecision(symbol, price);
34
+ },
35
+ formatQuantity: async (symbol, quantity) => {
36
+ const exchange = await getExchange();
37
+ const market = exchange.market(symbol);
38
+ const stepSize = market.limits?.amount?.min || market.precision?.amount;
39
+ if (stepSize !== undefined) {
40
+ return roundTicks(quantity, stepSize);
41
+ }
42
+ return exchange.amountToPrecision(symbol, quantity);
43
+ },
44
+ getOrderBook: async (symbol) => {
45
+ const exchange = await getExchange();
46
+ const bookData = await exchange.fetchOrderBook(symbol, MAX_DEPTH_LEVELS);
47
+ return {
48
+ symbol,
49
+ asks: bookData.asks.map(([price, quantity]) => ({
50
+ price: String(price),
51
+ quantity: String(quantity),
52
+ })),
53
+ bids: bookData.bids.map(([price, quantity]) => ({
54
+ price: String(price),
55
+ quantity: String(quantity),
56
+ })),
57
+ };
58
+ }
59
+ });
@@ -0,0 +1,10 @@
1
+ import { addFrame } from "backtest-kit";
2
+ import FrameName from "../../enum/FrameName.mjs";
3
+
4
+ addFrame({
5
+ frameName: FrameName.December2025,
6
+ interval: "1m",
7
+ startDate: new Date("2025-12-01T00:00:00Z"),
8
+ endDate: new Date("2025-12-31T23:59:59Z"),
9
+ note: "Боковик без явного роста или падения",
10
+ });
@@ -0,0 +1,10 @@
1
+ import { addFrame } from "backtest-kit";
2
+ import FrameName from "../../enum/FrameName.mjs";
3
+
4
+ addFrame({
5
+ frameName: FrameName.November2025,
6
+ interval: "1m",
7
+ startDate: new Date("2025-11-01T00:00:00Z"),
8
+ endDate: new Date("2025-11-30T23:59:59Z"),
9
+ note: "Боковик с общим трендом вниз и незначительными отскоками",
10
+ });
@@ -0,0 +1,10 @@
1
+ import { addFrame } from "backtest-kit";
2
+ import FrameName from "../../enum/FrameName.mjs";
3
+
4
+ addFrame({
5
+ frameName: FrameName.October2025,
6
+ interval: "1m",
7
+ startDate: new Date("2025-10-01T00:00:00Z"),
8
+ endDate: new Date("2025-10-31T23:59:59Z"),
9
+ note: "Резкое падение рынка с 9 по 11 число",
10
+ });
@@ -0,0 +1,10 @@
1
+ import "./exchange/binance.exchange.mjs";
2
+
3
+ import "./frame/dec_2025.frame.mjs";
4
+ import "./frame/nov_2025.frame.mjs";
5
+ import "./frame/oct_2025.frame.mjs";
6
+
7
+ import "./risk/rr_ratio.risk.mjs";
8
+ import "./risk/tp_distance.risk.mjs";
9
+
10
+ import "./strategy/main.strategy.mjs";
@@ -0,0 +1,39 @@
1
+ import { addRisk } from "backtest-kit";
2
+ import RiskName from "../../enum/RiskName.mjs";
3
+
4
+ addRisk({
5
+ riskName: RiskName.RiskRewardRatioRisk,
6
+ validations: [
7
+ {
8
+ validate: ({ pendingSignal, currentPrice }) => {
9
+ const {
10
+ priceOpen = currentPrice,
11
+ priceTakeProfit,
12
+ priceStopLoss,
13
+ position,
14
+ } = pendingSignal;
15
+ if (!priceOpen) {
16
+ return;
17
+ }
18
+ // Calculate reward (TP distance)
19
+ const reward =
20
+ position === "long"
21
+ ? priceTakeProfit - priceOpen
22
+ : priceOpen - priceTakeProfit;
23
+ // Calculate risk (SL distance)
24
+ const risk =
25
+ position === "long"
26
+ ? priceOpen - priceStopLoss
27
+ : priceStopLoss - priceOpen;
28
+ if (risk <= 0) {
29
+ throw new Error("Invalid SL: risk must be positive");
30
+ }
31
+ const rrRatio = reward / risk;
32
+ if (rrRatio < 2) {
33
+ throw new Error(`RR ratio ${rrRatio.toFixed(2)} < 2:1`);
34
+ }
35
+ },
36
+ note: "Risk-Reward ratio must be at least 1:2",
37
+ },
38
+ ],
39
+ });
@@ -0,0 +1,30 @@
1
+ import { addRisk } from "backtest-kit";
2
+ import RiskName from "../../enum/RiskName.mjs";
3
+
4
+ addRisk({
5
+ riskName: RiskName.TakeProfitDistanceRisk,
6
+ validations: [
7
+ {
8
+ validate: ({ pendingSignal, currentPrice }) => {
9
+ const {
10
+ priceOpen = currentPrice,
11
+ priceTakeProfit,
12
+ position,
13
+ } = pendingSignal;
14
+ if (!priceOpen) {
15
+ return;
16
+ }
17
+ // Calculate TP distance percentage
18
+ const tpDistance =
19
+ position === "long"
20
+ ? ((priceTakeProfit - priceOpen) / priceOpen) * 100
21
+ : ((priceOpen - priceTakeProfit) / priceOpen) * 100;
22
+
23
+ if (tpDistance < 1) {
24
+ throw new Error(`TP distance ${tpDistance.toFixed(2)}% < 1%`);
25
+ }
26
+ },
27
+ note: "TP distance must be at least 1%",
28
+ },
29
+ ],
30
+ });
@@ -0,0 +1,29 @@
1
+ import { addStrategy } from "backtest-kit";
2
+ import { ollama } from "@backtest-kit/ollama";
3
+
4
+ import { commitHistorySetup } from "../../func/market.func.mjs";
5
+
6
+ import StrategyName from "../../enum/StrategyName.mjs";
7
+ import RiskName from "../../enum/RiskName.mjs";
8
+
9
+ addStrategy({
10
+ strategyName: StrategyName.MainStrategy,
11
+ interval: "5m",
12
+ getSignal: async (symbol) => {
13
+ const messages = [];
14
+
15
+ {
16
+ await commitHistorySetup(symbol, messages);
17
+ }
18
+
19
+ return await ollama(
20
+ messages,
21
+ "glm-4.6:cloud",
22
+ process.env.CC_OLLAMA_API_KEY
23
+ );
24
+ },
25
+ riskList: [
26
+ RiskName.TakeProfitDistanceRisk,
27
+ RiskName.RiskRewardRatioRisk
28
+ ],
29
+ });
File without changes
@@ -0,0 +1,25 @@
1
+ import { singleshot } from "functools-kit";
2
+ import { parseArgs } from "util";
3
+
4
+ export const getArgs = singleshot(() => {
5
+ const { values } = parseArgs({
6
+ args: process.argv,
7
+ options: {
8
+ strategy: {
9
+ type: "string",
10
+ },
11
+ backtest: {
12
+ type: "boolean",
13
+ },
14
+ paper: {
15
+ type: "boolean",
16
+ },
17
+ live: {
18
+ type: "boolean",
19
+ },
20
+ },
21
+ strict: false,
22
+ allowPositionals: true,
23
+ });
24
+ return values;
25
+ });
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "ESNext",
4
+ "moduleResolution": "node",
5
+ "target": "ES2020",
6
+ "checkJs": true,
7
+ "allowSyntheticDefaultImports": true,
8
+ "ignoreDeprecations": "6.0",
9
+ "baseUrl": ".",
10
+ "paths": {
11
+ "*": ["node_modules/*"]
12
+ }
13
+ },
14
+ "include": [
15
+ "src/**/*",
16
+ "types/**/*",
17
+ "node_modules/functools-kit/types.d.ts",
18
+ "node_modules/backtest-kit/types.d.ts",
19
+ "node_modules/@backtest-kit/ollama/types.d.ts",
20
+ "node_modules/@backtest-kit/signals/types.d.ts"
21
+ ],
22
+ "exclude": [
23
+ "node_modules"
24
+ ]
25
+ }
@@ -9,20 +9,20 @@
9
9
  "test": "echo \"Error: no test specified\" && exit 1"
10
10
  },
11
11
  "dependencies": {
12
- "backtest-kit": "^1.12.1",
13
- "@backtest-kit/signals": "^0.0.5",
14
- "@backtest-kit/ollama": "^0.0.3",
15
- "agent-swarm-kit": "^1.1.180",
12
+ "@backtest-kit/ollama": "^0.0.7",
13
+ "@backtest-kit/signals": "^0.0.7",
14
+ "@huggingface/inference": "^4.7.1",
15
+ "@langchain/core": "^0.3.57",
16
+ "@langchain/xai": "^0.0.2",
17
+ "agent-swarm-kit": "^1.1.181",
18
+ "backtest-kit": "^1.13.2",
16
19
  "ccxt": "^4.4.41",
17
- "uuid": "^11.0.3",
18
20
  "dotenv": "^16.4.7",
21
+ "functools-kit": "^1.0.95",
19
22
  "ollama": "^0.6.0",
20
23
  "openai": "^4.97.0",
21
- "jsonrepair": "^3.12.0",
22
- "@langchain/core": "^0.3.57",
23
- "@langchain/xai": "^0.0.2",
24
- "@huggingface/inference": "^4.7.1",
25
- "sanitize-html": "^2.17.0"
24
+ "pinolog": "^1.0.5",
25
+ "uuid": "^11.0.3"
26
26
  },
27
27
  "devDependencies": {
28
28
  "dotenv-cli": "^7.4.2"