npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@backtest-kit/pinets

v15.4.0

Published

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.

Downloads

3,449

Readme

📜 @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.

//@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 name | Value | Meaning | |-----------|-------|---------| | "Signal" | 1 / -1 / 0 | Long / Short / no signal | | "Close" | close | Entry price | | "StopLoss" | price | Stop-loss level | | "TakeProfit" | price | Take-profit level | | "EstimatedTime" | minutes | Hold 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.

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' }
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.

The helper getSignal uses internally, exposed for custom graphs (e.g. multi-timeframe via @backtest-kit/graph). Maps position 1/-1/0long/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

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
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?

// ❌ 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

| Export | Description | |--------|-------------| | 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 | | lib | The internal IoC container for advanced use | | AXIS_SYMBOL | Axis 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.

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