### Create Project Directory and Clone TradingView Charting Library
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Integration
These bash commands are used to create a new directory for the project, navigate into it, and clone the TradingView Charting Library from its GitHub repository. It also includes a step to rename the cloned directory for easier reference.
```bash
mkdir chart
cd chart
git clone https://github.com/tradingview/charting_library
mv charting_library charting_library_clonned_data
```
--------------------------------
### Serve Static Files using npx serve
Source: https://github.com/tradingview/charting-library-tutorial/blob/master/README.md
Command to start a local development server using npx serve, which is used to serve the static files for the project.
```bash
npx serve
```
--------------------------------
### Mock Datafeed Implementation for TradingView Widget (JavaScript)
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Integration
This JavaScript code provides a mock implementation for the TradingView datafeed. It exports an object with placeholder methods like onReady, searchSymbols, resolveSymbol, getBars, subscribeBars, and unsubscribeBars. Each method logs a message to the console indicating it has been called, serving as a basic structure before actual data fetching logic is implemented.
```javascript
export default {
onReady: (callback) => {
console.log('[onReady]: Method call');
},
searchSymbols: (userInput, exchange, symbolType, onResultReadyCallback) => {
console.log('[searchSymbols]: Method call');
},
resolveSymbol: (symbolName, onSymbolResolvedCallback, onResolveErrorCallback) => {
console.log('[resolveSymbol]: Method call', symbolName);
},
getBars: (symbolInfo, resolution, from, to, onHistoryCallback, onErrorCallback, firstDataRequest) => {
console.log('[getBars]: Method call', symbolInfo);
},
subscribeBars: (symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback) => {
console.log('[subscribeBars]: Method call with subscribeUID:', subscribeUID);
},
unsubscribeBars: (subscriberUID) => {
console.log('[unsubscribeBars]: Method call with subscriberUID:', subscriberUID);
},
};
```
--------------------------------
### Initialize TradingView Charting Library Widget (JavaScript)
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Integration
This JavaScript code initializes the TradingView widget with essential configurations. It imports a Datafeed module (to be implemented later) and sets mandatory properties such as the default symbol, interval, fullscreen mode, the container ID, the datafeed object, and the path to the charting library.
```javascript
// Datafeed implementation, will be added later
import Datafeed from './datafeed.js';
window.tvWidget = new TradingView.widget({
symbol: 'Bitfinex:BTC/USD', // default symbol
interval: '1D', // default interval
fullscreen: true, // displays the chart in the fullscreen mode
container_id: 'tv_chart_container',
datafeed: Datafeed,
library_path: 'charting_library_clonned_data/charting_library/',
});
```
--------------------------------
### Configure Datafeed Initialization (onReady) - JavaScript
Source: https://context7.com/tradingview/charting-library-tutorial/llms.txt
The onReady method initializes the datafeed configuration for the TradingView widget. It specifies supported resolutions, exchanges, and symbol types, and is the first method called during chart initialization. This example shows how to define these configurations.
```javascript
import Datafeed from './datafeed.js';
// Initialize TradingView widget with custom datafeed
window.tvWidget = new TradingView.widget({
symbol: 'Bitfinex:BTC/USD',
interval: '1D',
fullscreen: true,
container: 'tv_chart_container',
datafeed: Datafeed,
library_path: '../charting_library_cloned_data/charting_library/',
});
// Configuration returned by onReady
const configurationData = {
supported_resolutions: ['1', '5', '15', '60', '180', '1D', '1W', '1M'],
exchanges: [
{
value: 'Bitfinex',
name: 'Bitfinex',
desc: 'Bitfinex',
},
{
value: 'Kraken',
name: 'Kraken',
desc: 'Kraken bitcoin exchange',
}
],
symbols_types: [
{
name: 'crypto',
value: 'crypto',
}
],
};
// Implementation
export default {
onReady: (callback) => {
console.log('[onReady]: Method call');
setTimeout(() => callback(configurationData));
},
};
```
--------------------------------
### HTML Structure for TradingView Charting Library Integration
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Integration
This HTML code sets up the basic structure for a TradingView charting application. It includes a title, references the Charting Library's minified JavaScript file, imports a custom datafeed module, and defines a div element that will serve as the container for the chart widget.
```html
TradingView Charting Library example
```
--------------------------------
### Implement Symbol Search (searchSymbols) - JavaScript
Source: https://context7.com/tradingview/charting-library-tutorial/llms.txt
The searchSymbols method allows users to find trading pairs across configured exchanges. It filters results based on user input, exchange, and symbol type, returning matching symbols. This example fetches all symbols from CryptoCompare and filters them.
```javascript
import { makeApiRequest, generateSymbol } from './helpers.js';
// Fetch all available trading pairs from CryptoCompare API
async function getAllSymbols() {
const data = await makeApiRequest('data/v3/all/exchanges');
let allSymbols = [];
for (const exchange of configurationData.exchanges) {
if (data.Data[exchange.value]) {
const pairs = data.Data[exchange.value].pairs;
for (const leftPairPart of Object.keys(pairs)) {
const symbols = pairs[leftPairPart].map(rightPairPart => {
const symbol = generateSymbol(exchange.value, leftPairPart, rightPairPart);
return {
symbol: symbol.short,
ticker: symbol.full,
description: symbol.short,
exchange: exchange.value,
type: 'crypto'
};
});
allSymbols = [...allSymbols, ...symbols];
}
}
}
return allSymbols;
}
// Search implementation with filtering
searchSymbols: async (userInput, exchange, symbolType, onResultReadyCallback) => {
console.log('[searchSymbols]: Method call');
const symbols = await getAllSymbols();
const newSymbols = symbols.filter(symbol => {
const isExchangeValid = exchange === '' || symbol.exchange === exchange;
const isFullSymbolContainsInput = symbol.ticker
.toLowerCase()
.indexOf(userInput.toLowerCase()) !== -1;
return isExchangeValid && isFullSymbolContainsInput;
});
onResultReadyCallback(newSymbols);
}
// Helper function to generate symbol identifiers
export function generateSymbol(exchange, fromSymbol, toSymbol) {
const short = `${fromSymbol}/${toSymbol}`;
return {
short,
full: `${exchange}:${short}`,
};
}
```
--------------------------------
### Clone Repository using Git
Source: https://github.com/tradingview/charting-library-tutorial/blob/master/README.md
Command to clone the tutorial repository. It's recommended to use this repository as a submodule in your own project for real-world applications.
```bash
git clone https://github.com/tradingview/charting-library-tutorial.git
```
--------------------------------
### Initialize Git Submodule
Source: https://github.com/tradingview/charting-library-tutorial/blob/master/README.md
Command to initialize and update Git submodules after cloning the repository, ensuring all necessary library components are downloaded.
```bash
git submodule update --init --recursive
```
--------------------------------
### Implement onReady Method for Datafeed Configuration (JavaScript)
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Datafeed-Implementation
This JavaScript code implements the 'onReady' method for a datafeed object. It's designed to be called by the Charting Library to retrieve the datafeed's configuration. The method simulates an asynchronous response using setTimeout to return the 'configurationData'.
```javascript
const configurationData = {
// ... configuration details here
};
export default {
onReady: (callback) => {
console.log('[onReady]: Method call');
setTimeout(() => callback(configurationData));
},
};
```
--------------------------------
### Make API Requests with Fetch (JavaScript)
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Datafeed-Implementation
This function makes asynchronous requests to the CryptoCompare API. It takes a path as input and returns the JSON response. Error handling is included to catch network or API errors.
```javascript
export async function makeApiRequest(path) {
try {
const response = await fetch(`https://min-api.cryptocompare.com/${path}`);
return response.json();
} catch(error) {
throw new Error(`CryptoCompare request error: ${error.status}`);
}
}
```
--------------------------------
### Configure Datafeed with Supported Resolutions and Exchanges (JavaScript)
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Datafeed-Implementation
This snippet defines the configuration data for a datafeed, including supported time resolutions (e.g., '1D' for daily), a list of exchanges with their values and display names, and supported symbol types. This data is returned by the 'onReady' method to the Charting Library.
```javascript
const configurationData = {
supported_resolutions: ['1D', '1W', '1M'],
exchanges: [
{
value: 'Bitfinex',
name: 'Bitfinex',
desc: 'Bitfinex',
},
{
value: 'Kraken',
name: 'Kraken',
desc: 'Kraken bitcoin exchange',
},
],
symbols_types: [
{
name: 'crypto',
value: 'crypto',
},
],
};
```
--------------------------------
### Initialize TradingView Widget and Manage Chart Lifecycle Events
Source: https://context7.com/tradingview/charting-library-tutorial/llms.txt
Initializes the TradingView widget with specified symbol, interval, and container. It also subscribes to chart interval changes, resetting the cache and data to ensure fresh data is loaded upon user interaction. Requires the TradingView charting library and a datafeed implementation.
```javascript
// main.js
import Datafeed from './datafeed.js';
window.tvWidget = new TradingView.widget({
symbol: 'Bitfinex:BTC/USD',
interval: '1D',
fullscreen: true,
container: 'tv_chart_container',
datafeed: Datafeed,
library_path: '../charting_library_cloned_data/charting_library/',
});
// Handle chart ready event and subscribe to interval changes
tvWidget.onChartReady(() => {
console.log('Chart is ready');
const chart = tvWidget.activeChart();
// Clear cache when user changes timeframe
chart.onIntervalChanged().subscribe(null, () => {
tvWidget.resetCache();
chart.resetData();
});
});
window.frames[0].focus();
```
--------------------------------
### Load All Symbols for Exchanges (JavaScript)
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Datafeed-Implementation
This asynchronous function retrieves all trading pairs from all configured exchanges using the CryptoCompare API. It iterates through exchanges and their pairs to construct a comprehensive list of symbols.
```javascript
import { makeApiRequest, generateSymbol } from './helpers.js';
// ...
async function getAllSymbols() {
const data = await makeApiRequest('data/v3/all/exchanges');
let allSymbols = [];
for (const exchange of configurationData.exchanges) {
const pairs = data.Data[exchange.value].pairs;
for (const leftPairPart of Object.keys(pairs)) {
const symbols = pairs[leftPairPart].map(rightPairPart => {
const symbol = generateSymbol(exchange.value, leftPairPart, rightPairPart);
return {
symbol: symbol.short,
full_name: symbol.full,
description: symbol.short,
exchange: exchange.value,
ticker: symbol.full,
type: 'crypto',
};
});
allSymbols = [...allSymbols, ...symbols];
}
}
return allSymbols;
}
```
--------------------------------
### Real-time Bar Streaming with subscribeBars and unsubscribeBars (JavaScript)
Source: https://context7.com/tradingview/charting-library-tutorial/llms.txt
Implements real-time bar streaming for TradingView charts. `subscribeBars` sets up WebSocket subscriptions for live data, and `unsubscribeBars` handles cleanup. It utilizes external `subscribeOnStream` and `unsubscribeFromStream` functions and manages a cache for the last bars. This method is crucial for displaying live price movements on the chart.
```javascript
import { subscribeOnStream, unsubscribeFromStream } from './streaming.js';
const lastBarsCache = new Map();
subscribeBars: (symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback) => {
console.log('[subscribeBars]: Method call with subscriberUID:', subscriberUID);
subscribeOnStream(
symbolInfo,
resolution,
onRealtimeCallback,
subscriberUID,
onResetCacheNeededCallback,
lastBarsCache.get(symbolInfo.ticker)
);
}
unsubscribeBars: (subscriberUID) => {
console.log('[unsubscribeBars]: Method call with subscriberUID:', subscriberUID);
unsubscribeFromStream(subscriberUID);
}
```
--------------------------------
### Helper: Make API Request - JavaScript
Source: https://context7.com/tradingview/charting-library-tutorial/llms.txt
Handles making authenticated API requests to the CryptoCompare data service. It constructs the full URL with an API key and fetches JSON data, returning the parsed response or throwing an error on failure. This function is essential for retrieving symbol metadata and historical data.
```javascript
// Helper function for API requests with authentication
export async function makeApiRequest(path) {
try {
const url = new URL(`https://min-api.cryptocompare.com/${path}`);
url.searchParams.append('api_key', apiKey);
const response = await fetch(url.toString());
return response.json();
} catch (error) {
throw new Error(`CryptoCompare request error: ${error.status}`);
}
}
```
--------------------------------
### Establish WebSocket Connection and Event Handlers (JavaScript)
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Streaming-Implementation
This JavaScript code initializes a WebSocket connection using Socket.IO to a specified streaming server. It includes handlers for connection, disconnection, and errors, and exports functions for subscribing and unsubscribing from data streams.
```javascript
const socket = io('wss://streamer.cryptocompare.com');
socket.on('connect', () => {
console.log('[socket] Connected');
});
socket.on('disconnect', (reason) => {
console.log('[socket] Disconnected:', reason);
});
socket.on('error', (error) => {
console.log('[socket] Error:', error);
});
export function subscribeOnStream() {
// todo
}
export function unsubscribeFromStream() {
// todo
}
```
--------------------------------
### Implement resolveSymbol for TradingView Library (JavaScript)
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Datafeed-Implementation
This implementation of resolveSymbol fetches all available symbols and finds the one matching the requested symbolName. It then constructs and returns the symbolInfo object required by the TradingView library.
```javascript
export default {
// ...
resolveSymbol: async (
symbolName,
onSymbolResolvedCallback,
onResolveErrorCallback
) => {
console.log('[resolveSymbol]: Method call', symbolName);
const symbols = await getAllSymbols();
const symbolItem = symbols.find(({ full_name }) => full_name === symbolName);
if (!symbolItem) {
console.log('[resolveSymbol]: Cannot resolve symbol', symbolName);
onResolveErrorCallback('cannot resolve symbol');
return;
}
const symbolInfo = {
name: symbolItem.symbol,
description: symbolItem.description,
type: symbolItem.type,
session: '24x7',
timezone: 'Etc/UTC',
ticker: symbolItem.full_name,
exchange: symbolItem.exchange,
minmov: 1,
pricescale: 100,
has_intraday: false,
has_no_volume: true,
has_weekly_and_monthly: false,
supported_resolutions: configurationData.supported_resolutions,
volume_precision: 2,
data_status: 'streaming',
};
console.log('[resolveSymbol]: Symbol resolved', symbolName);
onSymbolResolvedCallback(symbolInfo);
},
// ...
};
```
--------------------------------
### Manage WebSocket Stream for Real-Time Crypto Data in JavaScript
Source: https://context7.com/tradingview/charting-library-tutorial/llms.txt
This JavaScript code manages a WebSocket connection to CryptoCompare for real-time cryptocurrency trade data. It handles subscription multiplexing, processes incoming trade messages to update chart bars, and calculates bar times based on resolution. Dependencies include helper functions for symbol parsing and an API key. It takes symbol information, resolution, and callbacks as input and outputs real-time bar updates.
```javascript
// streaming.js
import { parseFullSymbol, apiKey } from './helpers.js';
const socket = new WebSocket('wss://streamer.cryptocompare.com/v2?api_key=' + apiKey);
const channelToSubscription = new Map();
socket.addEventListener('open', () => {
console.log('[socket] Connected');
});
socket.addEventListener('error', (error) => {
console.log('[socket] Error:', error);
});
// Calculate next bar start time based on resolution
function getNextBarTime(barTime, resolution) {
const date = new Date(barTime);
const interval = parseInt(resolution);
if (resolution === '1D') {
date.setUTCDate(date.getUTCDate() + 1);
date.setUTCHours(0, 0, 0, 0);
} else if (!isNaN(interval)) {
date.setUTCMinutes(date.getUTCMinutes() + interval);
}
return date.getTime();
}
// Process incoming trade messages
socket.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
const {
TYPE: eventType,
M: exchange,
FSYM: fromSymbol,
TSYM: toSymbol,
TS: tradeTime,
P: tradePrice,
Q: tradeVolume,
} = data;
// Handle Trade events only (TYPE === 0)
if (parseInt(eventType) !== 0) {
return;
}
const channelString = `0~${exchange}~${fromSymbol}~${toSymbol}`;
const subscriptionItem = channelToSubscription.get(channelString);
if (subscriptionItem === undefined) {
return;
}
const lastBar = subscriptionItem.lastBar;
const nextBarTime = getNextBarTime(lastBar.time, subscriptionItem.resolution);
let bar;
// Create new bar if trade time exceeds current bar period
if (tradeTime * 1000 >= nextBarTime) {
bar = {
time: nextBarTime,
open: tradePrice,
high: tradePrice,
low: tradePrice,
close: tradePrice,
volume: tradeVolume,
};
} else {
// Update existing bar
bar = {
...lastBar,
high: Math.max(lastBar.high, tradePrice),
low: Math.min(lastBar.low, tradePrice),
close: tradePrice,
volume: (lastBar.volume || 0) + tradeVolume,
};
}
subscriptionItem.lastBar = bar;
// Broadcast to all subscribers
subscriptionItem.handlers.forEach((handler) => handler.callback(bar));
});
export function subscribeOnStream(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback, lastBar) {
if (!symbolInfo || !symbolInfo.ticker) {
console.error('[subscribeBars]: Invalid symbolInfo:', symbolInfo);
return;
}
const parsedSymbol = parseFullSymbol(symbolInfo.ticker);
const channelString = `0~${parsedSymbol.exchange}~${parsedSymbol.fromSymbol}~${parsedSymbol.toSymbol}`;
const handler = {
id: subscriberUID,
callback: onRealtimeCallback,
};
let subscriptionItem = channelToSubscription.get(channelString);
if (subscriptionItem) {
// Add handler to existing subscription
console.log('Updating existing subscription with new resolution:', resolution);
subscriptionItem.resolution = resolution;
subscriptionItem.lastBar = lastBar;
subscriptionItem.handlers.push(handler);
return;
}
// Create new subscription
subscriptionItem = {
subscriberUID,
resolution,
lastBar,
handlers: [handler],
};
channelToSubscription.set(channelString, subscriptionItem);
console.log('[subscribeBars]: Subscribe to streaming. Channel:', channelString);
const subRequest = {
action: 'SubAdd',
subs: [channelString],
};
if (socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify(subRequest));
}
}
export function unsubscribeFromStream(subscriberUID) {
for (const channelString of channelToSubscription.keys()) {
const subscriptionItem = channelToSubscription.get(channelString);
const handlerIndex = subscriptionItem.handlers.findIndex(
(handler) => handler.id === subscriberUID
);
if (handlerIndex !== -1) {
subscriptionItem.handlers.splice(handlerIndex, 1);
if (subscriptionItem.handlers.length === 0) {
console.log('[unsubscribeBars]: Unsubscribe from streaming. Channel:', channelString);
const subRequest = {
action: 'SubRemove',
subs: [channelString],
};
socket.send(JSON.stringify(subRequest));
channelToSubscription.delete(channelString);
break;
}
}
}
}
```
--------------------------------
### Fetch Historical Bars using JavaScript
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Datafeed-Implementation
Fetches historical price bars for a given symbol, resolution, and time range using an external API (Cryptocompare). It utilizes the `parseFullSymbol` helper to construct the API request and filters data client-side due to API limitations. Handles API errors and no-data scenarios.
```javascript
import { makeApiRequest, parseFullSymbol, generateSymbol } from './helpers.js';
// ...
export default {
// ...
getBars: async (symbolInfo, resolution, from, to, onHistoryCallback, onErrorCallback, firstDataRequest) => {
console.log('[getBars]: Method call', symbolInfo, resolution, from, to);
const parsedSymbol = parseFullSymbol(symbolInfo.full_name);
const urlParameters = {
e: parsedSymbol.exchange,
fsym: parsedSymbol.fromSymbol,
tsym: parsedSymbol.toSymbol,
toTs: to,
limit: 2000,
};
const query = Object.keys(urlParameters)
.map(name => `${name}=${encodeURIComponent(urlParameters[name])}`)
.join('&');
try {
const data = await makeApiRequest(`data/histoday?${query}`);
if (data.Response && data.Response === 'Error' || data.Data.length === 0) {
// "noData" should be set if there is no data in the requested period.
onHistoryCallback([], { noData: true });
return;
}
let bars = [];
data.Data.forEach(bar => {
if (bar.time >= from && bar.time < to) {
bars = [...bars, {
time: bar.time * 1000,
low: bar.low,
high: bar.high,
open: bar.open,
close: bar.close,
}];
}
});
console.log(`[getBars]: returned ${bars.length} bar(s)`);
onHistoryCallback(bars, { noData: false });
} catch (error) {
console.log('[getBars]: Get error', error);
onErrorCallback(error);
}
},
//...
};
```
--------------------------------
### Include Socket.IO for WebSocket Connection (HTML)
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Streaming-Implementation
This HTML snippet includes the Socket.IO library, which is essential for establishing a WebSocket connection to the streaming API. Ensure this script is loaded before your main JavaScript file.
```html
```
--------------------------------
### Parse Crypto Pair Symbol using JavaScript
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Datafeed-Implementation
Parses a crypto pair symbol string (e.g., 'BINANCE:BTC/USDT') into its constituent parts: exchange, base currency, and quote currency. This function is crucial for correctly formatting API requests for historical data.
```javascript
export function parseFullSymbol(fullSymbol) {
const match = fullSymbol.match(/^(\w+):(\w+)\/(\w+)$/);
if (!match) {
return null;
}
return { exchange: match[1], fromSymbol: match[2], toSymbol: match[3] };
}
```
--------------------------------
### Generate Symbol Information (JavaScript)
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Datafeed-Implementation
This utility function generates a short and full symbol string based on the provided exchange, fromSymbol, and toSymbol. It's used to create standardized symbol representations.
```javascript
export function generateSymbol(exchange, fromSymbol, toSymbol) {
const short = `${fromSymbol}/${toSymbol}`;
return {
short,
full: `${exchange}:${short}`,
};
}
```
--------------------------------
### Search Symbols using JavaScript
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Datafeed-Implementation
Implements the symbol search functionality for the charting library. It fetches all available symbols from an API and filters them based on user input and an optional exchange filter. This allows users to quickly find and select trading instruments.
```javascript
searchSymbols: async (
userInput,
exchange,
symbolType,
onResultReadyCallback
) => {
console.log('[searchSymbols]: Method call');
const symbols = await getAllSymbols();
const newSymbols = symbols.filter(symbol => {
const isExchangeValid = exchange === '' || symbol.exchange === exchange;
const isFullSymbolContainsInput = symbol.full_name
.toLowerCase()
.indexOf(userInput.toLowerCase()) !== -1;
return isExchangeValid && isFullSymbolContainsInput;
});
onResultReadyCallback(newSymbols);
},
```
--------------------------------
### Resolve Trading Symbol Details - JavaScript
Source: https://context7.com/tradingview/charting-library-tutorial/llms.txt
The `resolveSymbol` method fetches metadata for a trading symbol, such as price scale, session hours, and supported resolutions. It's crucial for the chart to correctly display and format data. This function requires access to a list of all available symbols.
```javascript
import { parseFullSymbol } from './helpers.js';
resolveSymbol: async (symbolName, onSymbolResolvedCallback, onResolveErrorCallback, extension) => {
console.log('[resolveSymbol]: Method call', symbolName);
const symbols = await getAllSymbols();
const symbolItem = symbols.find(({ ticker }) => ticker === symbolName);
if (!symbolItem) {
console.log('[resolveSymbol]: Cannot resolve symbol', symbolName);
onResolveErrorCallback("unknown_symbol");
return;
}
// Symbol information object with all required properties
const symbolInfo = {
ticker: symbolItem.ticker,
name: symbolItem.symbol,
description: symbolItem.description,
type: symbolItem.type,
exchange: symbolItem.exchange,
listed_exchange: symbolItem.exchange,
session: '24x7',
timezone: 'Etc/UTC',
minmov: 1,
pricescale: 10000,
has_intraday: true,
intraday_multipliers: ["1", "60"],
has_daily: true,
daily_multipliers: ["1"],
visible_plots_set: "ohlcv",
supported_resolutions: configurationData.supported_resolutions,
volume_precision: 2,
data_status: 'streaming',
};
console.log('[resolveSymbol]: Symbol resolved', symbolName);
onSymbolResolvedCallback(symbolInfo);
}
```
--------------------------------
### Helper: Parse Full Symbol String - JavaScript
Source: https://context7.com/tradingview/charting-library-tutorial/llms.txt
A utility function to parse a symbol string formatted as 'Exchange:FROM/TO' into its constituent parts: exchange, fromSymbol, and toSymbol. This is used internally by other trading functions to extract symbol details.
```javascript
// Helper function to parse symbol format "Exchange:FROM/TO"
export function parseFullSymbol(fullSymbol) {
const match = fullSymbol.match(/^(\w+):(\w+)\/(\w+)$/);
if (!match) {
return null;
}
return {
exchange: match[1],
fromSymbol: match[2],
toSymbol: match[3],
};
}
```
--------------------------------
### Fetch Historical Bars Data - JavaScript
Source: https://context7.com/tradingview/charting-library-tutorial/llms.txt
The `getBars` method retrieves historical OHLCV data for a specified symbol and time range. It selects the correct API endpoint based on the resolution (minute, hour, day) and filters data to match the requested period. This function relies on `parseFullSymbol` and `makeApiRequest` helper functions.
```javascript
getBars: async (symbolInfo, resolution, periodParams, onHistoryCallback, onErrorCallback) => {
const { from, to, firstDataRequest } = periodParams;
console.log('[getBars]: Method call', symbolInfo, resolution, from, to);
const parsedSymbol = parseFullSymbol(symbolInfo.ticker);
// Select correct endpoint based on resolution
let endpoint;
if (resolution === '1D') {
endpoint = 'histoday';
} else if (resolution === '60') {
endpoint = 'histohour';
} else if (resolution === '1') {
endpoint = 'histominute';
} else {
onErrorCallback(`Invalid resolution: ${resolution}`);
return;
}
// Build API request parameters
const urlParameters = {
e: parsedSymbol.exchange,
fsym: parsedSymbol.fromSymbol,
tsym: parsedSymbol.toSymbol,
toTs: to,
limit: 2000,
};
const query = Object.keys(urlParameters)
.map(name => `${name}=${encodeURIComponent(urlParameters[name])}`)
.join('&');
try {
const data = await makeApiRequest(`data/v2/${endpoint}?${query}`);
if ((data.Response && data.Response === 'Error') ||
!data.Data || !data.Data.Data || data.Data.Data.length === 0) {
onHistoryCallback([], { noData: true });
return;
}
// Transform API response to TradingView bar format
let bars = [];
data.Data.Data.forEach(bar => {
if (bar.time >= from && bar.time < to) {
bars.push({
time: bar.time * 1000,
low: bar.low,
high: bar.high,
open: bar.open,
close: bar.close,
volume: bar.volumefrom,
});
}
});
// Cache last bar for streaming updates
if (firstDataRequest) {
lastBarsCache.set(symbolInfo.ticker, { ...bars[bars.length - 1] });
}
console.log(`[getBars]: returned ${bars.length} bar(s)`);
onHistoryCallback(bars, { noData: false });
} catch (error) {
console.log('[getBars]: Get error', error);
onErrorCallback(error);
}
}
```
--------------------------------
### Parse and Handle WebSocket Trade Updates
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Streaming-Implementation
This JavaScript code snippet demonstrates how to parse incoming WebSocket messages, filter for trade events (eventTypeStr === 0), and update the latest daily bar with new trade prices. It also handles the logic for generating a new bar if the trade occurs on a subsequent day.
```javascript
socket.on('m', data => {
console.log('[socket] Message:', data);
const [
eventTypeStr,
exchange,
fromSymbol,
toSymbol,
,
,
tradeTimeStr,
,
tradePriceStr,
] = data.split('~');
if (parseInt(eventTypeStr) !== 0) {
// skip all non-TRADE events
return;
}
const tradePrice = parseFloat(tradePriceStr);
const tradeTime = parseInt(tradeTimeStr);
const channelString = `0~${exchange}~${fromSymbol}~${toSymbol}`;
const subscriptionItem = channelToSubscription.get(channelString);
if (subscriptionItem === undefined) {
return;
}
const lastDailyBar = subscriptionItem.lastDailyBar;
const nextDailyBarTime = getNextDailyBarTime(lastDailyBar.time);
let bar;
if (tradeTime >= nextDailyBarTime) {
bar = {
time: nextDailyBarTime,
open: tradePrice,
high: tradePrice,
low: tradePrice,
close: tradePrice,
};
console.log('[socket] Generate new bar', bar);
} else {
bar = {
...lastDailyBar,
high: Math.max(lastDailyBar.high, tradePrice),
low: Math.min(lastDailyBar.low, tradePrice),
close: tradePrice,
};
console.log('[socket] Update the latest bar by price', tradePrice);
}
subscriptionItem.lastDailyBar = bar;
// send data to every subscriber of that symbol
subscriptionItem.handlers.forEach(handler => handler.callback(bar));
});
function getNextDailyBarTime(barTime) {
const date = new Date(barTime * 1000);
date.setDate(date.getDate() + 1);
return date.getTime() / 1000;
}
```
--------------------------------
### Implement TradingView Datafeed Methods (JavaScript)
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Streaming-Implementation
This JavaScript code defines the `subscribeBars` and `unsubscribeBars` methods for the TradingView datafeed. It integrates with the streaming functions to manage real-time data subscriptions based on symbol information and subscriber UIDs.
```javascript
import { subscribeOnStream, unsubscribeFromStream } from './streaming.js';
const lastBarsCache = new Map();
// ...
export default {
// ...
subscribeBars: (
symbolInfo,
resolution,
onRealtimeCallback,
subscribeUID,
onResetCacheNeededCallback
) => {
console.log('[subscribeBars]: Method call with subscribeUID:', subscribeUID);
subscribeOnStream(
symbolInfo,
resolution,
onRealtimeCallback,
subscribeUID,
onResetCacheNeededCallback,
lastBarsCache.get(symbolInfo.full_name)
);
},
unsubscribeBars: (subscriberUID) => {
console.log('[unsubscribeBars]: Method call with subscriberUID:', subscriberUID);
unsubscribeFromStream(subscriberUID);
},
};
```
--------------------------------
### Subscribe to Real-time Bars Data (JavaScript)
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Streaming-Implementation
This JavaScript function handles the subscription to real-time price bars for a given symbol and resolution. It manages channel subscriptions, associates handlers with specific UIDs, and emits a 'SubAdd' event to the WebSocket server.
```javascript
import { parseFullSymbol } from './helpers.js';
// ...
const channelToSubscription = new Map();
// ...
export function subscribeOnStream(
symbolInfo,
resolution,
onRealtimeCallback,
subscribeUID,
onResetCacheNeededCallback,
lastDailyBar
) {
const parsedSymbol = parseFullSymbol(symbolInfo.full_name);
const channelString = `0~${parsedSymbol.exchange}~${parsedSymbol.fromSymbol}~${parsedSymbol.toSymbol}`;
const handler = {
id: subscribeUID,
callback: onRealtimeCallback,
};
let subscriptionItem = channelToSubscription.get(channelString);
if (subscriptionItem) {
// already subscribed to the channel, use the existing subscription
subscriptionItem.handlers.push(handler);
return;
}
subscriptionItem = {
subscribeUID,
resolution,
lastDailyBar,
handlers: [handler],
};
channelToSubscription.set(channelString, subscriptionItem);
console.log('[subscribeBars]: Subscribe to streaming. Channel:', channelString);
socket.emit('SubAdd', { subs: [channelString] });
}
```
--------------------------------
### Unsubscribe from Real-time Data Streams (JavaScript)
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Streaming-Implementation
This JavaScript function removes a specific handler from a channel subscription and unsubscribes from the channel entirely if no handlers remain. It iterates through existing subscriptions, finds the matching handler, and emits a 'SubRemove' event when necessary.
```javascript
export function unsubscribeFromStream(subscriberUID) {
// find a subscription with id === subscriberUID
for (const channelString of channelToSubscription.keys()) {
const subscriptionItem = channelToSubscription.get(channelString);
const handlerIndex = subscriptionItem.handlers
.findIndex(handler => handler.id === subscriberUID);
if (handlerIndex !== -1) {
// remove from handlers
subscriptionItem.handlers.splice(handlerIndex, 1);
if (subscriptionItem.handlers.length === 0) {
// unsubscribe from the channel, if it was the last handler
console.log('[unsubscribeBars]: Unsubscribe from streaming. Channel:', channelString);
socket.emit('SubRemove', { subs: [channelString] });
channelToSubscription.delete(channelString);
break;
}
}
}
}
```
--------------------------------
### Save Last Bar Data in Datafeed
Source: https://github.com/tradingview/charting-library-tutorial/wiki/Streaming-Implementation
This JavaScript code snippet shows how to modify the `GetBars` method in `datafeed.js` to store the last bar's data. This is a workaround to have the last bar's information available for real-time updates when a dedicated bars streaming API is not available.
```javascript
//... (assuming bars and symbolInfo are defined in the scope)
if (firstDataRequest) {
lastBarsCache.set(symbolInfo.full_name, { ...bars[bars.length - 1] });
}
console.log(`[getBars]: returned ${bars.length} bar(s)`);
//...
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.