Giter Club home page Giter Club logo

charting-library-tutorial's Introduction

Advanced Charts: Connecting data via the Datafeed API

Note

This repository contains sample code for a tutorial on connecting data via the Datafeed API. You can find the full step-by-step guide in the Advanced Charts documentation.

What is Advanced Charts

Advanced Charts is a standalone solution that you can download, host on your servers, connect your own data to, and use in your site or app for free. Learn more about Advanced Charts on the TradingView website.

What is This Tutorial About

This tutorial explains how to implement real-time data streaming to Advanced Charts step-by-step using the Datafeed API. As an example, the tutorial describes connection via free CryptoCompare API that provides data from different crypto exchanges.

charting-library-tutorial's People

Contributors

anachuprina avatar aslam97 avatar ezhukovskiy avatar illetid avatar ivanhoegrozni avatar mmoshnogorskaia avatar nelsonogueh avatar romfrancois avatar slicedsilver avatar timocov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

charting-library-tutorial's Issues

apply custom scale marks on prices on price-axis

Screenshot (19)
`

defaultChartProps = {
    interval: '15',
    containerId: 'tv_chart_container',
    libraryPath: '/trade/charting_library/',
    chartsStorageUrl: 'https://saveload.tradingview.com',
    chartsStorageApiVersion: '1.1',
    clientId: 'tradingview.com',
    userId: 'public_user_id',
    fullscreen: false,
    autosize: true,
  };

const widgetOptions = {
      debug: false,
      symbol: this.getCurrInstrument(),
      datafeed: this.dataFeed,
      interval: this.defaultChartProps.interval,
      container_id: this.defaultChartProps.containerId,
      library_path: this.defaultChartProps.libraryPath,
      locale: 'en',
      disabled_features: [
        'use_localstorage_for_settings',
        'header_symbol_search',
      ],
      enabled_features: ['study_templates'],
      charts_storage_url: this.defaultChartProps.chartsStorageUrl,
      charts_storage_api_version: this.defaultChartProps
        .chartsStorageApiVersion,
      client_id: this.defaultChartProps.clientId,
      user_id: this.defaultChartProps.userId,
      fullscreen: this.defaultChartProps.fullscreen,
      autosize: this.defaultChartProps.autosize,
      studies_overrides: this.defaultChartProps.studiesOverrides,
      overrides: {
        // "mainSeriesProperties.showCountdown": true,
        'paneProperties.background': '#000000',
        'paneProperties.vertGridProperties.color': '#363c4e',
        'paneProperties.horzGridProperties.color': '#363c4e',
        'symbolWatermarkProperties.transparency': 90,
        'scalesProperties.textColor': '#AAA',
        'mainSeriesProperties.candleStyle.wickUpColor': '#336854',
        'mainSeriesProperties.candleStyle.wickDownColor': '#7f323f',
      },
      custom_css_url: './gray-chart-theme.css',
    };
`

i have a pricescale of 100 and minmov 1,,,

i want price axis to have marks on 5 dollars price gap...

am i supposed to provide pass some special property to show the prices on price scale ?

Datafeed history endpoing from from/to params

blank / white window

i copied the charting_library and datafeeds folders to charting_library_clonned_data
and then executed the npx serve command. Browser window remains blank. Inspector is showing this:

Bildschirmfoto 2021-07-18 um 14 50 34

Add support intraday resolutions

Greetings,

thank you team for this great tutorial. I managed to switch the timeframe to 1 minute chart successfully, but getBars method is calling only once. Currently only the last bar is receiving the updates, and new bars are not generated. What needs to be done in order to call getBars every 60 seconds ?

Hi Team,

I am facing issue with volume: internal error. When my trading paid is BTC/USDT but in chart section it comes USDT/BTC. Can you guide how to change it to BTC/USD. If I change it above error coming.I need to update time stamp, in chart section it shows 70.instead of 2021.Plz guide me.Thank you

Is there any way to start chart with zoom in?

Hello,
I would like to know if there is any way to start the chart with zoom 3x applied?
According to the documentation we have these parameters below.
Is there any parameters to set the initial zoom percentage onload?

const symbolInfo = {
            ticker: symbolItem.full_name,
            name: symbolItem.symbol,
            description: symbolItem.description,
            type: symbolItem.type,
            session: '24x7',
            timezone: 'Etc/UTC',
            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',
};

[QUESTION] Cannot set supported_resolutions resulition less than 1 Day

hi, i have follow this tutorial but i'm facing some issue when using 1 minutes data of history, this is my datafeed.js file

import {
	makeApiRequest,
	generateSymbol,
	parseFullSymbol,
} from './helpers.js';
import {
	subscribeOnStream,
	unsubscribeFromStream,
} from './streaming.js';

const lastBarsCache = new Map();

const configurationData = {
	supported_resolutions: [ '3S', '5S', '10S', '15S', '30S', '1', '3', '5', '15', '30', '1h', '2h', '3h', '4h', '8h', '1D', '1W', '2W', '1M', ],
	exchanges: [{
		value: 'Bitfinex',
		name: 'Bitfinex',
		desc: 'Bitfinex',
	},
	{
		// `exchange` argument for the `searchSymbols` method, if a user selects this exchange
		value: 'Kraken',

		// filter name
		name: 'Kraken',

		// full exchange name displayed in the filter popup
		desc: 'Kraken bitcoin exchange',
	},
	],
	symbols_types: [{
		name: 'crypto',

		// `symbolType` argument for the `searchSymbols` method, if a user selects this symbol type
		value: 'crypto',
	},
		// ...
	],
};

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,
					type: 'crypto',
				};
			});
			allSymbols = [...allSymbols, ...symbols];
		}
	}
	return allSymbols;
}

export default {
	onReady: (callback) => {
		console.log('[onReady]: Method call');
		setTimeout(() => callback(configurationData));
	},

	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);
	},

	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 = {
			ticker: symbolItem.full_name,
			name: symbolItem.symbol,
			description: symbolItem.description,
			type: symbolItem.type,
			session: '24x7',
			timezone: 'Etc/UTC',
			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);
	},

	getBars: async (symbolInfo, resolution, from, to, onHistoryCallback, onErrorCallback, firstDataRequest) => {
		// const { from, to, firstDataRequest } = periodParams;
		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: 10,
		};
		const query = Object.keys(urlParameters)
			.map(name => `${name}=${encodeURIComponent(urlParameters[name])}`)
			.join('&');
		try {
			const data = await makeApiRequest(`data/histominute?${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 = [];
			let bars = data.Data.map((bar) => {
				return {
					time: bar.time * 1000,
					low: bar.low,
					high: bar.high,
					open: bar.open,
					close: bar.close,
				}
			})
			// 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(bars)
			// 	}
			// });
			if (firstDataRequest) {
				// lastBarsCache.set(symbolInfo.full_name, {
				// 	...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);
		}
	},

	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);
	},
};

but we can't change the value, anyone can help ?

image

Live + updateFrequency datafeed?

Possible to implement live data (socket io) + data refresh with updateFrequency param (traditional way)?
This is to avoid data out of sync in case user Internet connection is disconnect and reconnect later, and some data in between is not getting updated in Live Chart.

Android tutorial - tvWidget is not defined

Is there any tutorial about how to implement this one in Android or iOS (or both)? I tried to combine the charting-library-example and this tutorial, but it completely did not work. I keep receiving errors:

I/chromium: [INFO:CONSOLE(1)] "Uncaught SyntaxError: Cannot use import statement outside a module", source: file:///android_asset/charting_library/src/datafeed.js (1)
I/chromium: [INFO:CONSOLE(1)] "Uncaught SyntaxError: Cannot use import statement outside a module", source: file:///android_asset/charting_library/src/main.js (1)
I/chromium: [INFO:CONSOLE(2)] "Uncaught ReferenceError: tvWidget is not defined", source: file:///android_asset/charting_library/index.html (2)

Here is how I did it:

  1. Clone an example of Android in charting-library-example
  2. Add folder charting_library into the source code that I just cloned
  3. Add folder src of this tutorial into the source code
  4. Copy the content of a file index of this tutorial and replace it into file index.js in the source code
  5. Run the source code (in both the emulator and real device)

Here is what my source code looks like after I finish all steps:
image

NextJS Example/Tutorial - window not defined error

Is there any tutorial or example that explains how to work with this library in Nextjs? I keep getting window is not defined error coming from the .standalone file. Not sure how to define window despite being in client side especially when the error is coming from the charting_library.standalone file.

The error occurs at the import TradingView from "../../chart/charting_library_clonned_data/charting_library/charting_library.standalone"; line and it then says window isn't defined.

import React, { useEffect, useLayoutEffect } from "react";
import Datafeed from "./datafeed";
import TradingView from "../../chart/charting_library_clonned_data/charting_library/charting_library.standalone";
export default function TradingViewChart (props) {
    const isOn = typeof window !== "undefined"
    const [chart, setChart] = useState(null)
  useEffect(() => {
    if (isOn) {
        const script = document.createElement("script");
        script.type = "text/jsx";
        script.src = "%PUBLIC_URL%/charting_library/charting_library.js";
        document.head.appendChild(script);

        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: "tv_chart_container",
        datafeed: Datafeed,
        library_path: "/charting_library/",
        });
        //Do not forget to remove the script on unmounting the component!
        return () => script.remove();
    }
  }, []); //eslint-disable-line

  return <div id="tv_chart_container"></div>;
};

Could not use drawing tools

Hello,
A big thank you for creating such a amazing product and making it available to us. I'm a newbie in
the world of JS so I'm writing this to the best of knowledge, so please be patient.

Version of TAC = CL v21.066 (internal id 132c7060 @ 2022-04-01T09:59:44.647Z)

I followed the charting-library-tutorial and used those five files i.e. index.html, main.js , datafeed.js, helper.js and streaming.js with some minor modifications like change in url structure of api endpoint, adding volume along with OHLC values to datafeed from feched data, updating timezone and exchange info etc. and it was enough to display data from backend on the chart. I did not altered the main library in any manner as I dont have that level of understanding of the JS. However, now I cant use any of the drawing tools. If I use those five files provided in the tutorial repo, everything works fine but if I use the files that I have modified drawing tools don't work anymore. Also, to be precise to moved those five files in the "charting_library_clonned_data" folder and updated filepaths in main.js and index.html.

Please refer below screenshots and index.html, main.js , datafeed.js, helper.js and streaming.js codes.

I have uploaded TAC.py on this repo file which requires fastapi with uvicorn and if you run it using uvicorn TAC:app --port 8000 --reload it will respond to the exchange and historical data request made by the library . Hope this will help. Please let me know if you need anything. Also, I have uploaded a json for exchange and OHLCV data of 1min for 2000 candles if you want to test it on the repo . History endpoint should be /{d_fysymbol}/{Resolution}/{Timestamp}/{Limit} i.e "/NSE-SBIN-EQ/1/1650707789/2000" and exchange end point should be /exchange.

You can find these files also on this repo.

TAC_BTC-USD_2

I might be completely wrong but looks like it has something to do with those two drawing_events with same ids in above screenshot. When I try to draw a trendline onSelectedLineToolChanged along with other mouse events are getting triggered but drawing_event isn't in the below image. Also, I could tell that trend line is being drawn on the chart by looking at light blue selected region both on time scale and price scale, as well as the enabled "Remove Drawings" option in the right click menu, but it does not show up in the "object tree". Also, tv-floating-toolbar tv-grouped-floating-toolbar ui-draggable div isn't in the "tv-chart-container".

TAC_SBIN-1

TAC_SBIN-2

<--! index.html-->

<--! index.html-->
<!DOCTYPE HTML>
<html>
    <head>
        <title>TradingView Charting Library example</title>
        <script
            type="text/javascript"
            src="charting_library/charting_library.js">
        </script>
        <script src="https://cdn.socket.io/4.4.1/socket.io.min.js" integrity="sha384-fKnu0iswBIqkjxrhQCTZ7qlLHOFEgNkRmK2vaO/LbTZSXdJfAu6ewRBdwHPhBo/H" crossorigin="anonymous"></script>
        <!-- Custom datafeed module. -->
        <script type="module" src="src/main.js"></script>
    </head>
    <body style="margin:0px;">
        <div id="tv_chart_container">
            <!-- This div will contain the Charting Library widget. -->
        </div>
    </body>
</html>

main.js

//main.js
// Datafeed implementation, will be added later//
import Datafeed from './datafeed.js';

window.tvWidget = new TradingView.widget({
	symbol: 'NSE:SBIN-EQ', // default symbol
	interval: '1', // default interval
	fullscreen: true, // displays the chart in the fullscreen mode
	container: 'tv_chart_container',
	timezone: 'Asia/Kolkata',
	datafeed: Datafeed,
	library_path: 'charting_library/',
	debug: true,
});

helpers.js

//helpers.js

// Make requests to CryptoCompare API
export async function makeApiRequest(path) {
	try {
		const response = await fetch(`http://localhost:8000/${path}`);
		return response.json();
	} catch (error) {
		throw new Error(`LocalHost request error: ${error.status}`);
	}
}

// Generate a symbol ID from a pair of the coins
export function generateSymbol(exchange, fromSymbol, toSymbol) {
	const short = `${fromSymbol}`;
	const type = `${toSymbol}`;
	const med = `${fromSymbol}-${toSymbol}`;
	return {
		short,
		med,
		type,
		full: `${exchange}:${med}`,
	};
}

export function decodeQueryParam(p) {
  return decodeURIComponent(p.replace(/\+/g, ' '));
}

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],
		fysymbol : `${match[1]}-${match[2]}-${match[3]}`,

	};
}

datafeed.js

//datafeed.js

import {
	makeApiRequest,
	generateSymbol,
	parseFullSymbol,
	decodeQueryParam,
} from './helpers.js';
import {
	subscribeOnStream,
	unsubscribeFromStream,
} from './streaming.js';

const lastBarsCache = new Map();

const configurationData = {
	supported_resolutions: ['1','5','10','15','30','45','60','1D', '1W', '1M'],
	//supported_resolutions: ['1'],
	exchanges: [{
		value: 'NSE',
		name: 'NSE',
		desc: 'National Stock Exchange',
	},],
	symbols_types: [{
				// `symbolType` argument for the `searchSymbols` method, if a user selects this symbol type
		name: 'EQ',
		value: 'EQ',

		// `symbolType` argument for the `searchSymbols` method, if a user selects this symbol type

	},
	{
				// `symbolType` argument for the `searchSymbols` method, if a user selects this symbol type
		name: 'Index',
		value: 'Index',
		},
	],

};

async function getAllSymbols() {
	const data = await makeApiRequest('exchange');
	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,
					type: symbol.type,
				};
			});
			allSymbols = [...allSymbols, ...symbols];
		}
	}
	return allSymbols;
}

export default {
	onReady: (callback) => {
		console.log('[onReady]: Method call');
		setTimeout(() => callback(configurationData));
	},

	searchSymbols: async (
		userInput,
		exchange,
		symbolType,
		onResultReadyCallback,
	) => {
		console.log('[searchSymbols]: Method call');
		const symbols = await getAllSymbols();
		console.log('symbols',symbols);
		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);
	},

	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 = {
			ticker: symbolItem.full_name,
			name: symbolItem.symbol,
			full_name : symbolItem.full_name,
			description: symbolItem.description,
			type: symbolItem.type,
			session: '0915-1530',
			timezone: 'Asia/Kolkata',
			exchange: symbolItem.exchange,
			minmov: 1,
			pricescale: 100,
			has_intraday: true,
			intraday_multipliers: ['1','15'],
			has_daily: true,
			daily_multipliers : ['1'],
			has_no_volume: false,
			has_weekly_and_monthly: false,
			visible_plots_set : "ohlcv",
			original_currency_code : "INR",
			supported_resolutions: ['1','5','10','15','30','45','60','1D', '1W', '1M'],
			volume_precision: 2,
			data_status: 'streaming',
		};

		console.log('[resolveSymbol]: Symbol resolved', symbolName);
		onSymbolResolvedCallback(symbolInfo);
	},

	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);
		console.log("parsedSymbol=",parsedSymbol);
		const urlParameters = {
			//e: parsedSymbol.exchange,
			//fsym: parsedSymbol.fromSymbol,
			//tsym: parsedSymbol.toSymbol,
			fys : parsedSymbol.fysymbol,
			//fromTs:from,
			rr: resolution,
			toTs: to,
			//countBack : countBack,
			limit: 2000,
		};
		const query = Object.keys(urlParameters)
			.map(name => `${encodeURIComponent(urlParameters[name])}`)
			.join('/');

		try {
			const data = await makeApiRequest(`${query}`);
			console.log(`[getBars]: returned `,data);
			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 => {
				//console.log('Data from HistAPI',data.Data);
				if (bar.time < to) {
					bars = [...bars, {
						time: bar.time * 1000,
						low: bar.low,
						high: bar.high,
						open: bar.open,
						close: bar.close,
						volume: bar.volume,
					}];
				}
			//console.log("bar.close",bar.close,"bar.volume",bar.volume);
			});
			console.log('firstDataRequest',firstDataRequest);
			if (firstDataRequest) {
				lastBarsCache.set(symbolInfo.full_name, {
					...bars[bars.length - 1],
				});
			console.log('firstDataRequest',firstDataRequest);
			}
			console.log(`[getBars]: returned ${bars.length} bar(s)`);
			onHistoryCallback(bars, {
				noData: false,
			});
		} catch (error) {
			console.log('[getBars]: Get error', error);
			onErrorCallback(error);
		}
	},

	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);
	},
};

streaming.js

//streaming.js

import { parseFullSymbol } from './helpers.js';

//const socket = io('wss://streamer.cryptocompare.com');

//const socket = io();
//{transports: ['websocket', 'polling', 'flashsocket']}
//);

//const socket = io(/ws/)

const socket = io('http://localhost:9000', {
     transports: ['websocket', 'polling', 'flashsocket']
});


const channelToSubscription = new Map();
console.log('ChannelToSubscription=',channelToSubscription ),

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);
});



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 = `${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;
}

export function subscribeOnStream(
	symbolInfo,
	resolution,
	onRealtimeCallback,
	subscribeUID,
	onResetCacheNeededCallback,
	lastDailyBar,
) {
	const parsedSymbol = parseFullSymbol(symbolInfo.full_name);
	const channelString = `${parsedSymbol.exchange}-${parsedSymbol.fromSymbol}-${parsedSymbol.toSymbol}`;
	console.log("chaneelString=",channelString)
	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] });
}

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;
			}
		}
	}
}

getBars are returning wrong periodParams

Hello,

I'm having the following problem, when the user selects a custom range in a TradingView chart, the getBars function it's getting wrong periodParams(from, to)

getBars: async ({ exchange, full_name, name, session, timezone }, resolution, { firstDataRequest, from, to }, onResult, _onError) => { const isUSExchange = usExchanges.includes(exchange); const websocket = isUSExchange ? obookWebSocket : multiWebSocket; if (firstDataRequest) { const { currency: currency_code, isin } = await getStockInfoOnClient(exchange, name); currency = currency_code; ISIN = isin; id = name; } switch (resolution) { case "1D": const res = await getChartData(Number(full_name), dayjs.unix(from).format("YYYY-MM-DD"), dayjs.unix(to).format("YYYY-MM-DD")); const beforeMarketOpenDiff = getCurrentSessionInterval(session.split(","), timezone) !== -1 ? 0 : dayjs().day() === 1 ? 3 : 1; if ( firstDataRequest && !dayjs .utc(res[res.length - 1]?.[0]) .add(beforeMarketOpenDiff, "days") .isSame(dayjs.utc(), "day") ) { onHistoryResult = null; current = { time: undefined, open: undefined, high: undefined, low: undefined, close: undefined }; updateHistory = isUSExchange ? ({ data }) => { const parsedData = JSON.parse(data); const LT: [{ o: number; h: number; l: number; c: number }, number][] = parsedData.LT; if (LT) { const [openRaw, closeRaw] = getMainSession(session.split(",")).split("-"); const open = dayjs.tz(openRaw, "HHmm", timezone).subtract(beforeMarketOpenDiff, "days"); const close = dayjs.tz(closeRaw, "HHmm", timezone).subtract(beforeMarketOpenDiff, "days"); const hasOldData = dayjs(LT[0][1]).isBefore(open); current = (hasOldData ? LT.filter(([_, time]) => dayjs(time).isBetween(open, close)) : LT).reduce<Current>((prev, [{ o, h, l, c }, time]) => { return { time, open: prev.open ?? o, high: Math.max(prev.high ?? h, h), low: Math.min(prev.low ?? l, l), close: c, }; }, current); } } : ({ data }) => { const parsedData = JSON.parse(data); const ts: [number, number, number, number, number][] = parsedData.ts; if (parsedData.ISIN === ISIN && ts) { const currentDate = dayjs(); current = ts .filter(([time]) => dayjs(time).isSame(currentDate, "day")) .reduce<Current>( (prev, [time, open, high, low, close]) => ({ time, open: prev.open ?? open, high: Math.max(prev.high ?? high, high), low: Math.min(prev.low ?? low, low), close, }), current ); } }; websocket.addEventListener("message", updateHistory); const onOpenTS = isUSExchange ? onOpenUSTS : onOpenDailyTS; websocket.readyState === 1 ? onOpenTS() : websocket.addEventListener("open", onOpenTS); } onResult( res.map(([time, open, high, low, close]) => ({ time, open, high, low, close })), { noData: !res.length && dayjs.unix(to).diff(dayjs.unix(from), "days") > 1 } ); break; case "1": if (firstDataRequest) { onHistoryResult = onResult; websocket.addEventListener("message", isUSExchange ? tsUSMessage : tsMessage); websocket.removeEventListener("message", updateHistory); const onOpenTS = isUSExchange ? onOpenUSTS : onOpenWeeklyTS; if (websocket.readyState === 1) onOpenTS(); else { websocket.addEventListener("open", onOpenTS); onResult([], { noData: true }); } } else onResult([], { noData: true }); break; } },


image

Change resolution time is not working

Hi

Maybe I'm missing something but when I change the resolution time from 1D to 1W I get the following error on the chart:

Invalid symbol.

Screen Shot 2021-02-07 at 09 10 27

Any guidance?

Thanks

cordova app for Android

I implement this repo to my project , but I found an error on my android device.

image (1)
debugger say error on line 1 of main.js >> import Datafeed from './datafeed.js';

I tested this application(cordova) on IOS and Android, IOS device is ok but Android is not.

help me please. thanksyou

Money Flow Index (MFI) indicator results are wrong than expected

Hello,

I cloned this repository according to the tutorial and tried to run the demo, but after I can see the chart display, I tried to use the MFI indicator, but the result was not what I expected. The value of MFI is fixed at 100. When I check the preview website provided by TradingView, this issue also exists.

Screenshot 2021-04-22 220916

Please help me as much as possible to solve this problem, thanks in advance ๐Ÿ˜ƒ

Problem to see streaming data

Hello friends, I am having problems when I can see the data of the symbols in streaming and I had a doubt in which regions cryptocompare works since I did as indicated in the tutorial to activate a vpn and it does not work for me

Negative to value for resolution greater or equal to 1 week or greater

image

Data is being taken from Binance Kline API.

Chart and its function work fine with resolutions lesser than week. I cannot figure out what is wrong and what should be done here.

has_intraday: true,
has_weekly_and_monthly: true,

To make it compatible with all resolutions but still it does not work greater than 1 week.

Chart easing

Good day! Is there a way to set graphs with smooth drawing(easing)?
Below is a short video of how I expect to set up

178009534-d098e7b3-2dc4-43fd-83b6-3129ae47196c.mp4

npx serve fail to load data correctly: Cannot read property 'time' of undefined

I tried to complete the tutorial.
First of all, I need to change container to container_id in main.js to avoid an error.
When I run npx serve, I see graph to be build, but no candles data to load.
In a console I see an error
Uncaught TypeError: Cannot read property 'time' of undefined
at Socket. (streaming.js:45)
at Socket.Emitter.emit (index.js:133)
at Socket.onevent (socket.js:270)
at Socket.onpacket (socket.js:228)
at Manager. (index.js:21)
at Manager.Emitter.emit (index.js:133)
at Manager.ondecoded (manager.js:332)
at Decoder. (index.js:21)
at Decoder.Emitter.emit (index.js:134)
at Decoder.add (index.js:246)

When I try to find out what is happening, I see that lastDailyBar is undefined, and those lines

if (firstDataRequest) {
           lastBarsCache.set(symbolInfo.full_name, {
   				...bars[bars.length - 1],
   			});
   		}

didn't fire because firstDataRequest is undefined, too. So never get the first candle and all others are undefined

Failed to load resource: the server responded with a status of 404 ()

Hi there,
I am trying to implement streaming with polygon.io websocket. I have configured the websocket in the streaming.js file but the chart seems to try to connect to wrong address:
image

This is my configuration from the streaming.js file:
const socket = io('wss://delayed.polygon.io/stocks');

The index.html contains the socket.io script :

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.2/socket.io.js"></script>

Any idea how to fix this? The chart works fine otherwise.

Tutorial not started because index.html contains error

In the index.html we can see:
src="charting_library_clonned_data/charting_library/charting_library.js">

BUT!
On the way we can found only charting_library.min.js

So index.html looks as clean white screen. No chart etc...
After manually renamed file charting_library.min.js to charting_library.js - al works fine!
All the same after manually edit source in the index.html

Please - make one of these steps so project stay works correctly without users action!
thanks!

How can Implement the react component like Function component?

Hello, I tried to adapt the example in react JavaScript tutorial from class component to function component, but when I try to render the component it says that container doesn't exist, I already created the div with ID.
Anyone has the example of this component in function component?
Thanks

Issue with seconds and milliseconds resolutions

I have a problem with resolutions .
Firstly, The chart doesn't show seconds resolutions, but I've already added seconds in supported_resolutions
Secondly, I need to use : 0,5S, 0.25S resolutions, Unfortunately, I could not find any information about values less then 1 second .

I use widget.

my configurationData:
const configurationData = { supported_resolutions: [ '3S', '5S', '10S', '15S', '30S', '1', '3', '5', '15', '30', '1h', '2h', '3h', '4h', '8h', '1D', '1W', '2W', '1M', ], exchanges: [ { value: 'BINANCE', name: 'BINANCE', desc: 'BINANCE', }, ], symbols_types: [ { name: 'crypto', value: 'crypto', }, // ... ], }

and my symbolInfo:
const symbolInfo = { ticker: symbolItem.full_name, name: symbolItem.symbol, description: symbolItem.description, type: symbolItem.type, session: '24x7', timezone: 'Etc/UTC', exchange: symbolItem.exchange, minmov: 1, pricescale: 100, minmove2: 0, has_seconds: true, has_intraday: true, has_no_volume: false, has_empty_bars: true, has_weekly_and_monthly: true, supported_resolutions: configurationData.supported_resolutions, volume_precision: 3, data_status: 'streaming', options: symbolItem.options, pair_id: symbolItem.pair_id, }

Can you help me with my issues? thank you!

Resolving symbol doesn't support resolving by short name

First of all thank you so much team for the ready to use tradingview implementation. This is really great.

It throws 'cannot resolve symbol' when compared.
Also I am not sure if this issue is with all or just me because I did some code changes and not really sure if that has cause the issue.
Please try to reproduce and check. If you are not able to reproduce then please close the issue :)

crp

Tutorial does not work out of the box

Have issues importing the TradingView class and once resolve gives an issue with a promise. Unfortunate because this seemed like a good way to get familiar with the backend

Yes I am on unstable. Yet changing container_id to container throws an error so it seems like its something else

"Get error TypeError: onHistoryCallback is not a function" not resolved even referencing issue 24

Resolved when I forced the library to update to use unstable. Can you just make a note of it on the tutorial page perhaps?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.