Skip to Content
TutorialsBasic Integration

Basic Integration Tutorial

Step-by-step guide to integrating GoCharting SDK into your application with built-in AutoFit and detailed explanations.

Prerequisites

Before starting this tutorial, ensure you have:

  • Node.js 16+ installed
  • Basic JavaScript/TypeScript knowledge
  • Modern web browser (Chrome 80+, Firefox 75+, Safari 13+, Edge 80+)
  • GoCharting SDK license key (use demo-550e8400-e29b-41d4-a716-446655440000 for testing)

Tutorial Overview

This tutorial will guide you through:

  1. Project Setup - Creating a new project and installing dependencies
  2. Basic Chart - Creating your first chart with built-in AutoFit
  3. Custom Datafeed - Implementing your own data source (composition-based)
  4. Configuration - Customizing chart appearance and behavior
  5. Event Handling - Responding to chart interactions
  6. Production Ready - Optimizing for production deployment

Step 1: Project Setup

Create New Project

# Create project directory mkdir my-trading-app cd my-trading-app # Initialize package.json npm init -y # Install GoCharting SDK npm install @gocharting/chart-sdk # Install peer dependencies npm install react react-dom luxon # Install development dependencies npm install --save-dev webpack webpack-cli webpack-dev-server html-webpack-plugin

Project Structure

my-trading-app/ ├── src/ │ ├── index.js # Main application file │ ├── datafeed.js # Custom datafeed object │ └── styles.css # Application styles ├── public/ │ └── index.html # HTML template ├── package.json └── webpack.config.js # Webpack configuration

Basic HTML Template

Create public/index.html:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>My Trading App - GoCharting SDK</title> <style> body { margin: 0; padding: 20px; font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; background-color: #1a1a1a; color: white; } .header { text-align: center; margin-bottom: 20px; } .controls { display: flex; gap: 15px; justify-content: center; margin-bottom: 20px; flex-wrap: wrap; } .controls select, .controls button { padding: 8px 12px; border: 1px solid #555; background-color: #2d2d2d; color: white; border-radius: 4px; cursor: pointer; } .controls select:hover, .controls button:hover { background-color: #3d3d3d; } #chart-container { width: 100%; height: 600px; border: 1px solid #444; border-radius: 8px; background-color: #1e1e1e; } .status { margin-top: 10px; padding: 10px; background-color: #2d2d2d; border-radius: 4px; text-align: center; } .loading { display: flex; align-items: center; justify-content: center; height: 600px; font-size: 18px; } .error { color: #ff6b6b; background-color: #2d1b1b; border: 1px solid #ff6b6b; } </style> </head> <body> <div class="header"> <h1> My Trading Application</h1> <p>Powered by GoCharting SDK</p> </div> <div class="controls"> <select id="symbol-selector"> <option value="NASDAQ:AAPL">Apple (AAPL)</option> <option value="NASDAQ:MSFT">Microsoft (MSFT)</option> <option value="NASDAQ:GOOGL">Google (GOOGL)</option> <option value="NYSE:TSLA">Tesla (TSLA)</option> <option value="BINANCE:BTCUSDT">Bitcoin (BTC)</option> </select> <select id="interval-selector"> <option value="1">1 Minute</option> <option value="5">5 Minutes</option> <option value="15">15 Minutes</option> <option value="60">1 Hour</option> <option value="1D" selected>1 Day</option> </select> <select id="theme-selector"> <option value="dark" selected>Dark Theme</option> <option value="light">Light Theme</option> </select> <button id="refresh-btn">Refresh Data</button> </div> <div id="chart-container"> <div class="loading">Loading chart...</div> </div> <div id="status" class="status">Initializing...</div> <script src="bundle.js"></script> </body> </html>

Step 2: Create Custom Datafeed

Create src/datafeed.js:

// Create a simple datafeed object (no inheritance needed) export const myDatafeed = { initialized: false, cache: new Map(), // Optional initialization method async _init(datacenter, token) { this.datacenter = datacenter; this.token = token; this.initialized = true; console.log(" Datafeed initialized"); }, async getBars(symbolInfo, resolution, periodParams) { const { from, to, firstDataRequest } = periodParams; console.log( ` Fetching bars for ${symbolInfo.name}, resolution: ${resolution}` ); try { // Check cache first const cacheKey = `${symbolInfo.name}-${resolution}-${from}-${to}`; if (this.cache.has(cacheKey)) { console.log(" Returning cached data"); return this.cache.get(cacheKey); } // For demo purposes, we'll generate sample data // In production, replace this with your actual API call const bars = this.generateSampleData( symbolInfo, from, to, resolution ); const result = { bars: bars, meta: { noData: bars.length === 0, }, }; // Cache the result this.cache.set(cacheKey, result); console.log(` Fetched ${bars.length} bars`); return result; } catch (error) { console.error(" Error fetching bars:", error); return { bars: [], meta: { noData: true }, }; } }, async resolveSymbol(symbolName, onResolve, onError) { console.log(`Resolving symbol: ${symbolName}`); try { // "NASDAQ:AAPL" or "BYBIT:FUTURE:BTCUSDT" const parts = symbolName.split(":"); const exchange = parts[0] || "NASDAQ"; const symbol = parts[parts.length - 1]; const segment = parts.length >= 3 ? parts[1] : this.getDefaultSegment(exchange); const name = this.getSymbolDescription(symbol); const assetType = this.getAssetType(exchange); const zone = this.getTimezone(exchange); const isCrypto = assetType === "CRYPTO"; const validIntervals = isCrypto ? ["1m", "5m", "15m", "30m", "1h", "4h", "1D", "1W", "1M"] : ["1m", "5m", "15m", "30m", "1h", "1D", "1W", "1M"]; // GoCharting security / SymbolInfo shape const symbolInfo = { exchange, segment, symbol, name, asset_type: assetType, source_id: symbol, pair: isCrypto ? { from: symbol.replace(/USDT$|USD$/i, ""), to: "USDT" } : undefined, tradeable: true, is_index: false, is_formula: false, delay_seconds: 0, data_status: "streaming", industry: "technology", symbol_logo_urls: [], contract_size: 1, tick_size: isCrypto ? 0.1 : 0.01, display_tick_size: isCrypto ? 1 : 0.01, volume_size_increment: isCrypto ? 0.001 : 1, max_tick_precision: isCrypto ? 1 : 2, max_volume_precision: isCrypto ? 3 : 0, quote_currency: isCrypto ? "USDT" : "USD", future_type: segment === "FUTURE" ? "PERP" : undefined, supports: { footprint: isCrypto }, exchange_info: { name: exchange.toLowerCase(), code: exchange, country_cd: "US", zone, has_unique_trade_id: true, logo_url: undefined, holidays: null, hours: isCrypto ? Array.from({ length: 7 }, () => ({ open: true })) : [ { open: false }, // Sunday { open: true }, { open: true }, { open: true }, { open: true }, { open: true }, { open: false }, // Saturday ], contains_ambiguous_symbols: false, valid_intervals: validIntervals, }, // Compatibility fields ticker: symbol, full_name: `${exchange}:${segment}:${symbol}`, description: name, type: this.getSymbolType(exchange), session: this.getSessionHours(exchange), timezone: zone, has_intraday: true, has_daily: true, supported_resolutions: validIntervals, }; console.log("Symbol resolved:", symbolInfo); onResolve(symbolInfo); } catch (error) { console.error("Error resolving symbol:", error); onError("Symbol not found"); } }, async searchSymbols(userInput, exchange, symbolType, onResultReadyCallback) { console.log(` Searching symbols: ${userInput}`); // Sample symbols for demo const allSymbols = [ { symbol: "AAPL", full_name: "NASDAQ:AAPL", description: "Apple Inc.", exchange: "NASDAQ", ticker: "AAPL", type: "stock", }, { symbol: "MSFT", full_name: "NASDAQ:MSFT", description: "Microsoft Corporation", exchange: "NASDAQ", ticker: "MSFT", type: "stock", }, { symbol: "GOOGL", full_name: "NASDAQ:GOOGL", description: "Alphabet Inc.", exchange: "NASDAQ", ticker: "GOOGL", type: "stock", }, { symbol: "TSLA", full_name: "NYSE:TSLA", description: "Tesla Inc.", exchange: "NYSE", ticker: "TSLA", type: "stock", }, { symbol: "BTCUSDT", full_name: "BINANCE:BTCUSDT", description: "Bitcoin / Tether", exchange: "BINANCE", ticker: "BTCUSDT", type: "crypto", }, ]; const filteredSymbols = allSymbols.filter( (symbol) => symbol.symbol.toLowerCase().includes(userInput.toLowerCase()) || symbol.description .toLowerCase() .includes(userInput.toLowerCase()) ); console.log(` Found ${filteredSymbols.length} symbols`); // Deliver results as an object: { searchInProgress, items } onResultReadyCallback({ searchInProgress: false, items: filteredSymbols }); }, // Helper methods generateSampleData(symbolInfo, from, to, resolution) { const bars = []; const interval = this.getIntervalMs(resolution); const startTime = from * 1000; const endTime = to * 1000; let currentTime = startTime; let price = 100 + Math.random() * 100; // Random starting price while (currentTime <= endTime) { const change = (Math.random() - 0.5) * 5; // Random price change const open = price; const close = Math.max(0.01, price + change); const high = Math.max(open, close) + Math.random() * 3; const low = Math.min(open, close) - Math.random() * 3; const volume = Math.floor(Math.random() * 1000000) + 100000; bars.push({ time: currentTime, open: Math.round(open * 100) / 100, high: Math.round(high * 100) / 100, low: Math.round(Math.max(0.01, low) * 100) / 100, close: Math.round(close * 100) / 100, volume: volume, }); price = close; currentTime += interval; } return bars; }, getIntervalMs(resolution) { const intervals = { 1: 60 * 1000, // 1 minute 5: 5 * 60 * 1000, // 5 minutes 15: 15 * 60 * 1000, // 15 minutes 30: 30 * 60 * 1000, // 30 minutes 60: 60 * 60 * 1000, // 1 hour 240: 4 * 60 * 60 * 1000, // 4 hours "1D": 24 * 60 * 60 * 1000, // 1 day "1W": 7 * 24 * 60 * 60 * 1000, // 1 week "1M": 30 * 24 * 60 * 60 * 1000, // 1 month (approximate) }; return intervals[resolution] || intervals["1D"]; }, getSymbolDescription(ticker) { const descriptions = { AAPL: "Apple Inc.", MSFT: "Microsoft Corporation", GOOGL: "Alphabet Inc.", TSLA: "Tesla Inc.", BTCUSDT: "Bitcoin / Tether", }; return descriptions[ticker] || `${ticker} Stock`; }, getSymbolType(exchange) { const types = { NASDAQ: "stock", NYSE: "stock", BINANCE: "crypto", BYBIT: "crypto", FOREX: "forex", }; return types[exchange] || "stock"; }, getAssetType(exchange) { const types = { NASDAQ: "EQUITY", NYSE: "EQUITY", BINANCE: "CRYPTO", BYBIT: "CRYPTO", FOREX: "FOREX", }; return types[exchange] || "EQUITY"; }, getDefaultSegment(exchange) { const segments = { NASDAQ: "EQUITY", NYSE: "EQUITY", BINANCE: "SPOT", BYBIT: "FUTURE", FOREX: "SPOT", }; return segments[exchange] || "EQUITY"; }, getSessionHours(exchange) { const sessions = { NASDAQ: "0930-1600", NYSE: "0930-1600", BINANCE: "24x7", BYBIT: "24x7", FOREX: "24x5", }; return sessions[exchange] || "0930-1600"; }, getTimezone(exchange) { const timezones = { NASDAQ: "America/New_York", NYSE: "America/New_York", BINANCE: "Etc/UTC", BYBIT: "Etc/UTC", FOREX: "Etc/UTC", }; return timezones[exchange] || "America/New_York"; } }

Step 3: Main Application

Create src/index.js:

import { createChart } from "@gocharting/chart-sdk"; import { myDatafeed } from "./datafeed.js"; import "./styles.css"; class TradingApp { constructor() { this.chart = null; this.dataProvider = null; this.currentSymbol = "NASDAQ:AAPL"; this.currentInterval = "1D"; this.currentTheme = "dark"; this.init(); } async init() { try { this.updateStatus("Initializing application..."); // Use the datafeed object (composition, no `new`) this.dataProvider = myDatafeed; // Create chart await this.createChart(); // Set up event listeners this.setupEventListeners(); this.updateStatus("Application ready"); } catch (error) { console.error(" Failed to initialize app:", error); this.updateStatus("Failed to initialize application", "error"); } } async createChart() { this.updateStatus("Creating chart..."); // Use the simplified createChart function with a FLAT config. // Only real createChart keys are valid — there is no nested // `config: { chart, ui, performance }` object, and `theme` is a // STRING ("light" | "dark"), not an object. this.chart = createChart("#chart-container", { symbol: this.currentSymbol, interval: this.currentInterval, datafeed: this.dataProvider, licenseKey: "demo-550e8400-e29b-41d4-a716-446655440000", // Replace with your license key theme: this.currentTheme, // "light" or "dark" autosize: true, // built-in AutoFit (default) trading: { enableTrading: false, // Disable trading for basic integration }, // `onReady` is a CONFIG callback (not an instance method) onReady: () => { console.log(" Chart is ready"); this.updateStatus( `Chart loaded: ${this.currentSymbol} (${this.currentInterval})` ); }, // `onError` is a CONFIG callback (not an instance method) onError: (error) => { console.error(" Chart error:", error); this.updateStatus(`Error: ${error.message}`, "error"); }, // All chart events arrive through appCallback as a single object appCallback: ({ eventType, message }) => { console.log(" Chart event:", eventType, message); }, }); } setupEventListeners() { // Symbol selector document .getElementById("symbol-selector") .addEventListener("change", (e) => { this.changeSymbol(e.target.value); }); // Interval selector document .getElementById("interval-selector") .addEventListener("change", (e) => { this.changeInterval(e.target.value); }); // Theme selector document .getElementById("theme-selector") .addEventListener("change", (e) => { this.changeTheme(e.target.value); }); // Refresh button document.getElementById("refresh-btn").addEventListener("click", () => { this.refreshChart(); }); } async changeSymbol(symbol) { if (this.chart && symbol !== this.currentSymbol) { this.updateStatus(`Loading ${symbol}...`); try { await this.chart.setSymbol(symbol); this.currentSymbol = symbol; } catch (error) { console.error(" Failed to change symbol:", error); this.updateStatus("Failed to change symbol", "error"); } } } async changeInterval(interval) { if (this.chart && interval !== this.currentInterval) { this.updateStatus(`Changing interval to ${interval}...`); try { await this.chart.setInterval(interval); this.currentInterval = interval; } catch (error) { console.error(" Failed to change interval:", error); this.updateStatus("Failed to change interval", "error"); } } } changeTheme(theme) { if (this.chart && theme !== this.currentTheme) { this.updateStatus(`Changing theme to ${theme}...`); try { this.chart.setTheme(theme); this.currentTheme = theme; this.updateBodyTheme(theme); } catch (error) { console.error(" Failed to change theme:", error); this.updateStatus("Failed to change theme", "error"); } } } refreshChart() { if (this.chart) { this.updateStatus("Refreshing chart data..."); // Clear the datafeed cache, then re-set the current symbol so the // chart re-runs getBars and pulls fresh data (there is no // `refreshData()` instance method). this.dataProvider.cache.clear(); this.chart.setSymbol(this.currentSymbol); } } updateStatus(message, type = "info") { const statusElement = document.getElementById("status"); statusElement.textContent = message; statusElement.className = `status ${type}`; console.log(` Status: ${message}`); } updateSymbolSelector() { const selector = document.getElementById("symbol-selector"); selector.value = this.currentSymbol; } updateIntervalSelector() { const selector = document.getElementById("interval-selector"); selector.value = this.currentInterval; } updateBodyTheme(theme) { document.body.className = `theme-${theme}`; } destroy() { if (this.chart) { this.chart.destroy(); this.chart = null; } } } // Initialize app when DOM is loaded document.addEventListener("DOMContentLoaded", () => { console.log(" Starting Trading App..."); window.tradingApp = new TradingApp(); }); // Cleanup on page unload window.addEventListener("beforeunload", () => { if (window.tradingApp) { window.tradingApp.destroy(); } });

Step 4: Styling

Create src/styles.css:

/* Additional styles for the application */ .theme-light { background-color: #ffffff; color: #333333; } .theme-light .controls select, .theme-light .controls button { background-color: #f5f5f5; color: #333333; border-color: #ddd; } .theme-light #chart-container { background-color: #ffffff; border-color: #ddd; } .theme-light .status { background-color: #f5f5f5; color: #333333; } /* Loading animation */ .loading::after { content: ""; display: inline-block; width: 20px; height: 20px; border: 3px solid #333; border-radius: 50%; border-top-color: #00d4aa; animation: spin 1s ease-in-out infinite; margin-left: 10px; } @keyframes spin { to { transform: rotate(360deg); } } /* Responsive design */ @media (max-width: 768px) { .controls { flex-direction: column; align-items: center; } .controls select, .controls button { width: 200px; } #chart-container { height: 400px; } }

Step 5: Build Configuration

Create webpack.config.js:

const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { mode: "development", entry: "./src/index.js", output: { path: path.resolve(__dirname, "dist"), filename: "bundle.js", clean: true, }, module: { rules: [ { test: /\.css$/i, use: ["style-loader", "css-loader"], }, { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: ["@babel/preset-env"], }, }, }, ], }, plugins: [ new HtmlWebpackPlugin({ template: "./public/index.html", }), ], devServer: { static: "./dist", port: 3000, open: true, hot: true, }, resolve: { fallback: { fs: false, path: false, }, }, };

Step 6: Run Your Application

Add scripts to package.json:

{ "scripts": { "start": "webpack serve", "build": "webpack --mode=production", "dev": "webpack serve --mode=development" } }

Run the application:

# Start development server npm start # Or build for production npm run build

Next Steps

Congratulations! You now have a working GoCharting SDK integration. Here’s what to explore next:

  1. Trading Integration - Add real trading capabilities
  2. Configuration Guide - Customize chart appearance

Ready to add more features? Check out our advanced tutorials!

Last updated on