@backtest-kit/sidekick 0.0.6 → 0.0.8

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.6",
3
+ "version": "0.0.8",
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
@@ -211,6 +211,21 @@ async function main() {
211
211
  console.log();
212
212
  }
213
213
 
214
+ // Create jsconfig.json from template
215
+ {
216
+ log.info('Creating jsconfig.json...');
217
+ const jsconfigContent = await renderTemplate(
218
+ path.join(templateDir, 'jsconfig.json.mustache'),
219
+ templateData
220
+ );
221
+ await fs.writeFile(
222
+ path.join(projectPath, 'jsconfig.json'),
223
+ jsconfigContent,
224
+ 'utf-8'
225
+ );
226
+ log.success('Created jsconfig.json');
227
+ }
228
+
214
229
  // Install dependencies
215
230
  {
216
231
  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,24 @@
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
+ "node_modules/functools-kit/types.d.ts",
17
+ "node_modules/backtest-kit/types.d.ts",
18
+ "node_modules/@backtest-kit/ollama/types.d.ts",
19
+ "node_modules/@backtest-kit/signals/types.d.ts"
20
+ ],
21
+ "exclude": [
22
+ "node_modules"
23
+ ]
24
+ }
@@ -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.3",
14
- "@backtest-kit/ollama": "^0.0.3",
12
+ "@backtest-kit/ollama": "^0.0.5",
13
+ "@backtest-kit/signals": "^0.0.6",
14
+ "@huggingface/inference": "^4.7.1",
15
+ "@langchain/core": "^0.3.57",
16
+ "@langchain/xai": "^0.0.2",
15
17
  "agent-swarm-kit": "^1.1.180",
18
+ "backtest-kit": "^1.12.3",
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"