π§ 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-446655440000for testing)
π― Tutorial Overview
This tutorial will guide you through:
- Project Setup - Creating a new project and installing dependencies
- Basic Chart - Creating your first chart with built-in AutoFit
- Custom Datafeed - Implementing your own data source (composition-based)
- Configuration - Customizing chart appearance and behavior
- Event Handling - Responding to chart interactions
- Production Ready - Optimizing for production deployment
β¨ Whatβs New in This Version:
- π Built-in AutoFit - No more sizing headaches, works everywhere automatically
- π§ Composition-based Datafeed - Simpler, cleaner architecture
- π± Responsive by Default - Charts adapt to any screen size
- π‘οΈ Bulletproof Error Handling - Graceful failures with helpful messages
π¦ 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-pluginProject Structure
my-trading-app/
βββ src/
β βββ index.js # Main application file
β βββ dataProvider.js # Custom data provider
β βββ styles.css # Application styles
βββ public/
β βββ index.html # HTML template
βββ package.json
βββ webpack.config.js # Webpack configurationBasic 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 {
// Parse symbol name (e.g., "NASDAQ:AAPL")
const [exchange, ticker] = symbolName.split(":");
// Create symbol info
const symbolInfo = {
name: symbolName,
full_name: symbolName,
description: this.getSymbolDescription(ticker),
type: this.getSymbolType(exchange),
session: this.getSessionHours(exchange),
timezone: this.getTimezone(exchange),
ticker: ticker,
has_intraday: true,
has_daily: true,
supported_resolutions: [
"1",
"5",
"15",
"30",
"60",
"240",
"1D",
"1W",
"1M",
],
// Price precision (SDK calculates pricescale internally)
max_tick_precision: 2,
tick_size: 0.01,
display_tick_size: 0.01,
};
console.log("β
Symbol resolved:", symbolInfo);
onResolve(symbolInfo);
} catch (error) {
console.error("β Error resolving symbol:", error);
onError("Symbol not found");
}
}
async searchSymbols(userInput, callback) {
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`);
callback(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",
FOREX: "forex",
};
return types[exchange] || "stock";
}
getSessionHours(exchange) {
const sessions = {
NASDAQ: "0930-1600",
NYSE: "0930-1600",
BINANCE: "24x7",
FOREX: "24x5",
};
return sessions[exchange] || "0930-1600";
}
getTimezone(exchange) {
const timezones = {
NASDAQ: "America/New_York",
NYSE: "America/New_York",
BINANCE: "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 { MyDataProvider } from "./dataProvider.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...");
// Create data provider
this.dataProvider = new MyDataProvider();
// 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...");
const chartConfig = {
theme: { name: this.currentTheme },
chart: {
chartType: "candlestick",
animations: true,
grid: {
horizontal: true,
vertical: true,
},
},
ui: {
showToolbar: true,
showVolumePanel: true,
showDrawingTools: true,
},
trading: {
enableTrading: false, // Disable trading for basic integration
},
performance: {
maxCandles: 5000,
animations: true,
},
};
// π Use the simplified createChart function
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
config: chartConfig,
});
// Set up chart event handlers
this.setupChartEvents();
}
setupChartEvents() {
this.chart.onChartReady(() => {
console.log("β
Chart is ready");
this.updateStatus(
`Chart loaded: ${this.currentSymbol} (${this.currentInterval})`
);
});
this.chart.onSymbolChange((symbolInfo) => {
console.log("π Symbol changed:", symbolInfo.name);
this.currentSymbol = symbolInfo.name;
this.updateSymbolSelector();
this.updateStatus(`Symbol: ${symbolInfo.description}`);
});
this.chart.onIntervalChange((interval) => {
console.log("β±οΈ Interval changed:", interval);
this.currentInterval = interval;
this.updateIntervalSelector();
this.updateStatus(`Interval changed to ${interval}`);
});
this.chart.onError((error) => {
console.error("β Chart error:", error);
this.updateStatus(`Error: ${error.message}`, "error");
});
}
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 cache and refresh
this.dataProvider.cache.clear();
this.chart.refreshData();
}
}
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:
- Trading Integration - Add real trading capabilities
- Configuration Guide - Customize chart appearance
π Related Documentation
- API Reference - Complete API documentation
- Configuration Guide - Chart configuration options
- Examples - Framework-specific examples
Ready to add more features? Check out our advanced tutorials!
Last updated on