@backtest-kit/sidekick 8.3.0 → 8.5.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 (49) hide show
  1. package/README.md +173 -173
  2. package/content/config/source/timeframe_15m.pine +113 -113
  3. package/content/config/source/timeframe_4h.pine +56 -56
  4. package/content/config/symbol.config.cjs +460 -460
  5. package/content/docker/ollama/docker-compose.yaml +34 -34
  6. package/content/docker/ollama/watch.sh +2 -2
  7. package/content/scripts/cache/cache_candles.mjs +47 -47
  8. package/content/scripts/cache/cache_model.mjs +42 -42
  9. package/content/scripts/cache/validate_candles.mjs +46 -46
  10. package/content/scripts/run_timeframe_15m.mjs +77 -77
  11. package/content/scripts/run_timeframe_4h.mjs +68 -68
  12. package/package.json +68 -68
  13. package/scripts/init.mjs +304 -304
  14. package/src/classes/BacktestLowerStopOnBreakevenAction.mjs +17 -17
  15. package/src/classes/BacktestPartialProfitTakingAction.mjs +24 -24
  16. package/src/classes/BacktestPositionMonitorAction.mjs +57 -57
  17. package/src/config/ccxt.mjs +15 -15
  18. package/src/config/params.mjs +1 -1
  19. package/src/config/setup.mjs +45 -45
  20. package/src/config/validate.mjs +14 -14
  21. package/src/enum/ActionName.mjs +5 -5
  22. package/src/enum/ExchangeName.mjs +3 -3
  23. package/src/enum/FrameName.mjs +6 -6
  24. package/src/enum/RiskName.mjs +4 -4
  25. package/src/enum/StrategyName.mjs +3 -3
  26. package/src/index.mjs +6 -6
  27. package/src/logic/action/backtest_lower_stop_on_breakeven.action.mjs +9 -9
  28. package/src/logic/action/backtest_partial_profit_taking.action.mjs +9 -9
  29. package/src/logic/action/backtest_position_monitor.action.mjs +9 -9
  30. package/src/logic/exchange/binance.exchange.mjs +69 -69
  31. package/src/logic/frame/dec_2025.frame.mjs +10 -10
  32. package/src/logic/frame/feb_2024.frame.mjs +10 -10
  33. package/src/logic/frame/nov_2025.frame.mjs +10 -10
  34. package/src/logic/frame/oct_2025.frame.mjs +10 -10
  35. package/src/logic/index.mjs +15 -15
  36. package/src/logic/risk/sl_distance.risk.mjs +32 -32
  37. package/src/logic/risk/tp_distance.risk.mjs +32 -32
  38. package/src/logic/strategy/main.strategy.mjs +48 -48
  39. package/src/main/bootstrap.mjs +52 -52
  40. package/src/math/timeframe_15m.math.mjs +68 -68
  41. package/src/math/timeframe_4h.math.mjs +53 -53
  42. package/src/utils/getArgs.mjs +17 -17
  43. package/template/CLAUDE.mustache +421 -421
  44. package/template/README.mustache +257 -257
  45. package/template/env.mustache +2 -2
  46. package/template/gitignore.mustache +29 -29
  47. package/template/jsconfig.json.mustache +26 -26
  48. package/template/package.mustache +37 -37
  49. package/template/types.mustache +11 -11
@@ -1,34 +1,34 @@
1
- version: "3.8"
2
-
3
- services:
4
- ollama:
5
- build: .
6
- container_name: ollama
7
- ports:
8
- - "11434:11434"
9
- volumes:
10
- - ./.ollama:/root/.ollama
11
- environment:
12
- OLLAMA_HOST: 0.0.0.0
13
- OLLAMA_MAX_LOADED_MODELS: 2
14
- OLLAMA_NUM_PARALLEL: 8
15
- OLLAMA_MAX_QUEUE: 96
16
- OLLAMA_KEEP_ALIVE: 24h
17
- OLLAMA_FLASH_ATTENTION: 1
18
- OLLAMA_KV_CACHE_TYPE: q4_0
19
- NVIDIA_VISIBLE_DEVICES: all
20
- NVIDIA_DRIVER_CAPABILITIES: compute,utility
21
- deploy:
22
- resources:
23
- reservations:
24
- devices:
25
- - driver: nvidia
26
- count: all
27
- capabilities: [gpu]
28
- restart: always
29
- healthcheck:
30
- test: ["CMD", "curl", "-f", "http://localhost:11434/api/version"]
31
- interval: 30s
32
- timeout: 5s
33
- retries: 3
34
- start_period: 20s
1
+ version: "3.8"
2
+
3
+ services:
4
+ ollama:
5
+ build: .
6
+ container_name: ollama
7
+ ports:
8
+ - "11434:11434"
9
+ volumes:
10
+ - ./.ollama:/root/.ollama
11
+ environment:
12
+ OLLAMA_HOST: 0.0.0.0
13
+ OLLAMA_MAX_LOADED_MODELS: 2
14
+ OLLAMA_NUM_PARALLEL: 8
15
+ OLLAMA_MAX_QUEUE: 96
16
+ OLLAMA_KEEP_ALIVE: 24h
17
+ OLLAMA_FLASH_ATTENTION: 1
18
+ OLLAMA_KV_CACHE_TYPE: q4_0
19
+ NVIDIA_VISIBLE_DEVICES: all
20
+ NVIDIA_DRIVER_CAPABILITIES: compute,utility
21
+ deploy:
22
+ resources:
23
+ reservations:
24
+ devices:
25
+ - driver: nvidia
26
+ count: all
27
+ capabilities: [gpu]
28
+ restart: always
29
+ healthcheck:
30
+ test: ["CMD", "curl", "-f", "http://localhost:11434/api/version"]
31
+ interval: 30s
32
+ timeout: 5s
33
+ retries: 3
34
+ start_period: 20s
@@ -1,2 +1,2 @@
1
- #!/usr/bin/env bash
2
- watch -n 1 -d nvidia-smi
1
+ #!/usr/bin/env bash
2
+ watch -n 1 -d nvidia-smi
@@ -1,47 +1,47 @@
1
- import { addExchangeSchema, warmCandles } from "backtest-kit";
2
- import { singleshot } from "functools-kit";
3
- import ccxt from "ccxt";
4
-
5
-
6
- const getExchange = singleshot(async () => {
7
- const exchange = new ccxt.binance({
8
- options: {
9
- defaultType: "spot",
10
- adjustForTimeDifference: true,
11
- recvWindow: 60000,
12
- },
13
- enableRateLimit: true,
14
- });
15
- await exchange.loadMarkets();
16
- return exchange;
17
- });
18
-
19
- addExchangeSchema({
20
- exchangeName: "binance_exchange",
21
- getCandles: async (symbol, interval, since, limit) => {
22
- const exchange = await getExchange();
23
- const candles = await exchange.fetchOHLCV(
24
- symbol,
25
- interval,
26
- since.getTime(),
27
- limit,
28
- );
29
- return candles.map(([timestamp, open, high, low, close, volume]) => ({
30
- timestamp,
31
- open,
32
- high,
33
- low,
34
- close,
35
- volume,
36
- }));
37
- },
38
- });
39
-
40
- const from = new Date("2024-02-01T00:00:00Z");
41
- const to = new Date("2024-02-29T23:59:59Z");
42
- const symbol = "BTCUSDT";
43
- const exchangeName = "binance_exchange";
44
-
45
- await warmCandles({ exchangeName, from, to, interval: "1m", symbol });
46
- await warmCandles({ exchangeName, from, to, interval: "15m", symbol });
47
- await warmCandles({ exchangeName, from, to, interval: "4h", symbol });
1
+ import { addExchangeSchema, warmCandles } from "backtest-kit";
2
+ import { singleshot } from "functools-kit";
3
+ import ccxt from "ccxt";
4
+
5
+
6
+ const getExchange = singleshot(async () => {
7
+ const exchange = new ccxt.binance({
8
+ options: {
9
+ defaultType: "spot",
10
+ adjustForTimeDifference: true,
11
+ recvWindow: 60000,
12
+ },
13
+ enableRateLimit: true,
14
+ });
15
+ await exchange.loadMarkets();
16
+ return exchange;
17
+ });
18
+
19
+ addExchangeSchema({
20
+ exchangeName: "binance_exchange",
21
+ getCandles: async (symbol, interval, since, limit) => {
22
+ const exchange = await getExchange();
23
+ const candles = await exchange.fetchOHLCV(
24
+ symbol,
25
+ interval,
26
+ since.getTime(),
27
+ limit,
28
+ );
29
+ return candles.map(([timestamp, open, high, low, close, volume]) => ({
30
+ timestamp,
31
+ open,
32
+ high,
33
+ low,
34
+ close,
35
+ volume,
36
+ }));
37
+ },
38
+ });
39
+
40
+ const from = new Date("2024-02-01T00:00:00Z");
41
+ const to = new Date("2024-02-29T23:59:59Z");
42
+ const symbol = "BTCUSDT";
43
+ const exchangeName = "binance_exchange";
44
+
45
+ await warmCandles({ exchangeName, from, to, interval: "1m", symbol });
46
+ await warmCandles({ exchangeName, from, to, interval: "15m", symbol });
47
+ await warmCandles({ exchangeName, from, to, interval: "4h", symbol });
@@ -1,42 +1,42 @@
1
- import { Ollama } from "ollama";
2
-
3
- const MODEL_NAME = "glm-4.7-flash:q4_K_M";
4
-
5
- const ollama = new Ollama({ host: "http://127.0.0.1:11434" });
6
-
7
- console.log(`Loading model ${MODEL_NAME}`);
8
-
9
- const pull = async () => {
10
- const response = await ollama.pull({
11
- model: MODEL_NAME,
12
- stream: true,
13
- });
14
-
15
- for await (const part of response) {
16
- if (!part.completed || !part.total) {
17
- continue;
18
- }
19
-
20
- // Calculate progress percentage
21
- const progress =
22
- part.total > 0 ? ((part.completed / part.total) * 100).toFixed(1) : 0;
23
-
24
- // Create simple progress bar
25
- const barLength = 40;
26
- const filledLength = Math.round((barLength * part.completed) / part.total);
27
- const bar = "█".repeat(filledLength) + "░".repeat(barLength - filledLength);
28
-
29
- // Display progress
30
- process.stdout.write(`\r[${bar}] ${progress}% ${part.status}`);
31
-
32
- if (part.status === "success") {
33
- console.log("\nModel pulled successfully!");
34
- break;
35
- }
36
- }
37
-
38
- console.log("Done!");
39
- };
40
-
41
- await pull();
42
-
1
+ import { Ollama } from "ollama";
2
+
3
+ const MODEL_NAME = "glm-4.7-flash:q4_K_M";
4
+
5
+ const ollama = new Ollama({ host: "http://127.0.0.1:11434" });
6
+
7
+ console.log(`Loading model ${MODEL_NAME}`);
8
+
9
+ const pull = async () => {
10
+ const response = await ollama.pull({
11
+ model: MODEL_NAME,
12
+ stream: true,
13
+ });
14
+
15
+ for await (const part of response) {
16
+ if (!part.completed || !part.total) {
17
+ continue;
18
+ }
19
+
20
+ // Calculate progress percentage
21
+ const progress =
22
+ part.total > 0 ? ((part.completed / part.total) * 100).toFixed(1) : 0;
23
+
24
+ // Create simple progress bar
25
+ const barLength = 40;
26
+ const filledLength = Math.round((barLength * part.completed) / part.total);
27
+ const bar = "█".repeat(filledLength) + "░".repeat(barLength - filledLength);
28
+
29
+ // Display progress
30
+ process.stdout.write(`\r[${bar}] ${progress}% ${part.status}`);
31
+
32
+ if (part.status === "success") {
33
+ console.log("\nModel pulled successfully!");
34
+ break;
35
+ }
36
+ }
37
+
38
+ console.log("Done!");
39
+ };
40
+
41
+ await pull();
42
+
@@ -1,46 +1,46 @@
1
- import { addExchangeSchema, checkCandles } from "backtest-kit";
2
- import { singleshot } from "functools-kit";
3
- import ccxt from "ccxt";
4
-
5
- const getExchange = singleshot(async () => {
6
- const exchange = new ccxt.binance({
7
- options: {
8
- defaultType: "spot",
9
- adjustForTimeDifference: true,
10
- recvWindow: 60000,
11
- },
12
- enableRateLimit: true,
13
- });
14
- await exchange.loadMarkets();
15
- return exchange;
16
- });
17
-
18
- addExchangeSchema({
19
- exchangeName: "binance_exchange",
20
- getCandles: async (symbol, interval, since, limit) => {
21
- const exchange = await getExchange();
22
- const candles = await exchange.fetchOHLCV(
23
- symbol,
24
- interval,
25
- since.getTime(),
26
- limit,
27
- );
28
- return candles.map(([timestamp, open, high, low, close, volume]) => ({
29
- timestamp,
30
- open,
31
- high,
32
- low,
33
- close,
34
- volume,
35
- }));
36
- },
37
- });
38
-
39
- const from = new Date("2024-02-01T00:00:00Z");
40
- const to = new Date("2024-02-29T23:59:59Z");
41
- const symbol = "BTCUSDT";
42
- const exchangeName = "binance_exchange";
43
-
44
- await checkCandles({ exchangeName, from, to, interval: "1m", symbol });
45
- await checkCandles({ exchangeName, from, to, interval: "15m", symbol });
46
- await checkCandles({ exchangeName, from, to, interval: "4h", symbol });
1
+ import { addExchangeSchema, checkCandles } from "backtest-kit";
2
+ import { singleshot } from "functools-kit";
3
+ import ccxt from "ccxt";
4
+
5
+ const getExchange = singleshot(async () => {
6
+ const exchange = new ccxt.binance({
7
+ options: {
8
+ defaultType: "spot",
9
+ adjustForTimeDifference: true,
10
+ recvWindow: 60000,
11
+ },
12
+ enableRateLimit: true,
13
+ });
14
+ await exchange.loadMarkets();
15
+ return exchange;
16
+ });
17
+
18
+ addExchangeSchema({
19
+ exchangeName: "binance_exchange",
20
+ getCandles: async (symbol, interval, since, limit) => {
21
+ const exchange = await getExchange();
22
+ const candles = await exchange.fetchOHLCV(
23
+ symbol,
24
+ interval,
25
+ since.getTime(),
26
+ limit,
27
+ );
28
+ return candles.map(([timestamp, open, high, low, close, volume]) => ({
29
+ timestamp,
30
+ open,
31
+ high,
32
+ low,
33
+ close,
34
+ volume,
35
+ }));
36
+ },
37
+ });
38
+
39
+ const from = new Date("2024-02-01T00:00:00Z");
40
+ const to = new Date("2024-02-29T23:59:59Z");
41
+ const symbol = "BTCUSDT";
42
+ const exchangeName = "binance_exchange";
43
+
44
+ await checkCandles({ exchangeName, from, to, interval: "1m", symbol });
45
+ await checkCandles({ exchangeName, from, to, interval: "15m", symbol });
46
+ await checkCandles({ exchangeName, from, to, interval: "4h", symbol });
@@ -1,77 +1,77 @@
1
- import { addExchangeSchema } from "backtest-kit";
2
- import { singleshot, randomString } from "functools-kit";
3
- import { run, File, toMarkdown } from "@backtest-kit/pinets";
4
- import ccxt from "ccxt";
5
-
6
- const SIGNAL_SCHEMA = {
7
- position: "Signal",
8
- priceOpen: "Close",
9
- priceTakeProfit: "TakeProfit",
10
- priceStopLoss: "StopLoss",
11
- minuteEstimatedTime: "EstimatedTime",
12
- d_RSI: "d_RSI",
13
- d_EmaFast: "d_EmaFast",
14
- d_EmaSlow: "d_EmaSlow",
15
- d_EmaTrend: "d_EmaTrend",
16
- d_ATR: "d_ATR",
17
- d_Volume: "d_Volume",
18
- d_VolMA: "d_VolMA",
19
- d_VolSpike: "d_VolSpike",
20
- d_Mom: "d_Mom",
21
- d_MomUp: "d_MomUp",
22
- d_MomDown: "d_MomDown",
23
- d_TrendUp: "d_TrendUp",
24
- d_TrendDown: "d_TrendDown",
25
- d_LongCond: "d_LongCond",
26
- d_ShortCond: "d_ShortCond",
27
- d_BarsSinceSignal: "d_BarsSinceSignal",
28
- };
29
-
30
- const SIGNAL_ID = randomString();
31
-
32
- const getExchange = singleshot(async () => {
33
- const exchange = new ccxt.binance({
34
- options: {
35
- defaultType: "spot",
36
- adjustForTimeDifference: true,
37
- recvWindow: 60000,
38
- },
39
- enableRateLimit: true,
40
- });
41
- await exchange.loadMarkets();
42
- return exchange;
43
- });
44
-
45
- addExchangeSchema({
46
- exchangeName: "binance_exchange",
47
- getCandles: async (symbol, interval, since, limit) => {
48
- const exchange = await getExchange();
49
- const candles = await exchange.fetchOHLCV(
50
- symbol,
51
- interval,
52
- since.getTime(),
53
- limit,
54
- );
55
- return candles.map(([timestamp, open, high, low, close, volume]) => ({
56
- timestamp,
57
- open,
58
- high,
59
- low,
60
- close,
61
- volume,
62
- }));
63
- },
64
- });
65
-
66
- const plots = await run(
67
- File.fromPath("timeframe_15m.pine"),
68
- {
69
- symbol: "BTCUSDT",
70
- timeframe: "15m",
71
- limit: 60,
72
- },
73
- "binance_exchange",
74
- new Date("2025-09-23T16:00:00.000Z"),
75
- );
76
-
77
- console.log(await toMarkdown(SIGNAL_ID, plots, SIGNAL_SCHEMA));
1
+ import { addExchangeSchema } from "backtest-kit";
2
+ import { singleshot, randomString } from "functools-kit";
3
+ import { run, File, toMarkdown } from "@backtest-kit/pinets";
4
+ import ccxt from "ccxt";
5
+
6
+ const SIGNAL_SCHEMA = {
7
+ position: "Signal",
8
+ priceOpen: "Close",
9
+ priceTakeProfit: "TakeProfit",
10
+ priceStopLoss: "StopLoss",
11
+ minuteEstimatedTime: "EstimatedTime",
12
+ d_RSI: "d_RSI",
13
+ d_EmaFast: "d_EmaFast",
14
+ d_EmaSlow: "d_EmaSlow",
15
+ d_EmaTrend: "d_EmaTrend",
16
+ d_ATR: "d_ATR",
17
+ d_Volume: "d_Volume",
18
+ d_VolMA: "d_VolMA",
19
+ d_VolSpike: "d_VolSpike",
20
+ d_Mom: "d_Mom",
21
+ d_MomUp: "d_MomUp",
22
+ d_MomDown: "d_MomDown",
23
+ d_TrendUp: "d_TrendUp",
24
+ d_TrendDown: "d_TrendDown",
25
+ d_LongCond: "d_LongCond",
26
+ d_ShortCond: "d_ShortCond",
27
+ d_BarsSinceSignal: "d_BarsSinceSignal",
28
+ };
29
+
30
+ const SIGNAL_ID = randomString();
31
+
32
+ const getExchange = singleshot(async () => {
33
+ const exchange = new ccxt.binance({
34
+ options: {
35
+ defaultType: "spot",
36
+ adjustForTimeDifference: true,
37
+ recvWindow: 60000,
38
+ },
39
+ enableRateLimit: true,
40
+ });
41
+ await exchange.loadMarkets();
42
+ return exchange;
43
+ });
44
+
45
+ addExchangeSchema({
46
+ exchangeName: "binance_exchange",
47
+ getCandles: async (symbol, interval, since, limit) => {
48
+ const exchange = await getExchange();
49
+ const candles = await exchange.fetchOHLCV(
50
+ symbol,
51
+ interval,
52
+ since.getTime(),
53
+ limit,
54
+ );
55
+ return candles.map(([timestamp, open, high, low, close, volume]) => ({
56
+ timestamp,
57
+ open,
58
+ high,
59
+ low,
60
+ close,
61
+ volume,
62
+ }));
63
+ },
64
+ });
65
+
66
+ const plots = await run(
67
+ File.fromPath("timeframe_15m.pine"),
68
+ {
69
+ symbol: "BTCUSDT",
70
+ timeframe: "15m",
71
+ limit: 60,
72
+ },
73
+ "binance_exchange",
74
+ new Date("2025-09-23T16:00:00.000Z"),
75
+ );
76
+
77
+ console.log(await toMarkdown(SIGNAL_ID, plots, SIGNAL_SCHEMA));
@@ -1,68 +1,68 @@
1
- import { addExchangeSchema } from "backtest-kit";
2
- import { singleshot, randomString } from "functools-kit";
3
- import { run, File, toMarkdown } from "@backtest-kit/pinets";
4
- import ccxt from "ccxt";
5
-
6
- const SIGNAL_SCHEMA = {
7
- allowLong: "AllowLong",
8
- allowShort: "AllowShort",
9
- allowBoth: "AllowBoth",
10
- noTrades: "NoTrades",
11
- rsi: "RSI",
12
- adx: "ADX",
13
- d_MACDLine: "d_MACDLine",
14
- d_SignalLine: "d_SignalLine",
15
- d_MACDHist: "d_MACDHist",
16
- d_DIPlus: "d_DIPlus",
17
- d_DIMinus: "d_DIMinus",
18
- d_StrongTrend: "d_StrongTrend",
19
- };
20
-
21
- const SIGNAL_ID = randomString();
22
-
23
- const getExchange = singleshot(async () => {
24
- const exchange = new ccxt.binance({
25
- options: {
26
- defaultType: "spot",
27
- adjustForTimeDifference: true,
28
- recvWindow: 60000,
29
- },
30
- enableRateLimit: true,
31
- });
32
- await exchange.loadMarkets();
33
- return exchange;
34
- });
35
-
36
- addExchangeSchema({
37
- exchangeName: "binance_exchange",
38
- getCandles: async (symbol, interval, since, limit) => {
39
- const exchange = await getExchange();
40
- const candles = await exchange.fetchOHLCV(
41
- symbol,
42
- interval,
43
- since.getTime(),
44
- limit,
45
- );
46
- return candles.map(([timestamp, open, high, low, close, volume]) => ({
47
- timestamp,
48
- open,
49
- high,
50
- low,
51
- close,
52
- volume,
53
- }));
54
- },
55
- });
56
-
57
- const plots = await run(
58
- File.fromPath("timeframe_4h.pine"),
59
- {
60
- symbol: "BTCUSDT",
61
- timeframe: "4h",
62
- limit: 60,
63
- },
64
- "binance_exchange",
65
- new Date("2025-09-23T23:00:00.000Z"),
66
- );
67
-
68
- console.log(await toMarkdown(SIGNAL_ID, plots, SIGNAL_SCHEMA));
1
+ import { addExchangeSchema } from "backtest-kit";
2
+ import { singleshot, randomString } from "functools-kit";
3
+ import { run, File, toMarkdown } from "@backtest-kit/pinets";
4
+ import ccxt from "ccxt";
5
+
6
+ const SIGNAL_SCHEMA = {
7
+ allowLong: "AllowLong",
8
+ allowShort: "AllowShort",
9
+ allowBoth: "AllowBoth",
10
+ noTrades: "NoTrades",
11
+ rsi: "RSI",
12
+ adx: "ADX",
13
+ d_MACDLine: "d_MACDLine",
14
+ d_SignalLine: "d_SignalLine",
15
+ d_MACDHist: "d_MACDHist",
16
+ d_DIPlus: "d_DIPlus",
17
+ d_DIMinus: "d_DIMinus",
18
+ d_StrongTrend: "d_StrongTrend",
19
+ };
20
+
21
+ const SIGNAL_ID = randomString();
22
+
23
+ const getExchange = singleshot(async () => {
24
+ const exchange = new ccxt.binance({
25
+ options: {
26
+ defaultType: "spot",
27
+ adjustForTimeDifference: true,
28
+ recvWindow: 60000,
29
+ },
30
+ enableRateLimit: true,
31
+ });
32
+ await exchange.loadMarkets();
33
+ return exchange;
34
+ });
35
+
36
+ addExchangeSchema({
37
+ exchangeName: "binance_exchange",
38
+ getCandles: async (symbol, interval, since, limit) => {
39
+ const exchange = await getExchange();
40
+ const candles = await exchange.fetchOHLCV(
41
+ symbol,
42
+ interval,
43
+ since.getTime(),
44
+ limit,
45
+ );
46
+ return candles.map(([timestamp, open, high, low, close, volume]) => ({
47
+ timestamp,
48
+ open,
49
+ high,
50
+ low,
51
+ close,
52
+ volume,
53
+ }));
54
+ },
55
+ });
56
+
57
+ const plots = await run(
58
+ File.fromPath("timeframe_4h.pine"),
59
+ {
60
+ symbol: "BTCUSDT",
61
+ timeframe: "4h",
62
+ limit: 60,
63
+ },
64
+ "binance_exchange",
65
+ new Date("2025-09-23T23:00:00.000Z"),
66
+ );
67
+
68
+ console.log(await toMarkdown(SIGNAL_ID, plots, SIGNAL_SCHEMA));