πŸ“œ @backtest-kit/pinets

Run TradingView Pine Script strategies in Node.js self hosted enviroment. Execute your existing Pine Script indicators and generate trading signals - pure technical analysis with 1:1 syntax compatibility.

screenshot

Ask DeepWiki npm TypeScript

Port your TradingView strategies to backtest-kit with zero rewrite. Powered by PineTS - an open-source Pine Script transpiler and runtime.

πŸ“š Backtest Kit Docs | 🌟 GitHub | πŸ“œ PineTS Docs

  • πŸ“œ Pine Script v5/v6: Native TradingView syntax with 1:1 compatibility
  • 🎯 60+ Indicators: SMA, EMA, RSI, MACD, Bollinger Bands, ATR, Stochastic, and more
  • πŸ”Œ Backtest Integration: Seamless getCandles integration with temporal context
  • πŸ“ File or Code: Load .pine files or pass code strings directly
  • πŸ—ΊοΈ Plot Extraction: Flexible mapping from Pine plot() outputs to structured data
  • ⚑ Cached Execution: Memoized file reads for repeated strategy runs
  • πŸ›‘οΈ Type Safe: Full TypeScript support with generics for extracted data

@backtest-kit/pinets executes TradingView Pine Script and extracts trading signals for backtest-kit:

Function Description
getSignal() Run Pine Script and get structured ISignalDto (position, TP/SL, estimated time)
run() Run Pine Script and return raw plot data
extract() Extract values from plots with custom mapping
dumpPlotData() Dump plot data to markdown files for debugging
usePine() Register custom Pine constructor
setLogger() Configure custom logger
File.fromPath() Load Pine Script from .pine file
Code.fromString() Use inline Pine Script code
npm install @backtest-kit/pinets pinets backtest-kit

Create a Pine Script file (strategy.pine):

//@version=5
indicator("Signal Strategy 100 candles of 1H timeframe")

// Indicators - faster settings for 1H
rsi = ta.rsi(close, 10)
atr = ta.atr(10)
ema_fast = ta.ema(close, 7)
ema_slow = ta.ema(close, 16)

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

// Levels - tighter SL, wider TP for better RR
sl_long = close - atr * 1.5
tp_long = close + atr * 3
sl_short = close + atr * 1.5
tp_short = close - atr * 3

// Plots for extraction
plot(close, "Close")
plot(long_cond ? 1 : short_cond ? -1 : 0, "Signal")
plot(long_cond ? sl_long : sl_short, "StopLoss")
plot(long_cond ? tp_long : tp_short, "TakeProfit")
plot(60, "EstimatedTime")  // 1 hour in minutes

Use it in your strategy:

import { File, getSignal } from '@backtest-kit/pinets';
import { addStrategy } from 'backtest-kit';

addStrategy({
strategyName: 'pine-ema-cross',
interval: '5m',
riskName: 'demo',
getSignal: async (symbol) => {
const source = File.fromPath('strategy.pine');

return await getSignal(source, {
symbol,
timeframe: '1h',
limit: 100,
});
}
});

No file needed - pass Pine Script as a string:

import { Code, getSignal } from '@backtest-kit/pinets';

const pineScript = `
//@version=5
indicator("RSI Strategy")

rsi = ta.rsi(close, 14)
atr = ta.atr(14)

long_cond = rsi < 30
short_cond = rsi > 70

plot(close, "Close")
plot(long_cond ? 1 : short_cond ? -1 : 0, "Signal")
plot(close - atr * 2, "StopLoss")
plot(close + atr * 3, "TakeProfit")
`;

const source = Code.fromString(pineScript);
const signal = await getSignal(source, {
symbol: 'BTCUSDT',
timeframe: '15m',
limit: 100,
});

For advanced use cases, extract any Pine plot() with custom mapping:

import { File, run, extract } from '@backtest-kit/pinets';

const source = File.fromPath('indicators.pine');

const plots = await run(source, {
symbol: 'ETHUSDT',
timeframe: '1h',
limit: 200,
});

const data = await extract(plots, {
// Simple: plot name -> number
rsi: 'RSI',
macd: 'MACD',

// Advanced: with transform and lookback
prevRsi: {
plot: 'RSI',
barsBack: 1, // Previous bar value
},
trendStrength: {
plot: 'ADX',
transform: (v) => v > 25 ? 'strong' : 'weak',
},
});

// data = { rsi: 55.2, macd: 12.5, prevRsi: 52.1, trendStrength: 'strong' }

Dump plot data to markdown files for analysis and debugging:

import { File, run, dumpPlotData } from '@backtest-kit/pinets';

const source = File.fromPath('strategy.pine');

const plots = await run(source, {
symbol: 'BTCUSDT',
timeframe: '1h',
limit: 100,
});

// Dump plots to ./dump/ta directory
await dumpPlotData('signal-001', plots, 'ema-cross', './dump/ta');

Register a custom Pine constructor for advanced configurations:

import { usePine } from '@backtest-kit/pinets';
import { Pine } from 'pinets';

// Use custom Pine instance
usePine(Pine);

Configure logging for debugging:

import { setLogger } from '@backtest-kit/pinets';

setLogger({
log: (method, data) => console.log(`[${method}]`, data),
info: (method, data) => console.info(`[${method}]`, data),
error: (method, data) => console.error(`[${method}]`, data),
});

For getSignal() to work, your Pine Script must include these plots:

Plot Name Value Description
"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)

Using custom plots is also possible with run, it allows to reconfigure the mapper

Instead of rewriting your TradingView strategies:

// ❌ Without pinets (manual rewrite)
import { getCandles } from 'backtest-kit';
import { RSI, EMA, ATR } from 'technicalindicators';

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 });
const emaSlow = EMA.calculate({ values: closes, period: 21 });
// ... rewrite all your Pine Script logic in JS
// βœ… With pinets (copy-paste from TradingView)
import { File, getSignal } from '@backtest-kit/pinets';

const signal = await getSignal(File.fromPath('strategy.pine'), {
symbol: 'BTCUSDT',
timeframe: '5m',
limit: 100,
});

Benefits:

  • πŸ“œ Use existing TradingView Pine Script as-is
  • 🎯 60+ built-in indicators (no manual calculation)
  • ⚑ Same code for backtest and live trading
  • πŸ”„ Full time-series semantics with lookback support
  • πŸ›‘οΈ Type-safe extraction with TypeScript generics

Fork/PR on GitHub.

MIT Β© tripolskypetr