Data Providers

PineTS supports multiple market data providers out of the box. Each provider fetches OHLCV candle data and symbol metadata, which PineTS uses to run your indicators.


Overview

There are two ways to use a provider:

1. Singleton (from the Provider registry):

import { PineTS, Provider } from 'pinets';

const pine = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);

2. Direct instantiation (for provider-specific config):

import { PineTS, BinanceProvider } from 'pinets';

const binance = new BinanceProvider();
const pine = new PineTS(binance, 'BTCUSDT', 'D', 100);

Providers that require API keys must be configured before use, either via .configure() on the singleton or by passing config to the constructor.

Timeframe Aggregation

If a provider doesn’t natively support a requested timeframe, PineTS automatically selects the best sub-timeframe and aggregates candles. For example, requesting '45' (45 min) from Binance will fetch '15' minute candles and aggregate 3:1. This is handled transparently by the BaseProvider class.


Binance

The Binance provider fetches cryptocurrency market data. No API key required.

Quick Start

import { PineTS, Provider } from 'pinets';

// Fetch 100 daily BTC candles and compute SMA
const pine = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);

const { result, plots } = await pine.run(`
//@version=6
indicator("BTC SMA Cross")
sma20 = ta.sma(close, 20)
sma50 = ta.sma(close, 50)
plot(sma20, "SMA 20", color.blue)
plot(sma50, "SMA 50", color.red)
`);

Symbols

Format Example Market
SYMBOL BTCUSDT, ETHUSDC Spot

Supported Timeframes

'1', '3', '5', '15', '30', '60', '120', '240', 'D', 'W', 'M'

Timeframes not in this list (e.g., '45', '180') are automatically aggregated from smaller candles.

Features

  • No API key required
  • Auto-pagination for fetching >1000 candles
  • 5-minute cache for repeated identical requests
  • Dual endpoint fallback: tries api.binance.com, falls back to api.binance.us

Examples

import { PineTS, Provider, BinanceProvider } from 'pinets';

// Using the singleton
const pine1 = new PineTS(Provider.Binance, 'BTCUSDT', 'D', 100);

// Direct instantiation (equivalent)
const binance = new BinanceProvider();
const pine2 = new PineTS(binance, 'ETHUSDT', '1W', 50);

// With date range
const sDate = new Date('2024-01-01').getTime();
const eDate = new Date('2024-06-30').getTime();
const pine3 = new PineTS(Provider.Binance, 'BTCUSDT', 'D', undefined, sDate, eDate);

FMP (Financial Modeling Prep)

The FMP provider fetches stock, forex, and crypto data from Financial Modeling Prep. Requires an API key.

Quick Start

import { PineTS, Provider } from 'pinets';

// Configure with your API key
Provider.FMP.configure({ apiKey: 'your-api-key' });

// Fetch 200 daily AAPL candles
const pine = new PineTS(Provider.FMP, 'AAPL', 'D', 200);

const { result } = await pine.run(`
//@version=6
indicator("AAPL RSI")
rsi = ta.rsi(close, 14)
plot(rsi, "RSI")
hline(70, "Overbought")
hline(30, "Oversold")
`);

Configuration

// Option 1: Configure the singleton
Provider.FMP.configure({ apiKey: 'your-api-key' });

// Option 2: Direct instantiation with config
import { FMPProvider } from 'pinets';
const fmp = new FMPProvider({ apiKey: 'your-api-key' });
const pine = new PineTS(fmp, 'AAPL', 'D', 200);
Option Type Required Description
apiKey string Yes Your FMP API key
baseUrl string No Override the API base URL

Symbols

Format Example Market
Ticker AAPL, MSFT, SPY US Stocks
Crypto BTCUSD, ETHUSD Cryptocurrency
Forex EURUSD Currency pairs

Supported Timeframes

Tier Timeframes
Free 'D' (daily only)
Paid '1', '5', '15', '30', '60', '240'

Features

  • Exchange-aware session times: maps exchanges to IANA timezones (e.g., NYSE = America/New_York, 0930-1600)
  • Session-aware closeTime: correctly computes bar close times including early-close days
  • Profile caching: symbol metadata is fetched once and cached

Examples

import { PineTS, Provider, FMPProvider } from 'pinets';

// Configure singleton
Provider.FMP.configure({ apiKey: 'your-api-key' });

// US Stock
const pine1 = new PineTS(Provider.FMP, 'AAPL', 'D', 200);

// Crypto via FMP
const pine2 = new PineTS(Provider.FMP, 'BTCUSD', 'D', 100);

// Forex
const pine3 = new PineTS(Provider.FMP, 'EURUSD', 'D', 100);

// Direct instantiation (separate instance with its own config)
const fmp = new FMPProvider({ apiKey: 'different-api-key' });
const pine4 = new PineTS(fmp, 'MSFT', 'D', 100);

Alpaca

The Alpaca provider fetches US stock and crypto data from Alpaca Markets. Requires an API key and secret.

Quick Start

import { PineTS, Provider } from 'pinets';

// Configure with your credentials
Provider.Alpaca.configure({
    apiKey: 'PK...',
    apiSecret: '...',
    paper: true,
    feed: 'iex',
});

// Fetch 200 daily AAPL candles
const pine = new PineTS(Provider.Alpaca, 'AAPL', 'D', 200);

const { result } = await pine.run(`
//@version=6
indicator("AAPL Bollinger Bands")
[middle, upper, lower] = ta.bb(close, 20, 2)
plot(middle, "Middle", color.blue)
plot(upper, "Upper", color.green)
plot(lower, "Lower", color.red)
`);

Configuration

// Option 1: Configure the singleton
Provider.Alpaca.configure({
    apiKey: 'PK...',
    apiSecret: '...',
    paper: true,
    feed: 'sip',
});

// Option 2: Direct instantiation
import { AlpacaProvider } from 'pinets';
const alpaca = new AlpacaProvider({
    apiKey: 'PK...',
    apiSecret: '...',
    paper: true,
    feed: 'iex',
});
const pine = new PineTS(alpaca, 'AAPL', 'D', 200);
Option Type Required Default Description
apiKey string Yes - Alpaca API key ID (PK...)
apiSecret string Yes - Alpaca secret key
paper boolean No true true for paper trading, false for live
feed 'sip' \| 'iex' No 'sip' 'sip' = full market data (paid), 'iex' = IEX exchange (free)
dataUrl string No - Override the market data API URL
tradingUrl string No - Override the trading/asset API URL

Symbols

Format Example Market
Ticker AAPL, MSFT, SPY US Stocks
Crypto BTC/USD, ETH/USD Cryptocurrency (note the slash)

Note: Crypto symbols use slash notation (BTC/USD), not concatenated (BTCUSD).

Supported Timeframes

'1', '3', '5', '15', '30', '45', '60', '120', '180', '240', 'D', 'W', 'M'

Alpaca natively supports all common timeframes including '45' and '180' which other providers aggregate.

Features

  • Calendar-aware closeTime for stocks: uses Alpaca’s trading calendar API for exact session times (handles early closes and DST)
  • Fixed-duration closeTime for crypto: 24/7 markets use computed period boundaries
  • Auto-pagination: handles large requests (10,000+ candles) via page tokens
  • Asset and calendar caching: symbol info and trading calendar fetched once per session

Data Feed Options

Feed Cost Coverage
'sip' Paid ($9/month) Full consolidated market data from all US exchanges
'iex' Free IEX exchange data only (may have gaps or lag)

Examples

import { PineTS, Provider, AlpacaProvider } from 'pinets';

// Configure singleton
Provider.Alpaca.configure({
    apiKey: 'PK...',
    apiSecret: '...',
    paper: true,
    feed: 'iex',
});

// US Stock
const pine1 = new PineTS(Provider.Alpaca, 'AAPL', 'D', 200);

// Crypto (note the slash notation)
const pine2 = new PineTS(Provider.Alpaca, 'BTC/USD', 'D', 100);

// Direct instantiation
const alpaca = new AlpacaProvider({
    apiKey: 'PK...',
    apiSecret: '...',
    feed: 'sip',
});
const pine3 = new PineTS(alpaca, 'MSFT', '60', 500);

Custom Data

You can bypass providers entirely by passing your own OHLCV data array:

import { PineTS } from 'pinets';

const customData = [
    {
        openTime: new Date('2024-01-01').getTime(),
        open: 42000,
        high: 43000,
        low: 41500,
        close: 42500,
        volume: 1234.56,
        closeTime: new Date('2024-01-02').getTime(),
    },
    {
        openTime: new Date('2024-01-02').getTime(),
        open: 42500,
        high: 44000,
        low: 42000,
        close: 43800,
        volume: 2345.67,
        closeTime: new Date('2024-01-03').getTime(),
    },
    // ... more candles
];

const pine = new PineTS(customData);
const { result } = await pine.run(`
//@version=6
indicator("Custom Data SMA")
plot(ta.sma(close, 10))
`);

Data Format

Each candle object must include:

Field Type Required Description
open number Yes Opening price
high number Yes Highest price
low number Yes Lowest price
close number Yes Closing price
volume number Yes Trading volume
openTime number No Bar open time (ms timestamp)
closeTime number No Bar close time (ms timestamp)

If openTime / closeTime are omitted, time-dependent functions (time, hour, dayofmonth, etc.) will not work correctly.


Custom Providers

You can create your own provider by implementing the IProvider interface or extending BaseProvider:

import { BaseProvider, PineTS } from 'pinets';
import type { ISymbolInfo, Kline } from 'pinets';

class MyProvider extends BaseProvider {
    constructor() {
        super({ requiresApiKey: false, providerName: 'MyProvider' });
    }

    protected getSupportedTimeframes() {
        return new Set(['1', '5', '15', '60', 'D']);
    }

    protected async _getMarketDataNative(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<Kline[]> {
        // Fetch data from your API
        const response = await fetch(`https://my-api.com/candles?symbol=${tickerId}&tf=${timeframe}`);
        const data = await response.json();

        return data.map((candle) => ({
            openTime: candle.time,
            open: candle.o,
            high: candle.h,
            low: candle.l,
            close: candle.c,
            volume: candle.v,
            closeTime: candle.time + 86400000, // next bar open
        }));
    }

    async getSymbolInfo(tickerId: string): Promise<ISymbolInfo> {
        // Return symbol metadata
        return { ticker: tickerId /* ... */ } as ISymbolInfo;
    }
}

// Use it
const provider = new MyProvider();
const pine = new PineTS(provider, 'AAPL', 'D', 100);

By extending BaseProvider, you get automatic timeframe aggregation for free. If you request a timeframe not in getSupportedTimeframes(), the base class will automatically fetch a supported sub-timeframe and aggregate.

You can also add your provider to the global registry for convenience:

import { Provider } from 'pinets';

Provider['MyProvider'] = new MyProvider();
// Now available as Provider.MyProvider

Timeframe Reference

Standard timeframe strings used across all providers:

String Description Period
'1' 1 minute Intraday
'3' 3 minutes Intraday
'5' 5 minutes Intraday
'15' 15 minutes Intraday
'30' 30 minutes Intraday
'45' 45 minutes Intraday
'60' 1 hour Intraday
'120' 2 hours Intraday
'180' 3 hours Intraday
'240' or '4H' 4 hours Intraday
'D' or '1D' 1 day Daily
'W' or '1W' 1 week Weekly
'M' or '1M' 1 month Monthly

Provider Support Matrix

Timeframe Binance FMP (Free) FMP (Paid) Alpaca
'1' Native - Native Native
'3' Native - - Native
'5' Native - Native Native
'15' Native - Native Native
'30' Native - Native Native
'45' Aggregated - - Native
'60' Native - Native Native
'120' Native - - Native
'180' Aggregated - - Native
'240' Native - Native Native
'D' Native Native Native Native
'W' Native - - Native
'M' Native - - Native

Native = provider fetches directly. Aggregated = PineTS fetches smaller candles and combines them. - = not available.