@backtest-kit/pinets

中文文档
23.7.0发布于 2026/10/29MITTypeScriptESM + CJS近 30 天下载 2750

Run TradingView Pine Script strategies in Node.js self hosted environment. Execute existing Pine Script indicators and generate trading signals with 1:1 syntax compatibility via PineTS runtime.

原始使用文档
文档目录9 节
  1. Why
  2. Quick start
  3. Required plots for getSignal()
  4. Custom extraction
  5. Debugging & customization
  6. Why not just rewrite it in JS?
  7. API reference
  8. 🤝 Contribute
  9. 📜 License

📜 @backtest-kit/pinets

Run TradingView Pine Script v5/v6 in a self-hosted Node.js environment for backtest-kit. Execute your existing .pine indicators with 1:1 syntax compatibility and extract structured trading signals — no TradingView account, no rewrite.

screenshot

Ask DeepWiki npm TypeScript

Powered by PineTS — an open-source Pine Script transpiler & runtime.

📚 Docs · 🌟 Reference implementation · 📜 PineTS Docs · 🐙 GitHub

npm install @backtest-kit/pinets pinets backtest-kit

Why

Your edge already exists as a TradingView Pine Script — rewriting it in JavaScript is error-prone busywork that drifts from the original. This package runs the .pine as-is inside backtest-kit's execution context: getCandles feeds it look-ahead-safe data, 60+ indicators are built in (no manual TA math), and the same script powers both backtest and live. You map its plot() outputs to a structured signal and you're done.

  • 📜 Pine Script v5/v6 — native TradingView syntax, 1:1 compatibility.
  • 🎯 60+ indicators — SMA, EMA, RSI, MACD, Bollinger Bands, ATR, Stochastic, ADX, …
  • 🔌 Engine integration — runs on backtest-kit's temporal context (no look-ahead).
  • 📁 File or inline — load a .pine file or pass a code string.
  • 🗺️ Flexible extraction — map any plot() to typed data, with lookback & transforms.
  • ⚡ Cached execution — memoized file reads for repeated runs.
  • 🛡️ Type-safe — full generics on extracted data.

Quick start

A Pine Script just needs to expose a few named plots; getSignal maps them to an ISignalDto.

strategy.pine + getSignal
//@version=5
indicator("EMA cross — 1H, 100 candles")

rsi = ta.rsi(close, 10)
atr = ta.atr(10)
ema_fast = ta.ema(close, 7)
ema_slow = ta.ema(close, 16)

long_cond  = ta.crossover(ema_fast, ema_slow)  and rsi < 65
short_cond = ta.crossunder(ema_fast, ema_slow) and rsi > 35

plot(close, "Close")
plot(long_cond ? 1 : short_cond ? -1 : 0, "Signal")
plot(long_cond ? close - atr*1.5 : close + atr*1.5, "StopLoss")
plot(long_cond ? close + atr*3   : close - atr*3,   "TakeProfit")
plot(60, "EstimatedTime")  // minutes
import { File, getSignal } from '@backtest-kit/pinets';
import { addStrategy } from 'backtest-kit';

addStrategy({
  strategyName: 'pine-ema-cross', interval: '5m', riskName: 'demo',
  getSignal: async (symbol) =>
    getSignal(File.fromPath('strategy.pine'), { symbol, timeframe: '1h', limit: 100 }),
});

Inline code needs no file:

import { Code, getSignal } from '@backtest-kit/pinets';
const signal = await getSignal(
  Code.fromString(`//@version=5\nindicator("RSI")\nrsi=ta.rsi(close,14)\natr=ta.atr(14)\nplot(close,"Close")\nplot(rsi<30?1:rsi>70?-1:0,"Signal")\nplot(close-atr*2,"StopLoss")\nplot(close+atr*3,"TakeProfit")`),
  { symbol: 'BTCUSDT', timeframe: '15m', limit: 100 });

Required plots for getSignal()

Plot nameValueMeaning
"Signal"1 / -1 / 0Long / Short / no signal
"Close"closeEntry price
"StopLoss"priceStop-loss level
"TakeProfit"priceTake-profit level
"EstimatedTime"minutesHold duration (optional, default 240)

Custom plots are fine too — use run + extract to remap them (below).


Custom extraction

run() returns raw plot data; extract() / extractRows() pull it into typed shapes with optional lookback and transforms.

extract() — latest bar values
import { File, run, extract } from '@backtest-kit/pinets';

const plots = await run(File.fromPath('indicators.pine'), { symbol: 'ETHUSDT', timeframe: '1h', limit: 200 });
const data = await extract(plots, {
  rsi: 'RSI', macd: 'MACD',                                  // plot name → number
  prevRsi: { plot: 'RSI', barsBack: 1 },                     // previous bar
  trendStrength: { plot: 'ADX', transform: (v) => v > 25 ? 'strong' : 'weak' },
});
// { rsi: 55.2, macd: 12.5, prevRsi: 52.1, trendStrength: 'strong' }
extractRows() — every bar, timestamped
import { File, run, extractRows } from '@backtest-kit/pinets';

const plots = await run(File.fromPath('indicators.pine'), { symbol: 'ETHUSDT', timeframe: '1h', limit: 200 });
const rows = await extractRows(plots, {
  rsi: 'RSI', macd: 'MACD',
  prevRsi: { plot: 'RSI', barsBack: 1 },
  trend: { plot: 'ADX', transform: (v) => v > 25 ? 'strong' : 'weak' },
});
// rows[1] = { timestamp: '2024-01-01T01:00:00.000Z', rsi: 52.1, macd: -1.5, prevRsi: 48.3, trend: 'weak' }

extract() vs extractRows(): single latest object vs array of all bars; missing value 0 vs null; no timestamp vs ISO timestamp; barsBack from the last bar vs from each bar's own index. Use extract for signal generation at the current bar, extractRows for dataset export / historical analysis.

toSignalDto() — turn extracted values into a signal

The helper getSignal uses internally, exposed for custom graphs (e.g. multi-timeframe via @backtest-kit/graph). Maps position 1/-1/0 → long/short/null, carrying TP/SL/estimated-time, with an optional explicit priceOpen:

import { run, extract, toSignalDto } from '@backtest-kit/pinets';
import { randomString } from 'functools-kit';

const plots = await run(File.fromPath('strategy.pine'), { symbol, timeframe: '15m', limit: 100 });
const data  = await extract(plots, { position: 'Signal', priceTakeProfit: 'TakeProfit', priceStopLoss: 'StopLoss', minuteEstimatedTime: 'EstimatedTime' });
const signal = toSignalDto(randomString(), data, null); // ISignalDto | null

Debugging & customization

dumpPlotData / markdown — inspect plot output
import { File, run, dumpPlotData, toMarkdown } from '@backtest-kit/pinets';
const plots = await run(File.fromPath('strategy.pine'), { symbol: 'BTCUSDT', timeframe: '1h', limit: 100 });
await dumpPlotData('signal-001', plots, 'ema-cross', './dump/ta');  // → markdown files
const md = await toMarkdown(plots);                                 // markdown table as a string
usePine / useIndicator / setLogger — swap internals
import { usePine, useIndicator, setLogger } from '@backtest-kit/pinets';
import { Pine } from 'pinets';

usePine(Pine);                       // register a custom Pine constructor
useIndicator(MyIndicatorCtor);       // register a custom indicator constructor
setLogger({ log: (m, d) => console.log(`[${m}]`, d), info: () => {}, error: console.error });

Why not just rewrite it in JS?

The difference
// ❌ Manual rewrite — re-derive every indicator, drift from the original
const candles = await getCandles('BTCUSDT', '5m', 100);
const closes = candles.map(c => c.close);
const rsi = RSI.calculate({ values: closes, period: 14 });
const emaFast = EMA.calculate({ values: closes, period: 9 });
// …port all the Pine logic by hand

// ✅ With pinets — copy the .pine straight from TradingView
const signal = await getSignal(File.fromPath('strategy.pine'), { symbol: 'BTCUSDT', timeframe: '5m', limit: 100 });

Use existing scripts as-is · 60+ indicators with no manual math · same code backtest & live · full time-series lookback semantics · type-safe extraction.


API reference

ExportDescription
getSignal(source, opts)Run Pine Script → structured ISignalDto (position, TP/SL, estimated time)
run(source, opts)Run Pine Script → raw plot data
extract(plots, mapping)Latest-bar values with custom mapping (missing → 0)
extractRows(plots, mapping)All bars as timestamped rows (missing → null)
toSignalDto(id, data, priceOpen?)Map extracted { position, … } → ISignalDto | null
dumpPlotData(id, plots, name, dir)Dump plot data to markdown files
toMarkdown(plots) · markdown(...)Render plots as a markdown table
File.fromPath(path)Load Pine Script from a .pine file (memoized)
Code.fromString(code)Use inline Pine Script
usePine(ctor) · useIndicator(ctor)Register a custom Pine / indicator constructor
setLogger(logger)Custom logger
libThe internal IoC container for advanced use
AXIS_SYMBOLAxis provider symbol token

Options (run/getSignal): symbol, timeframe (Pine candle interval), limit (candles to fetch — must cover indicator warmup; pre-warmup bars are N/A).

Types: PlotExtractConfig, PlotMapping, ExtractedData, ExtractedDataRow, CandleModel, PlotModel, PlotRecord, SymbolInfoModel, ILogger, IPine/TPineCtor, IIndicator/TIndicatorCtor, IProvider.

Complete source map

classes/{Code,File}.ts · function/{pine,indicator,run,extract,setup,strategy,dump,markdown}.function.ts · helpers/toSignalDto.ts · model/{Candle,Plot,SymbolInfo}.model.ts · interface/{Logger,Pine,Indicator,Provider}.interface.ts · lib/ IoC (core/{di,provide,types}, services/{base/LoggerService, cache/PineCacheService, connection/{Pine,Indicator}ConnectionService, context/ExchangeContextService, data/PineDataService, job/PineJobService, markdown/PineMarkdownService, provider/{Axis,Candle}ProviderService}). Every export above maps to one of these — nothing in src/ is undocumented.

🤝 Contribute

Fork / PR on GitHub.

📜 License

MIT © tripolskypetr

版本与发布记录

@backtest-kit/pinets 共发布 179 个版本,首次发布于 2026/01/24;过去 12 个月发布了 179 个版本。

版本发布日期距今
23.7.0latest2026/10/299 天前
23.6.02026/10/299 天前
23.5.02026/10/2613 天前
23.4.02026/10/2613 天前
23.3.02026/10/2514 天前
23.2.02026/10/2414 天前

关于 @backtest-kit/pinets 的常见问题

@backtest-kit/pinets 是什么?

Run TradingView Pine Script strategies in Node.js self hosted environment. Execute existing Pine Script indicators and generate trading signals with 1:1 syntax compatibility via PineTS runtime.

如何安装 @backtest-kit/pinets?

在项目目录运行 npm install @backtest-kit/pinets,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:pinets(^0.9.34)、typescript(^5.0.0)、backtest-kit(^23.7.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@backtest-kit/pinets 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@backtest-kit/pinets 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@backtest-kit/pinets 可以商用吗?

@backtest-kit/pinets 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@backtest-kit/pinets 的下载量有多少?

根据 npm 官方下载统计,@backtest-kit/pinets 在 2026/10/08 – 2026/11/06 这 30 天内被下载 2,750 次,日均约 92 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@backtest-kit/pinets 还在维护吗?

最新版本 23.7.0 发布于 2026/10/29(9 天前);至今共发布 179 个版本,其中过去 12 个月发布了 179 个;首次发布于 2026/01/24。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。