import { useEffect, useRef } from 'react'; import './App.css'; import { DataUpdates, HistoricalDataRequest, Price, UTCTimestamp, Viper as ViperCharts } from "@viper-charts/viper-charts"; // import "@viper-charts/viper-charts/dist/style.css"; import { ViperDatabase } from "./ViperDatabase"; const db = createDB(); function onSaveViperSettings(settings: any) { localStorage.setItem("settings", JSON.stringify(settings)); } async function onRequestTemplates() { return await db.templates.where("id").above(0).toArray(); } async function onSaveTemplate(id: number, { name, config }: any) { // Check if template exists at id const rows = await db.templates.where("id").equals(id).toArray(); const row = rows[0]; if (row) { // If so, update it await db.templates.update(id, { name, config }); } else { // If not, create it await db.templates.add({ name, config }); } return await db.templates.orderBy("id").last(); } async function onDeleteTemplate(id: number) { await db.templates.delete(id); } function createDB() { const db = new ViperDatabase(); return db; } async function getSourcesFromBinance() { const res = await fetch( "https://www.binance.com/api/v3/exchangeInfo" ); const json = await res.json(); const sources: any = {}; for (const item of json.symbols) { sources[item.symbol] = { source: "BINANCE", name: item.symbol, maxItemsPerRequest: 500, models: { candle: { model_id: "candle", name: "Candle", label: `Binance:${item.symbol}`, }, }, }; } return sources; } const onRequestHistoricalData = async (requests: HistoricalDataRequest[], api: ViperCharts) => { for (const { source, name, timeframe, dataModels, start, end, } of requests) { if (source === "BINANCE") { const tf = { 1000: "1s", 60000: "1m", [60000 * 5]: "5m", [60000 * 15]: "15m", [60000 * 60]: "1h", [60000 * 60 * 4]: "4h", [60000 * 60 * 24]: "1d", }[timeframe]; const res = await fetch(`https://www.binance.com/api/v3/klines?symbol=${name}&interval=${tf}&startTime=${start}&endTime=${end}`); const json = await res.json(); for (const dataModel of dataModels) { if (dataModel === "candle") { const data = new Map() as DataUpdates; for (const item of json) { const [timestamp, open, high, low, close, volume] = item; const time = new Date(timestamp).getTime(); data.set(time as UTCTimestamp, { open: +open as Price, high: +high as Price, low: +low as Price, close: +close as Price, volume: +volume }); } api.addData({ source, name, timeframe, dataModel }, data); } } } } } function App() { const viperChartRef = useRef(null); const htmlRef = useRef(null); useEffect(() => { console.log("Initializing..."); async function initializeViper() { // The sources available to ViperCharts const sources = { BINANCE: await getSourcesFromBinance(), }; if (!htmlRef.current) return; if (viperChartRef.current) { // destroy if already initializing. In some instances useEffect is called twice. viperChartRef.current.destroy(); }; // document.getElementById("chart")!, viperChartRef.current = new ViperCharts({ element: htmlRef.current, sources, settings: JSON.parse(localStorage.getItem("settings")!) || {}, onRequestHistoricalData, onSaveViperSettings, onRequestTemplates, onSaveTemplate, onDeleteTemplate, }); } initializeViper(); return () => { viperChartRef.current?.destroy(); viperChartRef.current = null; } }, [htmlRef]) return (
); } export default App;